From 155f303a2b899caa7462fa5c3aa17b88adeabed8 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 10 Jul 2024 15:53:16 +0100 Subject: [PATCH 1/2] Added MimeTypeConverters class Allows the converters to be shared between the autoconfigure codebase and the vector-stores code. Reenabled the vector-stores code and fixed the TestApplication (so that it uses the MimeTypeConverters). Also annotated tests to check for the `OPEN_API_KEY` environment variable. Related to #698 and the auto configure fix in a1afb0d --- ...goDBAtlasVectorStoreAutoConfiguration.java | 28 +------- ...DBAtlasVectorStoreAutoConfigurationIT.java | 2 + .../convert/MimeTypeConverters.java | 72 +++++++++++++++++++ .../ai/vectorstore/convert/package-info.java | 17 +++++ .../ai/vectorstore/MongoDBAtlasContainer.java | 2 +- .../MongoDBAtlasVectorStoreIT.java | 26 ++++--- 6 files changed, 111 insertions(+), 36 deletions(-) create mode 100644 vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/convert/MimeTypeConverters.java create mode 100644 vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/convert/package-info.java diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfiguration.java index 9d5bd857a8..629d270ec5 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfiguration.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfiguration.java @@ -17,6 +17,7 @@ import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.vectorstore.MongoDBAtlasVectorStore; +import org.springframework.ai.vectorstore.convert.MimeTypeConverters; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -25,14 +26,10 @@ import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.core.convert.converter.Converter; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.util.MimeType; import org.springframework.util.StringUtils; -import java.util.Arrays; - /** * @author Eddú Meléndez * @author Christian Tzolov @@ -64,34 +61,13 @@ MongoDBAtlasVectorStore vectorStore(MongoTemplate mongoTemplate, EmbeddingModel return new MongoDBAtlasVectorStore(mongoTemplate, embeddingModel, config, properties.isInitializeSchema()); } - @Bean - public Converter mimeTypeToStringConverter() { - return new Converter() { - @Override - public String convert(MimeType source) { - return source.toString(); - } - }; - } - - @Bean - public Converter stringToMimeTypeConverter() { - return new Converter() { - @Override - public MimeType convert(String source) { - return MimeType.valueOf(source); - } - }; - } - @Bean public BeanPostProcessor mongoCustomConversionsPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MongoCustomConversions) { - return new MongoCustomConversions( - Arrays.asList(mimeTypeToStringConverter(), stringToMimeTypeConverter())); + return new MongoCustomConversions(MimeTypeConverters.getConvertersToRegister()); } return bean; } diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfigurationIT.java index efef61d8c7..23bd357c76 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfigurationIT.java @@ -16,6 +16,7 @@ package org.springframework.ai.autoconfigure.vectorstore.mongo; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration; import org.springframework.ai.document.Document; @@ -42,6 +43,7 @@ * @author Eddú Meléndez */ @Testcontainers +@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+") class MongoDBAtlasVectorStoreAutoConfigurationIT { @Container diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/convert/MimeTypeConverters.java b/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/convert/MimeTypeConverters.java new file mode 100644 index 0000000000..2d99fcd57f --- /dev/null +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/convert/MimeTypeConverters.java @@ -0,0 +1,72 @@ +/* + * 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.vectorstore.convert; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.ReadingConverter; +import org.springframework.data.convert.WritingConverter; +import org.springframework.util.MimeType; + +import java.util.List; + +import static java.util.Arrays.asList; + +/** + * Provides converters for {@link org.springframework.util.MimeType } for use with Mongo. + * + * @author Ross Lawley + * @since 1.0.0 + */ +public class MimeTypeConverters { + + /** + * @return the converters to be registered. + */ + public static List> getConvertersToRegister() { + return asList(MimeTypeReadConverter.INSTANCE, MimeTypeWriteConverter.INSTANCE); + } + + /** + * Simple singleton to convert {@link String}s to their {@link MimeType} + * representation. + */ + @ReadingConverter + enum MimeTypeReadConverter implements Converter { + + INSTANCE; + + public MimeType convert(String source) { + return MimeType.valueOf(source); + } + + } + + /** + * Simple singleton to convert {@link MimeType}s to their {@link String} + * representation. + */ + @WritingConverter + enum MimeTypeWriteConverter implements Converter { + + INSTANCE; + + public String convert(MimeType source) { + return source.toString(); + } + + } + +} diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/convert/package-info.java b/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/convert/package-info.java new file mode 100644 index 0000000000..0f36c7a3bf --- /dev/null +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/convert/package-info.java @@ -0,0 +1,17 @@ +/* + * 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. + */ +@org.springframework.lang.NonNullApi +package org.springframework.ai.vectorstore.convert; diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasContainer.java b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasContainer.java index 60dfec22f2..8760de3cf8 100644 --- a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasContainer.java +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasContainer.java @@ -23,7 +23,7 @@ public class MongoDBAtlasContainer extends GenericContainer { public MongoDBAtlasContainer() { - super("mongodb/atlas:v1.15.1"); + super("mongodb/atlas:v1.24.0"); withPrivilegedMode(true); withCommand("/bin/bash", "-c", "atlas deployments setup local-test --type local --port 27778 --bindIpAll --username root --password root --force && tail -f /dev/null"); diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java index e804ff4fdb..bff0ff696d 100644 --- a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java @@ -15,20 +15,21 @@ */ package org.springframework.ai.vectorstore; -import com.mongodb.client.MongoClient; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; - +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.api.OpenAiApi; +import org.springframework.ai.vectorstore.convert.MimeTypeConverters; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -42,9 +43,10 @@ /** * @author Chris Smith + * @author Ross Lawley */ @Testcontainers -@Disabled("Disabled due to https://github.com/spring-projects/spring-ai/issues/698") +@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+") class MongoDBAtlasVectorStoreIT { @Container @@ -191,6 +193,17 @@ void searchWithFilters() { @EnableAutoConfiguration public static class TestApplication { + @Value("${spring.data.mongodb.uri}") + String mongoUri; + + @Value("${spring.data.mongodb.database}") + String databaseName; + + @Bean + public MongoCustomConversions customConversions() { + return new MongoCustomConversions(MimeTypeConverters.getConvertersToRegister()); + } + @Bean public VectorStore vectorStore(MongoTemplate mongoTemplate, EmbeddingModel embeddingModel) { return new MongoDBAtlasVectorStore(mongoTemplate, embeddingModel, @@ -200,11 +213,6 @@ public VectorStore vectorStore(MongoTemplate mongoTemplate, EmbeddingModel embed true); } - @Bean - public MongoTemplate mongoTemplate(MongoClient mongoClient) { - return new MongoTemplate(mongoClient, "springaisample"); - } - @Bean public EmbeddingModel embeddingModel() { return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY"))); From ae9976ab1a79fa3ab395ab66cbce32e428f6d4b0 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 7 Aug 2024 17:05:33 +0100 Subject: [PATCH 2/2] Use @AutoConfiguration before to register the custom conversions --- ...MongoDBAtlasVectorStoreAutoConfiguration.java | 16 +++------------- .../vectorstore/MongoDBAtlasVectorStoreIT.java | 2 +- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfiguration.java index 629d270ec5..b75801cb56 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfiguration.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mongo/MongoDBAtlasVectorStoreAutoConfiguration.java @@ -18,8 +18,6 @@ import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.vectorstore.MongoDBAtlasVectorStore; import org.springframework.ai.vectorstore.convert.MimeTypeConverters; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -35,7 +33,7 @@ * @author Christian Tzolov * @since 1.0.0 */ -@AutoConfiguration(after = MongoDataAutoConfiguration.class) +@AutoConfiguration(before = MongoDataAutoConfiguration.class) @ConditionalOnClass({ MongoDBAtlasVectorStore.class, EmbeddingModel.class, MongoTemplate.class }) @EnableConfigurationProperties(MongoDBAtlasVectorStoreProperties.class) public class MongoDBAtlasVectorStoreAutoConfiguration { @@ -62,16 +60,8 @@ MongoDBAtlasVectorStore vectorStore(MongoTemplate mongoTemplate, EmbeddingModel } @Bean - public BeanPostProcessor mongoCustomConversionsPostProcessor() { - return new BeanPostProcessor() { - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof MongoCustomConversions) { - return new MongoCustomConversions(MimeTypeConverters.getConvertersToRegister()); - } - return bean; - } - }; + public MongoCustomConversions mongoCustomConversions() { + return new MongoCustomConversions(MimeTypeConverters.getConvertersToRegister()); } } diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java index bff0ff696d..533efe6386 100644 --- a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStoreIT.java @@ -200,7 +200,7 @@ public static class TestApplication { String databaseName; @Bean - public MongoCustomConversions customConversions() { + public MongoCustomConversions mongoCustomConversions() { return new MongoCustomConversions(MimeTypeConverters.getConvertersToRegister()); }