diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/docker-compose.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/docker-compose.adoc
index 432a06e4c..50745c1e3 100644
--- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/docker-compose.adoc
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/docker-compose.adoc
@@ -43,9 +43,6 @@ The following service connection factories are provided in the `spring-ai-spring
| `QdrantConnectionDetails`
| Containers named `qdrant/qdrant`
-| `RedisConnectionDetails`
-| Containers named `redis/redis-stack-server`, `redis/redis-stack`
-
| `TypesenseConnectionDetails`
| Containers named `typesense/typesense`
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/testcontainers.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/testcontainers.adoc
index c1966544a..e646be203 100644
--- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/testcontainers.adoc
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/testcontainers.adoc
@@ -46,9 +46,6 @@ The following service connection factories are provided in the `spring-ai-spring
| `QdrantConnectionDetails`
| Containers of type `QdrantContainer`
-| `RedisConnectionDetails`
-| Containers of type `RedisStackContainer`
-
| `TypesenseConnectionDetails`
| Containers named "typesense/typesense"
diff --git a/spring-ai-spring-boot-autoconfigure/pom.xml b/spring-ai-spring-boot-autoconfigure/pom.xml
index 6ac477d1a..7a21efbfd 100644
--- a/spring-ai-spring-boot-autoconfigure/pom.xml
+++ b/spring-ai-spring-boot-autoconfigure/pom.xml
@@ -202,6 +202,12 @@
true
+
+ org.springframework.data
+ spring-data-redis
+ true
+
+
redis.clients
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisConnectionDetails.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisConnectionDetails.java
deleted file mode 100644
index df98d9522..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisConnectionDetails.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2023 - 2024 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.ai.autoconfigure.vectorstore.redis;
-
-import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
-
-/**
- * @author Eddú Meléndez
- */
-public interface RedisConnectionDetails extends ConnectionDetails {
-
- String getUri();
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java
index 4f203ed19..98b047153 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java
@@ -19,53 +19,38 @@ import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.RedisVectorStore;
import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig;
import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
+import redis.clients.jedis.JedisPooled;
/**
* @author Christian Tzolov
* @author Eddú Meléndez
*/
-@AutoConfiguration
-@ConditionalOnClass({ RedisVectorStore.class, EmbeddingModel.class })
+@AutoConfiguration(after = RedisAutoConfiguration.class)
+@ConditionalOnClass({ JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class })
+@ConditionalOnBean(JedisConnectionFactory.class)
@EnableConfigurationProperties(RedisVectorStoreProperties.class)
public class RedisVectorStoreAutoConfiguration {
- @Bean
- @ConditionalOnMissingBean(RedisConnectionDetails.class)
- public PropertiesRedisConnectionDetails redisConnectionDetails(RedisVectorStoreProperties properties) {
- return new PropertiesRedisConnectionDetails(properties);
- }
-
@Bean
@ConditionalOnMissingBean
public RedisVectorStore vectorStore(EmbeddingModel embeddingModel, RedisVectorStoreProperties properties,
- RedisConnectionDetails redisConnectionDetails) {
+ JedisConnectionFactory jedisConnectionFactory) {
var config = RedisVectorStoreConfig.builder()
- .withURI(redisConnectionDetails.getUri())
.withIndexName(properties.getIndex())
.withPrefix(properties.getPrefix())
.build();
- return new RedisVectorStore(config, embeddingModel, properties.isInitializeSchema());
- }
-
- static class PropertiesRedisConnectionDetails implements RedisConnectionDetails {
-
- private final RedisVectorStoreProperties properties;
-
- public PropertiesRedisConnectionDetails(RedisVectorStoreProperties properties) {
- this.properties = properties;
- }
-
- @Override
- public String getUri() {
- return this.properties.getUri();
- }
-
+ return new RedisVectorStore(config, embeddingModel,
+ new JedisPooled(jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort()),
+ properties.isInitializeSchema());
}
}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreProperties.java
index eb79a1a40..4799afb80 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreProperties.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreProperties.java
@@ -20,26 +20,17 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Julien Ruaux
+ * @author Eddú Meléndez
*/
@ConfigurationProperties(RedisVectorStoreProperties.CONFIG_PREFIX)
public class RedisVectorStoreProperties extends CommonVectorStoreProperties {
public static final String CONFIG_PREFIX = "spring.ai.vectorstore.redis";
- private String uri = "redis://localhost:6379";
-
private String index = "default-index";
private String prefix = "default:";
- public String getUri() {
- return this.uri;
- }
-
- public void setUri(String uri) {
- this.uri = uri;
- }
-
public String getIndex() {
return this.index;
}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfigurationIT.java
index b5721db77..78122a520 100644
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfigurationIT.java
+++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfigurationIT.java
@@ -28,6 +28,7 @@ import org.springframework.ai.transformers.TransformersEmbeddingModel;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -38,6 +39,7 @@ import com.redis.testcontainers.RedisStackContainer;
/**
* @author Julien Ruaux
+ * @author Eddú Meléndez
*/
@Testcontainers
class RedisVectorStoreAutoConfigurationIT {
@@ -52,32 +54,31 @@ class RedisVectorStoreAutoConfigurationIT {
ResourceUtils.getText("classpath:/test/data/great.depression.txt"), Map.of("depression", "bad")));
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(RedisVectorStoreAutoConfiguration.class))
+ .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class, RedisVectorStoreAutoConfiguration.class))
.withUserConfiguration(Config.class)
- .withPropertyValues("spring.ai.vectorstore.redis.index=myIdx")
- .withPropertyValues("spring.ai.vectorstore.redis.prefix=doc:");
+ .withPropertyValues("spring.data.redis.url=" + redisContainer.getRedisURI(),
+ "spring.ai.vectorstore.redis.index=myIdx", "spring.ai.vectorstore.redis.prefix=doc:");
@Test
void addAndSearch() {
- contextRunner.withPropertyValues("spring.ai.vectorstore.redis.uri=" + redisContainer.getRedisURI())
- .run(context -> {
- VectorStore vectorStore = context.getBean(VectorStore.class);
- vectorStore.add(documents);
+ contextRunner.run(context -> {
+ VectorStore vectorStore = context.getBean(VectorStore.class);
+ vectorStore.add(documents);
- List results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
+ List results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
- assertThat(results).hasSize(1);
- Document resultDoc = results.get(0);
- assertThat(resultDoc.getId()).isEqualTo(documents.get(0).getId());
- assertThat(resultDoc.getContent()).contains(
- "Spring AI provides abstractions that serve as the foundation for developing AI applications.");
+ assertThat(results).hasSize(1);
+ Document resultDoc = results.get(0);
+ assertThat(resultDoc.getId()).isEqualTo(documents.get(0).getId());
+ assertThat(resultDoc.getContent()).contains(
+ "Spring AI provides abstractions that serve as the foundation for developing AI applications.");
- // Remove all documents from the store
- vectorStore.delete(documents.stream().map(doc -> doc.getId()).toList());
+ // Remove all documents from the store
+ vectorStore.delete(documents.stream().map(doc -> doc.getId()).toList());
- results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
- assertThat(results).isEmpty();
- });
+ results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
+ assertThat(results).isEmpty();
+ });
}
@Configuration(proxyBeanMethods = false)
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStorePropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStorePropertiesTests.java
index 14852b7e8..cb3691031 100644
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStorePropertiesTests.java
+++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStorePropertiesTests.java
@@ -21,13 +21,13 @@ import org.junit.jupiter.api.Test;
/**
* @author Julien Ruaux
+ * @author Eddú Meléndez
*/
class RedisVectorStorePropertiesTests {
@Test
void defaultValues() {
var props = new RedisVectorStoreProperties();
- assertThat(props.getUri()).isEqualTo("redis://localhost:6379");
assertThat(props.getIndex()).isEqualTo("default-index");
assertThat(props.getPrefix()).isEqualTo("default:");
}
@@ -35,11 +35,9 @@ class RedisVectorStorePropertiesTests {
@Test
void customValues() {
var props = new RedisVectorStoreProperties();
- props.setUri("redis://redis.com:12345");
props.setIndex("myIdx");
props.setPrefix("doc:");
- assertThat(props.getUri()).isEqualTo("redis://redis.com:12345");
assertThat(props.getIndex()).isEqualTo("myIdx");
assertThat(props.getPrefix()).isEqualTo("doc:");
}
diff --git a/spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/redis/RedisDockerComposeConnectionDetailsFactory.java b/spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/redis/RedisDockerComposeConnectionDetailsFactory.java
deleted file mode 100644
index b0266aca7..000000000
--- a/spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/redis/RedisDockerComposeConnectionDetailsFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2023 - 2024 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.ai.docker.compose.service.connection.redis;
-
-import org.springframework.ai.autoconfigure.vectorstore.redis.RedisConnectionDetails;
-import org.springframework.boot.docker.compose.core.RunningService;
-import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory;
-import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource;
-
-/**
- * @author Eddú Meléndez
- */
-class RedisDockerComposeConnectionDetailsFactory extends DockerComposeConnectionDetailsFactory {
-
- private static final String[] REDIS_IMAGE_NAMES = { "redis/redis-stack", "redis/redis-stack-server" };
-
- private static final int REDIS_PORT = 6379;
-
- protected RedisDockerComposeConnectionDetailsFactory() {
- super(REDIS_IMAGE_NAMES);
- }
-
- @Override
- protected RedisConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) {
- return new RedisDockerComposeConnectionDetails(source.getRunningService());
- }
-
- /**
- * {@link RedisConnectionDetails} backed by a {@code Redis} {@link RunningService}.
- */
- static class RedisDockerComposeConnectionDetails extends DockerComposeConnectionDetails
- implements RedisConnectionDetails {
-
- private final String uri;
-
- RedisDockerComposeConnectionDetails(RunningService service) {
- super(service);
- this.uri = "redis://" + service.host() + ":" + service.ports().get(REDIS_PORT);
- }
-
- @Override
- public String getUri() {
- return this.uri;
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-docker-compose/src/main/resources/META-INF/spring.factories b/spring-ai-spring-boot-docker-compose/src/main/resources/META-INF/spring.factories
index c0ee2af6b..218716860 100644
--- a/spring-ai-spring-boot-docker-compose/src/main/resources/META-INF/spring.factories
+++ b/spring-ai-spring-boot-docker-compose/src/main/resources/META-INF/spring.factories
@@ -3,6 +3,5 @@ org.springframework.ai.docker.compose.service.connection.chroma.ChromaDockerComp
org.springframework.ai.docker.compose.service.connection.ollama.OllamaDockerComposeConnectionDetailsFactory,\
org.springframework.ai.docker.compose.service.connection.opensearch.OpenSearchDockerComposeConnectionDetailsFactory,\
org.springframework.ai.docker.compose.service.connection.qdrant.QdrantDockerComposeConnectionDetailsFactory,\
-org.springframework.ai.docker.compose.service.connection.redis.RedisDockerComposeConnectionDetailsFactory,\
org.springframework.ai.docker.compose.service.connection.typesense.TypesenseDockerComposeConnectionDetailsFactory,\
org.springframework.ai.docker.compose.service.connection.weaviate.WeaviateDockerComposeConnectionDetailsFactory
\ No newline at end of file
diff --git a/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/redis/RedisStackDockerComposeConnectionDetailsFactoryTests.java b/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/redis/RedisStackDockerComposeConnectionDetailsFactoryTests.java
deleted file mode 100644
index 70ef16c29..000000000
--- a/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/redis/RedisStackDockerComposeConnectionDetailsFactoryTests.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2023 - 2024 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.ai.docker.compose.service.connection.redis;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.autoconfigure.vectorstore.redis.RedisConnectionDetails;
-import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIntegrationTests;
-import org.testcontainers.utility.DockerImageName;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-class RedisStackDockerComposeConnectionDetailsFactoryTests extends AbstractDockerComposeIntegrationTests {
-
- RedisStackDockerComposeConnectionDetailsFactoryTests() {
- super("redis-compose.yaml", DockerImageName.parse("redis/redis-stack"));
- }
-
- @Test
- void runCreatesConnectionDetails() {
- RedisConnectionDetails connectionDetails = run(RedisConnectionDetails.class);
- assertThat(connectionDetails.getUri()).startsWith("redis://");
- }
-
-}
diff --git a/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/redis/RedisStackServerDockerComposeConnectionDetailsFactoryTests.java b/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/redis/RedisStackServerDockerComposeConnectionDetailsFactoryTests.java
deleted file mode 100644
index c7df8a8c7..000000000
--- a/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/redis/RedisStackServerDockerComposeConnectionDetailsFactoryTests.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2023 - 2024 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.ai.docker.compose.service.connection.redis;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.autoconfigure.vectorstore.redis.RedisConnectionDetails;
-import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIntegrationTests;
-import org.testcontainers.utility.DockerImageName;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-class RedisStackServerDockerComposeConnectionDetailsFactoryTests extends AbstractDockerComposeIntegrationTests {
-
- RedisStackServerDockerComposeConnectionDetailsFactoryTests() {
- super("redis-compose.yaml", DockerImageName.parse("redis/redis-stack-server"));
- }
-
- @Test
- void runCreatesConnectionDetails() {
- RedisConnectionDetails connectionDetails = run(RedisConnectionDetails.class);
- assertThat(connectionDetails.getUri()).startsWith("redis://");
- }
-
-}
diff --git a/spring-ai-spring-boot-docker-compose/src/test/resources/org/springframework/ai/docker/compose/service/connection/redis/redis-compose.yaml b/spring-ai-spring-boot-docker-compose/src/test/resources/org/springframework/ai/docker/compose/service/connection/redis/redis-compose.yaml
deleted file mode 100644
index 9511c464d..000000000
--- a/spring-ai-spring-boot-docker-compose/src/test/resources/org/springframework/ai/docker/compose/service/connection/redis/redis-compose.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-services:
- redis:
- image: '{imageName}'
- ports:
- - '6379'
diff --git a/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java b/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java
deleted file mode 100644
index 72007c4e6..000000000
--- a/spring-ai-spring-boot-testcontainers/src/main/java/org/springframework/ai/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2023 - 2024 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.ai.testcontainers.service.connection.redis;
-
-import com.redis.testcontainers.RedisStackContainer;
-import org.springframework.ai.autoconfigure.vectorstore.redis.RedisConnectionDetails;
-import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
-import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
-
-/**
- * @author Eddú Meléndez
- */
-class RedisContainerConnectionDetailsFactory
- extends ContainerConnectionDetailsFactory {
-
- @Override
- public RedisConnectionDetails getContainerConnectionDetails(ContainerConnectionSource source) {
- return new RedisContainerConnectionDetails(source);
- }
-
- /**
- * {@link RedisConnectionDetails} backed by a {@link ContainerConnectionSource}.
- */
- private static final class RedisContainerConnectionDetails extends ContainerConnectionDetails
- implements RedisConnectionDetails {
-
- private RedisContainerConnectionDetails(ContainerConnectionSource source) {
- super(source);
- }
-
- @Override
- public String getUri() {
- return getContainer().getRedisURI();
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-testcontainers/src/main/resources/META-INF/spring.factories b/spring-ai-spring-boot-testcontainers/src/main/resources/META-INF/spring.factories
index 2eab8182b..8fa2b068b 100644
--- a/spring-ai-spring-boot-testcontainers/src/main/resources/META-INF/spring.factories
+++ b/spring-ai-spring-boot-testcontainers/src/main/resources/META-INF/spring.factories
@@ -4,6 +4,5 @@ org.springframework.ai.testcontainers.service.connection.milvus.MilvusContainerC
org.springframework.ai.testcontainers.service.connection.ollama.OllamaContainerConnectionDetailsFactory,\
org.springframework.ai.testcontainers.service.connection.opensearch.OpenSearchContainerConnectionDetailsFactory,\
org.springframework.ai.testcontainers.service.connection.qdrant.QdrantContainerConnectionDetailsFactory,\
-org.springframework.ai.testcontainers.service.connection.redis.RedisContainerConnectionDetailsFactory,\
org.springframework.ai.testcontainers.service.connection.typesense.TypesenseContainerConnectionDetailsFactory,\
org.springframework.ai.testcontainers.service.connection.weaviate.WeaviateContainerConnectionDetailsFactory
diff --git a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactoryTest.java b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactoryTest.java
deleted file mode 100644
index aa53e64fd..000000000
--- a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactoryTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2023 - 2024 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.ai.testcontainers.service.connection.redis;
-
-import com.redis.testcontainers.RedisStackContainer;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.ResourceUtils;
-import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration;
-import org.springframework.ai.document.Document;
-import org.springframework.ai.embedding.EmbeddingModel;
-import org.springframework.ai.transformers.TransformersEmbeddingModel;
-import org.springframework.ai.vectorstore.SearchRequest;
-import org.springframework.ai.vectorstore.VectorStore;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.junit.jupiter.Testcontainers;
-
-import java.util.List;
-import java.util.Map;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@SpringJUnitConfig
-@Testcontainers
-@TestPropertySource(
- properties = { "spring.ai.vectorstore.redis.index=myIdx", "spring.ai.vectorstore.redis.prefix=doc:" })
-class RedisContainerConnectionDetailsFactoryTest {
-
- @Container
- @ServiceConnection
- static RedisStackContainer redisContainer = new RedisStackContainer(
- RedisStackContainer.DEFAULT_IMAGE_NAME.withTag(RedisStackContainer.DEFAULT_TAG));
-
- private List documents = List.of(
- new Document(ResourceUtils.getText("classpath:/test/data/spring.ai.txt"), Map.of("spring", "great")),
- new Document(ResourceUtils.getText("classpath:/test/data/time.shelter.txt")), new Document(
- ResourceUtils.getText("classpath:/test/data/great.depression.txt"), Map.of("depression", "bad")));
-
- @Autowired
- private VectorStore vectorStore;
-
- @Test
- void addAndSearch() {
- vectorStore.add(documents);
-
- List results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
-
- assertThat(results).hasSize(1);
- Document resultDoc = results.get(0);
- assertThat(resultDoc.getId()).isEqualTo(documents.get(0).getId());
- assertThat(resultDoc.getContent())
- .contains("Spring AI provides abstractions that serve as the foundation for developing AI applications.");
-
- // Remove all documents from the store
- vectorStore.delete(documents.stream().map(doc -> doc.getId()).toList());
-
- results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
- assertThat(results).isEmpty();
- }
-
- @Configuration(proxyBeanMethods = false)
- @ImportAutoConfiguration(RedisVectorStoreAutoConfiguration.class)
- static class Config {
-
- @Bean
- public EmbeddingModel embeddingModel() {
- return new TransformersEmbeddingModel();
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/vector-stores/spring-ai-redis-store/pom.xml b/vector-stores/spring-ai-redis-store/pom.xml
index 729ba0fe8..aee0919a0 100644
--- a/vector-stores/spring-ai-redis-store/pom.xml
+++ b/vector-stores/spring-ai-redis-store/pom.xml
@@ -34,6 +34,11 @@
${parent.version}
+
+ org.springframework.data
+ spring-data-redis
+
+
redis.clients
jedis
diff --git a/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java b/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java
index e28589618..990bee6f5 100644
--- a/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java
+++ b/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java
@@ -67,6 +67,7 @@ import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm;
*
* @author Julien Ruaux
* @author Christian Tzolov
+ * @author Eddú Meléndez
* @see VectorStore
* @see RedisVectorStoreConfig
* @see EmbeddingModel
@@ -100,8 +101,6 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
*/
public static final class RedisVectorStoreConfig {
- private final String uri;
-
private final String indexName;
private final String prefix;
@@ -119,7 +118,6 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
}
private RedisVectorStoreConfig(Builder builder) {
- this.uri = builder.uri;
this.indexName = builder.indexName;
this.prefix = builder.prefix;
this.contentFieldName = builder.contentFieldName;
@@ -147,8 +145,6 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
public static class Builder {
- private String uri = DEFAULT_URI;
-
private String indexName = DEFAULT_INDEX_NAME;
private String prefix = DEFAULT_PREFIX;
@@ -164,16 +160,6 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
private Builder() {
}
- /**
- * Configures the Redis URI to use.
- * @param uri the Redis URI to use
- * @return this builder
- */
- public Builder withURI(String uri) {
- this.uri = uri;
- return this;
- }
-
/**
* Configures the Redis index name to use.
* @param name the index name to use
@@ -247,8 +233,6 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
private final boolean initializeSchema;
- public static final String DEFAULT_URI = "redis://localhost:6379";
-
public static final String DEFAULT_INDEX_NAME = "spring-ai-index";
public static final String DEFAULT_CONTENT_FIELD_NAME = "content";
@@ -287,13 +271,14 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
private FilterExpressionConverter filterExpressionConverter;
- public RedisVectorStore(RedisVectorStoreConfig config, EmbeddingModel embeddingModel, boolean initializeSchema) {
+ public RedisVectorStore(RedisVectorStoreConfig config, EmbeddingModel embeddingModel, JedisPooled jedis,
+ boolean initializeSchema) {
Assert.notNull(config, "Config must not be null");
Assert.notNull(embeddingModel, "Embedding model must not be null");
this.initializeSchema = initializeSchema;
- this.jedis = new JedisPooled(config.uri);
+ this.jedis = jedis;
this.embeddingModel = embeddingModel;
this.config = config;
this.filterExpressionConverter = new RedisFilterExpressionConverter(this.config.metadataFields);
diff --git a/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreIT.java b/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreIT.java
index c766c1af4..44497602d 100644
--- a/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreIT.java
+++ b/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreIT.java
@@ -32,18 +32,23 @@ import org.springframework.ai.transformers.TransformersEmbeddingModel;
import org.springframework.ai.vectorstore.RedisVectorStore.MetadataField;
import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig;
import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import com.redis.testcontainers.RedisStackContainer;
+import redis.clients.jedis.JedisPooled;
/**
* @author Julien Ruaux
+ * @author Eddú Meléndez
*/
@Testcontainers
class RedisVectorStoreIT {
@@ -53,7 +58,9 @@ class RedisVectorStoreIT {
RedisStackContainer.DEFAULT_IMAGE_NAME.withTag(RedisStackContainer.DEFAULT_TAG));
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withUserConfiguration(TestApplication.class);
+ .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
+ .withUserConfiguration(TestApplication.class)
+ .withPropertyValues("spring.data.redis.url=" + redisContainer.getRedisURI());
List documents = List.of(
new Document("1", getText("classpath:/test/data/spring.ai.txt"), Map.of("meta1", "meta1")),
@@ -243,12 +250,15 @@ class RedisVectorStoreIT {
public static class TestApplication {
@Bean
- public RedisVectorStore vectorStore(EmbeddingModel embeddingModel) {
- return new RedisVectorStore(RedisVectorStoreConfig.builder()
- .withURI(redisContainer.getRedisURI())
- .withMetadataFields(MetadataField.tag("meta1"), MetadataField.tag("meta2"),
- MetadataField.tag("country"), MetadataField.numeric("year"))
- .build(), embeddingModel, true);
+ public RedisVectorStore vectorStore(EmbeddingModel embeddingModel,
+ JedisConnectionFactory jedisConnectionFactory) {
+ return new RedisVectorStore(
+ RedisVectorStoreConfig.builder()
+ .withMetadataFields(MetadataField.tag("meta1"), MetadataField.tag("meta2"),
+ MetadataField.tag("country"), MetadataField.numeric("year"))
+ .build(),
+ embeddingModel,
+ new JedisPooled(jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort()), true);
}
@Bean