From b3cfa2b900ea785e055e4ff71086eeb52f6578a3 Mon Sep 17 00:00:00 2001 From: Thomas Vitale Date: Tue, 14 May 2024 07:38:58 +0200 Subject: [PATCH 01/25] OpenAI: Add gpt-4o to chat model enum Signed-off-by: Thomas Vitale --- .../org/springframework/ai/openai/api/OpenAiApi.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java index 605697906d..cc42562679 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java @@ -109,10 +109,17 @@ public OpenAiApi(String baseUrl, String openAiToken, RestClient.Builder restClie /** * OpenAI Chat Completion Models: - * GPT-4 and GPT-4 Turbo and - * GPT-3.5 Turbo. + * - GPT-4o + * - GPT-4 and GPT-4 Turbo + * - GPT-3.5 Turbo. */ public enum ChatModel { + /** + * Multimodal flagship model that’s cheaper and faster than GPT-4 Turbo. + * Currently points to gpt-4o-2024-05-13. + */ + GPT_4_O("gpt-4o"), + /** * (New) GPT-4 Turbo - latest GPT-4 model intended to reduce cases * of “laziness” where the model doesn’t complete a task. From db4f0b0aaf4afcec95dc61cbfe47bf13fe87bd2e Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Tue, 14 May 2024 21:12:39 +0200 Subject: [PATCH 02/25] Add GPT-4o ITs and documentation updates --- .../ai/openai/chat/OpenAiChatClientIT.java | 26 ++++++++++++------- .../ROOT/pages/api/chat/openai-chat.adoc | 9 ++++--- .../modules/ROOT/pages/api/multimodality.adoc | 2 +- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java index e82395ff03..099a7394b9 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java @@ -24,6 +24,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; @@ -243,33 +245,37 @@ void streamFunctionCallTest() { assertThat(content).containsAnyOf("15.0", "15"); } - @Test - void multiModalityEmbeddedImage() throws IOException { + @ParameterizedTest(name = "{0} : {displayName} ") + @ValueSource(strings = { "gpt-4-vision-preview", "gpt-4o" }) + void multiModalityEmbeddedImage(String modelName) throws IOException { byte[] imageData = new ClassPathResource("/test.png").getContentAsByteArray(); var userMessage = new UserMessage("Explain what do you see on this picture?", List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData))); - ChatResponse response = chatClient.call(new Prompt(List.of(userMessage), - OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_VISION_PREVIEW.getValue()).build())); + ChatResponse response = chatClient + .call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withModel(modelName).build())); logger.info(response.getResult().getOutput().getContent()); - assertThat(response.getResult().getOutput().getContent()).contains("bananas", "apple", "bowl"); + assertThat(response.getResult().getOutput().getContent()).contains("bananas", "apple"); + assertThat(response.getResult().getOutput().getContent()).containsAnyOf("bowl", "basket"); } - @Test - void multiModalityImageUrl() throws IOException { + @ParameterizedTest(name = "{0} : {displayName} ") + @ValueSource(strings = { "gpt-4-vision-preview", "gpt-4o" }) + void multiModalityImageUrl(String modelName) throws IOException { var userMessage = new UserMessage("Explain what do you see on this picture?", List.of(new Media(MimeTypeUtils.IMAGE_PNG, "https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png"))); - ChatResponse response = chatClient.call(new Prompt(List.of(userMessage), - OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_VISION_PREVIEW.getValue()).build())); + ChatResponse response = chatClient + .call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withModel(modelName).build())); logger.info(response.getResult().getOutput().getContent()); - assertThat(response.getResult().getOutput().getContent()).contains("bananas", "apple", "bowl"); + assertThat(response.getResult().getOutput().getContent()).contains("bananas", "apple"); + assertThat(response.getResult().getOutput().getContent()).containsAnyOf("bowl", "basket"); } @Test diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc index 09a6d61087..99fd646346 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc @@ -145,13 +145,14 @@ Read more about xref:api/chat/functions/openai-chat-functions.adoc[OpenAI Functi == Multimodal Multimodality refers to a model's ability to simultaneously understand and process information from various sources, including text, images, audio, and other data formats. -Presently, the OpenAI `gpt-4-visual-preview` model offers multimodal support. Refer to the link:https://platform.openai.com/docs/guides/vision[Vision] guide for more information. +Presently, the OpenAI `gpt-4-visual-preview` and `gpt-4o` models offers multimodal support. +Refer to the link:https://platform.openai.com/docs/guides/vision[Vision] guide for more information. The OpenAI link:https://platform.openai.com/docs/api-reference/chat/create#chat-create-messages[User Message API] can incorporate a list of base64-encoded images or image urls with the message. Spring AI’s link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/Message.java[Message] interface facilitates multimodal AI models by introducing the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/Media.java[Media] type. This type encompasses data and details regarding media attachments in messages, utilizing Spring’s `org.springframework.util.MimeType` and a `java.lang.Object` for the raw media data. -Below is a code example excerpted from link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiChatClientIT.java[OpenAiChatClientIT.java], illustrating the fusion of user text with an image. +Below is a code example excerpted from link:https://github.com/spring-projects/spring-ai/blob/b3cfa2b900ea785e055e4ff71086eeb52f6578a3/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java[OpenAiChatClientIT.java], illustrating the fusion of user text with an image using the the `GPT_4_VISION_PREVIEW` model. [source,java] ---- @@ -164,7 +165,7 @@ ChatResponse response = chatClient.call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_VISION_PREVIEW.getValue()).build())); ---- -or the image URL equivalent: +or the image URL equivalent using the `GPT_4_O` model : [source,java] ---- @@ -173,7 +174,7 @@ var userMessage = new UserMessage("Explain what do you see on this picture?", "https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png"))); ChatResponse response = chatClient.call(new Prompt(List.of(userMessage), - OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_VISION_PREVIEW.getValue()).build())); + OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_O.getValue()).build())); ---- TIP: you can pass multiple images as well. diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/multimodality.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/multimodality.adoc index 37a43ca918..320dd8910d 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/multimodality.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/multimodality.adoc @@ -57,7 +57,7 @@ and produce a response like: Latest version of Spring AI provides multimodal support for the following Chat Clients: -* xref:api/chat/openai-chat.adoc#_multimodal[Open AI - (GPT-4-Vision model)] +* xref:api/chat/openai-chat.adoc#_multimodal[Open AI - (GPT-4-Vision and GPT-4o models)] * xref:api/chat/openai-chat.adoc#_multimodal[Ollama - (LlaVa and Baklava models)] * xref:api/chat/vertexai-gemini-chat.adoc#_multimodal[Vertex AI Gemini - (gemini-pro-vision model)] * xref:api/chat/anthropic-chat.adoc#_multimodal[Anthropic Claude 3] From b0799babf423b4c136e77a4841db26bf042a64d1 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Wed, 15 May 2024 06:19:42 +0200 Subject: [PATCH 03/25] Update Qdrant client version from 1.7.1 to 1.9.1 - Also update the qdrant/qdrant docker image version from v1.7.4 to v1.9.2 --- .../openai/chat/chatbot/ChatMemoryLongTermSystemPromptIT.java | 2 +- .../openai/chat/chatbot/LongShortTermChatMemoryWithRagIT.java | 2 +- .../ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java | 2 +- pom.xml | 2 +- .../qdrant/QdrantVectorStoreAutoConfigurationIT.java | 2 +- .../qdrant/QdrantContainerConnectionDetailsFactoryTest.java | 2 +- .../ai/vectorstore/qdrant/QdrantVectorStoreIT.java | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryLongTermSystemPromptIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryLongTermSystemPromptIT.java index 546fc07267..237c1d56ec 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryLongTermSystemPromptIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryLongTermSystemPromptIT.java @@ -58,7 +58,7 @@ public class ChatMemoryLongTermSystemPromptIT extends BaseMemoryTest { private static final int QDRANT_GRPC_PORT = 6334; @Container - static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.7.4"); + static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2"); @Autowired public ChatMemoryLongTermSystemPromptIT(RelevancyEvaluator relevancyEvaluator, ChatBot chatBot, diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/LongShortTermChatMemoryWithRagIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/LongShortTermChatMemoryWithRagIT.java index 8578be6680..9d89014e68 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/LongShortTermChatMemoryWithRagIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/LongShortTermChatMemoryWithRagIT.java @@ -86,7 +86,7 @@ public class LongShortTermChatMemoryWithRagIT { private static final int QDRANT_GRPC_PORT = 6334; @Container - static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.7.4"); + static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2"); @Autowired ChatBot chatBot; diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java index 4c5aa313f5..f6ae162bd9 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java @@ -69,7 +69,7 @@ public class OpenAiDefaultChatBotIT { private static final int QDRANT_GRPC_PORT = 6334; @Container - static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.7.4"); + static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2"); private final ChatClient chatClient; diff --git a/pom.xml b/pom.xml index 5401c16568..873b77ce21 100644 --- a/pom.xml +++ b/pom.xml @@ -130,7 +130,7 @@ 0.26.0 1.17.0 26.37.0 - 1.7.1 + 1.9.1 2.0.5 9.20.0 4.35.0 diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfigurationIT.java index d04c3c72cc..127dac087d 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfigurationIT.java @@ -50,7 +50,7 @@ public class QdrantVectorStoreAutoConfigurationIT { private static final int QDRANT_GRPC_PORT = 6334; @Container - static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.7.4"); + static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2"); List documents = List.of( new Document(getText("classpath:/test/data/spring.ai.txt"), Map.of("spring", "great")), diff --git a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/qdrant/QdrantContainerConnectionDetailsFactoryTest.java b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/qdrant/QdrantContainerConnectionDetailsFactoryTest.java index 09f3c1498d..c1c461a3e9 100644 --- a/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/qdrant/QdrantContainerConnectionDetailsFactoryTest.java +++ b/spring-ai-spring-boot-testcontainers/src/test/java/org/springframework/ai/testcontainers/service/connection/qdrant/QdrantContainerConnectionDetailsFactoryTest.java @@ -48,7 +48,7 @@ public class QdrantContainerConnectionDetailsFactoryTest { @Container @ServiceConnection - static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.7.4"); + static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2"); List documents = List.of( new Document(getText("classpath:/test/data/spring.ai.txt"), Map.of("spring", "great")), diff --git a/vector-stores/spring-ai-qdrant/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java b/vector-stores/spring-ai-qdrant/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java index b7a8eada1d..73e6e93274 100644 --- a/vector-stores/spring-ai-qdrant/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java +++ b/vector-stores/spring-ai-qdrant/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java @@ -62,7 +62,7 @@ public class QdrantVectorStoreIT { private static final int QDRANT_GRPC_PORT = 6334; @Container - static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.7.4"); + static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2"); List documents = List.of( new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", From a7eb28ac17debbaaaf227802eb0385f3daea4fd8 Mon Sep 17 00:00:00 2001 From: Lorenzo Caenazzo Date: Thu, 21 Mar 2024 09:43:29 +0100 Subject: [PATCH 04/25] Add real Function Calling Streaming support - Add Java reflection merge utilities that can access Azure private constructors and fields. - Azure merging, creation of flux windows. - Function call grouping for function processing. - Do not perform greedy operation on Flux. - Use "real" streaming on all client on function response. - Gerimi: fix missing method impl. - Mistral AI, OpenAI: fix missing stream flag in doCreateToolResponseRequest. - Fix code formatting. No wildcard imports. - Add Grogdunn to the javadoc authors. - Anthropic 3 API does not support streaming funciton calling yet. --- .../ai/anthropic/AnthropicChatClient.java | 7 + .../ai/anthropic/api/AnthropicApi.java | 9 +- .../ai/anthropic/AnthropicChatClientIT.java | 4 +- .../azure/openai/AzureOpenAiChatClient.java | 71 +++- .../ai/azure/openai/MergeUtils.java | 323 ++++++++++++++++++ .../AzureOpenAiChatClientFunctionCallIT.java | 57 +++- .../openai/function/MockWeatherService.java | 6 +- .../ai/mistralai/MistralAiChatClient.java | 71 ++-- .../ai/openai/OpenAiChatClient.java | 95 +++--- .../gemini/VertexAiGeminiChatClient.java | 54 +-- .../function/AbstractFunctionCallSupport.java | 39 ++- 11 files changed, 610 insertions(+), 126 deletions(-) create mode 100644 models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/MergeUtils.java diff --git a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatClient.java b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatClient.java index 73fddf1794..0f9bcbf443 100644 --- a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatClient.java +++ b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatClient.java @@ -450,4 +450,11 @@ protected boolean isToolFunctionCall(ResponseEntity response) { return response.getBody().content().stream().anyMatch(content -> content.type() == MediaContent.Type.TOOL_USE); } + @Override + protected Flux> doChatCompletionStream(ChatCompletionRequest request) { + // https://docs.anthropic.com/en/docs/tool-use + throw new UnsupportedOperationException( + "Streaming (stream=true) is not yet supported. We plan to add streaming support in a future beta version."); + } + } diff --git a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java index 46e6119d63..eb5a962893 100644 --- a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java +++ b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java @@ -29,6 +29,7 @@ import org.springframework.ai.model.ModelOptionsUtils; import org.springframework.ai.retry.RetryUtils; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; @@ -100,7 +101,13 @@ public AnthropicApi(String baseUrl, String anthropicApiKey, String anthropicVers .defaultStatusHandler(responseErrorHandler) .build(); - this.webClient = WebClient.builder().baseUrl(baseUrl).defaultHeaders(jsonContentHeaders).build(); + this.webClient = WebClient.builder() + .baseUrl(baseUrl) + .defaultHeaders(jsonContentHeaders) + .defaultStatusHandler(HttpStatusCode::isError, + resp -> Mono.just(new RuntimeException("Response exception, Status: [" + resp.statusCode() + + "], Body:[" + resp.bodyToMono(java.lang.String.class) + "]"))) + .build(); } /** diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatClientIT.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatClientIT.java index 7bc7ef2af3..92ba0a6eb4 100644 --- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatClientIT.java +++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatClientIT.java @@ -205,7 +205,7 @@ void functionCallTest() { .withModel(AnthropicApi.ChatModel.CLAUDE_3_OPUS.getValue()) .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) .withName("getCurrentWeather") - .withDescription("Get the weather in location") + .withDescription("Get the weather in location. Return temperature in 36°F or 36°C format.") .build())) .build(); @@ -213,7 +213,7 @@ void functionCallTest() { logger.info("Response: {}", response); - Generation generation = response.getResults().get(0); + Generation generation = response.getResult(); assertThat(generation.getOutput().getContent()).containsAnyOf("30.0", "30"); assertThat(generation.getOutput().getContent()).containsAnyOf("10.0", "10"); assertThat(generation.getOutput().getContent()).containsAnyOf("15.0", "15"); diff --git a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatClient.java b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatClient.java index 236a902d84..a49a42ff59 100644 --- a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatClient.java +++ b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatClient.java @@ -15,11 +15,6 @@ */ package org.springframework.ai.azure.openai; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import com.azure.ai.openai.OpenAIClient; import com.azure.ai.openai.models.ChatChoice; import com.azure.ai.openai.models.ChatCompletions; @@ -33,15 +28,14 @@ import com.azure.ai.openai.models.ChatRequestSystemMessage; import com.azure.ai.openai.models.ChatRequestToolMessage; import com.azure.ai.openai.models.ChatRequestUserMessage; -import com.azure.ai.openai.models.ChatResponseMessage; import com.azure.ai.openai.models.CompletionsFinishReason; import com.azure.ai.openai.models.ContentFilterResultsForPrompt; +import com.azure.ai.openai.models.FunctionCall; import com.azure.ai.openai.models.FunctionDefinition; import com.azure.core.util.BinaryData; import com.azure.core.util.IterableStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import reactor.core.publisher.Flux; import org.springframework.ai.azure.openai.metadata.AzureOpenAiChatResponseMetadata; import org.springframework.ai.chat.ChatClient; @@ -59,6 +53,14 @@ import org.springframework.ai.model.function.FunctionCallbackContext; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; +import reactor.core.publisher.Flux; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; /** * {@link ChatClient} implementation for {@literal Microsoft Azure AI} backed by @@ -68,6 +70,7 @@ * @author Ueibin Kim * @author John Blum * @author Christian Tzolov + * @author Grogdunn * @see ChatClient * @see com.azure.ai.openai.OpenAIClient */ @@ -158,17 +161,42 @@ public Flux stream(Prompt prompt) { IterableStream chatCompletionsStream = this.openAIClient .getChatCompletionsStream(options.getModel(), options); - return Flux.fromStream(chatCompletionsStream.stream() + Flux chatCompletionsFlux = Flux.fromIterable(chatCompletionsStream); + + final var isFunctionCall = new AtomicBoolean(false); + final var accessibleChatCompletionsFlux = chatCompletionsFlux // Note: the first chat completions can be ignored when using Azure OpenAI // service which is a known service bug. .skip(1) - .map(ChatCompletions::getChoices) - .flatMap(List::stream) + .map(chatCompletions -> { + final var toolCalls = chatCompletions.getChoices().get(0).getDelta().getToolCalls(); + isFunctionCall.set(toolCalls != null && !toolCalls.isEmpty()); + return chatCompletions; + }) + .windowUntil(chatCompletions -> { + if (isFunctionCall.get() && chatCompletions.getChoices() + .get(0) + .getFinishReason() == CompletionsFinishReason.TOOL_CALLS) { + isFunctionCall.set(false); + return true; + } + return false; + }, false) + .concatMapIterable(window -> { + final var reduce = window.reduce(MergeUtils.emptyChatCompletions(), MergeUtils::mergeChatCompletions); + return List.of(reduce); + }) + .flatMap(mono -> mono); + return accessibleChatCompletionsFlux + .switchMap(accessibleChatCompletions -> handleFunctionCallOrReturnStream(options, + Flux.just(accessibleChatCompletions))) + .flatMapIterable(ChatCompletions::getChoices) .map(choice -> { - var content = (choice.getDelta() != null) ? choice.getDelta().getContent() : null; + var content = Optional.ofNullable(choice.getMessage()).orElse(choice.getDelta()).getContent(); var generation = new Generation(content).withGenerationMetadata(generateChoiceMetadata(choice)); return new ChatResponse(List.of(generation)); - })); + }); + } /** @@ -522,9 +550,17 @@ protected List doGetUserMessages(ChatCompletionsOptions requ @Override protected ChatRequestMessage doGetToolResponseMessage(ChatCompletions response) { - ChatResponseMessage responseMessage = response.getChoices().get(0).getMessage(); + final var accessibleChatChoice = response.getChoices().get(0); + var responseMessage = Optional.ofNullable(accessibleChatChoice.getMessage()) + .orElse(accessibleChatChoice.getDelta()); ChatRequestAssistantMessage assistantMessage = new ChatRequestAssistantMessage(""); - assistantMessage.setToolCalls(responseMessage.getToolCalls()); + final var toolCalls = responseMessage.getToolCalls(); + assistantMessage.setToolCalls(toolCalls.stream().map(tc -> { + final var tc1 = (ChatCompletionsFunctionToolCall) tc; + var toDowncast = new ChatCompletionsFunctionToolCall(tc.getId(), + new FunctionCall(tc1.getFunction().getName(), tc1.getFunction().getArguments())); + return ((ChatCompletionsToolCall) toDowncast); + }).toList()); return assistantMessage; } @@ -533,6 +569,11 @@ protected ChatCompletions doChatCompletion(ChatCompletionsOptions request) { return this.openAIClient.getChatCompletions(request.getModel(), request); } + @Override + protected Flux doChatCompletionStream(ChatCompletionsOptions request) { + return Flux.fromIterable(this.openAIClient.getChatCompletionsStream(request.getModel(), request)); + } + @Override protected boolean isToolFunctionCall(ChatCompletions chatCompletions) { @@ -549,4 +590,4 @@ protected boolean isToolFunctionCall(ChatCompletions chatCompletions) { return choice.getFinishReason() == CompletionsFinishReason.TOOL_CALLS; } -} \ No newline at end of file +} diff --git a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/MergeUtils.java b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/MergeUtils.java new file mode 100644 index 0000000000..a4e995937c --- /dev/null +++ b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/MergeUtils.java @@ -0,0 +1,323 @@ +/* + * 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.azure.openai; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.azure.ai.openai.models.AzureChatExtensionsMessageContext; +import com.azure.ai.openai.models.ChatChoice; +import com.azure.ai.openai.models.ChatCompletions; +import com.azure.ai.openai.models.ChatCompletionsFunctionToolCall; +import com.azure.ai.openai.models.ChatCompletionsToolCall; +import com.azure.ai.openai.models.ChatResponseMessage; +import com.azure.ai.openai.models.CompletionsFinishReason; +import com.azure.ai.openai.models.CompletionsUsage; +import com.azure.ai.openai.models.ContentFilterResultsForChoice; +import com.azure.ai.openai.models.ContentFilterResultsForPrompt; +import com.azure.ai.openai.models.FunctionCall; + +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +/** + * Utility class for merging ChatCompletions instances and their associated objects. Uses + * reflection to create instances with private constructors and set private fields. + * + * @author Grogdunn + * @author Christian Tzolov + * @since 1.0.0 + */ +public class MergeUtils { + + /** + * Create a new instance of the given class. Can be used to create instances with + * private constructors. + * @param the type of the class to be created. + * @param clazz the class to create an instance of. + * @param args the arguments to pass to the constructor. + * @return a new instance of the given class. + */ + private static T newInstance(Class clazz, Object... args) { + return newInstance(0, clazz, args); + } + + /** + * Create a new instance of the given class using the constructor at the given index. + * Can be used to create instances with private constructors. + * @param the type of the class to be created. + * @param index the index of the constructor to use. + * @param clazz the class to create an instance of. + * @param args the arguments to pass to the constructor. + * @return a new instance of the given class. + */ + private static T newInstance(int index, Class clazz, Object... args) { + try { + @SuppressWarnings("unchecked") + Constructor constructor = (Constructor) clazz.getDeclaredConstructors()[index]; + constructor.setAccessible(true); + return constructor.newInstance(args); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Set the value of a private field in the given class instance. + * @param classInstance the class instance to set the field on. + * @param fieldName the name of the field to set. + * @param fieldValue the value to set the field to. + */ + private static void setField(Object classInstance, String fieldName, Object fieldValue) { + try { + Field field = classInstance.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(classInstance, fieldValue); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * @return an empty ChatCompletions instance. + */ + public static ChatCompletions emptyChatCompletions() { + String id = null; + List choices = new ArrayList<>(); + CompletionsUsage usage = null; + long createdAt = 0; + ChatCompletions chatCompletionsInstance = newInstance(ChatCompletions.class, id, createdAt, choices, usage); + List promptFilterResults = new ArrayList<>(); + setField(chatCompletionsInstance, "promptFilterResults", promptFilterResults); + String systemFingerprint = null; + setField(chatCompletionsInstance, "systemFingerprint", systemFingerprint); + + return chatCompletionsInstance; + } + + /** + * Merge two ChatCompletions instances into a single ChatCompletions instance. + * @param left the left ChatCompletions instance. + * @param right the right ChatCompletions instance. + * @return a merged ChatCompletions instance. + */ + public static ChatCompletions mergeChatCompletions(ChatCompletions left, ChatCompletions right) { + + Assert.isTrue(left != null, ""); + if (right == null) { + Assert.isTrue(left.getId() != null, ""); + return left; + } + Assert.isTrue(left.getId() != null || right.getId() != null, ""); + + String id = left.getId() != null ? left.getId() : right.getId(); + + List choices = null; + if (right.getChoices() == null) { + choices = left.getChoices(); + } + else { + if (CollectionUtils.isEmpty(left.getChoices())) { + choices = right.getChoices(); + } + else { + choices = List.of(mergeChatChoice(left.getChoices().get(0), right.getChoices().get(0))); + } + } + + // For these properties if right contains that use it! + CompletionsUsage usage = right.getUsage() == null ? left.getUsage() : right.getUsage(); + + OffsetDateTime createdAt = left.getCreatedAt().isAfter(right.getCreatedAt()) ? left.getCreatedAt() + : right.getCreatedAt(); + + ChatCompletions instance = newInstance(1, ChatCompletions.class, id, createdAt, choices, usage); + + List promptFilterResults = right.getPromptFilterResults() == null + ? left.getPromptFilterResults() : right.getPromptFilterResults(); + setField(instance, "promptFilterResults", promptFilterResults); + + String systemFingerprint = right.getSystemFingerprint() == null ? left.getSystemFingerprint() + : right.getSystemFingerprint(); + setField(instance, "systemFingerprint", systemFingerprint); + return instance; + } + + /** + * Merge two ChatChoice instances into a single ChatChoice instance. + * @param left the left ChatChoice instance to merge. + * @param right the right ChatChoice instance to merge. + * @return a merged ChatChoice instance. + */ + private static ChatChoice mergeChatChoice(ChatChoice left, ChatChoice right) { + + int index = Math.max(left.getIndex(), right.getIndex()); + + CompletionsFinishReason finishReason = left.getFinishReason() != null ? left.getFinishReason() + : right.getFinishReason(); + + var logprobs = left.getLogprobs() != null ? left.getLogprobs() : right.getLogprobs(); + + final ChatChoice instance = newInstance(ChatChoice.class, logprobs, index, finishReason); + + ChatResponseMessage message = null; + if (left.getMessage() == null) { + message = right.getMessage(); + } + else { + message = mergeChatResponseMessage(left.getMessage(), right.getMessage()); + } + + setField(instance, "message", message); + + ChatResponseMessage delta = null; + if (left.getDelta() == null) { + delta = right.getDelta(); + } + else { + delta = mergeChatResponseMessage(left.getDelta(), right.getDelta()); + } + setField(instance, "delta", delta); + + ContentFilterResultsForChoice contentFilterResults = left.getContentFilterResults() != null + ? left.getContentFilterResults() : right.getContentFilterResults(); + setField(instance, "contentFilterResults", contentFilterResults); + + var finishDetails = left.getFinishDetails() != null ? left.getFinishDetails() : right.getFinishDetails(); + setField(instance, "finishDetails", finishDetails); + + var enhancements = left.getEnhancements() != null ? left.getEnhancements() : right.getEnhancements(); + setField(instance, "enhancements", enhancements); + + return instance; + } + + /** + * Merge two ChatResponseMessage instances into a single ChatResponseMessage instance. + * @param left the left ChatResponseMessage instance to merge. + * @param right the right ChatResponseMessage instance to merge. + * @return a merged ChatResponseMessage instance. + */ + private static ChatResponseMessage mergeChatResponseMessage(ChatResponseMessage left, ChatResponseMessage right) { + + var role = left.getRole() != null ? left.getRole() : right.getRole(); + String content = null; + if (left.getContent() != null && right.getContent() != null) { + content = left.getContent().concat(right.getContent()); + } + else if (left.getContent() == null) { + content = right.getContent(); + } + else { + content = left.getContent(); + } + + ChatResponseMessage instance = newInstance(ChatResponseMessage.class, role, content); + + List toolCalls = new ArrayList<>(); + if (left.getToolCalls() == null) { + if (right.getToolCalls() != null) { + toolCalls.addAll(right.getToolCalls()); + } + } + else if (right.getToolCalls() == null) { + toolCalls.addAll(left.getToolCalls()); + } + else { + toolCalls.addAll(left.getToolCalls()); + final var lastToolIndex = toolCalls.size() - 1; + ChatCompletionsToolCall lastTool = toolCalls.get(lastToolIndex); + if (right.getToolCalls().get(0).getId() == null) { + + lastTool = mergeChatCompletionsToolCall(lastTool, right.getToolCalls().get(0)); + + toolCalls.remove(lastToolIndex); + toolCalls.add(lastTool); + } + else { + toolCalls.add(right.getToolCalls().get(0)); + } + } + + setField(instance, "toolCalls", toolCalls); + + FunctionCall functionCall = null; + + if (left.getFunctionCall() == null) { + functionCall = right.getFunctionCall(); + } + else { + functionCall = MergeUtils.mergeFunctionCall(left.getFunctionCall(), right.getFunctionCall()); + } + + setField(instance, "functionCall", functionCall); + + AzureChatExtensionsMessageContext context = left.getContext() != null ? left.getContext() : right.getContext(); + setField(instance, "context", context); + + return instance; + } + + /** + * Merge two ChatCompletionsToolCall instances into a single ChatCompletionsToolCall + * instance. + * @param left the left ChatCompletionsToolCall instance to merge. + * @param right the right ChatCompletionsToolCall instance to merge. + * @return a merged ChatCompletionsToolCall instance. + */ + private static ChatCompletionsToolCall mergeChatCompletionsToolCall(ChatCompletionsToolCall left, + ChatCompletionsToolCall right) { + Assert.isTrue(Objects.equals(left.getType(), right.getType()), + "Cannot merge different type of AccessibleChatCompletionsToolCall"); + if (!"function".equals(left.getType())) { + throw new UnsupportedOperationException("Only function chat completion tool is supported"); + } + + String id = left.getId() != null ? left.getId() : right.getId(); + var mergedFunction = mergeFunctionCall(((ChatCompletionsFunctionToolCall) left).getFunction(), + ((ChatCompletionsFunctionToolCall) right).getFunction()); + + return new ChatCompletionsFunctionToolCall(id, mergedFunction); + } + + /** + * Merge two FunctionCall instances into a single FunctionCall instance. + * @param left the left, input FunctionCall instance. + * @param right the right, input FunctionCall instance. + * @return a merged FunctionCall instance. + */ + private static FunctionCall mergeFunctionCall(FunctionCall left, FunctionCall right) { + var name = left.getName() != null ? left.getName() : right.getName(); + String arguments = null; + if (left.getArguments() != null && right.getArguments() != null) { + arguments = left.getArguments() + right.getArguments(); + } + else if (left.getArguments() == null) { + arguments = right.getArguments(); + } + else { + arguments = left.getArguments(); + } + return new FunctionCall(name, arguments); + } + +} diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java index 3e67dc3c69..08c81ebd13 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatClientFunctionCallIT.java @@ -17,6 +17,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import com.azure.ai.openai.OpenAIClient; import com.azure.ai.openai.OpenAIClientBuilder; @@ -29,6 +32,8 @@ import org.springframework.ai.azure.openai.AzureOpenAiChatClient; import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; +import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.Prompt; @@ -37,6 +42,7 @@ import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; +import reactor.core.publisher.Flux; import static org.assertj.core.api.Assertions.assertThat; @@ -47,6 +53,9 @@ class AzureOpenAiChatClientFunctionCallIT { private static final Logger logger = LoggerFactory.getLogger(AzureOpenAiChatClientFunctionCallIT.class); + @Autowired + private String selectedModel; + @Autowired private AzureOpenAiChatClient chatClient; @@ -58,7 +67,7 @@ void functionCallTest() { List messages = new ArrayList<>(List.of(userMessage)); var promptOptions = AzureOpenAiChatOptions.builder() - .withDeploymentName("gpt-4-0125-preview") + .withDeploymentName(selectedModel) .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) .withName("getCurrentWeather") .withDescription("Get the current weather in a given location") @@ -75,6 +84,40 @@ void functionCallTest() { assertThat(response.getResult().getOutput().getContent()).containsAnyOf("15.0", "15"); } + @Test + void streamFunctionCallTest() { + UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); + + List messages = new ArrayList<>(List.of(userMessage)); + + var promptOptions = AzureOpenAiChatOptions.builder() + .withDeploymentName(selectedModel) + .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) + .withName("getCurrentWeather") + .withDescription("Get the current weather in a given location") + .withResponseConverter((response) -> "" + response.temp() + response.unit()) + .build())) + .build(); + + Flux response = chatClient.stream(new Prompt(messages, promptOptions)); + + final var counter = new AtomicInteger(); + String content = response.doOnEach(listSignal -> counter.getAndIncrement()) + .collectList() + .block() + .stream() + .map(ChatResponse::getResults) + .flatMap(List::stream) + .map(Generation::getOutput) + .map(AssistantMessage::getContent) + .collect(Collectors.joining()); + logger.info("Response: {}", content); + assertThat(counter.get()).isGreaterThan(2); + assertThat(content).containsAnyOf("30.0", "30"); + assertThat(content).containsAnyOf("10.0", "10"); + assertThat(content).containsAnyOf("15.0", "15"); + } + @SpringBootConfiguration public static class TestConfiguration { @@ -86,12 +129,14 @@ public OpenAIClient openAIClient() { } @Bean - public AzureOpenAiChatClient azureOpenAiChatClient(OpenAIClient openAIClient) { + public AzureOpenAiChatClient azureOpenAiChatClient(OpenAIClient openAIClient, String selectedModel) { return new AzureOpenAiChatClient(openAIClient, - AzureOpenAiChatOptions.builder() - .withDeploymentName("gpt-4-0125-preview") - .withMaxTokens(500) - .build()); + AzureOpenAiChatOptions.builder().withDeploymentName(selectedModel).withMaxTokens(500).build()); + } + + @Bean + public String selectedModel() { + return Optional.ofNullable(System.getenv("AZURE_OPENAI_MODEL")).orElse("gpt-4-0125-preview"); } } diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/MockWeatherService.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/MockWeatherService.java index 898a1c61b6..92747ed302 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/MockWeatherService.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/MockWeatherService.java @@ -15,14 +15,14 @@ */ package org.springframework.ai.azure.openai.function; -import java.util.function.Function; - import com.fasterxml.jackson.annotation.JsonClassDescription; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import java.util.function.Function; + /** * @author Christian Tzolov */ @@ -87,4 +87,4 @@ else if (request.location().contains("San Francisco")) { return new Response(temperature, 15, 20, 2, 53, 45, Unit.C); } -} \ No newline at end of file +} diff --git a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatClient.java b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatClient.java index f5c1f4fd5b..98a25025d6 100644 --- a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatClient.java +++ b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatClient.java @@ -15,18 +15,8 @@ */ package org.springframework.ai.mistralai; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import reactor.core.publisher.Flux; - import org.springframework.ai.chat.ChatClient; import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.Generation; @@ -49,10 +39,15 @@ import org.springframework.retry.support.RetryTemplate; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; +import reactor.core.publisher.Flux; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * @author Ricken Bazolo * @author Christian Tzolov + * @author Grogdunn * @since 0.8.1 */ public class MistralAiChatClient extends @@ -148,29 +143,29 @@ public Flux stream(Prompt prompt) { // The rest of the chunks with same ID share the same role. ConcurrentHashMap roleMap = new ConcurrentHashMap<>(); - return completionChunks.map(chunk -> toChatCompletion(chunk)).map(chatCompletion -> { - - chatCompletion = handleFunctionCallOrReturn(request, ResponseEntity.of(Optional.of(chatCompletion))) - .getBody(); - - @SuppressWarnings("null") - String id = chatCompletion.id(); - - List generations = chatCompletion.choices().stream().map(choice -> { - if (choice.message().role() != null) { - roleMap.putIfAbsent(id, choice.message().role().name()); - } - String finish = (choice.finishReason() != null ? choice.finishReason().name() : ""); - var generation = new Generation(choice.message().content(), - Map.of("id", id, "role", roleMap.get(id), "finishReason", finish)); - if (choice.finishReason() != null) { - generation = generation - .withGenerationMetadata(ChatGenerationMetadata.from(choice.finishReason().name(), null)); - } - return generation; - }).toList(); - return new ChatResponse(generations); - }); + return completionChunks.map(chunk -> toChatCompletion(chunk)) + .switchMap( + cc -> handleFunctionCallOrReturnStream(request, Flux.just(ResponseEntity.of(Optional.of(cc))))) + .map(ResponseEntity::getBody) + .map(chatCompletion -> { + @SuppressWarnings("null") + String id = chatCompletion.id(); + + List generations = chatCompletion.choices().stream().map(choice -> { + if (choice.message().role() != null) { + roleMap.putIfAbsent(id, choice.message().role().name()); + } + String finish = (choice.finishReason() != null ? choice.finishReason().name() : ""); + var generation = new Generation(choice.message().content(), + Map.of("id", id, "role", roleMap.get(id), "finishReason", finish)); + if (choice.finishReason() != null) { + generation = generation.withGenerationMetadata( + ChatGenerationMetadata.from(choice.finishReason().name(), null)); + } + return generation; + }).toList(); + return new ChatResponse(generations); + }); }); } @@ -271,7 +266,7 @@ protected ChatCompletionRequest doCreateToolResponseRequest(ChatCompletionReques // Recursively call chatCompletionWithTools until the model doesn't call a // functions anymore. - ChatCompletionRequest newRequest = new ChatCompletionRequest(conversationHistory, false); + ChatCompletionRequest newRequest = new ChatCompletionRequest(conversationHistory, previousRequest.stream()); newRequest = ModelOptionsUtils.merge(newRequest, previousRequest, ChatCompletionRequest.class); return newRequest; @@ -299,6 +294,14 @@ protected ResponseEntity doChatCompletion(ChatCompletionRequest return this.mistralAiApi.chatCompletionEntity(request); } + @Override + protected Flux> doChatCompletionStream(ChatCompletionRequest request) { + return this.mistralAiApi.chatCompletionStream(request) + .map(this::toChatCompletion) + .map(Optional::ofNullable) + .map(ResponseEntity::of); + } + @Override protected boolean isToolFunctionCall(ResponseEntity chatCompletion) { diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatClient.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatClient.java index 8e86ecdd19..6ec6904dbf 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatClient.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatClient.java @@ -15,20 +15,8 @@ */ package org.springframework.ai.openai; -import java.util.ArrayList; -import java.util.Base64; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import reactor.core.publisher.Flux; - import org.springframework.ai.chat.ChatClient; import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.Generation; @@ -57,6 +45,17 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.MimeType; +import reactor.core.publisher.Flux; + +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * {@link ChatClient} and {@link StreamingChatClient} implementation for {@literal OpenAI} @@ -68,6 +67,7 @@ * @author John Blum * @author Josh Long * @author Jemin Huh + * @author Grogdunn * @see ChatClient * @see StreamingChatClient * @see OpenAiApi @@ -189,36 +189,37 @@ public Flux stream(Prompt prompt) { // Convert the ChatCompletionChunk into a ChatCompletion to be able to reuse // the function call handling logic. - return completionChunks.map(chunk -> chunkToChatCompletion(chunk)).map(chatCompletion -> { - try { - chatCompletion = handleFunctionCallOrReturn(request, ResponseEntity.of(Optional.of(chatCompletion))) - .getBody(); - - @SuppressWarnings("null") - String id = chatCompletion.id(); - - List generations = chatCompletion.choices().stream().map(choice -> { - if (choice.message().role() != null) { - roleMap.putIfAbsent(id, choice.message().role().name()); - } - String finish = (choice.finishReason() != null ? choice.finishReason().name() : ""); - var generation = new Generation(choice.message().content(), - Map.of("id", id, "role", roleMap.get(id), "finishReason", finish)); - if (choice.finishReason() != null) { - generation = generation.withGenerationMetadata( - ChatGenerationMetadata.from(choice.finishReason().name(), null)); - } - return generation; - }).toList(); - - return new ChatResponse(generations); - } - catch (Exception e) { - logger.error("Error processing chat completion", e); - return new ChatResponse(List.of()); - } - - }); + return completionChunks.map(chunk -> chunkToChatCompletion(chunk)) + .switchMap( + cc -> handleFunctionCallOrReturnStream(request, Flux.just(ResponseEntity.of(Optional.of(cc))))) + .map(ResponseEntity::getBody) + .map(chatCompletion -> { + try { + @SuppressWarnings("null") + String id = chatCompletion.id(); + + List generations = chatCompletion.choices().stream().map(choice -> { + if (choice.message().role() != null) { + roleMap.putIfAbsent(id, choice.message().role().name()); + } + String finish = (choice.finishReason() != null ? choice.finishReason().name() : ""); + var generation = new Generation(choice.message().content(), + Map.of("id", id, "role", roleMap.get(id), "finishReason", finish)); + if (choice.finishReason() != null) { + generation = generation.withGenerationMetadata( + ChatGenerationMetadata.from(choice.finishReason().name(), null)); + } + return generation; + }).toList(); + + return new ChatResponse(generations); + } + catch (Exception e) { + logger.error("Error processing chat completion", e); + return new ChatResponse(List.of()); + } + + }); }); } @@ -347,7 +348,7 @@ protected ChatCompletionRequest doCreateToolResponseRequest(ChatCompletionReques // Recursively call chatCompletionWithTools until the model doesn't call a // functions anymore. - ChatCompletionRequest newRequest = new ChatCompletionRequest(conversationHistory, false); + ChatCompletionRequest newRequest = new ChatCompletionRequest(conversationHistory, previousRequest.stream()); newRequest = ModelOptionsUtils.merge(newRequest, previousRequest, ChatCompletionRequest.class); return newRequest; @@ -368,6 +369,14 @@ protected ResponseEntity doChatCompletion(ChatCompletionRequest return this.openAiApi.chatCompletionEntity(request); } + @Override + protected Flux> doChatCompletionStream(ChatCompletionRequest request) { + return this.openAiApi.chatCompletionStream(request) + .map(this::chunkToChatCompletion) + .map(Optional::ofNullable) + .map(ResponseEntity::of); + } + @Override protected boolean isToolFunctionCall(ResponseEntity chatCompletion) { var body = chatCompletion.getBody(); diff --git a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java index ad74073fa7..dfb2a14bc5 100644 --- a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java +++ b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java @@ -15,12 +15,6 @@ */ package org.springframework.ai.vertexai.gemini; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.google.cloud.vertexai.VertexAI; @@ -38,8 +32,6 @@ import com.google.cloud.vertexai.generativeai.ResponseStream; import com.google.protobuf.Struct; import com.google.protobuf.util.JsonFormat; -import reactor.core.publisher.Flux; - import org.springframework.ai.chat.ChatClient; import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.Generation; @@ -60,9 +52,17 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import reactor.core.publisher.Flux; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * @author Christian Tzolov + * @author Grogdunn * @since 0.8.1 */ public class VertexAiGeminiChatClient @@ -167,18 +167,19 @@ public Flux stream(Prompt prompt) { ResponseStream responseStream = request.model .generateContentStream(request.contents); - return Flux.fromStream(responseStream.stream()).map(response -> { - response = handleFunctionCallOrReturn(request, response); - List generations = response.getCandidatesList() - .stream() - .map(candidate -> candidate.getContent().getPartsList()) - .flatMap(List::stream) - .map(Part::getText) - .map(t -> new Generation(t.toString())) - .toList(); - - return new ChatResponse(generations, toChatResponseMetadata(response)); - }); + return Flux.fromStream(responseStream.stream()) + .switchMap(r -> handleFunctionCallOrReturnStream(request, Flux.just(r))) + .map(response -> { + List generations = response.getCandidatesList() + .stream() + .map(candidate -> candidate.getContent().getPartsList()) + .flatMap(List::stream) + .map(Part::getText) + .map(t -> new Generation(t.toString())) + .toList(); + + return new ChatResponse(generations, toChatResponseMetadata(response)); + }); } catch (Exception e) { throw new RuntimeException("Failed to generate content", e); @@ -450,6 +451,19 @@ protected GenerateContentResponse doChatCompletion(GeminiRequest request) { } } + @Override + protected Flux doChatCompletionStream(GeminiRequest request) { + try { + ResponseStream responseStream = request.model + .generateContentStream(request.contents); + + return Flux.fromStream(responseStream.stream()); + } + catch (Exception e) { + throw new RuntimeException("Failed to generate content", e); + } + } + @Override protected boolean isToolFunctionCall(GenerateContentResponse response) { if (response == null || CollectionUtils.isEmpty(response.getCandidatesList()) diff --git a/spring-ai-core/src/main/java/org/springframework/ai/model/function/AbstractFunctionCallSupport.java b/spring-ai-core/src/main/java/org/springframework/ai/model/function/AbstractFunctionCallSupport.java index d1c4986257..d5be8ef6ca 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/model/function/AbstractFunctionCallSupport.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/model/function/AbstractFunctionCallSupport.java @@ -15,6 +15,10 @@ */ package org.springframework.ai.model.function; +import org.springframework.util.CollectionUtils; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -22,10 +26,9 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.springframework.util.CollectionUtils; - /** * @author Christian Tzolov + * @author Grogdunn */ public abstract class AbstractFunctionCallSupport { @@ -147,6 +150,36 @@ protected Resp handleFunctionCallOrReturn(Req request, Resp response) { return this.callWithFunctionSupport(newRequest); } + protected Flux callWithFunctionSupportStream(Req request) { + final Flux response = this.doChatCompletionStream(request); + return this.handleFunctionCallOrReturnStream(request, response); + } + + protected Flux handleFunctionCallOrReturnStream(Req request, Flux response) { + + return response.switchMap(resp -> { + if (!this.isToolFunctionCall(resp)) { + return Mono.just(resp); + } + + // The chat completion tool call requires the complete conversation + // history. Including the initial user message. + List conversationHistory = new ArrayList<>(); + + conversationHistory.addAll(this.doGetUserMessages(request)); + + Msg responseMessage = this.doGetToolResponseMessage(resp); + + // Add the assistant response to the message conversation history. + conversationHistory.add(responseMessage); + + Req newRequest = this.doCreateToolResponseRequest(request, responseMessage, conversationHistory); + + return this.callWithFunctionSupportStream(newRequest); + }); + + } + abstract protected Req doCreateToolResponseRequest(Req previousRequest, Msg responseMessage, List conversationHistory); @@ -156,6 +189,8 @@ abstract protected Req doCreateToolResponseRequest(Req previousRequest, Msg resp abstract protected Resp doChatCompletion(Req request); + abstract protected Flux doChatCompletionStream(Req request); + abstract protected boolean isToolFunctionCall(Resp response); } From 368f7bdfba935aa83c079da851c14b3ea409ca68 Mon Sep 17 00:00:00 2001 From: Mark Pollack Date: Wed, 15 May 2024 09:55:39 +0200 Subject: [PATCH 05/25] Remove filter on external knowledge. * Update question so that evaulator passes fix formatting --- .../ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java | 3 ++- .../ai/chat/prompt/transformer/QuestionContextAugmentor.java | 5 +---- .../ai/chat/prompt/transformer/VectorStoreRetriever.java | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java index f6ae162bd9..c22085a216 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java @@ -26,6 +26,7 @@ import org.springframework.ai.chat.prompt.transformer.TransformerContentType; import org.springframework.ai.document.Document; import org.springframework.ai.openai.OpenAiChatOptions; +import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.qdrant.QdrantContainer; @@ -91,7 +92,7 @@ public OpenAiDefaultChatBotIT(ChatClient chatClient, ChatBot chatBot, VectorStor void simpleChat() { loadData(); - var prompt = new Prompt(new UserMessage("What bike is good for city commuting?")); + var prompt = new Prompt(new UserMessage("What reliable road bike?")); var chatBotResponse = this.chatBot.call(new PromptContext(prompt)); String answer = chatBotResponse.getChatResponse().getResult().getOutput().getContent(); assertTrue(answer.contains("Celerity"), "Response does not include 'Celerity'"); diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java index 7a99d9eb85..12e4aada2a 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java @@ -61,10 +61,7 @@ public PromptContext transform(PromptContext promptContext) { } protected String doCreateContext(List data) { - return data.stream() - .filter(content -> content.getMetadata().containsKey(TransformerContentType.EXTERNAL_KNOWLEDGE)) - .map(Content::getContent) - .collect(Collectors.joining(System.lineSeparator())); + return data.stream().map(Content::getContent).collect(Collectors.joining(System.lineSeparator())); } private Map doCreateContextMap(Prompt prompt, String context) { diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/VectorStoreRetriever.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/VectorStoreRetriever.java index 92608b3d99..8a9e58b1cc 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/VectorStoreRetriever.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/VectorStoreRetriever.java @@ -23,6 +23,7 @@ import org.springframework.ai.document.Document; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.ai.vectorstore.filter.Filter; import java.util.List; import java.util.Objects; @@ -60,8 +61,7 @@ public PromptContext transform(PromptContext promptContext) { .map(m -> m.getContent()) .collect(Collectors.joining(System.lineSeparator())); - List documents = vectorStore.similaritySearch(searchRequest.withQuery(userMessage) - .withFilterExpression(TransformerContentType.EXTERNAL_KNOWLEDGE + "=='true'")); + List documents = vectorStore.similaritySearch(searchRequest.withQuery(userMessage)); logger.info("Retrieved {} documents for user message {}", documents.size(), userMessage); for (Document document : documents) { From d610dd6f1dfc9d3b97c6634425c5861c78978f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Sat, 11 May 2024 20:19:42 +0200 Subject: [PATCH 06/25] Add default collection name to Qdrant Add a default collection name similar to other vector store implementations. Currently, when using starters, qdrant requires a collection name. Otherwise, it fails. --- .../vectorstore/qdrant/QdrantVectorStoreProperties.java | 3 ++- .../qdrant/QdrantVectorStoreAutoConfigurationIT.java | 6 ++---- .../qdrant/QdrantVectorStorePropertiesTests.java | 4 +++- .../ai/vectorstore/qdrant/QdrantVectorStore.java | 2 ++ 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreProperties.java index c843f76930..585c8f0ab3 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreProperties.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreProperties.java @@ -15,6 +15,7 @@ */ package org.springframework.ai.autoconfigure.vectorstore.qdrant; +import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -29,7 +30,7 @@ public class QdrantVectorStoreProperties { /** * The name of the collection to use in Qdrant. */ - private String collectionName; + private String collectionName = QdrantVectorStore.DEFAULT_COLLECTION_NAME; /** * The host of the Qdrant server. diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfigurationIT.java index 127dac087d..52dda7e968 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfigurationIT.java @@ -40,13 +40,12 @@ /** * @author Christian Tzolov + * @author Eddú Meléndez * @since 0.8.1 */ @Testcontainers public class QdrantVectorStoreAutoConfigurationIT { - private static final String COLLECTION_NAME = "test_collection"; - private static final int QDRANT_GRPC_PORT = 6334; @Container @@ -61,8 +60,7 @@ public class QdrantVectorStoreAutoConfigurationIT { .withConfiguration(AutoConfigurations.of(QdrantVectorStoreAutoConfiguration.class)) .withUserConfiguration(Config.class) .withPropertyValues("spring.ai.vectorstore.qdrant.port=" + qdrantContainer.getMappedPort(QDRANT_GRPC_PORT), - "spring.ai.vectorstore.qdrant.host=" + qdrantContainer.getHost(), - "spring.ai.vectorstore.qdrant.collectionName=" + COLLECTION_NAME); + "spring.ai.vectorstore.qdrant.host=" + qdrantContainer.getHost()); @Test public void addAndSearch() { diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStorePropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStorePropertiesTests.java index 324d73b88b..878f298ab3 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStorePropertiesTests.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStorePropertiesTests.java @@ -16,11 +16,13 @@ package org.springframework.ai.autoconfigure.vectorstore.qdrant; import org.junit.jupiter.api.Test; +import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; import static org.assertj.core.api.Assertions.assertThat; /** * @author Christian Tzolov + * @author Eddú Meléndez */ public class QdrantVectorStorePropertiesTests { @@ -28,7 +30,7 @@ public class QdrantVectorStorePropertiesTests { public void defaultValues() { var props = new QdrantVectorStoreProperties(); - assertThat(props.getCollectionName()).isNull(); + assertThat(props.getCollectionName()).isEqualTo(QdrantVectorStore.DEFAULT_COLLECTION_NAME); assertThat(props.getHost()).isEqualTo("localhost"); assertThat(props.getPort()).isEqualTo(6334); assertThat(props.isUseTls()).isFalse(); diff --git a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java b/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java index 67a651bad7..fcbe464fbc 100644 --- a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java +++ b/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java @@ -59,6 +59,8 @@ public class QdrantVectorStore implements VectorStore, InitializingBean { private static final String DISTANCE_FIELD_NAME = "distance"; + public static final String DEFAULT_COLLECTION_NAME = "vector_store"; + private final EmbeddingClient embeddingClient; private final QdrantClient qdrantClient; From 867154c08251c661fbf3525bd58718a8810c7d84 Mon Sep 17 00:00:00 2001 From: Mark Pollack Date: Wed, 15 May 2024 13:57:34 +0200 Subject: [PATCH 07/25] Changed ChatBot to ChatService with other related name changes * PromptContext -> ChatServiceContext * Added PromptChange to ChatServiceContext to capture PromptTransformer changes * DefaultChatBot -> PromptTransformingChatService * DefaultStreamingChatBot -> StreamingPromptTransformingChatService * package name changes, chatbot->service and history->memory * Added fluent builders to a few PromptTransformer implementations * Add license headers --- .../ChatMemoryLongTermSystemPromptIT.java | 38 ++-- .../ChatMemoryShortTermMessageListIT.java | 42 ++-- .../ChatMemoryShortTermSystemPromptIT.java | 42 ++-- .../LongShortTermChatMemoryWithRagIT.java | 63 +++--- ...penAiPromptTransformingChatServiceIT.java} | 30 +-- .../chatbot/StreamingChatBotResponse.java | 72 ------- .../chat/{history => memory}/ChatMemory.java | 2 +- .../ChatMemoryChatServiceListener.java} | 24 +-- .../ChatMemoryRetriever.java | 81 ++++++-- .../InMemoryChatMemory.java | 2 +- .../LastMaxTokenSizeContentTransformer.java | 26 +-- .../MessageChatMemoryAugmentor.java | 19 +- .../SystemPromptChatMemoryAugmentor.java | 20 +- ...orStoreChatMemoryChatServiceListener.java} | 30 +-- .../VectorStoreChatMemoryRetriever.java | 18 +- .../AbstractPromptTransformer.java | 39 ++++ .../transformer/ChatServiceContext.java | 195 ++++++++++++++++++ .../chat/prompt/transformer/PromptChange.java | 31 +++ .../prompt/transformer/PromptContext.java | 189 ----------------- .../prompt/transformer/PromptTransformer.java | 19 +- .../transformer/QuestionContextAugmentor.java | 67 ++++-- .../transformer/TransformerContentType.java | 3 + .../transformer/VectorStoreRetriever.java | 32 ++- .../ChatBot.java => service/ChatService.java} | 21 +- .../ChatServiceListener.java} | 14 +- .../ChatServiceResponse.java} | 30 +-- .../PromptTransformingChatService.java} | 68 +++--- .../StreamingChatService.java} | 23 +-- .../service/StreamingChatServiceResponse.java | 73 +++++++ ...reamingPromptTransformingChatService.java} | 64 +++--- .../ai/evaluation/EvaluationRequest.java | 8 +- .../org/springframework/ai/model/Content.java | 2 +- .../{history => memory}/ChatMemoryTests.java | 32 +-- .../ai/evaluation/BaseMemoryTest.java | 59 +++--- 34 files changed, 836 insertions(+), 642 deletions(-) rename models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/{chatbot => service}/ChatMemoryLongTermSystemPromptIT.java (73%) rename models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/{chatbot => service}/ChatMemoryShortTermMessageListIT.java (65%) rename models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/{chatbot => service}/ChatMemoryShortTermSystemPromptIT.java (65%) rename models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/{chatbot => service}/LongShortTermChatMemoryWithRagIT.java (77%) rename models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/{chatbot/OpenAiDefaultChatBotIT.java => service/OpenAiPromptTransformingChatServiceIT.java} (85%) delete mode 100644 spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/StreamingChatBotResponse.java rename spring-ai-core/src/main/java/org/springframework/ai/chat/{history => memory}/ChatMemory.java (95%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{history/ChatMemoryChatBotListener.java => memory/ChatMemoryChatServiceListener.java} (61%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{history => memory}/ChatMemoryRetriever.java (52%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{history => memory}/InMemoryChatMemory.java (97%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{history => memory}/LastMaxTokenSizeContentTransformer.java (77%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{history => memory}/MessageChatMemoryAugmentor.java (72%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{history => memory}/SystemPromptChatMemoryAugmentor.java (81%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{history/VectorStoreChatMemoryChatBotListener.java => memory/VectorStoreChatMemoryChatServiceListener.java} (68%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{history => memory}/VectorStoreChatMemoryRetriever.java (77%) create mode 100644 spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/AbstractPromptTransformer.java create mode 100644 spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/ChatServiceContext.java create mode 100644 spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptChange.java delete mode 100644 spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptContext.java rename spring-ai-core/src/main/java/org/springframework/ai/chat/{chatbot/ChatBot.java => service/ChatService.java} (54%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{chatbot/ChatBotListener.java => service/ChatServiceListener.java} (61%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{chatbot/ChatBotResponse.java => service/ChatServiceResponse.java} (54%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{chatbot/DefaultChatBot.java => service/PromptTransformingChatService.java} (50%) rename spring-ai-core/src/main/java/org/springframework/ai/chat/{chatbot/StreamingChatBot.java => service/StreamingChatService.java} (51%) create mode 100644 spring-ai-core/src/main/java/org/springframework/ai/chat/service/StreamingChatServiceResponse.java rename spring-ai-core/src/main/java/org/springframework/ai/chat/{chatbot/DefaultStreamingChatBot.java => service/StreamingPromptTransformingChatService.java} (54%) rename spring-ai-core/src/test/java/org/springframework/ai/chat/{history => memory}/ChatMemoryTests.java (78%) diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryLongTermSystemPromptIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/ChatMemoryLongTermSystemPromptIT.java similarity index 73% rename from models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryLongTermSystemPromptIT.java rename to models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/ChatMemoryLongTermSystemPromptIT.java index 237c1d56ec..4516e08aef 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryLongTermSystemPromptIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/ChatMemoryLongTermSystemPromptIT.java @@ -14,25 +14,25 @@ * limitations under the License. */ -package org.springframework.ai.openai.chat.chatbot; +package org.springframework.ai.openai.chat.service; import java.util.List; import io.qdrant.client.QdrantClient; import io.qdrant.client.QdrantGrpcClient; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.springframework.ai.chat.chatbot.ChatBot; +import org.springframework.ai.chat.service.ChatService; +import org.springframework.ai.chat.service.StreamingChatService; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.qdrant.QdrantContainer; -import org.springframework.ai.chat.chatbot.DefaultChatBot; -import org.springframework.ai.chat.chatbot.DefaultStreamingChatBot; -import org.springframework.ai.chat.chatbot.StreamingChatBot; -import org.springframework.ai.chat.history.VectorStoreChatMemoryChatBotListener; -import org.springframework.ai.chat.history.VectorStoreChatMemoryRetriever; -import org.springframework.ai.chat.history.LastMaxTokenSizeContentTransformer; -import org.springframework.ai.chat.history.SystemPromptChatMemoryAugmentor; +import org.springframework.ai.chat.service.PromptTransformingChatService; +import org.springframework.ai.chat.service.StreamingPromptTransformingChatService; +import org.springframework.ai.chat.memory.VectorStoreChatMemoryChatServiceListener; +import org.springframework.ai.chat.memory.VectorStoreChatMemoryRetriever; +import org.springframework.ai.chat.memory.LastMaxTokenSizeContentTransformer; +import org.springframework.ai.chat.memory.SystemPromptChatMemoryAugmentor; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.evaluation.BaseMemoryTest; import org.springframework.ai.evaluation.RelevancyEvaluator; @@ -61,9 +61,9 @@ public class ChatMemoryLongTermSystemPromptIT extends BaseMemoryTest { static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2"); @Autowired - public ChatMemoryLongTermSystemPromptIT(RelevancyEvaluator relevancyEvaluator, ChatBot chatBot, - StreamingChatBot streamingChatBot) { - super(relevancyEvaluator, chatBot, streamingChatBot); + public ChatMemoryLongTermSystemPromptIT(RelevancyEvaluator relevancyEvaluator, ChatService chatService, + StreamingChatService streamingChatService) { + super(relevancyEvaluator, chatService, streamingChatService); } @SpringBootConfiguration @@ -98,26 +98,26 @@ public TokenCountEstimator tokenCountEstimator() { } @Bean - public ChatBot memoryChatBot(OpenAiChatClient chatClient, VectorStore vectorStore, + public ChatService memoryChatService(OpenAiChatClient chatClient, VectorStore vectorStore, TokenCountEstimator tokenCountEstimator) { - return DefaultChatBot.builder(chatClient) + return PromptTransformingChatService.builder(chatClient) .withRetrievers(List.of(new VectorStoreChatMemoryRetriever(vectorStore, 10))) .withContentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000))) .withAugmentors(List.of(new SystemPromptChatMemoryAugmentor())) - .withChatBotListeners(List.of(new VectorStoreChatMemoryChatBotListener(vectorStore))) + .withChatServiceListeners(List.of(new VectorStoreChatMemoryChatServiceListener(vectorStore))) .build(); } @Bean - public StreamingChatBot memoryStreamingChatBot(OpenAiChatClient streamingChatClient, VectorStore vectorStore, - TokenCountEstimator tokenCountEstimator) { + public StreamingChatService memoryStreamingChatService(OpenAiChatClient streamingChatClient, + VectorStore vectorStore, TokenCountEstimator tokenCountEstimator) { - return DefaultStreamingChatBot.builder(streamingChatClient) + return StreamingPromptTransformingChatService.builder(streamingChatClient) .withRetrievers(List.of(new VectorStoreChatMemoryRetriever(vectorStore, 10))) .withDocumentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000))) .withAugmentors(List.of(new SystemPromptChatMemoryAugmentor())) - .withChatBotListeners(List.of(new VectorStoreChatMemoryChatBotListener(vectorStore))) + .withChatServiceListeners(List.of(new VectorStoreChatMemoryChatServiceListener(vectorStore))) .build(); } diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryShortTermMessageListIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/ChatMemoryShortTermMessageListIT.java similarity index 65% rename from models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryShortTermMessageListIT.java rename to models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/ChatMemoryShortTermMessageListIT.java index 5d88ce34b9..d26f6c5630 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryShortTermMessageListIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/ChatMemoryShortTermMessageListIT.java @@ -13,22 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.ai.openai.chat.chatbot; +package org.springframework.ai.openai.chat.service; import java.util.List; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.springframework.ai.chat.chatbot.ChatBot; -import org.springframework.ai.chat.chatbot.DefaultChatBot; -import org.springframework.ai.chat.chatbot.DefaultStreamingChatBot; -import org.springframework.ai.chat.chatbot.StreamingChatBot; -import org.springframework.ai.chat.history.ChatMemory; -import org.springframework.ai.chat.history.ChatMemoryChatBotListener; -import org.springframework.ai.chat.history.ChatMemoryRetriever; -import org.springframework.ai.chat.history.InMemoryChatMemory; -import org.springframework.ai.chat.history.LastMaxTokenSizeContentTransformer; -import org.springframework.ai.chat.history.MessageChatMemoryAugmentor; +import org.springframework.ai.chat.service.ChatService; +import org.springframework.ai.chat.service.PromptTransformingChatService; +import org.springframework.ai.chat.service.StreamingPromptTransformingChatService; +import org.springframework.ai.chat.service.StreamingChatService; +import org.springframework.ai.chat.memory.ChatMemory; +import org.springframework.ai.chat.memory.ChatMemoryChatServiceListener; +import org.springframework.ai.chat.memory.ChatMemoryRetriever; +import org.springframework.ai.chat.memory.InMemoryChatMemory; +import org.springframework.ai.chat.memory.LastMaxTokenSizeContentTransformer; +import org.springframework.ai.chat.memory.MessageChatMemoryAugmentor; import org.springframework.ai.evaluation.BaseMemoryTest; import org.springframework.ai.evaluation.RelevancyEvaluator; import org.springframework.ai.openai.OpenAiChatClient; @@ -45,9 +45,9 @@ public class ChatMemoryShortTermMessageListIT extends BaseMemoryTest { @Autowired - public ChatMemoryShortTermMessageListIT(RelevancyEvaluator relevancyEvaluator, ChatBot chatBot, - StreamingChatBot streamingChatBot) { - super(relevancyEvaluator, chatBot, streamingChatBot); + public ChatMemoryShortTermMessageListIT(RelevancyEvaluator relevancyEvaluator, ChatService chatService, + StreamingChatService streamingChatService) { + super(relevancyEvaluator, chatService, streamingChatService); } @SpringBootConfiguration @@ -74,26 +74,26 @@ public TokenCountEstimator tokenCountEstimator() { } @Bean - public ChatBot memoryChatBot(OpenAiChatClient chatClient, ChatMemory chatHistory, + public ChatService memoryChatService(OpenAiChatClient chatClient, ChatMemory chatHistory, TokenCountEstimator tokenCountEstimator) { - return DefaultChatBot.builder(chatClient) + return PromptTransformingChatService.builder(chatClient) .withRetrievers(List.of(new ChatMemoryRetriever(chatHistory))) .withContentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000))) .withAugmentors(List.of(new MessageChatMemoryAugmentor())) - .withChatBotListeners(List.of(new ChatMemoryChatBotListener(chatHistory))) + .withChatServiceListeners(List.of(new ChatMemoryChatServiceListener(chatHistory))) .build(); } @Bean - public StreamingChatBot memoryStreamingChatBot(OpenAiChatClient streamingChatClient, ChatMemory chatHistory, - TokenCountEstimator tokenCountEstimator) { + public StreamingChatService memoryStreamingChatService(OpenAiChatClient streamingChatClient, + ChatMemory chatHistory, TokenCountEstimator tokenCountEstimator) { - return DefaultStreamingChatBot.builder(streamingChatClient) + return StreamingPromptTransformingChatService.builder(streamingChatClient) .withRetrievers(List.of(new ChatMemoryRetriever(chatHistory))) .withDocumentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000))) .withAugmentors(List.of(new MessageChatMemoryAugmentor())) - .withChatBotListeners(List.of(new ChatMemoryChatBotListener(chatHistory))) + .withChatServiceListeners(List.of(new ChatMemoryChatServiceListener(chatHistory))) .build(); } diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryShortTermSystemPromptIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/ChatMemoryShortTermSystemPromptIT.java similarity index 65% rename from models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryShortTermSystemPromptIT.java rename to models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/ChatMemoryShortTermSystemPromptIT.java index b3a9d43ed1..7ca4c795bd 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/ChatMemoryShortTermSystemPromptIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/ChatMemoryShortTermSystemPromptIT.java @@ -14,22 +14,22 @@ * limitations under the License. */ -package org.springframework.ai.openai.chat.chatbot; +package org.springframework.ai.openai.chat.service; import java.util.List; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.springframework.ai.chat.chatbot.ChatBot; -import org.springframework.ai.chat.chatbot.DefaultChatBot; -import org.springframework.ai.chat.chatbot.DefaultStreamingChatBot; -import org.springframework.ai.chat.chatbot.StreamingChatBot; -import org.springframework.ai.chat.history.ChatMemory; -import org.springframework.ai.chat.history.ChatMemoryChatBotListener; -import org.springframework.ai.chat.history.ChatMemoryRetriever; -import org.springframework.ai.chat.history.InMemoryChatMemory; -import org.springframework.ai.chat.history.LastMaxTokenSizeContentTransformer; -import org.springframework.ai.chat.history.SystemPromptChatMemoryAugmentor; +import org.springframework.ai.chat.service.ChatService; +import org.springframework.ai.chat.service.PromptTransformingChatService; +import org.springframework.ai.chat.service.StreamingPromptTransformingChatService; +import org.springframework.ai.chat.service.StreamingChatService; +import org.springframework.ai.chat.memory.ChatMemory; +import org.springframework.ai.chat.memory.ChatMemoryChatServiceListener; +import org.springframework.ai.chat.memory.ChatMemoryRetriever; +import org.springframework.ai.chat.memory.InMemoryChatMemory; +import org.springframework.ai.chat.memory.LastMaxTokenSizeContentTransformer; +import org.springframework.ai.chat.memory.SystemPromptChatMemoryAugmentor; import org.springframework.ai.evaluation.BaseMemoryTest; import org.springframework.ai.evaluation.RelevancyEvaluator; import org.springframework.ai.openai.OpenAiChatClient; @@ -46,9 +46,9 @@ public class ChatMemoryShortTermSystemPromptIT extends BaseMemoryTest { @Autowired - public ChatMemoryShortTermSystemPromptIT(RelevancyEvaluator relevancyEvaluator, ChatBot chatBot, - StreamingChatBot streamingChatBot) { - super(relevancyEvaluator, chatBot, streamingChatBot); + public ChatMemoryShortTermSystemPromptIT(RelevancyEvaluator relevancyEvaluator, ChatService chatService, + StreamingChatService streamingChatService) { + super(relevancyEvaluator, chatService, streamingChatService); } @SpringBootConfiguration @@ -75,26 +75,26 @@ public TokenCountEstimator tokenCountEstimator() { } @Bean - public ChatBot memoryChatBot(OpenAiChatClient chatClient, ChatMemory chatHistory, + public ChatService memoryChatService(OpenAiChatClient chatClient, ChatMemory chatHistory, TokenCountEstimator tokenCountEstimator) { - return DefaultChatBot.builder(chatClient) + return PromptTransformingChatService.builder(chatClient) .withRetrievers(List.of(new ChatMemoryRetriever(chatHistory))) .withContentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000))) .withAugmentors(List.of(new SystemPromptChatMemoryAugmentor())) - .withChatBotListeners(List.of(new ChatMemoryChatBotListener(chatHistory))) + .withChatServiceListeners(List.of(new ChatMemoryChatServiceListener(chatHistory))) .build(); } @Bean - public StreamingChatBot memoryStreamingChatBot(OpenAiChatClient streamingChatClient, ChatMemory chatHistory, - TokenCountEstimator tokenCountEstimator) { + public StreamingChatService memoryStreamingChatService(OpenAiChatClient streamingChatClient, + ChatMemory chatHistory, TokenCountEstimator tokenCountEstimator) { - return DefaultStreamingChatBot.builder(streamingChatClient) + return StreamingPromptTransformingChatService.builder(streamingChatClient) .withRetrievers(List.of(new ChatMemoryRetriever(chatHistory))) .withDocumentPostProcessors(List.of(new LastMaxTokenSizeContentTransformer(tokenCountEstimator, 1000))) .withAugmentors(List.of(new SystemPromptChatMemoryAugmentor())) - .withChatBotListeners(List.of(new ChatMemoryChatBotListener(chatHistory))) + .withChatServiceListeners(List.of(new ChatMemoryChatServiceListener(chatHistory))) .build(); } diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/LongShortTermChatMemoryWithRagIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/LongShortTermChatMemoryWithRagIT.java similarity index 77% rename from models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/LongShortTermChatMemoryWithRagIT.java rename to models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/LongShortTermChatMemoryWithRagIT.java index 9d89014e68..f8fc8f71f6 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/LongShortTermChatMemoryWithRagIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/LongShortTermChatMemoryWithRagIT.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.openai.chat.chatbot; +package org.springframework.ai.openai.chat.service; import java.util.List; import java.util.Map; @@ -26,24 +26,24 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ai.chat.chatbot.ChatBot; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; +import org.springframework.ai.chat.service.ChatService; +import org.springframework.ai.chat.service.PromptTransformingChatService; import org.springframework.ai.openai.OpenAiChatOptions; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.qdrant.QdrantContainer; -import org.springframework.ai.chat.chatbot.DefaultChatBot; -import org.springframework.ai.chat.history.ChatMemory; -import org.springframework.ai.chat.history.ChatMemoryChatBotListener; -import org.springframework.ai.chat.history.ChatMemoryRetriever; -import org.springframework.ai.chat.history.InMemoryChatMemory; -import org.springframework.ai.chat.history.LastMaxTokenSizeContentTransformer; -import org.springframework.ai.chat.history.SystemPromptChatMemoryAugmentor; -import org.springframework.ai.chat.history.VectorStoreChatMemoryChatBotListener; -import org.springframework.ai.chat.history.VectorStoreChatMemoryRetriever; +import org.springframework.ai.chat.memory.ChatMemory; +import org.springframework.ai.chat.memory.ChatMemoryChatServiceListener; +import org.springframework.ai.chat.memory.ChatMemoryRetriever; +import org.springframework.ai.chat.memory.InMemoryChatMemory; +import org.springframework.ai.chat.memory.LastMaxTokenSizeContentTransformer; +import org.springframework.ai.chat.memory.SystemPromptChatMemoryAugmentor; +import org.springframework.ai.chat.memory.VectorStoreChatMemoryChatServiceListener; +import org.springframework.ai.chat.memory.VectorStoreChatMemoryRetriever; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.transformer.PromptContext; import org.springframework.ai.chat.prompt.transformer.QuestionContextAugmentor; import org.springframework.ai.chat.prompt.transformer.TransformerContentType; import org.springframework.ai.chat.prompt.transformer.VectorStoreRetriever; @@ -89,7 +89,7 @@ public class LongShortTermChatMemoryWithRagIT { static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2"); @Autowired - ChatBot chatBot; + ChatService chatService; @Autowired RelevancyEvaluator relevancyEvaluator; @@ -122,29 +122,29 @@ public List apply(List documents) { } // @Autowired - // StreamingChatBot streamingChatBot; + // StreamingChatService streamingChatService; @Test - void memoryChatBot() { + void memoryChatService() { loadData(); var prompt = new Prompt(new UserMessage("My name is Christian and I like mountain bikes.")); - PromptContext promptContext = new PromptContext(prompt); + ChatServiceContext chatServiceContext = new ChatServiceContext(prompt); - var chatBotResponse1 = this.chatBot.call(promptContext); + var chatServiceResponse1 = this.chatService.call(chatServiceContext); - logger.info("Response1: " + chatBotResponse1.getChatResponse().getResult().getOutput().getContent()); + logger.info("Response1: " + chatServiceResponse1.getChatResponse().getResult().getOutput().getContent()); - var chatBotResponse2 = this.chatBot.call(new PromptContext( + var chatServiceResponse2 = this.chatService.call(new ChatServiceContext( new Prompt(new String("What is my name and what bike model would you suggest for me?")))); - logger.info("Response2: " + chatBotResponse2.getChatResponse().getResult().getOutput().getContent()); + logger.info("Response2: " + chatServiceResponse2.getChatResponse().getResult().getOutput().getContent()); - // logger.info(chatBotResponse2.getPromptContext().getContents().toString()); - assertThat(chatBotResponse2.getChatResponse().getResult().getOutput().getContent()).contains("Christian"); + // logger.info(chatServiceResponse2.getPromptContext().getContents().toString()); + assertThat(chatServiceResponse2.getChatResponse().getResult().getOutput().getContent()).contains("Christian"); EvaluationResponse evaluationResponse = this.relevancyEvaluator - .evaluate(new EvaluationRequest(chatBotResponse2)); + .evaluate(new EvaluationRequest(chatServiceResponse2)); assertTrue(evaluationResponse.isPass(), "Response is not relevant to the question"); @@ -187,12 +187,15 @@ public TokenCountEstimator tokenCountEstimator() { } @Bean - public ChatBot memoryChatBot(OpenAiChatClient chatClient, VectorStore vectorStore, + public ChatService memoryChatService(OpenAiChatClient chatClient, VectorStore vectorStore, TokenCountEstimator tokenCountEstimator, ChatMemory chatHistory) { - return DefaultChatBot.builder(chatClient) + return PromptTransformingChatService.builder(chatClient) .withRetrievers(List.of(new VectorStoreRetriever(vectorStore, SearchRequest.defaults()), - new ChatMemoryRetriever(chatHistory, Map.of(TransformerContentType.SHORT_TERM_MEMORY, "")), + ChatMemoryRetriever.builder() + .withChatHistory(chatHistory) + .withMetadata(Map.of(TransformerContentType.SHORT_TERM_MEMORY, "")) + .build(), new VectorStoreChatMemoryRetriever(vectorStore, 10, Map.of(TransformerContentType.LONG_TERM_MEMORY, "")))) @@ -214,19 +217,19 @@ public ChatBot memoryChatBot(OpenAiChatClient chatClient, VectorStore vectorStor Set.of(TransformerContentType.LONG_TERM_MEMORY)), new SystemPromptChatMemoryAugmentor(Set.of(TransformerContentType.SHORT_TERM_MEMORY)))) - .withChatBotListeners(List.of(new ChatMemoryChatBotListener(chatHistory), - new VectorStoreChatMemoryChatBotListener(vectorStore, + .withChatServiceListeners(List.of(new ChatMemoryChatServiceListener(chatHistory), + new VectorStoreChatMemoryChatServiceListener(vectorStore, Map.of(TransformerContentType.LONG_TERM_MEMORY, "")))) .build(); } // @Bean - // public StreamingChatBot memoryStreamingChatAgent(OpenAiChatClient + // public StreamingChatService memoryStreamingChatAgent(OpenAiChatClient // streamingChatClient, // VectorStore vectorStore, TokenCountEstimator tokenCountEstimator, ChatHistory // chatHistory) { - // return DefaultStreamingChatBot.builder(streamingChatClient) + // return StreamingPromptTransformingChatService.builder(streamingChatClient) // .withRetrievers(List.of(new ChatHistoryRetriever(chatHistory), new // DocumentChatHistoryRetriever(vectorStore, 10))) // .withDocumentPostProcessors(List.of(new diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java similarity index 85% rename from models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java rename to models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java index c22085a216..b164b8343d 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/chatbot/OpenAiDefaultChatBotIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.openai.chat.chatbot; +package org.springframework.ai.openai.chat.service; import java.util.List; @@ -22,20 +22,19 @@ import io.qdrant.client.QdrantGrpcClient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.springframework.ai.chat.chatbot.ChatBot; +import org.springframework.ai.chat.service.ChatService; import org.springframework.ai.chat.prompt.transformer.TransformerContentType; import org.springframework.ai.document.Document; import org.springframework.ai.openai.OpenAiChatOptions; -import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.qdrant.QdrantContainer; import org.springframework.ai.chat.ChatClient; -import org.springframework.ai.chat.chatbot.DefaultChatBot; +import org.springframework.ai.chat.service.PromptTransformingChatService; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.transformer.PromptContext; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; import org.springframework.ai.chat.prompt.transformer.QuestionContextAugmentor; import org.springframework.ai.chat.prompt.transformer.VectorStoreRetriever; import org.springframework.ai.embedding.EmbeddingClient; @@ -61,9 +60,9 @@ import static org.springframework.ai.openai.api.OpenAiApi.ChatModel.GPT_4_TURBO_PREVIEW; @Testcontainers -@SpringBootTest(classes = OpenAiDefaultChatBotIT.Config.class) +@SpringBootTest(classes = OpenAiPromptTransformingChatServiceIT.Config.class) @EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+") -public class OpenAiDefaultChatBotIT { +public class OpenAiPromptTransformingChatServiceIT { private static final String COLLECTION_NAME = "test_collection"; @@ -79,12 +78,13 @@ public class OpenAiDefaultChatBotIT { @Value("classpath:/data/acme/bikes.json") private Resource bikesResource; - private ChatBot chatBot; + private ChatService chatService; @Autowired - public OpenAiDefaultChatBotIT(ChatClient chatClient, ChatBot chatBot, VectorStore vectorStore) { + public OpenAiPromptTransformingChatServiceIT(ChatClient chatClient, ChatService chatService, + VectorStore vectorStore) { this.chatClient = chatClient; - this.chatBot = chatBot; + this.chatService = chatService; this.vectorStore = vectorStore; } @@ -93,8 +93,8 @@ void simpleChat() { loadData(); var prompt = new Prompt(new UserMessage("What reliable road bike?")); - var chatBotResponse = this.chatBot.call(new PromptContext(prompt)); - String answer = chatBotResponse.getChatResponse().getResult().getOutput().getContent(); + var chatServiceResponse = this.chatService.call(new ChatServiceContext(prompt)); + String answer = chatServiceResponse.getChatResponse().getResult().getOutput().getContent(); assertTrue(answer.contains("Celerity"), "Response does not include 'Celerity'"); // Use GPT 4 as a better model for determining relevancy. gpt 3.5 makes basic @@ -103,7 +103,7 @@ void simpleChat() { .withModel(GPT_4_TURBO_PREVIEW.getValue()) .build(); var relevancyEvaluator = new RelevancyEvaluator(this.chatClient, openAiChatOptions); - EvaluationRequest evaluationRequest = new EvaluationRequest(chatBotResponse); + EvaluationRequest evaluationRequest = new EvaluationRequest(chatServiceResponse); EvaluationResponse evaluationResponse = relevancyEvaluator.evaluate(evaluationRequest); assertTrue(evaluationResponse.isPass(), "Response is not relevant to the question"); @@ -148,8 +148,8 @@ public VectorStore qdrantVectorStore(EmbeddingClient embeddingClient) { } @Bean - public ChatBot chatBot(ChatClient chatClient, VectorStore vectorStore) { - return DefaultChatBot.builder(chatClient) + public ChatService chatService(ChatClient chatClient, VectorStore vectorStore) { + return PromptTransformingChatService.builder(chatClient) .withRetrievers(List.of(new VectorStoreRetriever(vectorStore, SearchRequest.defaults()))) .withAugmentors(List.of(new QuestionContextAugmentor())) .build(); diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/StreamingChatBotResponse.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/StreamingChatBotResponse.java deleted file mode 100644 index 5e699278cb..0000000000 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/StreamingChatBotResponse.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.springframework.ai.chat.chatbot; - -import reactor.core.publisher.Flux; - -import org.springframework.ai.chat.ChatResponse; -import org.springframework.ai.chat.prompt.transformer.PromptContext; - -/** - * Encapsulates the response from the ChatBot. Contains the most up-to-date PromptContext - * and the final ChatResponse - * - * @author Mark Pollack - * @since 1.0 M1 - */ -public class StreamingChatBotResponse { - - private final PromptContext promptContext; - - private final Flux chatResponse; - - public StreamingChatBotResponse(PromptContext promptContext, Flux chatResponse) { - this.promptContext = promptContext; - this.chatResponse = chatResponse; - } - - public PromptContext getPromptContext() { - return promptContext; - } - - public Flux getChatResponse() { - return chatResponse; - } - - @Override - public String toString() { - return "ChatBotResponse{" + "promptContext=" + promptContext + ", chatResponse=" + chatResponse + '}'; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((promptContext == null) ? 0 : promptContext.hashCode()); - result = prime * result + ((chatResponse == null) ? 0 : chatResponse.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - StreamingChatBotResponse other = (StreamingChatBotResponse) obj; - if (promptContext == null) { - if (other.promptContext != null) - return false; - } - else if (!promptContext.equals(other.promptContext)) - return false; - if (chatResponse == null) { - if (other.chatResponse != null) - return false; - } - else if (!chatResponse.equals(other.chatResponse)) - return false; - return true; - } - -} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/ChatMemory.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/ChatMemory.java similarity index 95% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/history/ChatMemory.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/memory/ChatMemory.java index 26d4f13428..52767072dd 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/ChatMemory.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/ChatMemory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.chat.history; +package org.springframework.ai.chat.memory; import java.util.List; diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/ChatMemoryChatBotListener.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/ChatMemoryChatServiceListener.java similarity index 61% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/history/ChatMemoryChatBotListener.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/memory/ChatMemoryChatServiceListener.java index 2ddf304894..934c3d43d7 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/ChatMemoryChatBotListener.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/ChatMemoryChatServiceListener.java @@ -14,47 +14,47 @@ * limitations under the License. */ -package org.springframework.ai.chat.history; +package org.springframework.ai.chat.memory; import java.util.List; -import org.springframework.ai.chat.chatbot.ChatBotResponse; -import org.springframework.ai.chat.chatbot.ChatBotListener; +import org.springframework.ai.chat.service.ChatServiceResponse; +import org.springframework.ai.chat.service.ChatServiceListener; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.MessageType; -import org.springframework.ai.chat.prompt.transformer.PromptContext; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; import org.springframework.ai.chat.prompt.transformer.TransformerContentType; /** * @author Christian Tzolov */ -public class ChatMemoryChatBotListener implements ChatBotListener { +public class ChatMemoryChatServiceListener implements ChatServiceListener { private final ChatMemory chatHistory; - public ChatMemoryChatBotListener(ChatMemory chatHistory) { + public ChatMemoryChatServiceListener(ChatMemory chatHistory) { this.chatHistory = chatHistory; } @Override - public void onStart(PromptContext promptContext) { - var messagesToAdd = promptContext.getPrompt() + public void onStart(ChatServiceContext chatServiceContext) { + var messagesToAdd = chatServiceContext.getPrompt() .getInstructions() .stream() .filter(m -> !m.getMetadata().containsKey(TransformerContentType.MEMORY)) .filter(m -> (m.getMessageType() == MessageType.ASSISTANT || m.getMessageType() == MessageType.USER)) .toList(); - this.chatHistory.add(promptContext.getConversationId(), messagesToAdd); + this.chatHistory.add(chatServiceContext.getConversationId(), messagesToAdd); } @Override - public void onComplete(ChatBotResponse chatBotResponse) { - List assistantMessages = chatBotResponse.getChatResponse() + public void onComplete(ChatServiceResponse chatServiceResponse) { + List assistantMessages = chatServiceResponse.getChatResponse() .getResults() .stream() .map(g -> (Message) g.getOutput()) .toList(); - this.chatHistory.add(chatBotResponse.getPromptContext().getConversationId(), assistantMessages); + this.chatHistory.add(chatServiceResponse.getPromptContext().getConversationId(), assistantMessages); } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/ChatMemoryRetriever.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/ChatMemoryRetriever.java similarity index 52% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/history/ChatMemoryRetriever.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/memory/ChatMemoryRetriever.java index 31d0838467..23908cbaa1 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/ChatMemoryRetriever.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/ChatMemoryRetriever.java @@ -14,68 +14,105 @@ * limitations under the License. */ -package org.springframework.ai.chat.history; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +package org.springframework.ai.chat.memory; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.MessageType; -import org.springframework.ai.chat.prompt.transformer.PromptContext; -import org.springframework.ai.chat.prompt.transformer.PromptTransformer; +import org.springframework.ai.chat.prompt.transformer.AbstractPromptTransformer; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; import org.springframework.ai.chat.prompt.transformer.TransformerContentType; import org.springframework.ai.document.Document; import org.springframework.ai.model.Content; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * @author Christian Tzolov */ -public class ChatMemoryRetriever implements PromptTransformer { +public class ChatMemoryRetriever extends AbstractPromptTransformer { private final ChatMemory chatHistory; /** * Additional metadata to be assigned to the retrieved history messages. */ - private final Map additionalMetadata; + private final Map metadata; private final int maxHistorySize; public ChatMemoryRetriever(ChatMemory chatHistory) { - this(chatHistory, Map.of()); + this(chatHistory, 1000, Map.of(), "ChatMemoryRetriever"); } - public ChatMemoryRetriever(ChatMemory chatHistory, Map additionalMetadata) { - this(chatHistory, 1000, additionalMetadata); - } - - public ChatMemoryRetriever(ChatMemory chatHistory, int maxHistorySize, Map additionalMetadata) { + public ChatMemoryRetriever(ChatMemory chatHistory, int maxHistorySize, Map metadata, String name) { this.chatHistory = chatHistory; - this.additionalMetadata = additionalMetadata; + this.metadata = metadata; this.maxHistorySize = maxHistorySize; + this.setName(name); } @Override - public PromptContext transform(PromptContext promptContext) { + public ChatServiceContext transform(ChatServiceContext chatServiceContext) { - List messageHistory = this.chatHistory.get(promptContext.getConversationId(), maxHistorySize); + List messageHistory = this.chatHistory.get(chatServiceContext.getConversationId(), maxHistorySize); List historyContent = (messageHistory != null) ? messageHistory.stream().filter(m -> m.getMessageType() != MessageType.SYSTEM).map(m -> { Content content = new Document(m.getContent(), new ArrayList<>(m.getMedia()), new HashMap<>(m.getMetadata())); - content.getMetadata().putAll(this.additionalMetadata); + content.getMetadata().putAll(this.metadata); content.getMetadata().put(TransformerContentType.MEMORY, true); return content; }).toList() : List.of(); List updatedContents = new ArrayList<>( - promptContext.getContents() != null ? promptContext.getContents() : List.of()); + chatServiceContext.getContents() != null ? chatServiceContext.getContents() : List.of()); updatedContents.addAll(historyContent); - return PromptContext.from(promptContext).withContents(updatedContents).build(); + return ChatServiceContext.from(chatServiceContext).withContents(updatedContents).build(); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private ChatMemory chatHistory; + + private Map metadata = Map.of(); + + private int maxHistorySize = 1000; + + private String name = "ChatMemoryRetriever"; + + public Builder withChatHistory(ChatMemory chatHistory) { + this.chatHistory = chatHistory; + return this; + } + + public Builder withMetadata(Map metadata) { + this.metadata = metadata; + return this; + } + + public Builder withMaxHistorySize(int maxHistorySize) { + this.maxHistorySize = maxHistorySize; + return this; + } + + public Builder withName(String name) { + this.name = name; + return this; + } + + public ChatMemoryRetriever build() { + return new ChatMemoryRetriever(this.chatHistory, this.maxHistorySize, this.metadata, this.name); + } + } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/InMemoryChatMemory.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/InMemoryChatMemory.java similarity index 97% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/history/InMemoryChatMemory.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/memory/InMemoryChatMemory.java index 80bf6671c9..34b78963e8 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/InMemoryChatMemory.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/InMemoryChatMemory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.chat.history; +package org.springframework.ai.chat.memory; import java.util.ArrayList; import java.util.List; diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/LastMaxTokenSizeContentTransformer.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/LastMaxTokenSizeContentTransformer.java similarity index 77% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/history/LastMaxTokenSizeContentTransformer.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/memory/LastMaxTokenSizeContentTransformer.java index 01b07ecd4d..b2e1626e6b 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/LastMaxTokenSizeContentTransformer.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/LastMaxTokenSizeContentTransformer.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package org.springframework.ai.chat.history; +package org.springframework.ai.chat.memory; import java.util.ArrayList; import java.util.List; import java.util.Set; -import org.springframework.ai.chat.prompt.transformer.PromptContext; -import org.springframework.ai.chat.prompt.transformer.PromptTransformer; +import org.springframework.ai.chat.prompt.transformer.AbstractPromptTransformer; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; import org.springframework.ai.model.Content; import org.springframework.ai.tokenizer.TokenCountEstimator; @@ -33,7 +33,7 @@ * * @author Christian Tzolov */ -public class LastMaxTokenSizeContentTransformer implements PromptTransformer { +public class LastMaxTokenSizeContentTransformer extends AbstractPromptTransformer { protected final TokenCountEstimator tokenCountEstimator; @@ -56,15 +56,15 @@ public LastMaxTokenSizeContentTransformer(TokenCountEstimator tokenCountEstimato this.filterTags = filterTags; } - protected List doGetDatumToModify(PromptContext promptContext) { - return promptContext.getContents() + protected List doGetDatumToModify(ChatServiceContext chatServiceContext) { + return chatServiceContext.getContents() .stream() .filter(content -> this.filterTags.stream().allMatch(tag -> content.getMetadata().containsKey(tag))) .toList(); } - protected List doGetDatumNotToModify(PromptContext promptContext) { - return promptContext.getContents() + protected List doGetDatumNotToModify(ChatServiceContext chatServiceContext) { + return chatServiceContext.getContents() .stream() .filter(content -> !this.filterTags.stream().allMatch(tag -> content.getMetadata().containsKey(tag))) .toList(); @@ -79,22 +79,22 @@ protected int doEstimateTokenCount(List datum) { } @Override - public PromptContext transform(PromptContext promptContext) { + public ChatServiceContext transform(ChatServiceContext chatServiceContext) { - List datum = this.doGetDatumToModify(promptContext); + List datum = this.doGetDatumToModify(chatServiceContext); int totalSize = this.doEstimateTokenCount(datum); if (totalSize <= this.maxTokenSize) { - return promptContext; + return chatServiceContext; } List purgedContent = this.purgeExcess(datum, totalSize); - var updatedContent = new ArrayList<>(doGetDatumNotToModify(promptContext)); + var updatedContent = new ArrayList<>(doGetDatumNotToModify(chatServiceContext)); updatedContent.addAll(purgedContent); - return PromptContext.from(promptContext).withContents(updatedContent).build(); + return ChatServiceContext.from(chatServiceContext).withContents(updatedContent).build(); } protected List purgeExcess(List datum, int totalSize) { diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/MessageChatMemoryAugmentor.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/MessageChatMemoryAugmentor.java similarity index 72% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/history/MessageChatMemoryAugmentor.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/memory/MessageChatMemoryAugmentor.java index dcf3d6daa7..e3b3ce847f 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/MessageChatMemoryAugmentor.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/MessageChatMemoryAugmentor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.chat.history; +package org.springframework.ai.chat.memory; import java.util.ArrayList; import java.util.List; @@ -26,22 +26,23 @@ import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.chat.prompt.transformer.AbstractPromptTransformer; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; +import org.springframework.ai.chat.prompt.transformer.PromptChange; import org.springframework.ai.chat.prompt.transformer.TransformerContentType; -import org.springframework.ai.chat.prompt.transformer.PromptContext; -import org.springframework.ai.chat.prompt.transformer.PromptTransformer; /** * @author Christian Tzolov */ -public class MessageChatMemoryAugmentor implements PromptTransformer { +public class MessageChatMemoryAugmentor extends AbstractPromptTransformer { @Override - public PromptContext transform(PromptContext promptContext) { + public ChatServiceContext transform(ChatServiceContext chatServiceContext) { - var originalPrompt = promptContext.getPrompt(); + var originalPrompt = chatServiceContext.getPrompt(); // Convert the retrieved contents into a list of messages. - List historyMessages = promptContext.getContents() + List historyMessages = chatServiceContext.getContents() .stream() .filter(content -> content.getMetadata().containsKey(TransformerContentType.MEMORY)) .map(content -> { @@ -63,8 +64,10 @@ else if (messageType == MessageType.USER) { promptMessages.addAll(originalPrompt.getInstructions()); Prompt newPrompt = new Prompt(promptMessages, (ChatOptions) originalPrompt.getOptions()); + PromptChange promptChange = new PromptChange(originalPrompt, newPrompt, this.getName(), + "Added chat memory as individual messages in the prompt"); - return PromptContext.from(promptContext).withPrompt(newPrompt).addPromptHistory(originalPrompt).build(); + return ChatServiceContext.from(chatServiceContext).withPrompt(newPrompt).withPromptChange(promptChange).build(); } } \ No newline at end of file diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/SystemPromptChatMemoryAugmentor.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/SystemPromptChatMemoryAugmentor.java similarity index 81% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/history/SystemPromptChatMemoryAugmentor.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/memory/SystemPromptChatMemoryAugmentor.java index b507faf167..2ed8306ee0 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/SystemPromptChatMemoryAugmentor.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/SystemPromptChatMemoryAugmentor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.chat.history; +package org.springframework.ai.chat.memory; import java.util.ArrayList; import java.util.HashSet; @@ -28,15 +28,16 @@ import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.chat.prompt.transformer.AbstractPromptTransformer; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; +import org.springframework.ai.chat.prompt.transformer.PromptChange; import org.springframework.ai.chat.prompt.transformer.TransformerContentType; -import org.springframework.ai.chat.prompt.transformer.PromptContext; -import org.springframework.ai.chat.prompt.transformer.PromptTransformer; import org.springframework.util.Assert; /** * @author Christian Tzolov */ -public class SystemPromptChatMemoryAugmentor implements PromptTransformer { +public class SystemPromptChatMemoryAugmentor extends AbstractPromptTransformer { public static final String DEFAULT_HISTORY_PROMPT = """ Use the conversation history from the HISTORY section to provide accurate answers. @@ -72,9 +73,9 @@ public SystemPromptChatMemoryAugmentor(String historyPrompt, Set metadat } @Override - public PromptContext transform(PromptContext promptContext) { + public ChatServiceContext transform(ChatServiceContext chatServiceContext) { - var originalPrompt = promptContext.getPrompt(); + var originalPrompt = chatServiceContext.getPrompt(); List systemMessages = (originalPrompt.getInstructions() != null) ? originalPrompt.getInstructions() .stream() @@ -89,7 +90,7 @@ public PromptContext transform(PromptContext promptContext) { SystemMessage originalSystemMessage = (!systemMessages.isEmpty()) ? (SystemMessage) systemMessages.get(0) : new SystemMessage(""); - String historyContext = promptContext.getContents() + String historyContext = chatServiceContext.getContents() .stream() .filter(content -> this.filterTags.stream().allMatch(tag -> content.getMetadata().containsKey(tag))) .map(content -> content.getMetadata().get(AbstractMessage.MESSAGE_TYPE) + ": " + content.getContent()) @@ -103,8 +104,9 @@ public PromptContext transform(PromptContext promptContext) { newPromptMessages.addAll(nonSystemMessages); Prompt newPrompt = new Prompt(newPromptMessages, (ChatOptions) originalPrompt.getOptions()); - - return PromptContext.from(promptContext).withPrompt(newPrompt).addPromptHistory(originalPrompt).build(); + PromptChange promptChange = new PromptChange(originalPrompt, newPrompt, this.getName(), + "Added chat memory into the system prompt"); + return ChatServiceContext.from(chatServiceContext).withPrompt(newPrompt).withPromptChange(promptChange).build(); } } \ No newline at end of file diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/VectorStoreChatMemoryChatBotListener.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/VectorStoreChatMemoryChatServiceListener.java similarity index 68% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/history/VectorStoreChatMemoryChatBotListener.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/memory/VectorStoreChatMemoryChatServiceListener.java index cf42bfb6e7..0f5aa01009 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/VectorStoreChatMemoryChatBotListener.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/VectorStoreChatMemoryChatServiceListener.java @@ -14,18 +14,18 @@ * limitations under the License. */ -package org.springframework.ai.chat.history; +package org.springframework.ai.chat.memory; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.springframework.ai.chat.chatbot.ChatBotListener; -import org.springframework.ai.chat.chatbot.ChatBotResponse; +import org.springframework.ai.chat.service.ChatServiceListener; +import org.springframework.ai.chat.service.ChatServiceResponse; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.MessageType; import org.springframework.ai.chat.prompt.transformer.TransformerContentType; -import org.springframework.ai.chat.prompt.transformer.PromptContext; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; import org.springframework.ai.document.Document; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.util.CollectionUtils; @@ -33,43 +33,43 @@ /** * @author Christian Tzolov */ -public class VectorStoreChatMemoryChatBotListener implements ChatBotListener { +public class VectorStoreChatMemoryChatServiceListener implements ChatServiceListener { private final VectorStore vectorStore; private final Map additionalMetadata; - public VectorStoreChatMemoryChatBotListener(VectorStore vectorStore) { + public VectorStoreChatMemoryChatServiceListener(VectorStore vectorStore) { this(vectorStore, new HashMap<>()); } - public VectorStoreChatMemoryChatBotListener(VectorStore vectorStore, Map additionalMetadata) { + public VectorStoreChatMemoryChatServiceListener(VectorStore vectorStore, Map additionalMetadata) { this.vectorStore = vectorStore; this.additionalMetadata = additionalMetadata; } @Override - public void onStart(PromptContext promptContext) { + public void onStart(ChatServiceContext chatServiceContext) { - if (!CollectionUtils.isEmpty(promptContext.getPrompt().getInstructions())) { - List docs = toDocuments(promptContext.getPrompt().getInstructions(), - promptContext.getConversationId()); + if (!CollectionUtils.isEmpty(chatServiceContext.getPrompt().getInstructions())) { + List docs = toDocuments(chatServiceContext.getPrompt().getInstructions(), + chatServiceContext.getConversationId()); this.vectorStore.add(docs); } } @Override - public void onComplete(ChatBotResponse chatBotResponse) { - if (!CollectionUtils.isEmpty(chatBotResponse.getChatResponse().getResults())) { - List assistantMessages = chatBotResponse.getChatResponse() + public void onComplete(ChatServiceResponse chatServiceResponse) { + if (!CollectionUtils.isEmpty(chatServiceResponse.getChatResponse().getResults())) { + List assistantMessages = chatServiceResponse.getChatResponse() .getResults() .stream() .map(g -> (org.springframework.ai.chat.messages.Message) g.getOutput()) .toList(); List docs = toDocuments(assistantMessages, - chatBotResponse.getPromptContext().getConversationId()); + chatServiceResponse.getPromptContext().getConversationId()); this.vectorStore.add(docs); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/VectorStoreChatMemoryRetriever.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/VectorStoreChatMemoryRetriever.java similarity index 77% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/history/VectorStoreChatMemoryRetriever.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/memory/VectorStoreChatMemoryRetriever.java index aff050179e..7b476e7049 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/history/VectorStoreChatMemoryRetriever.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/memory/VectorStoreChatMemoryRetriever.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.chat.history; +package org.springframework.ai.chat.memory; import java.util.ArrayList; import java.util.List; @@ -22,9 +22,9 @@ import java.util.stream.Collectors; import org.springframework.ai.chat.messages.MessageType; +import org.springframework.ai.chat.prompt.transformer.AbstractPromptTransformer; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; import org.springframework.ai.chat.prompt.transformer.TransformerContentType; -import org.springframework.ai.chat.prompt.transformer.PromptContext; -import org.springframework.ai.chat.prompt.transformer.PromptTransformer; import org.springframework.ai.document.Document; import org.springframework.ai.model.Content; import org.springframework.ai.vectorstore.SearchRequest; @@ -34,7 +34,7 @@ /** * @author Christian Tzolov */ -public class VectorStoreChatMemoryRetriever implements PromptTransformer { +public class VectorStoreChatMemoryRetriever extends AbstractPromptTransformer { private final VectorStore vectorStore; @@ -56,11 +56,11 @@ public VectorStoreChatMemoryRetriever(VectorStore vectorStore, int topK, Map updatedContents = new ArrayList<>( - promptContext.getContents() != null ? promptContext.getContents() : List.of()); + chatServiceContext.getContents() != null ? chatServiceContext.getContents() : List.of()); - String query = promptContext.getPrompt() + String query = chatServiceContext.getPrompt() .getInstructions() .stream() .filter(m -> m.getMessageType() == MessageType.USER) @@ -70,7 +70,7 @@ public PromptContext transform(PromptContext promptContext) { var searchRequest = SearchRequest.query(query) .withTopK(this.topK) .withFilterExpression( - TransformerContentType.CONVERSATION_ID + "=='" + promptContext.getConversationId() + "'"); + TransformerContentType.CONVERSATION_ID + "=='" + chatServiceContext.getConversationId() + "'"); List documents = this.vectorStore.similaritySearch(searchRequest); @@ -82,7 +82,7 @@ public PromptContext transform(PromptContext promptContext) { updatedContents.addAll(documents); } - return PromptContext.from(promptContext).withContents(updatedContents).build(); + return ChatServiceContext.from(chatServiceContext).withContents(updatedContents).build(); } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/AbstractPromptTransformer.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/AbstractPromptTransformer.java new file mode 100644 index 0000000000..0636c95437 --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/AbstractPromptTransformer.java @@ -0,0 +1,39 @@ +/* + * Copyright 2024 - 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.chat.prompt.transformer; + +/** + * AbstractPromptTransformer is an abstract class that provides a base implementation of + * the PromptTransformer interface. It includes a name field and corresponding accessor + * methods, as well as a default implementation for the transform method. + * + * @author Mark Pollack + * @author Christian Tzolov + * @since 1.0.0 M1 + */ +public abstract class AbstractPromptTransformer implements PromptTransformer { + + private String name = getClass().getSimpleName(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/ChatServiceContext.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/ChatServiceContext.java new file mode 100644 index 0000000000..23191f8f33 --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/ChatServiceContext.java @@ -0,0 +1,195 @@ +/* + * Copyright 2024-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.chat.prompt.transformer; + +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.model.Content; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Represents the execution context for the {@link ChatService}. This context is used to + * pass initial parameters to the service and facilitate data sharing between different + * components within the service. + * + *

+ * The {@code ChatServiceContext} includes essential information such as the initial + * prompt and a conversation ID, which are crucial for the correct operation of the chat + * service. + *

+ * + * @author Mark Pollack + * @author Christian Tzolov + * @since 1.0.0 M1 + */ +public class ChatServiceContext { + + private Prompt prompt; // The most up-to-date prompt to use + + private List contents; // The most up-to-date data to use + + private List promptChanges; // The changes make due to transformations + + private String conversationId; + + /** + * Contextual data that can be shared between processing steps in a ChatService + * implementation. + */ + private Map context = new ConcurrentHashMap<>(); + + public ChatServiceContext(Prompt prompt) { + this(prompt, "default", new ArrayList<>()); + } + + public ChatServiceContext(Prompt prompt, String conversationId) { + this(prompt, conversationId, new ArrayList<>()); + } + + public ChatServiceContext(Prompt prompt, String conversationId, List contents) { + this.prompt = prompt; + this.conversationId = conversationId; + this.promptChanges = new ArrayList<>(); + this.promptChanges.add(new PromptChange(null, prompt, "none", "initial prompt")); + this.contents = contents; + } + + public Prompt getPrompt() { + return this.prompt; + } + + public void updatePrompt(Prompt prompt, String transformerName, String description) { + this.promptChanges.add(new PromptChange(this.prompt, prompt, transformerName, description)); + this.prompt = prompt; // set the new prompt as current + } + + public void addData(Content datum) { + this.contents.add(datum); + } + + public List getContents() { + return this.contents; + } + + public void setContents(List contents) { + this.contents = contents; + } + + public List getPromptChanges() { + return this.promptChanges; + } + + public String getConversationId() { + return this.conversationId; + } + + public Map getContext() { + return this.context; + } + + public static Builder from(ChatServiceContext chatServiceContext) { + return ChatServiceContext.builder() + .withContents(new ArrayList<>( + chatServiceContext.getContents() != null ? chatServiceContext.getContents() : List.of())) + .withPrompt(chatServiceContext.getPrompt().copy()) // deep copy + .withMetadata( + new HashMap<>(chatServiceContext.getContext() != null ? chatServiceContext.getContext() : Map.of())) + .withPromptChanges(new ArrayList<>( + chatServiceContext.getPromptChanges() != null ? chatServiceContext.getPromptChanges() : List.of())) + .withConversationId(chatServiceContext.getConversationId()); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private Prompt prompt; + + private List contents; + + private List promptChanges; + + private String conversationId; + + private Map context = new HashMap<>(); + + public Builder withPrompt(Prompt prompt) { + this.prompt = prompt; + return this; + } + + public Builder withContents(List contents) { + this.contents = new ArrayList<>(contents); + return this; + } + + public Builder withPromptChanges(List promptChanges) { + this.promptChanges = new ArrayList<>(promptChanges); + return this; + } + + public Builder withPromptChange(PromptChange promptChange) { + this.promptChanges.add(promptChange); + return this; + } + + public Builder withConversationId(String conversationId) { + this.conversationId = conversationId; + return this; + } + + public Builder withMetadata(Map context) { + this.context = new HashMap<>(context); + return this; + } + + public ChatServiceContext build() { + ChatServiceContext chatServiceContext = new ChatServiceContext(this.prompt, this.conversationId, + this.contents); + chatServiceContext.promptChanges = promptChanges; + chatServiceContext.context = context; + return chatServiceContext; + } + + } + + @Override + public String toString() { + return "ChatServiceContext{" + "prompt=" + prompt + ", contents=" + contents + ", promptHistory=" + + promptChanges + ", conversationId='" + conversationId + '\'' + ", metadata=" + context + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof ChatServiceContext that)) + return false; + return Objects.equals(prompt, that.prompt) && Objects.equals(contents, that.contents) + && Objects.equals(promptChanges, that.promptChanges) + && Objects.equals(conversationId, that.conversationId) && Objects.equals(context, that.context); + } + + @Override + public int hashCode() { + return Objects.hash(prompt, contents, promptChanges, conversationId, context); + } + +} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptChange.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptChange.java new file mode 100644 index 0000000000..b8e8e1e704 --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptChange.java @@ -0,0 +1,31 @@ +/* + * Copyright 2024 - 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.chat.prompt.transformer; + +import org.springframework.ai.chat.prompt.Prompt; + +/** + * The PromptChange class represents a change made to a Prompt object. It contains + * information about the original prompt, the revised prompt, the name of the transformer + * that made the change, and a description of the change. + * + * @author Mark Pollack + * @author Christian Tzolov + * @since 1.0.0 M1 + */ +public record PromptChange(Prompt original, Prompt revised, String transformerName, String description) { + +} \ No newline at end of file diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptContext.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptContext.java deleted file mode 100644 index 7cfce9f89a..0000000000 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptContext.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2024-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.chat.prompt.transformer; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.Content; - -/** - * The shared, at the moment, mutable, data structure that can be used to implement - * ChatBot functionality. - * - * @author Mark Pollack - * @author Christian Tzolov - * @since 1.0.0 - */ -public class PromptContext { - - private Prompt prompt; // The most up-to-date prompt to use - - private List contents; // The most up-to-date data to use - - private List promptHistory; - - private String conversationId = "default"; - - private Map metadata = new HashMap<>(); - - public PromptContext(Prompt prompt) { - this(prompt, new ArrayList<>()); - } - - public PromptContext(Prompt prompt, String conversationId) { - this(prompt, new ArrayList<>()); - this.conversationId = conversationId; - } - - public PromptContext(Prompt prompt, List contents) { - this.prompt = prompt; - this.promptHistory = new ArrayList<>(); - this.promptHistory.add(prompt); - this.contents = contents; - } - - public Prompt getPrompt() { - return prompt; - } - - public void setPrompt(Prompt prompt) { - this.prompt = prompt; - } - - public void addData(Content datum) { - this.contents.add(datum); - } - - public List getContents() { - return contents; - } - - public void setContents(List contents) { - this.contents = contents; - } - - public void addPromptHistory(Prompt prompt) { - this.promptHistory.add(prompt); - } - - public List getPromptHistory() { - return promptHistory; - } - - public String getConversationId() { - return conversationId; - } - - public Map getMetadata() { - return metadata; - } - - public static Builder from(PromptContext promptContext) { - return PromptContext.builder() - .withContents( - new ArrayList<>(promptContext.getContents() != null ? promptContext.getContents() : List.of())) - .withPrompt(promptContext.getPrompt().copy()) // deep copy - .withMetadata(new HashMap<>(promptContext.getMetadata() != null ? promptContext.getMetadata() : Map.of())) - .withPromptHistory(new ArrayList<>( - promptContext.getPromptHistory() != null ? promptContext.getPromptHistory() : List.of())) - .withConversationId(promptContext.getConversationId()); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private Prompt prompt; - - private List contents; - - private List promptHistory; - - private String conversationId; - - private Map metadata = new HashMap<>(); - - public Builder withPrompt(Prompt prompt) { - this.prompt = prompt; - return this; - } - - public Builder withContents(List contents) { - this.contents = new ArrayList<>(contents); - return this; - } - - public Builder withPromptHistory(List promptHistory) { - this.promptHistory = new ArrayList<>(promptHistory); - return this; - } - - public Builder addPromptHistory(Prompt prompt) { - this.promptHistory.add(prompt); - return this; - } - - public Builder withConversationId(String conversationId) { - this.conversationId = conversationId; - return this; - } - - public Builder withMetadata(Map metadata) { - this.metadata = new HashMap<>(metadata); - return this; - } - - public PromptContext build() { - PromptContext promptContext = new PromptContext(prompt, contents); - promptContext.promptHistory = promptHistory; - promptContext.conversationId = conversationId; - promptContext.metadata = metadata; - return promptContext; - } - - } - - @Override - public String toString() { - return "PromptContext{" + "prompt=" + prompt + ", contents=" + contents + ", promptHistory=" + promptHistory - + ", conversationId='" + conversationId + '\'' + ", metadata=" + metadata + '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof PromptContext that)) - return false; - return Objects.equals(prompt, that.prompt) && Objects.equals(contents, that.contents) - && Objects.equals(promptHistory, that.promptHistory) - && Objects.equals(conversationId, that.conversationId) && Objects.equals(metadata, that.metadata); - } - - @Override - public int hashCode() { - return Objects.hash(prompt, contents, promptHistory, conversationId, metadata); - } - -} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptTransformer.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptTransformer.java index 7d596a0e9e..67c74c5a3b 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptTransformer.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/PromptTransformer.java @@ -17,23 +17,24 @@ package org.springframework.ai.chat.prompt.transformer; /** - * Responsible for transforming a Prompt. The PromptContext contains the necessary data to - * make the transformation + * Responsible for transforming a Prompt. The ChatServiceContext contains the necessary + * data to make the transformation * - * Implementations may retrieve data and modify the Prompt object in the PromptContext as - * needed. + * Implementations may retrieve data and modify the Prompt object in the + * ChatServiceContext as needed. * * @author Mark Pollack - * @since 1.0 M1 + * @author Christian Tzolov + * @since 1.0.0 M1 */ @FunctionalInterface public interface PromptTransformer { /** - * Transforms the given PromptContext. - * @param context the PromptContext to transform - * @return the transformed PromptContext + * Transforms the given ChatServiceContext. + * @param context the ChatServiceContext to transform + * @return the transformed ChatServiceContext */ - PromptContext transform(PromptContext context); + ChatServiceContext transform(ChatServiceContext context); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java index 12e4aada2a..05d79438e4 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java @@ -32,11 +32,15 @@ * additional context to create a new prompt. The default user text contains the * placeholder names "question" and "context". The "question" placeholder is filled using * the value of the current UserMessage and the "context" placeholder is filled with - * Documents contained in the PromptContext's Nodes. + * Documents contained in the ChatServiceContext's Nodes. + * + * @author Mark Pollack + * @author Christian Tzolov + * @since 1.0.0 M1 */ -public class QuestionContextAugmentor implements PromptTransformer { +public class QuestionContextAugmentor extends AbstractPromptTransformer { - private static final String DEFAULT_USER_PROMPT_TEXT = """ + private static final String DEFAULT_USER_TEXT = """ "Context information is below.\\n" "---------------------\\n" "{context}\\n" @@ -48,16 +52,26 @@ public class QuestionContextAugmentor implements PromptTransformer { "Answer: " """; + private String userText; + + public QuestionContextAugmentor() { + this.userText = DEFAULT_USER_TEXT; + this.setName("QuestionContextAugmentor"); + } + + public String getUserText() { + return userText; + } + @Override - public PromptContext transform(PromptContext promptContext) { - String context = doCreateContext(promptContext.getContents()); - Map contextMap = doCreateContextMap(promptContext.getPrompt(), context); - Prompt prompt = doCreatePrompt(promptContext.getPrompt(), contextMap); - promptContext.setPrompt(prompt); - promptContext.addPromptHistory(prompt); // BUG? shouldn't this be original - // promptContext.getPrompt()? + public ChatServiceContext transform(ChatServiceContext chatServiceContext) { + String context = doCreateContext(chatServiceContext.getContents()); + Map contextMap = doCreateContextMap(chatServiceContext.getPrompt(), context); + Prompt prompt = doCreatePrompt(chatServiceContext.getPrompt(), contextMap); + chatServiceContext.updatePrompt(prompt, this.getName(), "Updated prompt with Q/A user text"); + // For now return the modified instance instead of a copy - return promptContext; + return chatServiceContext; } protected String doCreateContext(List data) { @@ -75,7 +89,7 @@ private Map doCreateContextMap(Prompt prompt, String context) { } protected Prompt doCreatePrompt(Prompt originalPrompt, Map contextMap) { - PromptTemplate promptTemplate = new PromptTemplate(DEFAULT_USER_PROMPT_TEXT); + PromptTemplate promptTemplate = new PromptTemplate(getUserText()); Message userMessageToAppend = promptTemplate.createMessage(contextMap); List messageList = originalPrompt.getInstructions() .stream() @@ -85,4 +99,33 @@ protected Prompt doCreatePrompt(Prompt originalPrompt, Map conte return new Prompt(messageList, (ChatOptions) originalPrompt.getOptions()); } + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String name; + + private String userText; + + public Builder withName(String name) { + this.name = name; + return this; + } + + public Builder withUserText(String userText) { + this.userText = userText; + return this; + } + + public QuestionContextAugmentor build() { + QuestionContextAugmentor instance = new QuestionContextAugmentor(); + instance.userText = this.userText != null ? this.userText : instance.userText; + instance.setName(this.name != null ? this.name : instance.getName()); + return instance; + } + + } + } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/TransformerContentType.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/TransformerContentType.java index 270e859435..b4839ecf9e 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/TransformerContentType.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/TransformerContentType.java @@ -17,7 +17,10 @@ package org.springframework.ai.chat.prompt.transformer; /** + * This class provides constants for different content types used by transformers. + * * @author Christian Tzolov + * @since 1.0.0 M1 */ public class TransformerContentType { diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/VectorStoreRetriever.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/VectorStoreRetriever.java index 8a9e58b1cc..30851f644a 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/VectorStoreRetriever.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/VectorStoreRetriever.java @@ -23,16 +23,29 @@ import org.springframework.ai.document.Document; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; -import org.springframework.ai.vectorstore.filter.Filter; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** - * Transforms the PromptContext by retrieving documents from a VectorStore + * A transformer class that retrieves documents from a {@link VectorStore} + * + *

+ * The {@code VectorStoreRetriever} leverages a {@link SearchRequest} to query the + * {@link VectorStore} and retrieve documents that are semantically similar to the user's + * input. These documents are then added to the {@link ChatServiceContext} for further + * processing. + *

+ * + * @see VectorStore + * @see SearchRequest + * @see ChatServiceContext + * @author Mark Pollack + * @author Christian Tzolov + * @since 1.0.0 M1 */ -public class VectorStoreRetriever implements PromptTransformer { +public class VectorStoreRetriever extends AbstractPromptTransformer { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -41,8 +54,13 @@ public class VectorStoreRetriever implements PromptTransformer { private final SearchRequest searchRequest; public VectorStoreRetriever(VectorStore vectorStore, SearchRequest searchRequest) { + this(vectorStore, searchRequest, "VectorStoreRetriever"); + } + + public VectorStoreRetriever(VectorStore vectorStore, SearchRequest searchRequest, String name) { this.vectorStore = vectorStore; this.searchRequest = searchRequest; + this.setName(name); } public VectorStore getVectorStore() { @@ -54,8 +72,8 @@ public SearchRequest getSearchRequest() { } @Override - public PromptContext transform(PromptContext promptContext) { - List instructions = promptContext.getPrompt().getInstructions(); + public ChatServiceContext transform(ChatServiceContext chatServiceContext) { + List instructions = chatServiceContext.getPrompt().getInstructions(); String userMessage = instructions.stream() .filter(m -> m.getMessageType() == MessageType.USER) .map(m -> m.getContent()) @@ -67,9 +85,9 @@ public PromptContext transform(PromptContext promptContext) { for (Document document : documents) { var content = new Document(document.getContent(), document.getMetadata()); // content.getMetadata().put(TransformerContentType.DOMAIN_DATA, true); - promptContext.addData(content); + chatServiceContext.addData(content); } - return promptContext; + return chatServiceContext; } @Override diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/ChatBot.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatService.java similarity index 54% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/ChatBot.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatService.java index ff4eeb5108..647ad9c3c0 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/ChatBot.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatService.java @@ -14,27 +14,26 @@ * limitations under the License. */ -package org.springframework.ai.chat.chatbot; +package org.springframework.ai.chat.service; -import org.springframework.ai.chat.prompt.transformer.PromptContext; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; /** - * A ChatBot encapsulates the logic to perform common AI use cases such as Retrieval - * Augmented Generation. + * A ChatService encapsulates the logic to implement AI use cases. * * @author Mark Pollack * @since 1.0 M1 */ -public interface ChatBot { +public interface ChatService { /** - * Call the chatbot to execute AI actions - * @param promptContext A shared data structure used by the ChatBot to perform - * processing of the Prompt. It includes the intial Prompt and a conversation ID at + * Call the service to execute AI actions + * @param chatServiceContext A data structure used by the ChatService to perform + * processing of the Prompt. It includes the initial Prompt and a conversation ID at * the start of execution. - * @return the ChatBotResponse that contains the ChatResponse and the latest - * PromptContext + * @return the ChatServiceResponse that contains the ChatResponse and the latest + * ChatServiceContext */ - ChatBotResponse call(PromptContext promptContext); + ChatServiceResponse call(ChatServiceContext chatServiceContext); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/ChatBotListener.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatServiceListener.java similarity index 61% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/ChatBotListener.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatServiceListener.java index 7e591b46db..8e8596cc00 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/ChatBotListener.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatServiceListener.java @@ -14,23 +14,23 @@ * limitations under the License. */ -package org.springframework.ai.chat.chatbot; +package org.springframework.ai.chat.service; -import org.springframework.ai.chat.prompt.transformer.PromptContext; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; /** - * The ChatBotListener is a callback interface that can be implemented by classes that - * want to be notified of the completion of a ChatBot execution. + * The ChatServiceListener is a callback interface that can be implemented by classes that + * want to be notified of the completion of a ChatService execution. * * @author Mark Pollack * @author Christian Tzolov */ -public interface ChatBotListener { +public interface ChatServiceListener { - default void onStart(PromptContext promptContext) { + default void onStart(ChatServiceContext chatServiceContext) { } - void onComplete(ChatBotResponse chatBotResponse); + void onComplete(ChatServiceResponse chatServiceResponse); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/ChatBotResponse.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatServiceResponse.java similarity index 54% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/ChatBotResponse.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatServiceResponse.java index 344d8d08b0..ecd46969d1 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/ChatBotResponse.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatServiceResponse.java @@ -14,33 +14,33 @@ * limitations under the License. */ -package org.springframework.ai.chat.chatbot; +package org.springframework.ai.chat.service; import org.springframework.ai.chat.ChatResponse; -import org.springframework.ai.chat.prompt.transformer.PromptContext; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; import java.util.Objects; /** - * Encapsulates the response from the ChatBot. Contains the most up-to-date PromptContext - * and the final ChatResponse + * Encapsulates the response from the ChatService. Contains the most up-to-date + * ChatServiceContext and the final ChatResponse * * @author Mark Pollack * @since 1.0 M1 */ -public class ChatBotResponse { +public class ChatServiceResponse { - private final PromptContext promptContext; + private final ChatServiceContext chatServiceContext; private final ChatResponse chatResponse; - public ChatBotResponse(PromptContext promptContext, ChatResponse chatResponse) { - this.promptContext = promptContext; + public ChatServiceResponse(ChatServiceContext chatServiceContext, ChatResponse chatResponse) { + this.chatServiceContext = chatServiceContext; this.chatResponse = chatResponse; } - public PromptContext getPromptContext() { - return promptContext; + public ChatServiceContext getPromptContext() { + return chatServiceContext; } public ChatResponse getChatResponse() { @@ -49,21 +49,23 @@ public ChatResponse getChatResponse() { @Override public String toString() { - return "ChatBotResponse{" + "promptContext=" + promptContext + ", chatResponse=" + chatResponse + '}'; + return "ChatServiceResponse{" + "chatServiceContext=" + chatServiceContext + ", chatResponse=" + chatResponse + + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof ChatBotResponse that)) + if (!(o instanceof ChatServiceResponse that)) return false; - return Objects.equals(promptContext, that.promptContext) && Objects.equals(chatResponse, that.chatResponse); + return Objects.equals(chatServiceContext, that.chatServiceContext) + && Objects.equals(chatResponse, that.chatResponse); } @Override public int hashCode() { - return Objects.hash(promptContext, chatResponse); + return Objects.hash(chatServiceContext, chatResponse); } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/DefaultChatBot.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/PromptTransformingChatService.java similarity index 50% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/DefaultChatBot.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/service/PromptTransformingChatService.java index ebbac06caf..761ae5f429 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/DefaultChatBot.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/PromptTransformingChatService.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.ai.chat.chatbot; +package org.springframework.ai.chat.service; import org.springframework.ai.chat.ChatClient; import org.springframework.ai.chat.ChatResponse; -import org.springframework.ai.chat.prompt.transformer.PromptContext; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; import org.springframework.ai.chat.prompt.transformer.PromptTransformer; import java.util.ArrayList; @@ -25,10 +25,15 @@ import java.util.Objects; /** + * A PromptTransformingChatService implements the ChatService interface and performs + * transformation of the prompt using a series of PromptTransformers. It also provides a + * builder class for easier construction of the PromptTransformingChatService instance. + * * @author Mark Pollack * @author Christian Tzolov + * @since 1.0 M1 */ -public class DefaultChatBot implements ChatBot { +public class PromptTransformingChatService implements ChatService { private ChatClient chatClient; @@ -38,60 +43,60 @@ public class DefaultChatBot implements ChatBot { private List augmentors; - private List chatBotListeners; + private List chatServiceListeners; - public DefaultChatBot(ChatClient chatClient, List retrievers, + public PromptTransformingChatService(ChatClient chatClient, List retrievers, List documentPostProcessors, List augmentors, - List chatBotListeners) { + List chatServiceListeners) { Objects.requireNonNull(chatClient, "chatClient must not be null"); this.chatClient = chatClient; this.retrievers = retrievers; this.documentPostProcessors = documentPostProcessors; this.augmentors = augmentors; - this.chatBotListeners = chatBotListeners; + this.chatServiceListeners = chatServiceListeners; } - public static DefaultChatBotBuilder builder(ChatClient chatClient) { - return new DefaultChatBotBuilder().withChatClient(chatClient); + public static Builder builder(ChatClient chatClient) { + return new Builder().withChatClient(chatClient); } @Override - public ChatBotResponse call(PromptContext promptContext) { + public ChatServiceResponse call(ChatServiceContext chatServiceContext) { - PromptContext promptContextOnStart = PromptContext.from(promptContext).build(); + ChatServiceContext chatServiceContextOnStart = ChatServiceContext.from(chatServiceContext).build(); // Perform retrieval of documents and messages for (PromptTransformer retriever : this.retrievers) { - promptContext = retriever.transform(promptContext); + chatServiceContext = retriever.transform(chatServiceContext); } // Perform post processing of all retrieved documents and messages for (PromptTransformer documentPostProcessor : this.documentPostProcessors) { - promptContext = documentPostProcessor.transform(promptContext); + chatServiceContext = documentPostProcessor.transform(chatServiceContext); } // Perform prompt augmentation for (PromptTransformer augmentor : this.augmentors) { - promptContext = augmentor.transform(promptContext); + chatServiceContext = augmentor.transform(chatServiceContext); } // Invoke Listeners onStart - for (ChatBotListener listener : this.chatBotListeners) { - listener.onStart(promptContextOnStart); + for (ChatServiceListener listener : this.chatServiceListeners) { + listener.onStart(chatServiceContextOnStart); } // Perform generation - ChatResponse chatResponse = this.chatClient.call(promptContext.getPrompt()); + ChatResponse chatResponse = this.chatClient.call(chatServiceContext.getPrompt()); // Invoke Listeners onComplete - ChatBotResponse chatBotResponse = new ChatBotResponse(promptContext, chatResponse); - for (ChatBotListener listener : this.chatBotListeners) { - listener.onComplete(chatBotResponse); + ChatServiceResponse chatServiceResponse = new ChatServiceResponse(chatServiceContext, chatResponse); + for (ChatServiceListener listener : this.chatServiceListeners) { + listener.onComplete(chatServiceResponse); } - return chatBotResponse; + return chatServiceResponse; } - public static class DefaultChatBotBuilder { + public static class Builder { private ChatClient chatClient; @@ -101,35 +106,36 @@ public static class DefaultChatBotBuilder { private List augmentors = new ArrayList<>(); - private List chatBotListeners = new ArrayList<>(); + private List chatServiceListeners = new ArrayList<>(); - public DefaultChatBotBuilder withChatClient(ChatClient chatClient) { + public Builder withChatClient(ChatClient chatClient) { this.chatClient = chatClient; return this; } - public DefaultChatBotBuilder withRetrievers(List retrievers) { + public Builder withRetrievers(List retrievers) { this.retrievers = retrievers; return this; } - public DefaultChatBotBuilder withContentPostProcessors(List documentPostProcessors) { + public Builder withContentPostProcessors(List documentPostProcessors) { this.documentPostProcessors = documentPostProcessors; return this; } - public DefaultChatBotBuilder withAugmentors(List augmentors) { + public Builder withAugmentors(List augmentors) { this.augmentors = augmentors; return this; } - public DefaultChatBotBuilder withChatBotListeners(List chatBotListeners) { - this.chatBotListeners = chatBotListeners; + public Builder withChatServiceListeners(List chatServiceListeners) { + this.chatServiceListeners = chatServiceListeners; return this; } - public DefaultChatBot build() { - return new DefaultChatBot(chatClient, retrievers, documentPostProcessors, augmentors, chatBotListeners); + public PromptTransformingChatService build() { + return new PromptTransformingChatService(chatClient, retrievers, documentPostProcessors, augmentors, + chatServiceListeners); } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/StreamingChatBot.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/StreamingChatService.java similarity index 51% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/StreamingChatBot.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/service/StreamingChatService.java index d5d4843b15..7abb8e63e4 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/StreamingChatBot.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/StreamingChatService.java @@ -13,28 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.ai.chat.chatbot; +package org.springframework.ai.chat.service; -import org.springframework.ai.chat.prompt.transformer.PromptContext; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; /** - * A ChatBot encapsulates the logic to perform common AI use cases such as Retrieval - * Augmented Generation. + * A ChatService encapsulates the logic to implement AI use cases. * * @author Mark Pollack * @author Christian Tzolov * @since 1.0 M1 */ -public interface StreamingChatBot { +public interface StreamingChatService { /** - * Call the chatbot to execute AI actions - * @param promptContext A shared data structure used by the ChatBot to perform - * processing of the Prompt. It includes the intial Prompt and a conversation ID at - * the start of execution. - * @return the StreamingChatBotResponse that contains the ChatResponse and the latest - * PromptContext + * Call the service to execute AI actions + * @param chatServiceContext A shared data structure used by the ChatService to + * perform processing of the Prompt. It includes the intial Prompt and a conversation + * ID at the start of execution. + * @return the StreamingChatServiceResponse that contains the ChatResponse and the + * latest ChatServiceContext */ - StreamingChatBotResponse stream(PromptContext promptContext); + StreamingChatServiceResponse stream(ChatServiceContext chatServiceContext); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/service/StreamingChatServiceResponse.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/StreamingChatServiceResponse.java new file mode 100644 index 0000000000..5b97e127e7 --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/StreamingChatServiceResponse.java @@ -0,0 +1,73 @@ +package org.springframework.ai.chat.service; + +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; +import reactor.core.publisher.Flux; + +import org.springframework.ai.chat.ChatResponse; + +/** + * Encapsulates the response from the ChatService. Contains the most up-to-date + * ChatServiceContext and the final ChatResponse + * + * @author Mark Pollack + * @since 1.0 M1 + */ +public class StreamingChatServiceResponse { + + private final ChatServiceContext chatServiceContext; + + private final Flux chatResponse; + + public StreamingChatServiceResponse(ChatServiceContext chatServiceContext, Flux chatResponse) { + this.chatServiceContext = chatServiceContext; + this.chatResponse = chatResponse; + } + + public ChatServiceContext getPromptContext() { + return chatServiceContext; + } + + public Flux getChatResponse() { + return chatResponse; + } + + @Override + public String toString() { + return "ChatServiceResponse{" + "chatServiceContext=" + chatServiceContext + ", chatResponse=" + chatResponse + + '}'; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((chatServiceContext == null) ? 0 : chatServiceContext.hashCode()); + result = prime * result + ((chatResponse == null) ? 0 : chatResponse.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + StreamingChatServiceResponse other = (StreamingChatServiceResponse) obj; + if (chatServiceContext == null) { + if (other.chatServiceContext != null) + return false; + } + else if (!chatServiceContext.equals(other.chatServiceContext)) + return false; + if (chatResponse == null) { + if (other.chatResponse != null) + return false; + } + else if (!chatResponse.equals(other.chatResponse)) + return false; + return true; + } + +} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/DefaultStreamingChatBot.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/StreamingPromptTransformingChatService.java similarity index 54% rename from spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/DefaultStreamingChatBot.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/service/StreamingPromptTransformingChatService.java index bdc79d0b8e..c942415310 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/chatbot/DefaultStreamingChatBot.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/StreamingPromptTransformingChatService.java @@ -13,25 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.ai.chat.chatbot; +package org.springframework.ai.chat.service; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; import reactor.core.publisher.Flux; import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.StreamingChatClient; import org.springframework.ai.chat.messages.MessageAggregator; -import org.springframework.ai.chat.prompt.transformer.PromptContext; import org.springframework.ai.chat.prompt.transformer.PromptTransformer; /** * @author Mark Pollack * @author Christian Tzolov */ -public class DefaultStreamingChatBot implements StreamingChatBot { +public class StreamingPromptTransformingChatService implements StreamingChatService { private StreamingChatClient streamingChatClient; @@ -41,63 +41,63 @@ public class DefaultStreamingChatBot implements StreamingChatBot { private List augmentors; - private List chatBotListeners; + private List chatServiceListeners; - public DefaultStreamingChatBot(StreamingChatClient chatClient, List retrievers, + public StreamingPromptTransformingChatService(StreamingChatClient chatClient, List retrievers, List documentPostProcessors, List augmentors, - List chatBotListeners) { + List chatServiceListeners) { Objects.requireNonNull(chatClient, "chatClient must not be null"); this.streamingChatClient = chatClient; this.retrievers = retrievers; this.documentPostProcessors = documentPostProcessors; this.augmentors = augmentors; - this.chatBotListeners = chatBotListeners; + this.chatServiceListeners = chatServiceListeners; } - public static DefaultChatBotBuilder builder(StreamingChatClient chatClient) { - return new DefaultChatBotBuilder().withChatClient(chatClient); + public static Builder builder(StreamingChatClient chatClient) { + return new Builder().withChatClient(chatClient); } @Override - public StreamingChatBotResponse stream(PromptContext promptContext) { + public StreamingChatServiceResponse stream(ChatServiceContext chatServiceContext) { - PromptContext promptContextOnStart = PromptContext.from(promptContext).build(); + ChatServiceContext chatServiceContextOnStart = ChatServiceContext.from(chatServiceContext).build(); // Perform retrieval of documents and messages for (PromptTransformer retriever : this.retrievers) { - promptContext = retriever.transform(promptContext); + chatServiceContext = retriever.transform(chatServiceContext); } // Perform post processing of all retrieved documents and messages for (PromptTransformer documentPostProcessor : this.documentPostProcessors) { - promptContext = documentPostProcessor.transform(promptContext); + chatServiceContext = documentPostProcessor.transform(chatServiceContext); } // Perform prompt augmentation for (PromptTransformer augmentor : this.augmentors) { - promptContext = augmentor.transform(promptContext); + chatServiceContext = augmentor.transform(chatServiceContext); } // Invoke Listeners onStart - for (ChatBotListener listener : this.chatBotListeners) { - listener.onStart(promptContextOnStart); + for (ChatServiceListener listener : this.chatServiceListeners) { + listener.onStart(chatServiceContextOnStart); } // Perform generation - final var promptContext2 = promptContext; + final var promptContext2 = chatServiceContext; Flux fluxChatResponse = new MessageAggregator() - .aggregate(this.streamingChatClient.stream(promptContext.getPrompt()), chatResponse -> { - for (ChatBotListener listener : this.chatBotListeners) { - listener.onComplete(new ChatBotResponse(promptContext2, chatResponse)); + .aggregate(this.streamingChatClient.stream(chatServiceContext.getPrompt()), chatResponse -> { + for (ChatServiceListener listener : this.chatServiceListeners) { + listener.onComplete(new ChatServiceResponse(promptContext2, chatResponse)); } }); // Invoke Listeners onComplete - return new StreamingChatBotResponse(promptContext, fluxChatResponse); + return new StreamingChatServiceResponse(chatServiceContext, fluxChatResponse); } - public static class DefaultChatBotBuilder { + public static class Builder { private StreamingChatClient chatClient; @@ -107,36 +107,36 @@ public static class DefaultChatBotBuilder { private List augmentors = new ArrayList<>(); - private List chatBotListeners = new ArrayList<>(); + private List chatServiceListeners = new ArrayList<>(); - public DefaultChatBotBuilder withChatClient(StreamingChatClient chatClient) { + public Builder withChatClient(StreamingChatClient chatClient) { this.chatClient = chatClient; return this; } - public DefaultChatBotBuilder withRetrievers(List retrievers) { + public Builder withRetrievers(List retrievers) { this.retrievers = retrievers; return this; } - public DefaultChatBotBuilder withDocumentPostProcessors(List documentPostProcessors) { + public Builder withDocumentPostProcessors(List documentPostProcessors) { this.documentPostProcessors = documentPostProcessors; return this; } - public DefaultChatBotBuilder withAugmentors(List augmentors) { + public Builder withAugmentors(List augmentors) { this.augmentors = augmentors; return this; } - public DefaultChatBotBuilder withChatBotListeners(List chatBotListeners) { - this.chatBotListeners = chatBotListeners; + public Builder withChatServiceListeners(List chatServiceListeners) { + this.chatServiceListeners = chatServiceListeners; return this; } - public DefaultStreamingChatBot build() { - return new DefaultStreamingChatBot(chatClient, retrievers, documentPostProcessors, augmentors, - chatBotListeners); + public StreamingPromptTransformingChatService build() { + return new StreamingPromptTransformingChatService(chatClient, retrievers, documentPostProcessors, + augmentors, chatServiceListeners); } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/evaluation/EvaluationRequest.java b/spring-ai-core/src/main/java/org/springframework/ai/evaluation/EvaluationRequest.java index 657370e86f..9d30bdfab4 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/evaluation/EvaluationRequest.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/evaluation/EvaluationRequest.java @@ -1,7 +1,7 @@ package org.springframework.ai.evaluation; import org.springframework.ai.chat.ChatResponse; -import org.springframework.ai.chat.chatbot.ChatBotResponse; +import org.springframework.ai.chat.service.ChatServiceResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.model.Content; @@ -16,9 +16,9 @@ public class EvaluationRequest { private final ChatResponse chatResponse; - public EvaluationRequest(ChatBotResponse chatBotResponse) { - this(chatBotResponse.getPromptContext().getPromptHistory().get(0), - chatBotResponse.getPromptContext().getContents(), chatBotResponse.getChatResponse()); + public EvaluationRequest(ChatServiceResponse chatServiceResponse) { + this(chatServiceResponse.getPromptContext().getPromptChanges().get(0).revised(), + chatServiceResponse.getPromptContext().getContents(), chatServiceResponse.getChatResponse()); } public EvaluationRequest(Prompt prompt, List dataList, ChatResponse chatResponse) { diff --git a/spring-ai-core/src/main/java/org/springframework/ai/model/Content.java b/spring-ai-core/src/main/java/org/springframework/ai/model/Content.java index a221d7f47c..4ecaf351ba 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/model/Content.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/model/Content.java @@ -19,7 +19,7 @@ public interface Content { /** * Get the content of the message. */ - String getContent(); + String getContent(); // TODO consider getText /** * Get the media associated with the content. diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/history/ChatMemoryTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/memory/ChatMemoryTests.java similarity index 78% rename from spring-ai-core/src/test/java/org/springframework/ai/chat/history/ChatMemoryTests.java rename to spring-ai-core/src/test/java/org/springframework/ai/chat/memory/ChatMemoryTests.java index f1b2fca904..741fd53418 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/chat/history/ChatMemoryTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/memory/ChatMemoryTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.chat.history; +package org.springframework.ai.chat.memory; import java.util.List; @@ -29,12 +29,12 @@ import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.Generation; import org.springframework.ai.chat.StreamingChatClient; -import org.springframework.ai.chat.chatbot.ChatBotResponse; -import org.springframework.ai.chat.chatbot.DefaultChatBot; +import org.springframework.ai.chat.service.ChatServiceResponse; +import org.springframework.ai.chat.service.PromptTransformingChatService; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.transformer.PromptContext; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; import org.springframework.ai.model.Content; import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; @@ -61,15 +61,15 @@ public void chatMemoryMessageListAugmentor() { ChatMemory chatHistory = new InMemoryChatMemory(); - DefaultChatBot chatBot = DefaultChatBot.builder(chatClient) - .withRetrievers(List.of(new ChatMemoryRetriever(chatHistory))) + PromptTransformingChatService chatService = PromptTransformingChatService.builder(chatClient) + .withRetrievers(List.of(ChatMemoryRetriever.builder().withChatHistory(chatHistory).build())) .withContentPostProcessors( List.of(new LastMaxTokenSizeContentTransformer(new JTokkitTokenCountEstimator(), 10))) .withAugmentors(List.of(new MessageChatMemoryAugmentor())) - .withChatBotListeners(List.of(new ChatMemoryChatBotListener(chatHistory))) + .withChatServiceListeners(List.of(new ChatMemoryChatServiceListener(chatHistory))) .build(); - chatClientUserMessages(chatBot, chatHistory); + chatClientUserMessages(chatService, chatHistory); } @Test @@ -77,32 +77,32 @@ public void chatMemorySystemPromptAugmentor() { ChatMemory chatHistory = new InMemoryChatMemory(); - DefaultChatBot chatBot = DefaultChatBot.builder(chatClient) + PromptTransformingChatService chatService = PromptTransformingChatService.builder(chatClient) .withRetrievers(List.of(new ChatMemoryRetriever(chatHistory))) .withContentPostProcessors( List.of(new LastMaxTokenSizeContentTransformer(new JTokkitTokenCountEstimator(), 10))) .withAugmentors(List.of(new SystemPromptChatMemoryAugmentor())) - .withChatBotListeners(List.of(new ChatMemoryChatBotListener(chatHistory))) + .withChatServiceListeners(List.of(new ChatMemoryChatServiceListener(chatHistory))) .build(); - chatClientUserMessages(chatBot, chatHistory); + chatClientUserMessages(chatService, chatHistory); } - public void chatClientUserMessages(DefaultChatBot chatBot, ChatMemory chatHistory) { + public void chatClientUserMessages(PromptTransformingChatService chatService, ChatMemory chatHistory) { when(chatClient.call(promptCaptor.capture())) .thenReturn(new ChatResponse(List.of(new Generation("assistant:1")))) .thenReturn(new ChatResponse(List.of(new Generation("assistant:2")))) .thenReturn(new ChatResponse(List.of(new Generation("assistant:3")))); - var promptContext = PromptContext.builder() + var promptContext = ChatServiceContext.builder() .withConversationId("test-session-id") .withPrompt(new Prompt( List.of(new UserMessage("user:1"), new UserMessage("user:2"), new UserMessage("user:3"), new UserMessage("user:4"), new UserMessage("user:5")))) .build(); - ChatBotResponse response1 = chatBot.call(promptContext); + ChatServiceResponse response1 = chatService.call(promptContext); assertThat(response1.getChatResponse().getResult().getOutput().getContent()).isEqualTo("assistant:1"); @@ -112,7 +112,7 @@ public void chatClientUserMessages(DefaultChatBot chatBot, ChatMemory chatHistor List history = chatHistory.get("test-session-id", 1000); assertThat(history).hasSize(6); - ChatBotResponse response2 = chatBot.call(PromptContext.builder() + ChatServiceResponse response2 = chatService.call(ChatServiceContext.builder() .withConversationId("test-session-id") .withPrompt(new Prompt( List.of(new UserMessage("user:6"), new UserMessage("user:7"), new UserMessage("user:8")))) @@ -129,7 +129,7 @@ public void chatClientUserMessages(DefaultChatBot chatBot, ChatMemory chatHistor assertThat(contents.get(1).getContent()).isEqualTo("user:5"); assertThat(contents.get(2).getContent()).isEqualTo("assistant:1"); - ChatBotResponse response3 = chatBot.call(PromptContext.builder() + ChatServiceResponse response3 = chatService.call(ChatServiceContext.builder() .withConversationId("test-session-id") .withPrompt(new Prompt(List.of(new UserMessage("user:9")))).build()); assertThat(response3.getChatResponse().getResult().getOutput().getContent()).isEqualTo("assistant:3"); diff --git a/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BaseMemoryTest.java b/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BaseMemoryTest.java index 345c556125..6721d2bb62 100644 --- a/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BaseMemoryTest.java +++ b/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BaseMemoryTest.java @@ -22,11 +22,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ai.chat.chatbot.ChatBot; -import org.springframework.ai.chat.chatbot.StreamingChatBot; +import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; +import org.springframework.ai.chat.service.ChatService; +import org.springframework.ai.chat.service.StreamingChatService; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.transformer.PromptContext; import static org.assertj.core.api.Assertions.assertThat; @@ -39,48 +39,49 @@ public class BaseMemoryTest { protected RelevancyEvaluator relevancyEvaluator; - protected ChatBot chatBot; + protected ChatService chatService; - protected StreamingChatBot streamingChatBot; + protected StreamingChatService streamingChatService; - public BaseMemoryTest(RelevancyEvaluator relevancyEvaluator, ChatBot chatBot, - StreamingChatBot streamingChatClient) { + public BaseMemoryTest(RelevancyEvaluator relevancyEvaluator, ChatService chatService, + StreamingChatService streamingChatClient) { this.relevancyEvaluator = relevancyEvaluator; - this.chatBot = chatBot; - this.streamingChatBot = streamingChatClient; + this.chatService = chatService; + this.streamingChatService = streamingChatClient; } @Test - void memoryChatBot() { + void memoryChatService() { var prompt = new Prompt(new UserMessage("my name John Vincent Atanasoff")); - PromptContext promptContext = new PromptContext(prompt); + ChatServiceContext chatServiceContext = new ChatServiceContext(prompt); - var chatBotResponse1 = this.chatBot.call(promptContext); + var chatServiceResponse1 = this.chatService.call(chatServiceContext); - logger.info("Response1: " + chatBotResponse1.getChatResponse().getResult().getOutput().getContent()); + logger.info("Response1: " + chatServiceResponse1.getChatResponse().getResult().getOutput().getContent()); // response varies too much. - // assertThat(chatBotResponse1.getChatResponse().getResult().getOutput().getContent()).contains("John"); + // assertThat(chatServiceResponse1.getChatResponse().getResult().getOutput().getContent()).contains("John"); - var chatBotResponse2 = this.chatBot.call(new PromptContext(new Prompt(new String("What is my name?")))); - logger.info("Response2: " + chatBotResponse2.getChatResponse().getResult().getOutput().getContent()); - assertThat(chatBotResponse2.getChatResponse().getResult().getOutput().getContent()) + var chatServiceResponse2 = this.chatService + .call(new ChatServiceContext(new Prompt(new String("What is my name?")))); + logger.info("Response2: " + chatServiceResponse2.getChatResponse().getResult().getOutput().getContent()); + assertThat(chatServiceResponse2.getChatResponse().getResult().getOutput().getContent()) .contains("John Vincent Atanasoff"); EvaluationResponse evaluationResponse = this.relevancyEvaluator - .evaluate(new EvaluationRequest(chatBotResponse2)); + .evaluate(new EvaluationRequest(chatServiceResponse2)); logger.info("" + evaluationResponse); } @Test - void memoryStreamingChatBot() { + void memoryStreamingChatService() { var prompt = new Prompt(new UserMessage("my name John Vincent Atanasoff")); - PromptContext promptContext = new PromptContext(prompt); + ChatServiceContext chatServiceContext = new ChatServiceContext(prompt); - var fluxChatBotResponse1 = this.streamingChatBot.stream(promptContext); + var fluxChatServiceResponse1 = this.streamingChatService.stream(chatServiceContext); - String chatBotResponse1 = fluxChatBotResponse1.getChatResponse() + String chatServiceResponse1 = fluxChatServiceResponse1.getChatResponse() .collectList() .block() .stream() @@ -88,13 +89,13 @@ void memoryStreamingChatBot() { .map(response -> response.getResult().getOutput().getContent()) .collect(Collectors.joining()); - logger.info("Response1: " + chatBotResponse1); - // response varies too much assertThat(chatBotResponse1).contains("John"); + logger.info("Response1: " + chatServiceResponse1); + // response varies too much assertThat(chatServiceResponse1).contains("John"); - var fluxChatBotResponse2 = this.streamingChatBot - .stream(new PromptContext(new Prompt(new String("What is my name?")))); + var fluxChatServiceResponse2 = this.streamingChatService + .stream(new ChatServiceContext(new Prompt(new String("What is my name?")))); - String chatBotResponse2 = fluxChatBotResponse2.getChatResponse() + String chatServiceResponse2 = fluxChatServiceResponse2.getChatResponse() .collectList() .block() .stream() @@ -102,8 +103,8 @@ void memoryStreamingChatBot() { .map(response -> response.getResult().getOutput().getContent()) .collect(Collectors.joining()); - logger.info("Response2: " + chatBotResponse2); - assertThat(chatBotResponse2).contains("John Vincent Atanasoff"); + logger.info("Response2: " + chatServiceResponse2); + assertThat(chatServiceResponse2).contains("John Vincent Atanasoff"); } } From 82f473f2bc64619e6f99265645f8dd48492cdad2 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Wed, 15 May 2024 16:59:18 +0200 Subject: [PATCH 08/25] resolve javadoc reference issue --- .../ai/chat/prompt/transformer/ChatServiceContext.java | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/ChatServiceContext.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/ChatServiceContext.java index 23191f8f33..82868e5284 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/ChatServiceContext.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/ChatServiceContext.java @@ -17,6 +17,7 @@ package org.springframework.ai.chat.prompt.transformer; import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.chat.service.ChatService; import org.springframework.ai.model.Content; import java.util.*; From 227f0703ec63f873c08de98ee4d5b4ff1b43f702 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 16 May 2024 09:41:01 +0200 Subject: [PATCH 09/25] Fix: function calling not able to resolve input types Resolves #726 --- .../ai/model/function/TypeResolverHelper.java | 14 +-- .../function/StandaloneWeatherFunction.java | 34 +++++++ .../model/function/TypeResolverHelperIT.java | 90 +++++++++++++++++++ 3 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 spring-ai-core/src/test/java/org/springframework/ai/model/function/StandaloneWeatherFunction.java create mode 100644 spring-ai-core/src/test/java/org/springframework/ai/model/function/TypeResolverHelperIT.java diff --git a/spring-ai-core/src/main/java/org/springframework/ai/model/function/TypeResolverHelper.java b/spring-ai-core/src/main/java/org/springframework/ai/model/function/TypeResolverHelper.java index 7d40ebac71..f35411b157 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/model/function/TypeResolverHelper.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/model/function/TypeResolverHelper.java @@ -22,6 +22,8 @@ import net.jodah.typetools.TypeResolver; +import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; + /** * @author Christian Tzolov */ @@ -58,6 +60,12 @@ public static Type getFunctionArgumentType(Class> funct } public static Type getFunctionArgumentType(Type functionType, int argumentIndex) { + + // Resolves: https://github.com/spring-projects/spring-ai/issues/726 + if (!(functionType instanceof ParameterizedType)) { + functionType = FunctionTypeUtils.discoverFunctionTypeFromClass(FunctionTypeUtils.getRawType(functionType)); + } + var argumentType = functionType instanceof ParameterizedType ? ((ParameterizedType) functionType).getActualTypeArguments()[argumentIndex] : Object.class; @@ -77,10 +85,4 @@ public static Class toRawClass(Type type) { : null; } - // public static void main(String[] args) { - // Class> clazz = MockWeatherService.class; - // System.out.println(getFunctionInputType(clazz)); - - // } - } diff --git a/spring-ai-core/src/test/java/org/springframework/ai/model/function/StandaloneWeatherFunction.java b/spring-ai-core/src/test/java/org/springframework/ai/model/function/StandaloneWeatherFunction.java new file mode 100644 index 0000000000..b5ac63a520 --- /dev/null +++ b/spring-ai-core/src/test/java/org/springframework/ai/model/function/StandaloneWeatherFunction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2024-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.model.function; + +import java.util.function.Function; + +import org.springframework.ai.model.function.TypeResolverHelperIT.WeatherRequest; +import org.springframework.ai.model.function.TypeResolverHelperIT.WeatherResponse; + +/** + * @author Christian Tzolov + */ +public class StandaloneWeatherFunction implements Function { + + @Override + public WeatherResponse apply(WeatherRequest weatherRequest) { + return new WeatherResponse(42.0f); + } + +} diff --git a/spring-ai-core/src/test/java/org/springframework/ai/model/function/TypeResolverHelperIT.java b/spring-ai-core/src/test/java/org/springframework/ai/model/function/TypeResolverHelperIT.java new file mode 100644 index 0000000000..f4647be23c --- /dev/null +++ b/spring-ai-core/src/test/java/org/springframework/ai/model/function/TypeResolverHelperIT.java @@ -0,0 +1,90 @@ +/* + * 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.model.function; + +import java.lang.reflect.Type; +import java.util.function.Function; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.function.context.config.FunctionContextUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.support.GenericApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +class TypeResolverHelperIT { + + @Autowired + GenericApplicationContext applicationContext; + + @ParameterizedTest(name = "{0} : {displayName} ") + @ValueSource(strings = { "weatherClassDefinition", "weatherFunctionDefinition", "standaloneWeatherFunction" }) + void beanInputTypeResolutionTest(String beanName) { + assertThat(applicationContext).isNotNull(); + Type beanType = FunctionContextUtils.findType(applicationContext.getBeanFactory(), beanName); + assertThat(beanType).isNotNull(); + Type functionInputType = TypeResolverHelper.getFunctionArgumentType(beanType, 0); + assertThat(functionInputType).isNotNull(); + assertThat(functionInputType.getTypeName()).isEqualTo(WeatherRequest.class.getName()); + + } + + public record WeatherRequest(String city) { + } + + public record WeatherResponse(float temperatureInCelsius) { + } + + public static class Outer { + + public static class InnerWeatherFunction implements Function { + + @Override + public WeatherResponse apply(WeatherRequest weatherRequest) { + return new WeatherResponse(42.0f); + } + + } + + } + + @SpringBootConfiguration + public static class TypeResolverHelperConfiguration { + + @Bean() + Outer.InnerWeatherFunction weatherClassDefinition() { + return new Outer.InnerWeatherFunction(); + } + + @Bean() + Function weatherFunctionDefinition() { + return new Outer.InnerWeatherFunction(); + } + + @Bean() + StandaloneWeatherFunction standaloneWeatherFunction() { + return new StandaloneWeatherFunction(); + } + + } + +} From fd9c98661d42ffbd233b29b54f63fbf69af4826e Mon Sep 17 00:00:00 2001 From: Mark Pollack Date: Thu, 16 May 2024 12:29:47 +0200 Subject: [PATCH 10/25] API refactoring * Added more natural method names to DocumentReader, Transformer, Writer * Changed Content getMedia to return Collection, deprecated use of List * Change constructor for Media to accept URL and Resource, deprecate Object constructor * Update ETL documentation and a few tests to avoid now deprecated APIs. --- .../ai/anthropic/AnthropicChatClientIT.java | 4 +- .../BedrockAnthropic3ChatClientIT.java | 4 +- .../ollama/OllamaChatClientMultimodalIT.java | 4 +- .../ai/openai/chat/OpenAiChatClientIT.java | 17 +++--- ...OpenAiPromptTransformingChatServiceIT.java | 18 +++++- .../gemini/VertexAiGeminiChatClientIT.java | 4 +- .../ai/chat/messages/AbstractMessage.java | 33 +++++------ .../ai/chat/messages/Media.java | 34 +++++++++++ .../ai/chat/messages/UserMessage.java | 3 +- .../springframework/ai/document/Document.java | 7 +-- .../ai/document/DocumentReader.java | 4 ++ .../ai/document/DocumentTransformer.java | 4 ++ .../ai/document/DocumentWriter.java | 4 ++ .../org/springframework/ai/model/Content.java | 15 ++++- .../ai/transformer/splitter/TextSplitter.java | 8 +++ .../splitter/TokenTextSplitter.java | 4 +- .../modules/ROOT/pages/api/etl-pipeline.adoc | 58 ++++++++++++++----- 17 files changed, 165 insertions(+), 60 deletions(-) diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatClientIT.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatClientIT.java index 92ba0a6eb4..ce5b45d37e 100644 --- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatClientIT.java +++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatClientIT.java @@ -182,12 +182,12 @@ void beanStreamOutputConverterRecords() { @Test void multiModalityTest() throws IOException { - byte[] imageData = new ClassPathResource("/test.png").getContentAsByteArray(); + var imageData = new ClassPathResource("/test.png"); var userMessage = new UserMessage("Explain what do you see on this picture?", List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData))); - ChatResponse response = chatClient.call(new Prompt(List.of(userMessage))); + var response = chatClient.call(new Prompt(List.of(userMessage))); logger.info(response.getResult().getOutput().getContent()); assertThat(response.getResult().getOutput().getContent()).contains("bananas", "apple", "basket"); diff --git a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic3/BedrockAnthropic3ChatClientIT.java b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic3/BedrockAnthropic3ChatClientIT.java index 8a98b88219..9568f4f69d 100644 --- a/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic3/BedrockAnthropic3ChatClientIT.java +++ b/models/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic3/BedrockAnthropic3ChatClientIT.java @@ -206,12 +206,12 @@ void beanStreamOutputConverterRecords() { @Test void multiModalityTest() throws IOException { - byte[] imageData = new ClassPathResource("/test.png").getContentAsByteArray(); + var imageData = new ClassPathResource("/test.png"); var userMessage = new UserMessage("Explain what do you see o this picture?", List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData))); - ChatResponse response = client.call(new Prompt(List.of(userMessage))); + var response = client.call(new Prompt(List.of(userMessage))); logger.info(response.getResult().getOutput().getContent()); assertThat(response.getResult().getOutput().getContent()).contains("bananas", "apple", "basket"); diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientMultimodalIT.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientMultimodalIT.java index 4d587e005c..0061ea4b38 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientMultimodalIT.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientMultimodalIT.java @@ -71,12 +71,12 @@ public static void beforeAll() throws IOException, InterruptedException { @Test void multiModalityTest() throws IOException { - byte[] imageData = new ClassPathResource("/test.png").getContentAsByteArray(); + var imageData = new ClassPathResource("/test.png"); var userMessage = new UserMessage("Explain what do you see on this picture?", List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData))); - ChatResponse response = client.call(new Prompt(List.of(userMessage))); + var response = client.call(new Prompt(List.of(userMessage))); logger.info(response.getResult().getOutput().getContent()); assertThat(response.getResult().getOutput().getContent()).contains("bananas", "apple", "basket"); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java index 099a7394b9..0366aa626d 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java @@ -16,6 +16,7 @@ package org.springframework.ai.openai.chat; import java.io.IOException; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -249,12 +250,12 @@ void streamFunctionCallTest() { @ValueSource(strings = { "gpt-4-vision-preview", "gpt-4o" }) void multiModalityEmbeddedImage(String modelName) throws IOException { - byte[] imageData = new ClassPathResource("/test.png").getContentAsByteArray(); + var imageData = new ClassPathResource("/test.png"); var userMessage = new UserMessage("Explain what do you see on this picture?", List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData))); - ChatResponse response = chatClient + var response = chatClient .call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withModel(modelName).build())); logger.info(response.getResult().getOutput().getContent()); @@ -266,9 +267,9 @@ void multiModalityEmbeddedImage(String modelName) throws IOException { @ValueSource(strings = { "gpt-4-vision-preview", "gpt-4o" }) void multiModalityImageUrl(String modelName) throws IOException { - var userMessage = new UserMessage("Explain what do you see on this picture?", - List.of(new Media(MimeTypeUtils.IMAGE_PNG, - "https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png"))); + var userMessage = new UserMessage("Explain what do you see on this picture?", List + .of(new Media(MimeTypeUtils.IMAGE_PNG, + new URL("https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png")))); ChatResponse response = chatClient .call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withModel(modelName).build())); @@ -281,9 +282,9 @@ void multiModalityImageUrl(String modelName) throws IOException { @Test void streamingMultiModalityImageUrl() throws IOException { - var userMessage = new UserMessage("Explain what do you see on this picture?", - List.of(new Media(MimeTypeUtils.IMAGE_PNG, - "https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png"))); + var userMessage = new UserMessage("Explain what do you see on this picture?", List + .of(new Media(MimeTypeUtils.IMAGE_PNG, + new URL("https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/_images/multimodal.test.png")))); Flux response = streamingChatClient.stream(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_VISION_PREVIEW.getValue()).build())); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java index b164b8343d..be45cee3ef 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java @@ -17,6 +17,7 @@ package org.springframework.ai.openai.chat.service; import java.util.List; +import java.util.function.Supplier; import io.qdrant.client.QdrantClient; import io.qdrant.client.QdrantGrpcClient; @@ -112,7 +113,7 @@ void simpleChat() { void loadData() { JsonReader jsonReader = new JsonReader(bikesResource, "name", "price", "shortDescription", "description"); var textSplitter = new TokenTextSplitter(); - List splitDocuments = textSplitter.apply(jsonReader.get()); + List splitDocuments = textSplitter.split(jsonReader.get()); for (Document splitDocument : splitDocuments) { splitDocument.getMetadata().put(TransformerContentType.EXTERNAL_KNOWLEDGE, "true"); @@ -121,6 +122,21 @@ void loadData() { vectorStore.accept(splitDocuments); } + void loadData2() { + JsonReader jsonReader = null; + TokenTextSplitter tokenTextSplitter = null; + VectorStore vectorStore = null; + + List documents = jsonReader.read(); + List splitDocuments = tokenTextSplitter.split(documents); + vectorStore.write(splitDocuments); + + // Now in java.util.Function style. + + Supplier> docs = jsonReader::read; + + } + @SpringBootConfiguration static class Config { diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java index b9cfa126c4..9eff8981e3 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java @@ -186,12 +186,12 @@ void beanStreamOutputConverterRecords() { @Test void multiModalityTest() throws IOException { - byte[] data = new ClassPathResource("/vertex.test.png").getContentAsByteArray(); + var data = new ClassPathResource("/vertex.test.png"); var userMessage = new UserMessage("Explain what do you see o this picture?", List.of(new Media(MimeTypeUtils.IMAGE_PNG, data))); - ChatResponse response = client.call(new Prompt(List.of(userMessage))); + var response = client.call(new Prompt(List.of(userMessage))); // Response should contain something like: // I see a bunch of bananas in a golden basket. The bananas are ripe and yellow. diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessage.java index 3c0d7a8555..b58cb3c218 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessage.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/AbstractMessage.java @@ -18,12 +18,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import org.springframework.core.io.Resource; import org.springframework.util.Assert; @@ -44,7 +39,7 @@ public abstract class AbstractMessage implements Message { protected final String textContent; - protected final List mediaData; + protected final List media; /** * Additional options for the message to influence the response, not a generative map. @@ -59,25 +54,25 @@ protected AbstractMessage(MessageType messageType, String content, Map(); + this.media = new ArrayList<>(); this.metadata = new HashMap<>(metadata); this.metadata.put(MESSAGE_TYPE, messageType); } - protected AbstractMessage(MessageType messageType, String textContent, List mediaData) { - this(messageType, textContent, mediaData, Map.of(MESSAGE_TYPE, messageType)); + protected AbstractMessage(MessageType messageType, String textContent, List media) { + this(messageType, textContent, media, Map.of(MESSAGE_TYPE, messageType)); } - protected AbstractMessage(MessageType messageType, String textContent, List mediaData, + protected AbstractMessage(MessageType messageType, String textContent, Collection media, Map metadata) { Assert.notNull(messageType, "Message type must not be null"); Assert.notNull(textContent, "Content must not be null"); - Assert.notNull(mediaData, "media data must not be null"); + Assert.notNull(media, "media data must not be null"); this.messageType = messageType; this.textContent = textContent; - this.mediaData = new ArrayList<>(mediaData); + this.media = new ArrayList<>(media); this.metadata = new HashMap<>(metadata); this.metadata.put(MESSAGE_TYPE, messageType); } @@ -94,7 +89,7 @@ protected AbstractMessage(MessageType messageType, Resource resource, Map(metadata); this.metadata.put(MESSAGE_TYPE, messageType); - this.mediaData = new ArrayList<>(); + this.media = new ArrayList<>(); try (InputStream inputStream = resource.getInputStream()) { this.textContent = StreamUtils.copyToString(inputStream, Charset.defaultCharset()); @@ -110,8 +105,8 @@ public String getContent() { } @Override - public List getMedia() { - return this.mediaData; + public List getMedia(String... dummy) { + return this.media; } @Override @@ -126,7 +121,7 @@ public MessageType getMessageType() { @Override public int hashCode() { - return Objects.hash(this.messageType, this.textContent, this.mediaData, this.metadata); + return Objects.hash(this.messageType, this.textContent, this.media, this.metadata); } @Override @@ -139,8 +134,8 @@ public boolean equals(Object obj) { } AbstractMessage other = (AbstractMessage) obj; return Objects.equals(this.messageType, other.messageType) - && Objects.equals(this.textContent, other.textContent) - && Objects.equals(this.mediaData, other.mediaData) && Objects.equals(this.metadata, other.metadata); + && Objects.equals(this.textContent, other.textContent) && Objects.equals(this.media, other.media) + && Objects.equals(this.metadata, other.metadata); } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/Media.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/Media.java index ffe3779386..7077230b8c 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/Media.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/Media.java @@ -15,9 +15,13 @@ */ package org.springframework.ai.chat.messages; +import org.springframework.core.io.Resource; import org.springframework.util.Assert; import org.springframework.util.MimeType; +import java.io.IOException; +import java.net.URL; + /** * The Media class represents the data and metadata of a media attachment in a message. It * consists of a MIME type and the raw data. @@ -33,16 +37,46 @@ public class Media { private final Object data; + /** + * The Media class represents the data and metadata of a media attachment in a + * message. It consists of a MIME type and the raw data. + * + * This class is used as a parameter in the constructor of the UserMessage class. + * @deprecated This constructor is deprecated since version 1.0.0 M1 and will be + * removed in a future release. + */ + @Deprecated(since = "1.0.0 M1", forRemoval = true) public Media(MimeType mimeType, Object data) { Assert.notNull(mimeType, "MimeType must not be null"); this.mimeType = mimeType; this.data = data; } + public Media(MimeType mimeType, URL url) { + Assert.notNull(mimeType, "MimeType must not be null"); + this.mimeType = mimeType; + this.data = url.toString(); + } + + public Media(MimeType mimeType, Resource resource) { + Assert.notNull(mimeType, "MimeType must not be null"); + this.mimeType = mimeType; + try { + this.data = resource.getContentAsByteArray(); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + public MimeType getMimeType() { return this.mimeType; } + /** + * Get the media data object + * @return a java.net.URL.toString() or a byte[] + */ public Object getData() { return this.data; } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/UserMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/UserMessage.java index e792c985b1..85f071eeb5 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/UserMessage.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/messages/UserMessage.java @@ -16,6 +16,7 @@ package org.springframework.ai.chat.messages; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -44,7 +45,7 @@ public UserMessage(String textContent, Media... media) { this(textContent, Arrays.asList(media)); } - public UserMessage(String textContent, List mediaList, Map metadata) { + public UserMessage(String textContent, Collection mediaList, Map metadata) { super(MessageType.USER, textContent, mediaList, metadata); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/document/Document.java b/spring-ai-core/src/main/java/org/springframework/ai/document/Document.java index 30c4b479a7..dbc5fac8f9 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/document/Document.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/document/Document.java @@ -15,10 +15,7 @@ */ package org.springframework.ai.document; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -112,7 +109,7 @@ public String getContent() { } @Override - public List getMedia() { + public List getMedia(String... dummy) { return this.media; } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentReader.java b/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentReader.java index 9d93a218bb..75b4fe2b26 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentReader.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentReader.java @@ -20,4 +20,8 @@ public interface DocumentReader extends Supplier> { + default List read() { + return get(); + } + } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentTransformer.java b/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentTransformer.java index 1253d6816e..8c325a7bd0 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentTransformer.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentTransformer.java @@ -20,4 +20,8 @@ public interface DocumentTransformer extends Function, List> { + default List transform(List transform) { + return apply(transform); + } + } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentWriter.java b/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentWriter.java index b91dc9a2e3..31aeaf905a 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentWriter.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentWriter.java @@ -23,4 +23,8 @@ */ public interface DocumentWriter extends Consumer> { + default void write(List documents) { + accept(documents); + } + } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/model/Content.java b/spring-ai-core/src/main/java/org/springframework/ai/model/Content.java index 4ecaf351ba..ea1eb741a3 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/model/Content.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/model/Content.java @@ -2,6 +2,7 @@ import org.springframework.ai.chat.messages.Media; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -24,7 +25,19 @@ public interface Content { /** * Get the media associated with the content. */ - List getMedia(); + default Collection getMedia() { + return getMedia(""); + } + + /** + * Retrieves the collection of media attachments associated with the content. + * @param dummy a dummy parameter to ensure method signature uniqueness + * @return a list of Media objects representing the media attachments + * @deprecated This method is deprecated since version 1.0.0 M1 and will be removed in + * a future release + */ + @Deprecated(since = "1.0.0 M1", forRemoval = true) + List getMedia(String... dummy); /** * return Get the metadata associated with the content. diff --git a/spring-ai-core/src/main/java/org/springframework/ai/transformer/splitter/TextSplitter.java b/spring-ai-core/src/main/java/org/springframework/ai/transformer/splitter/TextSplitter.java index 9c5f0671b9..697643af2c 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/transformer/splitter/TextSplitter.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/transformer/splitter/TextSplitter.java @@ -43,6 +43,14 @@ public List apply(List documents) { return doSplitDocuments(documents); } + public List split(List documents) { + return this.apply(documents); + } + + public List split(Document document) { + return this.apply(List.of(document)); + } + public void setCopyContentFormatter(boolean copyContentFormatter) { this.copyContentFormatter = copyContentFormatter; } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/transformer/splitter/TokenTextSplitter.java b/spring-ai-core/src/main/java/org/springframework/ai/transformer/splitter/TokenTextSplitter.java index c820b51a3e..cc034b49cb 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/transformer/splitter/TokenTextSplitter.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/transformer/splitter/TokenTextSplitter.java @@ -68,10 +68,10 @@ public TokenTextSplitter(int defaultChunkSize, int minChunkSizeChars, int minChu @Override protected List splitText(String text) { - return split(text, this.defaultChunkSize); + return doSplit(text, this.defaultChunkSize); } - public List split(String text, int chunkSize) { + protected List doSplit(String text, int chunkSize) { if (text == null || text.trim().isEmpty()) { return new ArrayList<>(); } diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/etl-pipeline.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/etl-pipeline.adoc index d0656f1608..807d15f7bc 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/etl-pipeline.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/etl-pipeline.adoc @@ -26,12 +26,22 @@ Let's say we have the following instances of those three ETL types * `TokenTextSplitter` an implementation of `DocumentTransformer` * `VectorStore` an implementation of `DocumentWriter` -To perform the basic loading of data into a Vector Database for use with the Retrieval Augmented Generation pattern, use the following code. +To perform the basic loading of data into a Vector Database for use with the Retrieval Augmented Generation pattern, use the following code in Java function style syntax. + [source,java] ---- vectorStore.accept(tokenTextSplitter.apply(pdfReader.get())); ---- +Alternatively, you can use method names that are more naturally expressive for the domain + +[source,java] +---- +vectorStore.write(tokenTextSplitter.split(pdfReader.read())); +---- + + + == Getting Started To begin creating a Spring AI RAG application, follow these steps: @@ -57,6 +67,9 @@ Provides a source of documents from diverse origins. ---- public interface DocumentReader extends Supplier> { + default List read() { + return get(); + } } ---- @@ -68,14 +81,17 @@ Example: [source,java] ---- @Component -public class MyAiApp { +class MyAiAppComponent { + + private final Resource resource; - @Value("classpath:bikes.json") // This is the json document to load - private Resource resource; + MyAiAppComponent(@Value("classpath:bikes.json") Resource resource) { + this.resource = resource; + } List loadJsonAsDocuments() { JsonReader jsonReader = new JsonReader(resource, "description"); - return jsonReader.get(); + return jsonReader.read(); } } ---- @@ -88,16 +104,18 @@ Example: [source,java] ---- @Component -public class MyTextReader { +class MyTextReader { - @Value("classpath:text-source.txt") // This is the text document to load - private Resource resource; + private final Resource resource; + MyTextReader(@Value("classpath:text-source.txt") Resource resource) { + this.resource = resource; + } List loadText() { TextReader textReader = new TextReader(resource); textReader.getCustomMetadata().put("filename", "text-source.txt"); - return textReader.get(); + return textReader.read(); } } ---- @@ -123,7 +141,7 @@ public class MyPagePdfDocumentReader { .withPagesPerDocument(1) .build()); - return pdfReader.get(); + return pdfReader.read(); } } @@ -153,7 +171,7 @@ public class MyPagePdfDocumentReader { .withPagesPerDocument(1) .build()); - return pdfReader.get(); + return pdfReader.read(); } } ---- @@ -167,14 +185,18 @@ Example: [source,java] ---- @Component -public class MyTikaDocumentReader { +class MyTikaDocumentReader { - @Value("classpath:/word-sample.docx") // This is the word document to load - private Resource resource; + private final Resource resource; + + MyTikaDocumentReader(@Value("classpath:/word-sample.docx") + Resource resource) { + this.resource = resource; + } List loadText() { TikaDocumentReader tikaDocumentReader = new TikaDocumentReader(resource); - return tikaDocumentReader.get(); + return tikaDocumentReader.read(); } } ---- @@ -187,6 +209,9 @@ Transforms a batch of documents as part of the processing workflow. ---- public interface DocumentTransformer extends Function, List> { + default List transform(List transform) { + return apply(transform); + } } ---- @@ -213,6 +238,9 @@ Manages the final stage of the ETL process, preparing documents for storage. ```java public interface DocumentWriter extends Consumer> { + default void write(List documents) { + accept(documents); + } } ``` ==== FileDocumentWriter From 549c480489d556c0d61ba895d6248bae33648f3d Mon Sep 17 00:00:00 2001 From: Mark Pollack Date: Thu, 16 May 2024 14:01:20 +0200 Subject: [PATCH 11/25] Refactoring * Put creation of EvaluationRequest in ChatServiceResponse * Add string constructor to QuestionContextAugmentor * change vectorStore accept() usage to write() --- .../chat/service/LongShortTermChatMemoryWithRagIT.java | 2 +- .../service/OpenAiPromptTransformingChatServiceIT.java | 8 ++++---- .../chat/prompt/transformer/QuestionContextAugmentor.java | 6 +++++- .../ai/chat/service/ChatServiceResponse.java | 6 ++++++ .../springframework/ai/evaluation/EvaluationRequest.java | 5 ----- .../org/springframework/ai/evaluation/BaseMemoryTest.java | 2 +- 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/LongShortTermChatMemoryWithRagIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/LongShortTermChatMemoryWithRagIT.java index f8fc8f71f6..c2739a0188 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/LongShortTermChatMemoryWithRagIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/LongShortTermChatMemoryWithRagIT.java @@ -144,7 +144,7 @@ void memoryChatService() { assertThat(chatServiceResponse2.getChatResponse().getResult().getOutput().getContent()).contains("Christian"); EvaluationResponse evaluationResponse = this.relevancyEvaluator - .evaluate(new EvaluationRequest(chatServiceResponse2)); + .evaluate(chatServiceResponse2.toEvaluationRequest()); assertTrue(evaluationResponse.isPass(), "Response is not relevant to the question"); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java index be45cee3ef..a5f1ed415b 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/service/OpenAiPromptTransformingChatServiceIT.java @@ -104,8 +104,8 @@ void simpleChat() { .withModel(GPT_4_TURBO_PREVIEW.getValue()) .build(); var relevancyEvaluator = new RelevancyEvaluator(this.chatClient, openAiChatOptions); - EvaluationRequest evaluationRequest = new EvaluationRequest(chatServiceResponse); - EvaluationResponse evaluationResponse = relevancyEvaluator.evaluate(evaluationRequest); + + EvaluationResponse evaluationResponse = relevancyEvaluator.evaluate(chatServiceResponse.toEvaluationRequest()); assertTrue(evaluationResponse.isPass(), "Response is not relevant to the question"); } @@ -113,13 +113,13 @@ void simpleChat() { void loadData() { JsonReader jsonReader = new JsonReader(bikesResource, "name", "price", "shortDescription", "description"); var textSplitter = new TokenTextSplitter(); - List splitDocuments = textSplitter.split(jsonReader.get()); + List splitDocuments = textSplitter.split(jsonReader.read()); for (Document splitDocument : splitDocuments) { splitDocument.getMetadata().put(TransformerContentType.EXTERNAL_KNOWLEDGE, "true"); } - vectorStore.accept(splitDocuments); + vectorStore.write(splitDocuments); } void loadData2() { diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java index 05d79438e4..bdd4830df7 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/transformer/QuestionContextAugmentor.java @@ -55,7 +55,11 @@ public class QuestionContextAugmentor extends AbstractPromptTransformer { private String userText; public QuestionContextAugmentor() { - this.userText = DEFAULT_USER_TEXT; + this(DEFAULT_USER_TEXT); + } + + public QuestionContextAugmentor(String userText) { + this.userText = userText; this.setName("QuestionContextAugmentor"); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatServiceResponse.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatServiceResponse.java index ecd46969d1..436bd437a6 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatServiceResponse.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/service/ChatServiceResponse.java @@ -18,6 +18,7 @@ import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.prompt.transformer.ChatServiceContext; +import org.springframework.ai.evaluation.EvaluationRequest; import java.util.Objects; @@ -47,6 +48,11 @@ public ChatResponse getChatResponse() { return chatResponse; } + public EvaluationRequest toEvaluationRequest() { + return new EvaluationRequest(getPromptContext().getPromptChanges().get(0).revised(), + getPromptContext().getContents(), getChatResponse()); + } + @Override public String toString() { return "ChatServiceResponse{" + "chatServiceContext=" + chatServiceContext + ", chatResponse=" + chatResponse diff --git a/spring-ai-core/src/main/java/org/springframework/ai/evaluation/EvaluationRequest.java b/spring-ai-core/src/main/java/org/springframework/ai/evaluation/EvaluationRequest.java index 9d30bdfab4..0939a69818 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/evaluation/EvaluationRequest.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/evaluation/EvaluationRequest.java @@ -16,11 +16,6 @@ public class EvaluationRequest { private final ChatResponse chatResponse; - public EvaluationRequest(ChatServiceResponse chatServiceResponse) { - this(chatServiceResponse.getPromptContext().getPromptChanges().get(0).revised(), - chatServiceResponse.getPromptContext().getContents(), chatServiceResponse.getChatResponse()); - } - public EvaluationRequest(Prompt prompt, List dataList, ChatResponse chatResponse) { this.prompt = prompt; this.dataList = dataList; diff --git a/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BaseMemoryTest.java b/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BaseMemoryTest.java index 6721d2bb62..5fa6674816 100644 --- a/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BaseMemoryTest.java +++ b/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BaseMemoryTest.java @@ -69,7 +69,7 @@ void memoryChatService() { .contains("John Vincent Atanasoff"); EvaluationResponse evaluationResponse = this.relevancyEvaluator - .evaluate(new EvaluationRequest(chatServiceResponse2)); + .evaluate(chatServiceResponse2.toEvaluationRequest()); logger.info("" + evaluationResponse); } From 1b1daa7ee7e9a916a74b12fa9c72de6bd9575a74 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 16 May 2024 15:35:00 +0200 Subject: [PATCH 12/25] Add GEMINI_PRO_1_5_PRO and GEMINI_PRO_1_5_FLASH and update geminie maven BOM to 26.39.0 Also fix few Ollama docs and code formatting issue. --- .../ai/ollama/OllamaChatClient.java | 1 + .../ai/ollama/OllamaEmbeddingClient.java | 9 +++++++++ .../ai/ollama/OllamaChatClientMultimodalIT.java | 3 +-- .../ai/ollama/OllamaChatRequestTests.java | 6 +++--- .../gemini/VertexAiGeminiChatClient.java | 6 +++++- ...ertexAiGeminiChatClientFunctionCallingIT.java | 8 +++++--- pom.xml | 2 +- .../modules/ROOT/pages/api/chat/ollama-chat.adoc | 6 ++++-- .../ollama/OllamaAutoConfiguration.java | 7 ++----- .../tool/FunctionCallWithFunctionBeanIT.java | 16 ++++++++++++---- 10 files changed, 43 insertions(+), 21 deletions(-) diff --git a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatClient.java b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatClient.java index 26ef069bad..273d988667 100644 --- a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatClient.java +++ b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatClient.java @@ -87,6 +87,7 @@ public OllamaChatClient withModel(String model) { /** * @deprecated Use {@link OllamaOptions} constructor instead. */ + @Deprecated public OllamaChatClient withDefaultOptions(OllamaOptions options) { this.defaultOptions = options; return this; diff --git a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaEmbeddingClient.java b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaEmbeddingClient.java index 296498e502..1748709aac 100644 --- a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaEmbeddingClient.java +++ b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaEmbeddingClient.java @@ -66,6 +66,11 @@ public OllamaEmbeddingClient(OllamaApi ollamaApi) { this.ollamaApi = ollamaApi; } + public OllamaEmbeddingClient(OllamaApi ollamaApi, OllamaOptions defaultOptions) { + this.ollamaApi = ollamaApi; + this.defaultOptions = defaultOptions; + } + /** * @deprecated Use {@link OllamaOptions#setModel} instead. */ @@ -75,6 +80,10 @@ public OllamaEmbeddingClient withModel(String model) { return this; } + /** + * @deprecated Use {@link OllamaOptions} constructor instead. + */ + @Deprecated public OllamaEmbeddingClient withDefaultOptions(OllamaOptions options) { this.defaultOptions = options; return this; diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientMultimodalIT.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientMultimodalIT.java index 0061ea4b38..8599cfe9ed 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientMultimodalIT.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatClientMultimodalIT.java @@ -25,8 +25,8 @@ import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.ollama.OllamaContainer; -import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.messages.Media; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.Prompt; @@ -38,7 +38,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.core.io.ClassPathResource; import org.springframework.util.MimeTypeUtils; -import org.testcontainers.ollama.OllamaContainer; import static org.assertj.core.api.Assertions.assertThat; diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatRequestTests.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatRequestTests.java index 1a6b720c76..f78b8f2fc3 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatRequestTests.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatRequestTests.java @@ -30,7 +30,7 @@ */ public class OllamaChatRequestTests { - OllamaChatClient client = new OllamaChatClient(new OllamaApi()).withDefaultOptions( + OllamaChatClient client = new OllamaChatClient(new OllamaApi(), new OllamaOptions().withModel("MODEL_NAME").withTopK(99).withTemperature(66.6f).withNumGPU(1)); @Test @@ -105,8 +105,8 @@ public void createRequestWithPromptOptionsModelOverride() { @Test public void createRequestWithDefaultOptionsModelOverride() { - OllamaChatClient client2 = new OllamaChatClient(new OllamaApi()) - .withDefaultOptions(new OllamaOptions().withModel("DEFAULT_OPTIONS_MODEL")); + OllamaChatClient client2 = new OllamaChatClient(new OllamaApi(), + new OllamaOptions().withModel("DEFAULT_OPTIONS_MODEL")); var request = client2.ollamaChatRequest(new Prompt("Test message content"), true); diff --git a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java index dfb2a14bc5..4091cdd369 100644 --- a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java +++ b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java @@ -99,7 +99,11 @@ public enum ChatModel { GEMINI_PRO_VISION("gemini-pro-vision"), - GEMINI_PRO("gemini-pro"); + GEMINI_PRO("gemini-pro"), + + GEMINI_PRO_1_5_PRO("gemini-1.5-pro-preview-0514"), + + GEMINI_PRO_1_5_FLASH("gemini-1.5-flash-preview-0514"); ChatModel(String value) { this.value = value; diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java index 4b424db2c5..2026729e3a 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java @@ -68,7 +68,7 @@ public void afterEach() { } @Test - @Disabled("Google Vertex AI degraded support for parallel function calls") + // @Disabled("Google Vertex AI degraded support for parallel function calls") public void functionCallExplicitOpenApiSchema() { UserMessage userMessage = new UserMessage( @@ -98,7 +98,8 @@ public void functionCallExplicitOpenApiSchema() { """; var promptOptions = VertexAiGeminiChatOptions.builder() - .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) + // .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) + .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO.getValue()) .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) .withName("get_current_weather") .withDescription("Get the current weather in a given location") @@ -125,7 +126,8 @@ public void functionCallTestInferredOpenApiSchema() { List messages = new ArrayList<>(List.of(userMessage)); var promptOptions = VertexAiGeminiChatOptions.builder() - .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) + .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO.getValue()) + // .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) .withFunctionCallbacks(List.of( FunctionCallbackWrapper.builder(new MockWeatherService()) .withSchemaType(SchemaType.OPEN_API_SCHEMA) diff --git a/pom.xml b/pom.xml index 873b77ce21..32af3af00e 100644 --- a/pom.xml +++ b/pom.xml @@ -129,7 +129,7 @@ 2.16.1 0.26.0 1.17.0 - 26.37.0 + 26.39.0 1.9.1 2.0.5 9.20.0 diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/ollama-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/ollama-chat.adoc index 8071a745d8..de61f88518 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/ollama-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/ollama-chat.adoc @@ -249,8 +249,8 @@ Next, create an `OllamaChatClient` instance and use it to text generations reque ---- var ollamaApi = new OllamaApi(); -var chatClient = new OllamaChatClient(ollamaApi).withModel(MODEL) - .withDefaultOptions(OllamaOptions.create() +var chatClient = new OllamaChatClient(ollamaApi, + OllamaOptions.create() .withModel(OllamaOptions.DEFAULT_MODEL) .withTemperature(0.9f)); @@ -274,6 +274,8 @@ image::ollama-chat-completion-api.jpg[OllamaApi Chat Completion API Diagram, 800 Here is a simple snippet showing how to use the API programmatically: +NOTE: The `OllamaApi` is low level api and is not recommended for direct use. Use the `OllamaChatClient` instead. + [source,java] ---- OllamaApi ollamaApi = diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java index d3378ec88e..8b788e73bc 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java @@ -57,9 +57,7 @@ public OllamaApi ollamaApi(OllamaConnectionDetails connectionDetails, RestClient @ConditionalOnProperty(prefix = OllamaChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true) public OllamaChatClient ollamaChatClient(OllamaApi ollamaApi, OllamaChatProperties properties) { - - return new OllamaChatClient(ollamaApi).withModel(properties.getModel()) - .withDefaultOptions(properties.getOptions()); + return new OllamaChatClient(ollamaApi, properties.getOptions()); } @Bean @@ -68,8 +66,7 @@ public OllamaChatClient ollamaChatClient(OllamaApi ollamaApi, OllamaChatProperti matchIfMissing = true) public OllamaEmbeddingClient ollamaEmbeddingClient(OllamaApi ollamaApi, OllamaEmbeddingProperties properties) { - return new OllamaEmbeddingClient(ollamaApi).withModel(properties.getModel()) - .withDefaultOptions(properties.getOptions()); + return new OllamaEmbeddingClient(ollamaApi, properties.getOptions()); } private static class PropertiesOllamaConnectionDetails implements OllamaConnectionDetails { diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionBeanIT.java index d2066f0b2f..9084456758 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionBeanIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionBeanIT.java @@ -54,9 +54,10 @@ class FunctionCallWithFunctionBeanIT { @Test void functionCallTest() { - contextRunner - .withPropertyValues("spring.ai.vertex.ai.gemini.chat.options.model=" - + VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) + contextRunner.withPropertyValues("spring.ai.vertex.ai.gemini.chat.options.model=" + // + VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) + + VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO.getValue()) + // + VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_FLASH.getValue()) .run(context -> { VertexAiGeminiChatClient chatClient = context.getBean(VertexAiGeminiChatClient.class); @@ -67,15 +68,22 @@ void functionCallTest() { If the information was not fetched call the function again. Repeat at most 3 times. """); var userMessage = new UserMessage( - "What's the weather like in San Francisco, Paris and in Tokyo (Japan)?"); + // "What's the weather like in San Francisco, Paris and in Tokyo? + // Please let me know how many function calls you've preformed."); + "What's the weather like in San Francisco, Paris and in Tokyo?"); ChatResponse response = chatClient.call(new Prompt(List.of(systemMessage, userMessage), VertexAiGeminiChatOptions.builder().withFunction("weatherFunction").build())); + // ChatResponse response = chatClient.call(new + // Prompt(List.of(userMessage), + // VertexAiGeminiChatOptions.builder().withFunction("weatherFunction").build())); logger.info("Response: {}", response); assertThat(response.getResult().getOutput().getContent()).contains("30", "10", "15"); + Thread.sleep(10000); + response = chatClient.call(new Prompt(List.of(systemMessage, userMessage), VertexAiGeminiChatOptions.builder().withFunction("weatherFunction3").build())); From 7887159e68bc4c29b9ee3ab902a3ce7c5124cdc9 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 17 May 2024 06:43:47 +0200 Subject: [PATCH 13/25] Improve Gemini antora documentation and fix broken links --- .../gemini/VertexAiGeminiChatClient.java | 2 +- .../gemini/VertexAiGeminiChatOptions.java | 6 ++++++ .../gemini/VertexAiGeminiChatClientIT.java | 2 +- ...texAiGeminiChatClientFunctionCallingIT.java | 10 +++++----- .../images/vertex-ai-gemini-native-api.jpg | Bin 0 -> 457715 bytes .../ROOT/pages/api/chat/anthropic-chat.adoc | 2 +- .../ROOT/pages/api/chat/azure-openai-chat.adoc | 2 +- .../api/chat/bedrock/bedrock-anthropic.adoc | 2 +- .../api/chat/bedrock/bedrock-anthropic3.adoc | 2 +- .../pages/api/chat/bedrock/bedrock-cohere.adoc | 2 +- .../pages/api/chat/bedrock/bedrock-llama.adoc | 2 +- .../pages/api/chat/bedrock/bedrock-titan.adoc | 2 +- .../ROOT/pages/api/chat/mistralai-chat.adoc | 2 +- .../ROOT/pages/api/chat/ollama-chat.adoc | 2 +- .../ROOT/pages/api/chat/openai-chat.adoc | 2 +- .../pages/api/chat/vertexai-gemini-chat.adoc | 12 ++++++++++-- .../pages/api/chat/vertexai-palm2-chat.adoc | 2 +- .../ROOT/pages/api/chat/watsonx-ai-chat.adoc | 2 +- 18 files changed, 35 insertions(+), 21 deletions(-) create mode 100644 spring-ai-docs/src/main/antora/modules/ROOT/images/vertex-ai-gemini-native-api.jpg diff --git a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java index 4091cdd369..e30df03cef 100644 --- a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java +++ b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClient.java @@ -120,7 +120,7 @@ public String getValue() { public VertexAiGeminiChatClient(VertexAI vertexAI) { this(vertexAI, VertexAiGeminiChatOptions.builder() - .withModel(ChatModel.GEMINI_PRO_VISION.getValue()) + .withModel(ChatModel.GEMINI_PRO_VISION) .withTemperature(0.8f) .build()); } diff --git a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatOptions.java b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatOptions.java index 7d4e9875a5..311ff16af6 100644 --- a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatOptions.java +++ b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatOptions.java @@ -28,6 +28,7 @@ import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.model.function.FunctionCallingOptions; +import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatClient.ChatModel; import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.util.Assert; @@ -144,6 +145,11 @@ public Builder withModel(String modelName) { return this; } + public Builder withModel(ChatModel model) { + this.options.setModel(model.getValue()); + return this; + } + public Builder withFunctionCallbacks(List functionCallbacks) { this.options.functionCallbacks = functionCallbacks; return this; diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java index 9eff8981e3..58f687eb44 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatClientIT.java @@ -234,7 +234,7 @@ public VertexAI vertexAiApi() { public VertexAiGeminiChatClient vertexAiEmbedding(VertexAI vertexAi) { return new VertexAiGeminiChatClient(vertexAi, VertexAiGeminiChatOptions.builder() - .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_VISION.getValue()) + .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_VISION) .build()); } diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java index 2026729e3a..d5209e82e8 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java @@ -98,8 +98,8 @@ public void functionCallExplicitOpenApiSchema() { """; var promptOptions = VertexAiGeminiChatOptions.builder() - // .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) - .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO.getValue()) + // .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO) + .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO) .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) .withName("get_current_weather") .withDescription("Get the current weather in a given location") @@ -126,7 +126,7 @@ public void functionCallTestInferredOpenApiSchema() { List messages = new ArrayList<>(List.of(userMessage)); var promptOptions = VertexAiGeminiChatOptions.builder() - .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO.getValue()) + .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO) // .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) .withFunctionCallbacks(List.of( FunctionCallbackWrapper.builder(new MockWeatherService()) @@ -168,7 +168,7 @@ public void functionCallTestInferredOpenApiSchemaStream() { List messages = new ArrayList<>(List.of(userMessage)); var promptOptions = VertexAiGeminiChatOptions.builder() - .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) + .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO) .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) .withSchemaType(SchemaType.OPEN_API_SCHEMA) .withName("getCurrentWeather") @@ -227,7 +227,7 @@ public VertexAI vertexAiApi() { public VertexAiGeminiChatClient vertexAiEmbedding(VertexAI vertexAi) { return new VertexAiGeminiChatClient(vertexAi, VertexAiGeminiChatOptions.builder() - .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) + .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO) .withTemperature(0.9f) .build()); } diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/images/vertex-ai-gemini-native-api.jpg b/spring-ai-docs/src/main/antora/modules/ROOT/images/vertex-ai-gemini-native-api.jpg new file mode 100644 index 0000000000000000000000000000000000000000..68b44f1bbb89d9afe31a25982ea8a03e1f73b405 GIT binary patch literal 457715 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 z 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

})LKGKdCZM+gM(>$V{@MY%yG@2l#wQlRzj*L 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 - Offers a pre-configured link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/convert/support/GenericConversionService.html[GenericConversionService] for transforming LLM output into the desired format. No default `FormatProvider` implementation is provided. -* `AbstractMessageOutputConverter` - Supplies a pre-configured https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jms/support/converter/MessageConverter.html[MessageConverter] for converting LLM output into the desired format. No default `FormatProvider` implementation is provided. -* `BeanOutputConverter` - Configured with a designated Java class (e.g., Bean), this converter employs a `FormatProvider` implementation that directs the AI Model to produce a JSON response compliant with a `DRAFT_2020_12`, `JSON Schema` derived from the specified Java class. Subsequently, it utilizes an `ObjectMapper` to deserialize the JSON output into a Java object instance of the target class. +* `AbstractMessageOutputConverter` - Supplies a pre-configured https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jms/support/converter/MessageConverter.html[MessageConverter] for converting LLM output into the desired format. No default `FormatProvider` implementation is provided. +* `BeanOutputConverter` - Configured with a designated Java class (e.g., Bean) or a link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/ParameterizedTypeReference.html[ParameterizedTypeReference], this converter employs a `FormatProvider` implementation that directs the AI Model to produce a JSON response compliant with a `DRAFT_2020_12`, `JSON Schema` derived from the specified Java class. Subsequently, it utilizes an `ObjectMapper` to deserialize the JSON output into a Java object instance of the target class. * `MapOutputConverter` - Extends the functionality of `AbstractMessageOutputConverter` with a `FormatProvider` implementation that guides the AI Model to generate an RFC8259 compliant JSON response. Additionally, it incorporates a converter implementation that utilizes the provided `MessageConverter` to translate the JSON payload into a `java.util.Map` instance. * `ListOutputConverter` - Extends the `AbstractConversionServiceOutputConverter` and includes a `FormatProvider` implementation tailored for comma-delimited list output. The converter implementation employs the provided `ConversionService` to transform the model text output into a `java.util.List`. @@ -129,6 +129,29 @@ Generation generation = chatClient.call( ActorsFilms actorsFilms = beanOutputConverter.convert(generation.getOutput().getContent()); ---- +==== Generic Bean Types + +Use the `ParameterizedTypeReference` constructor to specify a more complex target class structure. +For example, to represent a list of actors and their filmographies: + +[source,java] +---- +BeanOutputConverter> outputConverter = new BeanOutputConverter<>( + new ParameterizedTypeReference>() { }); + +String format = outputConverter.getFormat(); +String template = """ + Generate the filmography of 5 movies for Tom Hanks and Bill Murray. + {format} + """; + +Prompt prompt = new Prompt(new PromptTemplate(template, Map.of("format", format)).createMessage()); + +Generation generation = chatClient.call(prompt).getResult(); + +List actorsFilms = outputConverter.convert(generation.getOutput().getContent()); +---- + === Map Output Converter Following sniped shows how to use `MapOutputConverter` to generate a list of numbers. From 275189970d84e3468083920e69b100c5b7f34129 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Sat, 18 May 2024 06:05:50 +0200 Subject: [PATCH 24/25] BeanOutputConverter: fix javadoc --- .../org/springframework/ai/converter/BeanOutputConverter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-ai-core/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java b/spring-ai-core/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java index aca2902b0b..04bb6d0d2f 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java @@ -69,8 +69,8 @@ public class BeanOutputConverter implements StructuredOutputConverter { * Constructor to initialize with the target type's class. * @param clazz The target type's class. */ - public BeanOutputConverter(Class typeClass) { - this(ParameterizedTypeReference.forType(typeClass)); + public BeanOutputConverter(Class clazz) { + this(ParameterizedTypeReference.forType(clazz)); } /** From 09e122de5e0e17ebfddfc86560c71087604ce9a6 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Sat, 18 May 2024 06:03:02 +0200 Subject: [PATCH 25/25] Add Mistral AI tool_call_id to ChatCompletionMessage. The tool call ID that this message is responding to, applicable for the TOOL role. --- .../ai/mistralai/MistralAiChatClient.java | 7 ++++--- .../ai/mistralai/api/MistralAiApi.java | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatClient.java b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatClient.java index 98a25025d6..9b7e19de09 100644 --- a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatClient.java +++ b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatClient.java @@ -250,7 +250,8 @@ protected ChatCompletionRequest doCreateToolResponseRequest(ChatCompletionReques // message. for (ToolCall toolCall : responseMessage.toolCalls()) { - var functionName = toolCall.function().name(); + String id = toolCall.id(); + String functionName = toolCall.function().name(); String functionArguments = toolCall.function().arguments(); if (!this.functionCallbackRegister.containsKey(functionName)) { @@ -260,8 +261,8 @@ protected ChatCompletionRequest doCreateToolResponseRequest(ChatCompletionReques String functionResponse = this.functionCallbackRegister.get(functionName).call(functionArguments); // Add the function response to the conversation. - conversationHistory - .add(new ChatCompletionMessage(functionResponse, ChatCompletionMessage.Role.TOOL, functionName, null)); + conversationHistory.add(new ChatCompletionMessage(functionResponse, ChatCompletionMessage.Role.TOOL, + functionName, null, id)); } // Recursively call chatCompletionWithTools until the model doesn't call a diff --git a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiApi.java b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiApi.java index 5732c871a6..16f2465c53 100644 --- a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiApi.java +++ b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiApi.java @@ -452,6 +452,8 @@ public record ResponseFormat(@JsonProperty("type") String type) { * types. * @param toolCalls The tool calls generated by the model, such as function calls. * Applicable only for {@link Role#ASSISTANT} role and null otherwise. + * @param toolCallId Tool call that this message is responding to. Only applicable for + * the {@link Role#TOOL} role and null otherwise. */ @JsonInclude(Include.NON_NULL) public record ChatCompletionMessage( @@ -459,9 +461,22 @@ public record ChatCompletionMessage( @JsonProperty("content") String content, @JsonProperty("role") Role role, @JsonProperty("name") String name, - @JsonProperty("tool_calls") List toolCalls) { + @JsonProperty("tool_calls") List toolCalls, + @JsonProperty("tool_call_id") String toolCallId) { // @formatter:on + /** + * Message comprising the conversation. + * @param content The contents of the message. + * @param role The role of the messages author. Could be one of the {@link Role} + * types. + * @param toolCalls The tool calls generated by the model, such as function calls. + * Applicable only for {@link Role#ASSISTANT} role and null otherwise. + */ + public ChatCompletionMessage(String content, Role role, String name, List toolCalls) { + this(content, role, name, toolCalls, null); + } + /** * Create a chat completion message with the given content and role. All other * fields are null. @@ -469,7 +484,7 @@ public record ChatCompletionMessage( * @param role The role of the author of this message. */ public ChatCompletionMessage(String content, Role role) { - this(content, role, null, null); + this(content, role, null, null, null); } /**

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 literal 0 HcmV?d00001 diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/anthropic-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/anthropic-chat.adoc index 6bb1870486..def68f7c26 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/anthropic-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/anthropic-chat.adoc @@ -128,7 +128,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatOptions.java[AnthropicChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatOptions.java[AnthropicChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Function Calling diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/azure-openai-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/azure-openai-chat.adoc index 000d7f4c4f..759aca4041 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/azure-openai-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/azure-openai-chat.adoc @@ -132,7 +132,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatOptions.java[AzureOpenAiChatOptions.java] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatOptions.java[AzureOpenAiChatOptions.java] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Function Calling diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-anthropic.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-anthropic.adoc index 806b5d9ebf..d6fc03d60c 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-anthropic.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-anthropic.adoc @@ -116,7 +116,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic/AnthropicChatOptions.java[AnthropicChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic/AnthropicChatOptions.java[AnthropicChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Sample Controller diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-anthropic3.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-anthropic3.adoc index af1ac613ae..3b4cd66af8 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-anthropic3.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-anthropic3.adoc @@ -113,7 +113,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic3/Anthropic3ChatOptions.java[AnthropicChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic3/Anthropic3ChatOptions.java[AnthropicChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Multimodal diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-cohere.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-cohere.adoc index 9f0c53592b..8133f3bc26 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-cohere.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-cohere.adoc @@ -109,7 +109,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatOptions.java[BedrockCohereChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatOptions.java[BedrockCohereChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Sample Controller diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-llama.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-llama.adoc index 1d04b2b8ff..435b71e47f 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-llama.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-llama.adoc @@ -107,7 +107,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/llama/BedrockLlamaChatOptions.java[BedrockLlamaChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/llama/BedrockLlamaChatOptions.java[BedrockLlamaChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Sample Controller diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-titan.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-titan.adoc index 7ba332e67a..38978fcfc2 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-titan.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock/bedrock-titan.adoc @@ -105,7 +105,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanChatOptions.java[BedrockTitanChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanChatOptions.java[BedrockTitanChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Sample Controller diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/mistralai-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/mistralai-chat.adoc index a53638d7bf..26fb6afeed 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/mistralai-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/mistralai-chat.adoc @@ -130,7 +130,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatOptions.java[MistralAiChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatOptions.java[MistralAiChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Function Calling diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/ollama-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/ollama-chat.adoc index de61f88518..0fb32b9975 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/ollama-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/ollama-chat.adoc @@ -126,7 +126,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaOptions.java[OllamaOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific link:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaOptions.java[OllamaOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Multimodal diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc index 99fd646346..440a4a6b6b 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/openai-chat.adoc @@ -134,7 +134,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatOptions.java[OpenAiChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatOptions.java[OpenAiChatOptions] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Function Calling diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-gemini-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-gemini-chat.adoc index 3b4b8944e8..ef66bcdf84 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-gemini-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-gemini-chat.adoc @@ -65,7 +65,7 @@ The prefix `spring.ai.vertex.ai.gemini.chat` is the property prefix that lets yo |==== | Property | Description | Default -| spring.ai.vertex.ai.gemini.chat.options.model | This is the https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini[Vertex AI Gemini Chat model] to use | gemini-pro-vision +| spring.ai.vertex.ai.gemini.chat.options.model | Supported https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini[Vertex AI Gemini Chat model] to use include the (1.0 ) `gemini-pro`, `gemini-pro-vision` and the new (1.5) `gemini-1.5-pro-preview-0514`, `gemini-1.5-flash-preview-0514` models. | gemini-pro-vision | spring.ai.vertex.ai.gemini.chat.options.temperature | Controls the randomness of the output. Values can range over [0.0,1.0], inclusive. A value closer to 1.0 will produce responses that are more varied, while a value closer to 0.0 will typically result in less surprising responses from the generative. This value specifies default to be used by the backend while making the call to the generative. | 0.8 | spring.ai.vertex.ai.gemini.chat.options.topK | The maximum number of tokens to consider when sampling. The generative uses combined Top-k and nucleus sampling. Top-k sampling considers the set of topK most probable tokens. | - | spring.ai.vertex.ai.gemini.chat.options.topP | The maximum cumulative probability of tokens to consider when sampling. The generative uses combined Top-k and nucleus sampling. Nucleus sampling considers the smallest set of tokens whose probability sum is at least topP. | - @@ -100,7 +100,8 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific `VertexAiChatPaLm2Options` you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific `VertexAiChatPaLm2Options` you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the +https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Function Calling @@ -211,6 +212,7 @@ VertexAI vertexApi = new VertexAI(projectId, location); var chatClient = new VertexAiGeminiChatClient(vertexApi, VertexAiGeminiChatOptions.builder() + .withModel(ChatModel.GEMINI_PRO_1_5_PRO) .withTemperature(0.4) .build()); @@ -221,3 +223,9 @@ ChatResponse response = chatClient.call( The `VertexAiGeminiChatOptions` provides the configuration information for the chat requests. The `VertexAiGeminiChatOptions.Builder` is fluent options builder. +== Low-level Java Client [[low-level-api]] + +Following class diagram illustrates the Vertex AI Gemini native Java API: + +image::vertex-ai-gemini-native-api.jpg[w=800,align="center"] + diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-palm2-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-palm2-chat.adoc index f67a467f05..045df41e2b 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-palm2-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-palm2-chat.adoc @@ -97,7 +97,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific `VertexAiPaLm2ChatOptions` you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific `VertexAiPaLm2ChatOptions` you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. == Sample Controller diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/watsonx-ai-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/watsonx-ai-chat.adoc index 1cc4445f45..79ce3d8d9e 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/watsonx-ai-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/watsonx-ai-chat.adoc @@ -92,7 +92,7 @@ ChatResponse response = chatClient.call( )); ---- -TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-watsonx-ai/src/main/java/org/springframework/ai/watsonx/WatsonxAiChatOptions.java[WatsonxAiChatOptions.java] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. +TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-watsonx-ai/src/main/java/org/springframework/ai/watsonx/WatsonxAiChatOptions.java[WatsonxAiChatOptions.java] you can use a portable https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptions.java[ChatOptions] instance, created with the https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/chat/prompt/ChatOptionsBuilder.java[ChatOptionsBuilder#builder()]. NOTE: For more information go to https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-model-parameters.html?context=wx[watsonx-parameters-info] From 14d620e2caebbe8d0d2234d12690c787170f6760 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 17 May 2024 06:47:43 +0200 Subject: [PATCH 14/25] minor doc fix --- .../modules/ROOT/pages/api/chat/vertexai-gemini-chat.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-gemini-chat.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-gemini-chat.adoc index ef66bcdf84..ecf11add0b 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-gemini-chat.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/vertexai-gemini-chat.adoc @@ -65,7 +65,7 @@ The prefix `spring.ai.vertex.ai.gemini.chat` is the property prefix that lets yo |==== | Property | Description | Default -| spring.ai.vertex.ai.gemini.chat.options.model | Supported https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini[Vertex AI Gemini Chat model] to use include the (1.0 ) `gemini-pro`, `gemini-pro-vision` and the new (1.5) `gemini-1.5-pro-preview-0514`, `gemini-1.5-flash-preview-0514` models. | gemini-pro-vision +| spring.ai.vertex.ai.gemini.chat.options.model | Supported https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini[Vertex AI Gemini Chat model] to use include the (1.0 ) `gemini-pro`, `gemini-pro-vision` and the new (1.5) `gemini-1.5-pro-preview-0514`, `gemini-1.5-flash-preview-0514` models. | gemini-pro | spring.ai.vertex.ai.gemini.chat.options.temperature | Controls the randomness of the output. Values can range over [0.0,1.0], inclusive. A value closer to 1.0 will produce responses that are more varied, while a value closer to 0.0 will typically result in less surprising responses from the generative. This value specifies default to be used by the backend while making the call to the generative. | 0.8 | spring.ai.vertex.ai.gemini.chat.options.topK | The maximum number of tokens to consider when sampling. The generative uses combined Top-k and nucleus sampling. Top-k sampling considers the set of topK most probable tokens. | - | spring.ai.vertex.ai.gemini.chat.options.topP | The maximum cumulative probability of tokens to consider when sampling. The generative uses combined Top-k and nucleus sampling. Nucleus sampling considers the smallest set of tokens whose probability sum is at least topP. | - From 654e22be9d174b43e3bc51a6d1457ea2adcec17a Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Sat, 11 May 2024 15:36:38 +0300 Subject: [PATCH 15/25] Add TypeReference StructuredOutputConverter A ParameterizedTypeReference alternative of BeanOutputConverter that can cover list of beans as well. Based on suggestion: https://twitter.com/kisco_/status/1788862522998546440 --- ...trizedTypeReferencefOutputConverterIT.java | 109 ++++++++++ ...meterizedTypeReferenceOutputConverter.java | 179 +++++++++++++++++ .../TypeReferenceOutputConverterTest.java | 190 ++++++++++++++++++ 3 files changed, 478 insertions(+) create mode 100644 models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientParametrizedTypeReferencefOutputConverterIT.java create mode 100644 spring-ai-core/src/main/java/org/springframework/ai/converter/ParameterizedTypeReferenceOutputConverter.java create mode 100644 spring-ai-core/src/test/java/org/springframework/ai/converter/TypeReferenceOutputConverterTest.java diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientParametrizedTypeReferencefOutputConverterIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientParametrizedTypeReferencefOutputConverterIT.java new file mode 100644 index 0000000000..146715f34d --- /dev/null +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientParametrizedTypeReferencefOutputConverterIT.java @@ -0,0 +1,109 @@ +/* + * 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.openai.chat; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; +import org.springframework.ai.chat.messages.AssistantMessage; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.chat.prompt.PromptTemplate; +import org.springframework.ai.converter.ParameterizedTypeReferenceOutputConverter; +import org.springframework.ai.openai.OpenAiTestConfiguration; +import org.springframework.ai.openai.testutils.AbstractIT; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.ParameterizedTypeReference; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(classes = OpenAiTestConfiguration.class) +@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+") +class OpenAiChatClientParametrizedTypeReferencefOutputConverterIT extends AbstractIT { + + private static final Logger logger = LoggerFactory + .getLogger(OpenAiChatClientParametrizedTypeReferencefOutputConverterIT.class); + + record ActorsFilmsRecord(String actor, List movies) { + } + + @Test + void typeRefOutputConverterRecords() { + + ParameterizedTypeReferenceOutputConverter> outputConverter = new ParameterizedTypeReferenceOutputConverter<>( + new ParameterizedTypeReference>() { + }); + + String format = outputConverter.getFormat(); + String template = """ + Generate the filmography of 5 movies for Tom Hanks and Bill Murray. + {format} + """; + PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format)); + Prompt prompt = new Prompt(promptTemplate.createMessage()); + Generation generation = chatClient.call(prompt).getResult(); + + List actorsFilms = outputConverter.convert(generation.getOutput().getContent()); + logger.info("" + actorsFilms); + assertThat(actorsFilms).hasSize(2); + assertThat(actorsFilms.get(0).actor()).isEqualTo("Tom Hanks"); + assertThat(actorsFilms.get(0).movies()).hasSize(5); + assertThat(actorsFilms.get(1).actor()).isEqualTo("Bill Murray"); + assertThat(actorsFilms.get(1).movies()).hasSize(5); + } + + @Test + void typeRefStreamOutputConverterRecords() { + + ParameterizedTypeReferenceOutputConverter> outputConverter = new ParameterizedTypeReferenceOutputConverter<>( + new ParameterizedTypeReference>() { + }); + + String format = outputConverter.getFormat(); + String template = """ + Generate the filmography of 5 movies for Tom Hanks and Bill Murray. + {format} + """; + PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format)); + Prompt prompt = new Prompt(promptTemplate.createMessage()); + + String generationTextFromStream = streamingChatClient.stream(prompt) + .collectList() + .block() + .stream() + .map(ChatResponse::getResults) + .flatMap(List::stream) + .map(Generation::getOutput) + .map(AssistantMessage::getContent) + .collect(Collectors.joining()); + + List actorsFilms = outputConverter.convert(generationTextFromStream); + logger.info("" + actorsFilms); + assertThat(actorsFilms).hasSize(2); + assertThat(actorsFilms.get(0).actor()).isEqualTo("Tom Hanks"); + assertThat(actorsFilms.get(0).movies()).hasSize(5); + assertThat(actorsFilms.get(1).actor()).isEqualTo("Bill Murray"); + assertThat(actorsFilms.get(1).movies()).hasSize(5); + } + +} \ No newline at end of file diff --git a/spring-ai-core/src/main/java/org/springframework/ai/converter/ParameterizedTypeReferenceOutputConverter.java b/spring-ai-core/src/main/java/org/springframework/ai/converter/ParameterizedTypeReferenceOutputConverter.java new file mode 100644 index 0000000000..a0b7926195 --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/converter/ParameterizedTypeReferenceOutputConverter.java @@ -0,0 +1,179 @@ +/* + * 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.converter; + +import java.util.Objects; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.github.victools.jsonschema.generator.SchemaGenerator; +import com.github.victools.jsonschema.generator.SchemaGeneratorConfig; +import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder; +import com.github.victools.jsonschema.module.jackson.JacksonModule; + +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.lang.NonNull; +import java.lang.reflect.Type; + +import static com.github.victools.jsonschema.generator.OptionPreset.PLAIN_JSON; +import static com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_2020_12; + +/** + * An implementation of {@link StructuredOutputConverter} that transforms the LLM output + * to a specific object type using JSON schema. This parser works by generating a JSON + * schema based on a given Java class type reference, which is then used to validate and + * transform the LLM output into the desired type. + * + * @param The target type to which the output will be converted. + * @author Mark Pollack + * @author Christian Tzolov + * @author Sebastian Ullrich + * @author Kirk Lund + * @author Josh Long + */ +public class ParameterizedTypeReferenceOutputConverter implements StructuredOutputConverter { + + /** Holds the generated JSON schema for the target type. */ + private String jsonSchema; + + /** + * The target class type reference to which the output will be converted. + */ + @SuppressWarnings({ "FieldMayBeFinal", "rawtypes" }) + private TypeReference typeRef; + + /** The object mapper used for deserialization and other JSON operations. */ + @SuppressWarnings("FieldMayBeFinal") + private ObjectMapper objectMapper; + + /** + * Constructor to initialize with the target class type reference. + * @param typeRef The target type's class. + */ + public ParameterizedTypeReferenceOutputConverter(ParameterizedTypeReference typeRef) { + this(new CustomizedTypeReference<>(typeRef), null); + } + + /** + * Constructor to initialize with the target class type reference, a custom object + * mapper, and a line endings normalizer to ensure consistent line endings on any + * platform. + * @param typeRef The target class type reference. + * @param objectMapper Custom object mapper for JSON operations. endings. + */ + public ParameterizedTypeReferenceOutputConverter(ParameterizedTypeReference typeRef, ObjectMapper objectMapper) { + this(new CustomizedTypeReference<>(typeRef), objectMapper); + } + + private static class CustomizedTypeReference extends TypeReference { + + private final Type type; + + CustomizedTypeReference(ParameterizedTypeReference typeRef) { + this.type = typeRef.getType(); + } + + @Override + public Type getType() { + return this.type; + } + + } + + /** + * Constructor to initialize with the target class type reference, a custom object + * mapper, and a line endings normalizer to ensure consistent line endings on any + * platform. + * @param typeRef The target class type reference. + * @param objectMapper Custom object mapper for JSON operations. endings. + */ + private ParameterizedTypeReferenceOutputConverter(TypeReference typeRef, ObjectMapper objectMapper) { + Objects.requireNonNull(typeRef, "Type reference cannot be null;"); + this.typeRef = typeRef; + this.objectMapper = objectMapper != null ? objectMapper : getObjectMapper(); + generateSchema(); + } + + /** + * Generates the JSON schema for the target type. + */ + private void generateSchema() { + JacksonModule jacksonModule = new JacksonModule(); + SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(DRAFT_2020_12, PLAIN_JSON) + .with(jacksonModule); + SchemaGeneratorConfig config = configBuilder.build(); + SchemaGenerator generator = new SchemaGenerator(config); + JsonNode jsonNode = generator.generateSchema(this.typeRef.getType()); + ObjectWriter objectWriter = new ObjectMapper().writer(new DefaultPrettyPrinter() + .withObjectIndenter(new DefaultIndenter().withLinefeed(System.lineSeparator()))); + try { + this.jsonSchema = objectWriter.writeValueAsString(jsonNode); + } + catch (JsonProcessingException e) { + throw new RuntimeException("Could not pretty print json schema for " + this.typeRef, e); + } + } + + @Override + /** + * Parses the given text to transform it to the desired target type. + * @param text The LLM output in string format. + * @return The parsed output in the desired target type. + */ + public T convert(@NonNull String text) { + try { + return (T) this.objectMapper.readValue(text, this.typeRef); + } + catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + /** + * Configures and returns an object mapper for JSON operations. + * @return Configured object mapper. + */ + protected ObjectMapper getObjectMapper() { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return mapper; + } + + /** + * Provides the expected format of the response, instructing that it should adhere to + * the generated JSON schema. + * @return The instruction format string. + */ + @Override + public String getFormat() { + String template = """ + Your response should be in JSON format. + Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation. + Do not include markdown code blocks in your response. + Remove the ```json markdown from the output. + Here is the JSON Schema instance your output must adhere to: + ```%s``` + """; + return String.format(template, this.jsonSchema); + } + +} diff --git a/spring-ai-core/src/test/java/org/springframework/ai/converter/TypeReferenceOutputConverterTest.java b/spring-ai-core/src/test/java/org/springframework/ai/converter/TypeReferenceOutputConverterTest.java new file mode 100644 index 0000000000..abddd15673 --- /dev/null +++ b/spring-ai-core/src/test/java/org/springframework/ai/converter/TypeReferenceOutputConverterTest.java @@ -0,0 +1,190 @@ +/* + * 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.converter; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import org.springframework.core.ParameterizedTypeReference; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Sebastian Ullrich + * @author Kirk Lund + * @author Christian Tzolov + */ +@ExtendWith(MockitoExtension.class) +class TypeReferenceOutputConverterTest { + + @Mock + private ObjectMapper objectMapperMock; + + @Test + public void shouldHavePreConfiguredDefaultObjectMapper() { + var converter = new ParameterizedTypeReferenceOutputConverter<>(new ParameterizedTypeReference() { + }); + var objectMapper = converter.getObjectMapper(); + assertThat(objectMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse(); + } + + @Nested + class ParserTest { + + @Test + public void shouldParseFieldNamesFromString() { + var converter = new ParameterizedTypeReferenceOutputConverter<>( + new ParameterizedTypeReference() { + }); + var testClass = converter.convert("{ \"someString\": \"some value\" }"); + assertThat(testClass.getSomeString()).isEqualTo("some value"); + } + + @Test + public void shouldParseFieldNamesFromArrayString() { + var converter = new ParameterizedTypeReferenceOutputConverter<>( + new ParameterizedTypeReference>() { + }); + List testClass = converter.convert("[{ \"someString\": \"some value\" }]"); + assertThat(testClass).hasSize(1); + assertThat(testClass.get(0).getSomeString()).isEqualTo("some value"); + } + + @Test + public void shouldParseJsonPropertiesFromString() { + var converter = new ParameterizedTypeReferenceOutputConverter<>( + new ParameterizedTypeReference() { + }); + var testClass = converter.convert("{ \"string_property\": \"some value\" }"); + assertThat(testClass.getSomeString()).isEqualTo("some value"); + } + + @Test + public void shouldParseJsonPropertiesFromArrayString() { + var converter = new ParameterizedTypeReferenceOutputConverter<>( + new ParameterizedTypeReference>() { + }); + List testClass = converter + .convert("[{ \"string_property\": \"some value\" }]"); + assertThat(testClass).hasSize(1); + assertThat(testClass.get(0).getSomeString()).isEqualTo("some value"); + } + + } + + @Nested + class FormatTest { + + @Test + public void shouldReturnFormatContainingResponseInstructionsAndJsonSchema() { + var converter = new ParameterizedTypeReferenceOutputConverter<>( + new ParameterizedTypeReference() { + }); + assertThat(converter.getFormat()).isEqualTo( + """ + Your response should be in JSON format. + Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation. + Do not include markdown code blocks in your response. + Remove the ```json markdown from the output. + Here is the JSON Schema instance your output must adhere to: + ```{ + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "type" : "object", + "properties" : { + "someString" : { + "type" : "string" + } + } + }``` + """); + } + + @Test + public void shouldReturnFormatContainingJsonSchemaIncludingPropertyAndPropertyDescription() { + var converter = new ParameterizedTypeReferenceOutputConverter<>( + new ParameterizedTypeReference() { + }); + assertThat(converter.getFormat()).contains(""" + ```{ + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "type" : "object", + "properties" : { + "string_property" : { + "type" : "string", + "description" : "string_property_description" + } + } + }``` + """); + } + + @Test + void normalizesLineEndings() { + var converter = new ParameterizedTypeReferenceOutputConverter<>( + new ParameterizedTypeReference() { + }); + + String formatOutput = converter.getFormat(); + + // validate that output contains \n line endings + assertThat(formatOutput).contains(System.lineSeparator()).doesNotContain("\r\n").doesNotContain("\r"); + } + + } + + public static class TestClass { + + private String someString; + + @SuppressWarnings("unused") + public TestClass() { + } + + public TestClass(String someString) { + this.someString = someString; + } + + public String getSomeString() { + return someString; + } + + } + + public static class TestClassWithJsonAnnotations { + + @JsonProperty("string_property") + @JsonPropertyDescription("string_property_description") + private String someString; + + public TestClassWithJsonAnnotations() { + } + + public String getSomeString() { + return someString; + } + + } + +} \ No newline at end of file From 8c758617f3974799469406a0948396820a5ebead Mon Sep 17 00:00:00 2001 From: Josh Long Date: Mon, 6 May 2024 12:34:18 +0200 Subject: [PATCH 16/25] moving to ApplicationListener to avoid slowing down the application on startup. --- pom.xml | 4 +- .../README.md | 0 .../pom.xml | 0 ...zureAiSearchFilterExpressionConverter.java | 0 .../vectorstore/azure/AzureVectorStore.java | 126 ++++++++---------- ...iSearchFilterExpressionConverterTests.java | 0 .../vectorstore/azure/AzureVectorStoreIT.java | 0 .../CassandraFilterExpressionConverter.java | 17 ++- .../ai/vectorstore/CassandraVectorStore.java | 26 +--- .../CassandraVectorStoreConfig.java | 24 ++-- .../README.md | 0 .../pom.xml | 0 .../springframework/ai/chroma/ChromaApi.java | 0 .../ai/vectorstore/ChromaVectorStore.java | 18 ++- .../ai/vectorstore/JsonUtils.java | 0 .../ai/chroma/ChromaApiIT.java | 0 .../vectorstore/BasicAuthChromaWhereIT.java | 0 .../ai/vectorstore/ChromaVectorStoreIT.java | 0 .../TokenSecuredChromaWhereIT.java | 0 .../src/test/resources/api.yaml | 0 .../src/test/resources/server.htpasswd | 0 .../vectorstore/ElasticsearchVectorStore.java | 9 +- .../ai/vectorstore/MilvusVectorStore.java | 15 ++- .../vectorstore/MongoDBAtlasVectorStore.java | 26 ++-- .../ai/vectorstore/Neo4jVectorStore.java | 57 ++++---- .../ai/vectorstore/PgVectorStore.java | 65 +++++---- .../vectorstore/qdrant/QdrantVectorStore.java | 62 +++++---- .../ai/vectorstore/RedisVectorStore.java | 67 ++++------ 28 files changed, 246 insertions(+), 270 deletions(-) rename vector-stores/{spring-ai-azure => spring-ai-azure-vector-store}/README.md (100%) rename vector-stores/{spring-ai-azure => spring-ai-azure-vector-store}/pom.xml (100%) rename vector-stores/{spring-ai-azure => spring-ai-azure-vector-store}/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java (100%) rename vector-stores/{spring-ai-azure => spring-ai-azure-vector-store}/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java (84%) rename vector-stores/{spring-ai-azure => spring-ai-azure-vector-store}/src/test/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverterTests.java (100%) rename vector-stores/{spring-ai-azure => spring-ai-azure-vector-store}/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java (100%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/README.md (100%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/pom.xml (100%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/src/main/java/org/springframework/ai/chroma/ChromaApi.java (100%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java (94%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/src/main/java/org/springframework/ai/vectorstore/JsonUtils.java (100%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/src/test/java/org/springframework/ai/chroma/ChromaApiIT.java (100%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java (100%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java (100%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java (100%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/src/test/resources/api.yaml (100%) rename vector-stores/{spring-ai-chroma => spring-ai-chroma-store}/src/test/resources/server.htpasswd (100%) diff --git a/pom.xml b/pom.xml index 32af3af00e..771b4e5ad6 100644 --- a/pom.xml +++ b/pom.xml @@ -56,8 +56,8 @@ document-readers/pdf-reader document-readers/tika-reader vector-stores/spring-ai-pinecone - vector-stores/spring-ai-chroma - vector-stores/spring-ai-azure + vector-stores/spring-ai-chroma-store + vector-stores/spring-ai-azure-vector-store vector-stores/spring-ai-weaviate vector-stores/spring-ai-redis vector-stores/spring-ai-gemfire diff --git a/vector-stores/spring-ai-azure/README.md b/vector-stores/spring-ai-azure-vector-store/README.md similarity index 100% rename from vector-stores/spring-ai-azure/README.md rename to vector-stores/spring-ai-azure-vector-store/README.md diff --git a/vector-stores/spring-ai-azure/pom.xml b/vector-stores/spring-ai-azure-vector-store/pom.xml similarity index 100% rename from vector-stores/spring-ai-azure/pom.xml rename to vector-stores/spring-ai-azure-vector-store/pom.xml diff --git a/vector-stores/spring-ai-azure/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java b/vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java similarity index 100% rename from vector-stores/spring-ai-azure/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java rename to vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java diff --git a/vector-stores/spring-ai-azure/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java b/vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java similarity index 84% rename from vector-stores/spring-ai-azure/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java rename to vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java index 165b190b06..071a48fa54 100644 --- a/vector-stores/spring-ai-azure/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java +++ b/vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java @@ -15,45 +15,30 @@ */ package org.springframework.ai.vectorstore.azure; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.TypeReference; import com.azure.core.util.Context; import com.azure.search.documents.SearchClient; import com.azure.search.documents.SearchDocument; import com.azure.search.documents.indexes.SearchIndexClient; -import com.azure.search.documents.indexes.models.HnswAlgorithmConfiguration; -import com.azure.search.documents.indexes.models.HnswParameters; -import com.azure.search.documents.indexes.models.SearchField; -import com.azure.search.documents.indexes.models.SearchFieldDataType; -import com.azure.search.documents.indexes.models.SearchIndex; -import com.azure.search.documents.indexes.models.VectorSearch; -import com.azure.search.documents.indexes.models.VectorSearchAlgorithmMetric; -import com.azure.search.documents.indexes.models.VectorSearchProfile; -import com.azure.search.documents.models.IndexDocumentsResult; -import com.azure.search.documents.models.IndexingResult; -import com.azure.search.documents.models.SearchOptions; -import com.azure.search.documents.models.VectorSearchOptions; -import com.azure.search.documents.models.VectorizedQuery; +import com.azure.search.documents.indexes.models.*; +import com.azure.search.documents.models.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import java.util.*; +import java.util.stream.Collectors; + /** * Uses Azure Cognitive Search as a backing vector store. Documents can be preloaded into * a Cognitive Search index and managed via Azure tools or added and managed through this @@ -63,8 +48,9 @@ * @author Greg Meyer * @author Xiangyang Yu * @author Christian Tzolov + * @author Josh Long */ -public class AzureVectorStore implements VectorStore, InitializingBean { +public class AzureVectorStore implements VectorStore, ApplicationListener { private static final Logger logger = LoggerFactory.getLogger(AzureVectorStore.class); @@ -115,6 +101,53 @@ public class AzureVectorStore implements VectorStore, InitializingBean { */ private final List filterMetadataFields; + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + + int dimensions = this.embeddingClient.dimensions(); + + List fields = new ArrayList<>(); + + fields.add(new SearchField(ID_FIELD_NAME, SearchFieldDataType.STRING).setKey(true) + .setFilterable(true) + .setSortable(true)); + fields.add(new SearchField(EMBEDDING_FIELD_NAME, SearchFieldDataType.collection(SearchFieldDataType.SINGLE)) + .setSearchable(true) + .setVectorSearchDimensions(dimensions) + // This must match a vector search configuration name. + .setVectorSearchProfileName(SPRING_AI_VECTOR_PROFILE)); + fields.add(new SearchField(CONTENT_FIELD_NAME, SearchFieldDataType.STRING).setSearchable(true) + .setFilterable(true)); + fields.add(new SearchField(METADATA_FIELD_NAME, SearchFieldDataType.STRING).setSearchable(true) + .setFilterable(true)); + + for (MetadataField filterableMetadataField : this.filterMetadataFields) { + fields.add(new SearchField(METADATA_FIELD_PREFIX + filterableMetadataField.name(), + filterableMetadataField.fieldType()) + .setSearchable(false) + .setFacetable(true)); + } + + SearchIndex searchIndex = new SearchIndex(this.indexName).setFields(fields) + // VectorSearch configuration is required for a vector field. The name used + // for the vector search algorithm configuration must match the configuration + // used by the search field used for vector search. + .setVectorSearch(new VectorSearch() + .setProfiles(Collections + .singletonList(new VectorSearchProfile(SPRING_AI_VECTOR_PROFILE, SPRING_AI_VECTOR_CONFIG))) + .setAlgorithms(Collections.singletonList(new HnswAlgorithmConfiguration(SPRING_AI_VECTOR_CONFIG) + .setParameters(new HnswParameters().setM(4) + .setEfConstruction(400) + .setEfSearch(1000) + .setMetric(VectorSearchAlgorithmMetric.COSINE))))); + + SearchIndex index = this.searchIndexClient.createOrUpdateIndex(searchIndex); + + logger.info("Created search index: " + index.getName()); + + this.searchClient = this.searchIndexClient.getSearchClient(this.indexName); + } + public record MetadataField(String name, SearchFieldDataType fieldType) { public static MetadataField text(String name) { @@ -325,51 +358,4 @@ private List toFloatList(List doubleList) { private record AzureSearchDocument(String id, String content, List embedding, String metadata) { } - @Override - public void afterPropertiesSet() throws Exception { - - int dimensions = this.embeddingClient.dimensions(); - - List fields = new ArrayList<>(); - - fields.add(new SearchField(ID_FIELD_NAME, SearchFieldDataType.STRING).setKey(true) - .setFilterable(true) - .setSortable(true)); - fields.add(new SearchField(EMBEDDING_FIELD_NAME, SearchFieldDataType.collection(SearchFieldDataType.SINGLE)) - .setSearchable(true) - .setVectorSearchDimensions(dimensions) - // This must match a vector search configuration name. - .setVectorSearchProfileName(SPRING_AI_VECTOR_PROFILE)); - fields.add(new SearchField(CONTENT_FIELD_NAME, SearchFieldDataType.STRING).setSearchable(true) - .setFilterable(true)); - fields.add(new SearchField(METADATA_FIELD_NAME, SearchFieldDataType.STRING).setSearchable(true) - .setFilterable(true)); - - for (MetadataField filterableMetadataField : this.filterMetadataFields) { - fields.add(new SearchField(METADATA_FIELD_PREFIX + filterableMetadataField.name(), - filterableMetadataField.fieldType()) - .setSearchable(false) - .setFacetable(true)); - } - - SearchIndex searchIndex = new SearchIndex(this.indexName).setFields(fields) - // VectorSearch configuration is required for a vector field. The name used - // for the vector search algorithm configuration must match the configuration - // used by the search field used for vector search. - .setVectorSearch(new VectorSearch() - .setProfiles(Collections - .singletonList(new VectorSearchProfile(SPRING_AI_VECTOR_PROFILE, SPRING_AI_VECTOR_CONFIG))) - .setAlgorithms(Collections.singletonList(new HnswAlgorithmConfiguration(SPRING_AI_VECTOR_CONFIG) - .setParameters(new HnswParameters().setM(4) - .setEfConstruction(400) - .setEfSearch(1000) - .setMetric(VectorSearchAlgorithmMetric.COSINE))))); - - SearchIndex index = this.searchIndexClient.createOrUpdateIndex(searchIndex); - - logger.info("Created search index: " + index.getName()); - - this.searchClient = this.searchIndexClient.getSearchClient(this.indexName); - } - } diff --git a/vector-stores/spring-ai-azure/src/test/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverterTests.java b/vector-stores/spring-ai-azure-vector-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverterTests.java similarity index 100% rename from vector-stores/spring-ai-azure/src/test/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverterTests.java rename to vector-stores/spring-ai-azure-vector-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverterTests.java diff --git a/vector-stores/spring-ai-azure/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java b/vector-stores/spring-ai-azure-vector-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java similarity index 100% rename from vector-stores/spring-ai-azure/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java rename to vector-stores/spring-ai-azure-vector-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java diff --git a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java index 3efd440341..15028f7250 100644 --- a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java +++ b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java @@ -15,30 +15,29 @@ */ package org.springframework.ai.vectorstore; -import java.util.Collection; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; - import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata; import com.datastax.oss.driver.api.core.type.DataTypes; import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry; import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; - import org.springframework.ai.vectorstore.filter.Filter; import org.springframework.ai.vectorstore.filter.Filter.ExpressionType; import org.springframework.ai.vectorstore.filter.Filter.Key; import org.springframework.ai.vectorstore.filter.Filter.Value; import org.springframework.ai.vectorstore.filter.converter.AbstractFilterExpressionConverter; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + /** - * Converts {@link Expression} into CQL where clauses. + * Converts {@link org.springframework.ai.vectorstore.filter.Filter.Expression} into CQL where clauses. * * @author Mick Semb Wever * @since 1.0.0 */ -final class CassandraFilterExpressionConverter extends AbstractFilterExpressionConverter { +class CassandraFilterExpressionConverter extends AbstractFilterExpressionConverter { private final Map columnsByName; diff --git a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java index a9532d850a..c06cfdfc56 100644 --- a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java +++ b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java @@ -15,22 +15,7 @@ */ package org.springframework.ai.vectorstore; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import com.datastax.oss.driver.api.core.cql.BoundStatement; -import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder; -import com.datastax.oss.driver.api.core.cql.PreparedStatement; -import com.datastax.oss.driver.api.core.cql.Row; -import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import com.datastax.oss.driver.api.core.cql.*; import com.datastax.oss.driver.api.core.data.CqlVector; import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; import com.datastax.oss.driver.api.querybuilder.QueryBuilder; @@ -41,14 +26,17 @@ import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; -import org.springframework.ai.vectorstore.CassandraVectorStoreConfig; import org.springframework.ai.vectorstore.CassandraVectorStoreConfig.SchemaColumn; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; import org.springframework.beans.factory.InitializingBean; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + /** * The CassandraVectorStore is for managing and querying vector data in an Apache * Cassandra db. It offers functionalities like adding, deleting, and performing @@ -91,7 +79,7 @@ * @see EmbeddingClient * @since 1.0.0 */ -public final class CassandraVectorStore implements VectorStore, InitializingBean, AutoCloseable { +public class CassandraVectorStore implements VectorStore, InitializingBean, AutoCloseable { /** * Indexes are automatically created with COSINE. This can be changed manually via diff --git a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java index 288894648b..aebe211c8a 100644 --- a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java +++ b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java @@ -15,18 +15,6 @@ */ package org.springframework.ai.vectorstore; -import java.net.InetSocketAddress; -import java.time.Duration; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.function.Function; -import java.util.stream.Stream; - import com.datastax.oss.driver.api.core.CqlSession; import com.datastax.oss.driver.api.core.CqlSessionBuilder; import com.datastax.oss.driver.api.core.cql.SimpleStatement; @@ -44,12 +32,18 @@ import com.datastax.oss.driver.api.querybuilder.schema.CreateTableStart; import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting; import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.springframework.lang.Nullable; +import java.net.InetSocketAddress; +import java.time.Duration; +import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.function.Function; +import java.util.stream.Stream; + /** * Configuration for the Cassandra vector store. * @@ -66,7 +60,7 @@ * @author Mick Semb Wever * @since 1.0.0 */ -public final class CassandraVectorStoreConfig implements AutoCloseable { +public class CassandraVectorStoreConfig implements AutoCloseable { public static final String DEFAULT_KEYSPACE_NAME = "springframework"; diff --git a/vector-stores/spring-ai-chroma/README.md b/vector-stores/spring-ai-chroma-store/README.md similarity index 100% rename from vector-stores/spring-ai-chroma/README.md rename to vector-stores/spring-ai-chroma-store/README.md diff --git a/vector-stores/spring-ai-chroma/pom.xml b/vector-stores/spring-ai-chroma-store/pom.xml similarity index 100% rename from vector-stores/spring-ai-chroma/pom.xml rename to vector-stores/spring-ai-chroma-store/pom.xml diff --git a/vector-stores/spring-ai-chroma/src/main/java/org/springframework/ai/chroma/ChromaApi.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/ChromaApi.java similarity index 100% rename from vector-stores/spring-ai-chroma/src/main/java/org/springframework/ai/chroma/ChromaApi.java rename to vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/ChromaApi.java diff --git a/vector-stores/spring-ai-chroma/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java similarity index 94% rename from vector-stores/spring-ai-chroma/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java rename to vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java index 9f89e7a00d..411398a6d6 100644 --- a/vector-stores/spring-ai-chroma/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java +++ b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java @@ -15,12 +15,6 @@ */ package org.springframework.ai.vectorstore; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - import org.springframework.ai.chroma.ChromaApi; import org.springframework.ai.chroma.ChromaApi.AddEmbeddingsRequest; import org.springframework.ai.chroma.ChromaApi.DeleteEmbeddingsRequest; @@ -29,19 +23,24 @@ import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; import org.springframework.ai.vectorstore.filter.converter.ChromaFilterExpressionConverter; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import java.util.*; + /** * {@link ChromaVectorStore} is a concrete implementation of the {@link VectorStore} * interface. It is responsible for adding, deleting, and searching documents based on * their similarity to a query, using the {@link ChromaApi} and {@link EmbeddingClient} * for embedding calculations. For more information about how it does this, see the * official Chroma website. + * + * @author Josh Long */ -public class ChromaVectorStore implements VectorStore, InitializingBean { +public class ChromaVectorStore implements VectorStore, ApplicationListener { public static final String DISTANCE_FIELD_NAME = "distance"; @@ -147,12 +146,11 @@ public List similaritySearch(SearchRequest request) { } @Override - public void afterPropertiesSet() throws Exception { + public void onApplicationEvent(ApplicationReadyEvent event) { var collection = this.chromaApi.getCollection(this.collectionName); if (collection == null) { collection = this.chromaApi.createCollection(new ChromaApi.CreateCollectionRequest(this.collectionName)); } this.collectionId = collection.id(); } - } diff --git a/vector-stores/spring-ai-chroma/src/main/java/org/springframework/ai/vectorstore/JsonUtils.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/JsonUtils.java similarity index 100% rename from vector-stores/spring-ai-chroma/src/main/java/org/springframework/ai/vectorstore/JsonUtils.java rename to vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/JsonUtils.java diff --git a/vector-stores/spring-ai-chroma/src/test/java/org/springframework/ai/chroma/ChromaApiIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/chroma/ChromaApiIT.java similarity index 100% rename from vector-stores/spring-ai-chroma/src/test/java/org/springframework/ai/chroma/ChromaApiIT.java rename to vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/chroma/ChromaApiIT.java diff --git a/vector-stores/spring-ai-chroma/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java similarity index 100% rename from vector-stores/spring-ai-chroma/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java rename to vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java diff --git a/vector-stores/spring-ai-chroma/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java similarity index 100% rename from vector-stores/spring-ai-chroma/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java rename to vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java diff --git a/vector-stores/spring-ai-chroma/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java similarity index 100% rename from vector-stores/spring-ai-chroma/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java rename to vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java diff --git a/vector-stores/spring-ai-chroma/src/test/resources/api.yaml b/vector-stores/spring-ai-chroma-store/src/test/resources/api.yaml similarity index 100% rename from vector-stores/spring-ai-chroma/src/test/resources/api.yaml rename to vector-stores/spring-ai-chroma-store/src/test/resources/api.yaml diff --git a/vector-stores/spring-ai-chroma/src/test/resources/server.htpasswd b/vector-stores/spring-ai-chroma-store/src/test/resources/server.htpasswd similarity index 100% rename from vector-stores/spring-ai-chroma/src/test/resources/server.htpasswd rename to vector-stores/spring-ai-chroma-store/src/test/resources/server.htpasswd diff --git a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java index 72a29472f6..8dcb1b5bc5 100644 --- a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java +++ b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java @@ -37,7 +37,8 @@ import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.Filter; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; import org.springframework.util.Assert; import java.io.IOException; @@ -49,9 +50,10 @@ /** * @author Jemin Huh * @author Wei Jiang + * @author Josh Long * @since 1.0.0 */ -public class ElasticsearchVectorStore implements VectorStore, InitializingBean { +public class ElasticsearchVectorStore implements VectorStore, ApplicationListener { // divided by 2 to get score in the range [0, 1] public static final String COSINE_SIMILARITY_FUNCTION = "(cosineSimilarity(params.query_vector, 'embedding') + 1.0) / 2"; @@ -219,10 +221,9 @@ private CreateIndexResponse createIndexMapping() { } @Override - public void afterPropertiesSet() { + public void onApplicationEvent(ApplicationReadyEvent event) { if (!indexExists()) { createIndexMapping(); } } - } diff --git a/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java b/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java index 0a6004a1c0..76a54fddb6 100644 --- a/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java +++ b/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java @@ -55,14 +55,16 @@ import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; import org.springframework.ai.vectorstore.filter.converter.MilvusFilterExpressionConverter; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * @author Christian Tzolov + * @author Josh Long */ -public class MilvusVectorStore implements VectorStore, InitializingBean { +public class MilvusVectorStore implements VectorStore, ApplicationListener { private static final Logger logger = LoggerFactory.getLogger(MilvusVectorStore.class); @@ -96,6 +98,11 @@ public class MilvusVectorStore implements VectorStore, InitializingBean { private final MilvusVectorStoreConfig config; + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + this.createCollection(); + } + /** * Configuration for the Milvus vector store. */ @@ -378,10 +385,6 @@ private List toFloatList(List embeddingDouble) { // --------------------------------------------------------------------------------- // Initialization // --------------------------------------------------------------------------------- - @Override - public void afterPropertiesSet() throws Exception { - this.createCollection(); - } void releaseCollection() { if (isDatabaseCollectionExists()) { diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStore.java b/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStore.java index 0fbb099ce1..231c196b72 100644 --- a/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStore.java +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStore.java @@ -25,7 +25,8 @@ import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.query.Criteria; @@ -36,9 +37,10 @@ /** * @author Chris Smith + * @author Josh Long * @since 1.0.0 */ -public class MongoDBAtlasVectorStore implements VectorStore, InitializingBean { +public class MongoDBAtlasVectorStore implements VectorStore, ApplicationListener { public static final String ID_FIELD_NAME = "_id"; @@ -76,16 +78,6 @@ public MongoDBAtlasVectorStore(MongoTemplate mongoTemplate, EmbeddingClient embe } - @Override - public void afterPropertiesSet() throws Exception { - // Create the collection if it does not exist - if (!mongoTemplate.collectionExists(this.config.collectionName)) { - mongoTemplate.createCollection(this.config.collectionName); - } - // Create search index, command doesn't do anything if already existing - mongoTemplate.executeCommand(createSearchIndex()); - } - /** * Provides the Definition for the search index */ @@ -174,6 +166,16 @@ public List similaritySearch(SearchRequest request) { .toList(); } + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + // Create the collection if it does not exist + if (!mongoTemplate.collectionExists(this.config.collectionName)) { + mongoTemplate.createCollection(this.config.collectionName); + } + // Create search index, command doesn't do anything if already existing + mongoTemplate.executeCommand(createSearchIndex()); + } + public static class MongoDBVectorStoreConfig { private final String collectionName; diff --git a/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java b/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java index 1c50ccb908..bb1799584c 100644 --- a/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java +++ b/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java @@ -22,7 +22,8 @@ import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.Neo4jVectorFilterExpressionConverter; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; import org.springframework.util.Assert; import java.util.HashMap; @@ -34,8 +35,9 @@ /** * @author Gerrit Meier * @author Michael Simons + * @author Josh Long */ -public class Neo4jVectorStore implements VectorStore, InitializingBean { +public class Neo4jVectorStore implements VectorStore, ApplicationListener { /** * An enum to configure the distance function used in the Neo4j vector index. @@ -50,8 +52,8 @@ public enum Neo4jDistanceType { this.name = name; } - } + } /** * Configuration for the Neo4j vector store. */ @@ -70,8 +72,8 @@ public static final class Neo4jVectorStoreConfig { private final String indexName; // needed for similarity search call - private final String indexNameNotSanitized; + private final String indexNameNotSanitized; private final String idProperty; private final String constraintName; @@ -249,10 +251,34 @@ public Neo4jVectorStoreConfig build() { return new Neo4jVectorStoreConfig(this); } + } } + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + + try (var session = this.driver.session(this.config.sessionConfig)) { + + session + .run("CREATE CONSTRAINT %s IF NOT EXISTS FOR (n:%s) REQUIRE n.%s IS UNIQUE" + .formatted(this.config.constraintName, this.config.label, this.config.idProperty)) + .consume(); + + var statement = """ + CREATE VECTOR INDEX %s IF NOT EXISTS FOR (n:%s) ON (n.%s) + OPTIONS {indexConfig: { + `vector.dimensions`: %d, + `vector.similarity_function`: '%s' + }} + """.formatted(this.config.indexName, this.config.label, this.config.embeddingProperty, + this.config.embeddingDimension, this.config.distanceType.name); + session.run(statement).consume(); + session.run("CALL db.awaitIndexes()").consume(); + } + } + public static final int DEFAULT_EMBEDDING_DIMENSION = 1536; public static final String DEFAULT_LABEL = "Document"; @@ -348,29 +374,6 @@ public List similaritySearch(SearchRequest request) { } } - @Override - public void afterPropertiesSet() { - - try (var session = this.driver.session(this.config.sessionConfig)) { - - session - .run("CREATE CONSTRAINT %s IF NOT EXISTS FOR (n:%s) REQUIRE n.%s IS UNIQUE" - .formatted(this.config.constraintName, this.config.label, this.config.idProperty)) - .consume(); - - var statement = """ - CREATE VECTOR INDEX %s IF NOT EXISTS FOR (n:%s) ON (n.%s) - OPTIONS {indexConfig: { - `vector.dimensions`: %d, - `vector.similarity_function`: '%s' - }} - """.formatted(this.config.indexName, this.config.label, this.config.embeddingProperty, - this.config.embeddingDimension, this.config.distanceType.name); - session.run(statement).consume(); - session.run("CALL db.awaitIndexes()").consume(); - } - } - private Map documentToRecord(Document document) { var embedding = this.embeddingClient.embed(document); document.setEmbedding(embedding); diff --git a/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java b/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java index c2b7954d90..eab9e10245 100644 --- a/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java +++ b/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java @@ -35,7 +35,8 @@ import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; import org.springframework.ai.vectorstore.filter.converter.PgVectorFilterExpressionConverter; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -49,8 +50,9 @@ * vector index will be auto-created if not available. * * @author Christian Tzolov + * @author Josh Long */ -public class PgVectorStore implements VectorStore, InitializingBean { +public class PgVectorStore implements VectorStore, ApplicationListener { private static final Logger logger = LoggerFactory.getLogger(PgVectorStore.class); @@ -78,6 +80,38 @@ public class PgVectorStore implements VectorStore, InitializingBean { private PgIndexType createIndexMethod; + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + try { + // Enable the PGVector, JSONB and UUID support. + this.jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS vector"); + this.jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS hstore"); + this.jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\""); + + // Remove existing VectorStoreTable + if (this.removeExistingVectorStoreTable) { + this.jdbcTemplate.execute("DROP TABLE IF EXISTS " + VECTOR_TABLE_NAME); + } + + this.jdbcTemplate.execute(String.format(""" + CREATE TABLE IF NOT EXISTS %s ( + id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, + content text, + metadata json, + embedding vector(%d) + ) + """, VECTOR_TABLE_NAME, this.embeddingDimensions())); + + if (this.createIndexMethod != PgIndexType.NONE) { + this.jdbcTemplate.execute(String.format(""" + CREATE INDEX IF NOT EXISTS %s ON %s USING %s (embedding %s) + """, VECTOR_INDEX_NAME, VECTOR_TABLE_NAME, this.createIndexMethod, this.getDistanceType().index)); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + /** * By default, pgvector performs exact nearest neighbor search, which provides perfect * recall. You can add an index to use approximate nearest neighbor search, which @@ -331,33 +365,6 @@ private String comparisonOperator() { // --------------------------------------------------------------------------------- // Initialize // --------------------------------------------------------------------------------- - @Override - public void afterPropertiesSet() throws Exception { - // Enable the PGVector, JSONB and UUID support. - this.jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS vector"); - this.jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS hstore"); - this.jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\""); - - // Remove existing VectorStoreTable - if (this.removeExistingVectorStoreTable) { - this.jdbcTemplate.execute("DROP TABLE IF EXISTS " + VECTOR_TABLE_NAME); - } - - this.jdbcTemplate.execute(String.format(""" - CREATE TABLE IF NOT EXISTS %s ( - id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, - content text, - metadata json, - embedding vector(%d) - ) - """, VECTOR_TABLE_NAME, this.embeddingDimensions())); - - if (this.createIndexMethod != PgIndexType.NONE) { - this.jdbcTemplate.execute(String.format(""" - CREATE INDEX IF NOT EXISTS %s ON %s USING %s (embedding %s) - """, VECTOR_INDEX_NAME, VECTOR_TABLE_NAME, this.createIndexMethod, this.getDistanceType().index)); - } - } int embeddingDimensions() { // The manually set dimensions have precedence over the computed one. diff --git a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java b/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java index fcbe464fbc..72dcf09b53 100644 --- a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java +++ b/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java @@ -15,24 +15,12 @@ */ package org.springframework.ai.vectorstore.qdrant; -import static io.qdrant.client.PointIdFactory.id; -import static io.qdrant.client.ValueFactory.value; -import static io.qdrant.client.VectorsFactory.vectors; -import static io.qdrant.client.WithPayloadSelectorFactory.enable; - import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutionException; -import org.springframework.ai.document.Document; -import org.springframework.ai.embedding.EmbeddingClient; -import org.springframework.ai.vectorstore.SearchRequest; -import org.springframework.ai.vectorstore.VectorStore; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; - import io.qdrant.client.QdrantClient; import io.qdrant.client.grpc.Collections.Distance; import io.qdrant.client.grpc.Collections.VectorParams; @@ -44,6 +32,19 @@ import io.qdrant.client.grpc.Points.SearchPoints; import io.qdrant.client.grpc.Points.UpdateStatus; +import org.springframework.ai.document.Document; +import org.springframework.ai.embedding.EmbeddingClient; +import org.springframework.ai.vectorstore.SearchRequest; +import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.util.Assert; + +import static io.qdrant.client.PointIdFactory.id; +import static io.qdrant.client.ValueFactory.value; +import static io.qdrant.client.VectorsFactory.vectors; +import static io.qdrant.client.WithPayloadSelectorFactory.enable; + /** * Qdrant vectorStore implementation. This store supports creating, updating, deleting, * and similarity searching of documents in a Qdrant collection. @@ -51,12 +52,12 @@ * @author Anush Shetty * @author Christian Tzolov * @author Eddú Meléndez + * @author Josh Long * @since 0.8.1 */ -public class QdrantVectorStore implements VectorStore, InitializingBean { +public class QdrantVectorStore implements VectorStore, ApplicationListener { private static final String CONTENT_FIELD_NAME = "doc_content"; - private static final String DISTANCE_FIELD_NAME = "distance"; public static final String DEFAULT_COLLECTION_NAME = "vector_store"; @@ -84,10 +85,10 @@ public static final class QdrantVectorStoreConfig { * * @param builder The configuration builder. */ + private QdrantVectorStoreConfig(Builder builder) { this.collectionName = builder.collectionName; } - /** * Start building a new configuration. * @return The entry point for creating a new configuration. @@ -126,10 +127,10 @@ public QdrantVectorStoreConfig build() { return new QdrantVectorStoreConfig(this); } + } } - /** * Constructs a new QdrantVectorStore. * @param config The configuration for the store. @@ -158,6 +159,24 @@ public QdrantVectorStore(QdrantClient qdrantClient, String collectionName, Embed this.qdrantClient = qdrantClient; } + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + + // Create the collection if it does not exist. + if (!isCollectionExists()) { + var vectorParams = VectorParams.newBuilder() + .setDistance(Distance.Cosine) + .setSize(this.embeddingClient.dimensions()) + .build(); + try { + this.qdrantClient.createCollectionAsync(this.collectionName, vectorParams).get(); + } // + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + /** * Adds a list of documents to the vector store. * @param documents The list of documents to be added. @@ -284,17 +303,6 @@ private List toFloatList(List doubleList) { return doubleList.stream().map(d -> d.floatValue()).toList(); } - @Override - public void afterPropertiesSet() throws Exception { - // Create the collection if it does not exist. - if (!isCollectionExists()) { - var vectorParams = VectorParams.newBuilder() - .setDistance(Distance.Cosine) - .setSize(this.embeddingClient.dimensions()) - .build(); - this.qdrantClient.createCollectionAsync(this.collectionName, vectorParams).get(); - } - } private boolean isCollectionExists() { try { diff --git a/vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java b/vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java index ce267ebeb6..744411622d 100644 --- a/vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java +++ b/vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java @@ -15,42 +15,29 @@ */ package org.springframework.ai.vectorstore; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; - import redis.clients.jedis.JedisPooled; import redis.clients.jedis.Pipeline; import redis.clients.jedis.json.Path2; -import redis.clients.jedis.search.FTCreateParams; -import redis.clients.jedis.search.IndexDataType; -import redis.clients.jedis.search.Query; -import redis.clients.jedis.search.RediSearchUtil; +import redis.clients.jedis.search.*; import redis.clients.jedis.search.Schema.FieldType; -import redis.clients.jedis.search.SearchResult; -import redis.clients.jedis.search.schemafields.NumericField; -import redis.clients.jedis.search.schemafields.SchemaField; -import redis.clients.jedis.search.schemafields.TagField; -import redis.clients.jedis.search.schemafields.TextField; -import redis.clients.jedis.search.schemafields.VectorField; +import redis.clients.jedis.search.schemafields.*; import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; +import java.text.MessageFormat; +import java.util.*; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + /** * The RedisVectorStore is for managing and querying vector data in a Redis database. It * offers functionalities like adding, deleting, and performing similarity searches on @@ -68,11 +55,27 @@ * * @author Julien Ruaux * @author Christian Tzolov + * @author Josh Long * @see VectorStore * @see RedisVectorStoreConfig * @see EmbeddingClient */ -public class RedisVectorStore implements VectorStore, InitializingBean { +public class RedisVectorStore implements VectorStore, ApplicationListener { + + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + // If index already exists don't do anything + if (this.jedis.ftList().contains(this.config.indexName)) { + return; + } + + String response = this.jedis.ftCreate(this.config.indexName, + FTCreateParams.createParams().on(IndexDataType.JSON).addPrefix(this.config.prefix), schemaFields()); + if (!RESPONSE_OK.test(response)) { + String message = MessageFormat.format("Could not create index: {0}", response); + throw new RuntimeException(message); + } + } public enum Algorithm { @@ -402,22 +405,6 @@ private String nativeExpressionFilter(SearchRequest request) { return "(" + this.filterExpressionConverter.convertExpression(request.getFilterExpression()) + ")"; } - @Override - public void afterPropertiesSet() { - - // If index already exists don't do anything - if (this.jedis.ftList().contains(this.config.indexName)) { - return; - } - - String response = this.jedis.ftCreate(this.config.indexName, - FTCreateParams.createParams().on(IndexDataType.JSON).addPrefix(this.config.prefix), schemaFields()); - if (!RESPONSE_OK.test(response)) { - String message = MessageFormat.format("Could not create index: {0}", response); - throw new RuntimeException(message); - } - } - private Iterable schemaFields() { Map vectorAttrs = new HashMap<>(); vectorAttrs.put("DIM", this.embeddingClient.dimensions()); From c5b7dd9c46ada0ca4843e7449eee1854da9e5485 Mon Sep 17 00:00:00 2001 From: Josh Long Date: Mon, 6 May 2024 12:36:37 +0200 Subject: [PATCH 17/25] i ran the spring javaformat maven plugin to clean up any source code incompatabilities. --- .../vectorstore/azure/AzureVectorStore.java | 42 +++++++++---------- .../CassandraFilterExpressionConverter.java | 3 +- .../ai/vectorstore/ChromaVectorStore.java | 1 + .../vectorstore/ElasticsearchVectorStore.java | 1 + .../ai/vectorstore/MilvusVectorStore.java | 2 +- .../ai/vectorstore/Neo4jVectorStore.java | 10 ++--- .../ai/vectorstore/PgVectorStore.java | 16 +++---- .../vectorstore/qdrant/QdrantVectorStore.java | 25 +++++------ 8 files changed, 53 insertions(+), 47 deletions(-) diff --git a/vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java b/vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java index 071a48fa54..9a5205cfb9 100644 --- a/vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java +++ b/vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java @@ -109,37 +109,37 @@ public void onApplicationEvent(ApplicationReadyEvent event) { List fields = new ArrayList<>(); fields.add(new SearchField(ID_FIELD_NAME, SearchFieldDataType.STRING).setKey(true) - .setFilterable(true) - .setSortable(true)); + .setFilterable(true) + .setSortable(true)); fields.add(new SearchField(EMBEDDING_FIELD_NAME, SearchFieldDataType.collection(SearchFieldDataType.SINGLE)) - .setSearchable(true) - .setVectorSearchDimensions(dimensions) - // This must match a vector search configuration name. - .setVectorSearchProfileName(SPRING_AI_VECTOR_PROFILE)); + .setSearchable(true) + .setVectorSearchDimensions(dimensions) + // This must match a vector search configuration name. + .setVectorSearchProfileName(SPRING_AI_VECTOR_PROFILE)); fields.add(new SearchField(CONTENT_FIELD_NAME, SearchFieldDataType.STRING).setSearchable(true) - .setFilterable(true)); + .setFilterable(true)); fields.add(new SearchField(METADATA_FIELD_NAME, SearchFieldDataType.STRING).setSearchable(true) - .setFilterable(true)); + .setFilterable(true)); for (MetadataField filterableMetadataField : this.filterMetadataFields) { fields.add(new SearchField(METADATA_FIELD_PREFIX + filterableMetadataField.name(), filterableMetadataField.fieldType()) - .setSearchable(false) - .setFacetable(true)); + .setSearchable(false) + .setFacetable(true)); } SearchIndex searchIndex = new SearchIndex(this.indexName).setFields(fields) - // VectorSearch configuration is required for a vector field. The name used - // for the vector search algorithm configuration must match the configuration - // used by the search field used for vector search. - .setVectorSearch(new VectorSearch() - .setProfiles(Collections - .singletonList(new VectorSearchProfile(SPRING_AI_VECTOR_PROFILE, SPRING_AI_VECTOR_CONFIG))) - .setAlgorithms(Collections.singletonList(new HnswAlgorithmConfiguration(SPRING_AI_VECTOR_CONFIG) - .setParameters(new HnswParameters().setM(4) - .setEfConstruction(400) - .setEfSearch(1000) - .setMetric(VectorSearchAlgorithmMetric.COSINE))))); + // VectorSearch configuration is required for a vector field. The name used + // for the vector search algorithm configuration must match the configuration + // used by the search field used for vector search. + .setVectorSearch(new VectorSearch() + .setProfiles(Collections + .singletonList(new VectorSearchProfile(SPRING_AI_VECTOR_PROFILE, SPRING_AI_VECTOR_CONFIG))) + .setAlgorithms(Collections.singletonList(new HnswAlgorithmConfiguration(SPRING_AI_VECTOR_CONFIG) + .setParameters(new HnswParameters().setM(4) + .setEfConstruction(400) + .setEfSearch(1000) + .setMetric(VectorSearchAlgorithmMetric.COSINE))))); SearchIndex index = this.searchIndexClient.createOrUpdateIndex(searchIndex); diff --git a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java index 15028f7250..f3270ac811 100644 --- a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java +++ b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java @@ -32,7 +32,8 @@ import java.util.stream.Collectors; /** - * Converts {@link org.springframework.ai.vectorstore.filter.Filter.Expression} into CQL where clauses. + * Converts {@link org.springframework.ai.vectorstore.filter.Filter.Expression} into CQL + * where clauses. * * @author Mick Semb Wever * @since 1.0.0 diff --git a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java index 411398a6d6..3c9741cbef 100644 --- a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java +++ b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java @@ -153,4 +153,5 @@ public void onApplicationEvent(ApplicationReadyEvent event) { } this.collectionId = collection.id(); } + } diff --git a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java index 8dcb1b5bc5..cff3a8efdf 100644 --- a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java +++ b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java @@ -226,4 +226,5 @@ public void onApplicationEvent(ApplicationReadyEvent event) { createIndexMapping(); } } + } diff --git a/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java b/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java index 76a54fddb6..b86438b6cd 100644 --- a/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java +++ b/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java @@ -64,7 +64,7 @@ * @author Christian Tzolov * @author Josh Long */ -public class MilvusVectorStore implements VectorStore, ApplicationListener { +public class MilvusVectorStore implements VectorStore, ApplicationListener { private static final Logger logger = LoggerFactory.getLogger(MilvusVectorStore.class); diff --git a/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java b/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java index bb1799584c..8b5b3024f4 100644 --- a/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java +++ b/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java @@ -52,8 +52,8 @@ public enum Neo4jDistanceType { this.name = name; } - } + /** * Configuration for the Neo4j vector store. */ @@ -74,6 +74,7 @@ public static final class Neo4jVectorStoreConfig { // needed for similarity search call private final String indexNameNotSanitized; + private final String idProperty; private final String constraintName; @@ -251,7 +252,6 @@ public Neo4jVectorStoreConfig build() { return new Neo4jVectorStoreConfig(this); } - } } @@ -262,9 +262,9 @@ public void onApplicationEvent(ApplicationReadyEvent event) { try (var session = this.driver.session(this.config.sessionConfig)) { session - .run("CREATE CONSTRAINT %s IF NOT EXISTS FOR (n:%s) REQUIRE n.%s IS UNIQUE" - .formatted(this.config.constraintName, this.config.label, this.config.idProperty)) - .consume(); + .run("CREATE CONSTRAINT %s IF NOT EXISTS FOR (n:%s) REQUIRE n.%s IS UNIQUE" + .formatted(this.config.constraintName, this.config.label, this.config.idProperty)) + .consume(); var statement = """ CREATE VECTOR INDEX %s IF NOT EXISTS FOR (n:%s) ON (n.%s) diff --git a/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java b/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java index eab9e10245..2eb9eefeef 100644 --- a/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java +++ b/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java @@ -52,7 +52,7 @@ * @author Christian Tzolov * @author Josh Long */ -public class PgVectorStore implements VectorStore, ApplicationListener { +public class PgVectorStore implements VectorStore, ApplicationListener { private static final Logger logger = LoggerFactory.getLogger(PgVectorStore.class); @@ -82,7 +82,7 @@ public class PgVectorStore implements VectorStore, ApplicationListener { private static final String CONTENT_FIELD_NAME = "doc_content"; + private static final String DISTANCE_FIELD_NAME = "distance"; public static final String DEFAULT_COLLECTION_NAME = "vector_store"; @@ -89,6 +90,7 @@ public static final class QdrantVectorStoreConfig { private QdrantVectorStoreConfig(Builder builder) { this.collectionName = builder.collectionName; } + /** * Start building a new configuration. * @return The entry point for creating a new configuration. @@ -127,10 +129,10 @@ public QdrantVectorStoreConfig build() { return new QdrantVectorStoreConfig(this); } - } } + /** * Constructs a new QdrantVectorStore. * @param config The configuration for the store. @@ -165,16 +167,16 @@ public void onApplicationEvent(ApplicationReadyEvent event) { // Create the collection if it does not exist. if (!isCollectionExists()) { var vectorParams = VectorParams.newBuilder() - .setDistance(Distance.Cosine) - .setSize(this.embeddingClient.dimensions()) - .build(); - try { - this.qdrantClient.createCollectionAsync(this.collectionName, vectorParams).get(); - } // - catch (Exception e) { - throw new RuntimeException(e); - } - } + .setDistance(Distance.Cosine) + .setSize(this.embeddingClient.dimensions()) + .build(); + try { + this.qdrantClient.createCollectionAsync(this.collectionName, vectorParams).get(); + } // + catch (Exception e) { + throw new RuntimeException(e); + } + } } /** @@ -303,7 +305,6 @@ private List toFloatList(List doubleList) { return doubleList.stream().map(d -> d.floatValue()).toList(); } - private boolean isCollectionExists() { try { return this.qdrantClient.listCollectionsAsync().get().stream().anyMatch(c -> c.equals(this.collectionName)); From 04d854cc49237e4af80e95ef51fc9c65f77cf28f Mon Sep 17 00:00:00 2001 From: Josh Long Date: Mon, 6 May 2024 12:51:20 +0200 Subject: [PATCH 18/25] make project names consistent --- models/spring-ai-watsonx-ai/pom.xml | 2 ++ pom.xml | 4 ++-- .../README.md | 0 .../pom.xml | 6 +++--- .../azure/AzureAiSearchFilterExpressionConverter.java | 0 .../ai/vectorstore/azure/AzureVectorStore.java | 0 .../azure/AzureAiSearchFilterExpressionConverterTests.java | 0 .../ai/vectorstore/azure/AzureVectorStoreIT.java | 0 vector-stores/spring-ai-chroma-store/pom.xml | 2 +- .../springframework/ai/vectorstore/ChromaVectorStore.java | 2 +- vector-stores/spring-ai-milvus-store/pom.xml | 4 ++-- vector-stores/spring-ai-mongodb-atlas-store/pom.xml | 4 ++-- vector-stores/spring-ai-neo4j-store/pom.xml | 2 +- vector-stores/spring-ai-pgvector-store/pom.xml | 2 +- vector-stores/spring-ai-pinecone/pom.xml | 3 +-- vector-stores/spring-ai-qdrant/pom.xml | 4 ++-- .../ai/vectorstore/qdrant/QdrantVectorStore.java | 1 - vector-stores/spring-ai-redis/pom.xml | 4 ++-- .../README.md | 0 .../pom.xml | 3 +-- .../ai/vectorstore/WeaviateFilterExpressionConverter.java | 0 .../springframework/ai/vectorstore/WeaviateVectorStore.java | 0 .../vectorstore/WeaviateFilterExpressionConverterTests.java | 0 .../ai/vectorstore/WeaviateVectorStoreIT.java | 0 .../src/test/resources/docker-compose.yml | 0 25 files changed, 21 insertions(+), 22 deletions(-) rename vector-stores/{spring-ai-azure-vector-store => spring-ai-azure-store}/README.md (100%) rename vector-stores/{spring-ai-azure-vector-store => spring-ai-azure-store}/pom.xml (93%) rename vector-stores/{spring-ai-azure-vector-store => spring-ai-azure-store}/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java (100%) rename vector-stores/{spring-ai-azure-vector-store => spring-ai-azure-store}/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java (100%) rename vector-stores/{spring-ai-azure-vector-store => spring-ai-azure-store}/src/test/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverterTests.java (100%) rename vector-stores/{spring-ai-azure-vector-store => spring-ai-azure-store}/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java (100%) rename vector-stores/{spring-ai-weaviate => spring-ai-weaviate-store}/README.md (100%) rename vector-stores/{spring-ai-weaviate => spring-ai-weaviate-store}/pom.xml (96%) rename vector-stores/{spring-ai-weaviate => spring-ai-weaviate-store}/src/main/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverter.java (100%) rename vector-stores/{spring-ai-weaviate => spring-ai-weaviate-store}/src/main/java/org/springframework/ai/vectorstore/WeaviateVectorStore.java (100%) rename vector-stores/{spring-ai-weaviate => spring-ai-weaviate-store}/src/test/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverterTests.java (100%) rename vector-stores/{spring-ai-weaviate => spring-ai-weaviate-store}/src/test/java/org/springframework/ai/vectorstore/WeaviateVectorStoreIT.java (100%) rename vector-stores/{spring-ai-weaviate => spring-ai-weaviate-store}/src/test/resources/docker-compose.yml (100%) diff --git a/models/spring-ai-watsonx-ai/pom.xml b/models/spring-ai-watsonx-ai/pom.xml index 8cd07c4e2b..7f28cc147a 100644 --- a/models/spring-ai-watsonx-ai/pom.xml +++ b/models/spring-ai-watsonx-ai/pom.xml @@ -11,6 +11,8 @@ spring-ai-watsonx-ai + Spring AI WatsonX AI + 17 17 diff --git a/pom.xml b/pom.xml index 771b4e5ad6..d9f873b09d 100644 --- a/pom.xml +++ b/pom.xml @@ -57,8 +57,8 @@ document-readers/tika-reader vector-stores/spring-ai-pinecone vector-stores/spring-ai-chroma-store - vector-stores/spring-ai-azure-vector-store - vector-stores/spring-ai-weaviate + vector-stores/spring-ai-azure-store + vector-stores/spring-ai-weaviate-store vector-stores/spring-ai-redis vector-stores/spring-ai-gemfire spring-ai-spring-boot-starters/spring-ai-starter-vertex-ai-palm2 diff --git a/vector-stores/spring-ai-azure-vector-store/README.md b/vector-stores/spring-ai-azure-store/README.md similarity index 100% rename from vector-stores/spring-ai-azure-vector-store/README.md rename to vector-stores/spring-ai-azure-store/README.md diff --git a/vector-stores/spring-ai-azure-vector-store/pom.xml b/vector-stores/spring-ai-azure-store/pom.xml similarity index 93% rename from vector-stores/spring-ai-azure-vector-store/pom.xml rename to vector-stores/spring-ai-azure-store/pom.xml index 08f680f83e..19dbf15af6 100644 --- a/vector-stores/spring-ai-azure-vector-store/pom.xml +++ b/vector-stores/spring-ai-azure-store/pom.xml @@ -8,10 +8,10 @@ 1.0.0-SNAPSHOT ../../pom.xml - spring-ai-azure-vector-store + spring-ai-azure-store jar - Spring AI - Azure AI Search Vector Store - Spring AI - Azure AI Search Vector Store + Spring AI Vector Store - Azure AI Search + Spring AI Vector Store - Azure AI Search https://github.com/spring-projects/spring-ai diff --git a/vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java b/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java similarity index 100% rename from vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java rename to vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java diff --git a/vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java b/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java similarity index 100% rename from vector-stores/spring-ai-azure-vector-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java rename to vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java diff --git a/vector-stores/spring-ai-azure-vector-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverterTests.java b/vector-stores/spring-ai-azure-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverterTests.java similarity index 100% rename from vector-stores/spring-ai-azure-vector-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverterTests.java rename to vector-stores/spring-ai-azure-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverterTests.java diff --git a/vector-stores/spring-ai-azure-vector-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java b/vector-stores/spring-ai-azure-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java similarity index 100% rename from vector-stores/spring-ai-azure-vector-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java rename to vector-stores/spring-ai-azure-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java diff --git a/vector-stores/spring-ai-chroma-store/pom.xml b/vector-stores/spring-ai-chroma-store/pom.xml index d6232a5761..586c6a88ba 100644 --- a/vector-stores/spring-ai-chroma-store/pom.xml +++ b/vector-stores/spring-ai-chroma-store/pom.xml @@ -10,7 +10,7 @@ spring-ai-chroma-store jar - Spring AI Chroma Vector Store + Spring AI Vector Store - Chroma Spring AI Chroma Vector Store https://github.com/spring-projects/spring-ai diff --git a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java index 3c9741cbef..e3c03863d7 100644 --- a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java +++ b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java @@ -38,7 +38,7 @@ * for embedding calculations. For more information about how it does this, see the * official Chroma website. * - * @author Josh Long + * */ public class ChromaVectorStore implements VectorStore, ApplicationListener { diff --git a/vector-stores/spring-ai-milvus-store/pom.xml b/vector-stores/spring-ai-milvus-store/pom.xml index 460f9dbbf4..798ef5be03 100644 --- a/vector-stores/spring-ai-milvus-store/pom.xml +++ b/vector-stores/spring-ai-milvus-store/pom.xml @@ -10,8 +10,8 @@ spring-ai-milvus-store jar - Spring AI Milvus Vector Store - Spring AI Milvus Vector Store + Spring AI Vector Store - Milvus + Spring AI Vector Store - Milvus https://github.com/spring-projects/spring-ai diff --git a/vector-stores/spring-ai-mongodb-atlas-store/pom.xml b/vector-stores/spring-ai-mongodb-atlas-store/pom.xml index 5c71ee2363..abf7a5a2d0 100644 --- a/vector-stores/spring-ai-mongodb-atlas-store/pom.xml +++ b/vector-stores/spring-ai-mongodb-atlas-store/pom.xml @@ -10,8 +10,8 @@ spring-ai-mongodb-atlas-store jar - Spring AI MongoDB Atlas Vector Store - Spring AI MongoDB Atlas Vector Store + Spring AI Vector Store - MongoDB Atlas + Spring AI Vector Store - MongoDB Atlas https://github.com/spring-projects-experimental/spring-ai diff --git a/vector-stores/spring-ai-neo4j-store/pom.xml b/vector-stores/spring-ai-neo4j-store/pom.xml index 430f6f0eb0..9cbb5a1e23 100644 --- a/vector-stores/spring-ai-neo4j-store/pom.xml +++ b/vector-stores/spring-ai-neo4j-store/pom.xml @@ -10,7 +10,7 @@ spring-ai-neo4j-store jar - Spring AI Vector Store - neo4j + Spring AI Vector Store - Neo4J Spring AI Neo4j Vector Store https://github.com/spring-projects/spring-ai diff --git a/vector-stores/spring-ai-pgvector-store/pom.xml b/vector-stores/spring-ai-pgvector-store/pom.xml index 9c23e4b052..056e6e172e 100644 --- a/vector-stores/spring-ai-pgvector-store/pom.xml +++ b/vector-stores/spring-ai-pgvector-store/pom.xml @@ -10,7 +10,7 @@ spring-ai-pgvector-store jar - Spring AI Vector Store - pgvector + Spring AI Vector Store - PGVector Spring AI PGVector Vector Store https://github.com/spring-projects/spring-ai diff --git a/vector-stores/spring-ai-pinecone/pom.xml b/vector-stores/spring-ai-pinecone/pom.xml index f9a40f83bf..38fe9c4fdd 100644 --- a/vector-stores/spring-ai-pinecone/pom.xml +++ b/vector-stores/spring-ai-pinecone/pom.xml @@ -10,8 +10,7 @@ spring-ai-pinecone jar - spring-ai-pinecone - spring-ai-pinecone + Spring AI Vector Store - Pinecone https://github.com/spring-projects/spring-ai diff --git a/vector-stores/spring-ai-qdrant/pom.xml b/vector-stores/spring-ai-qdrant/pom.xml index 95c578d282..77ec45ffa9 100644 --- a/vector-stores/spring-ai-qdrant/pom.xml +++ b/vector-stores/spring-ai-qdrant/pom.xml @@ -10,8 +10,8 @@ spring-ai-qdrant jar - spring-ai-qdrant - spring-ai-qdrant + Spring AI Vector Store - QDrant + https://github.com/spring-projects/spring-ai diff --git a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java b/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java index 1b54c2f003..607a1ecb7a 100644 --- a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java +++ b/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java @@ -261,7 +261,6 @@ public List similaritySearch(SearchRequest request) { /** * Extracts metadata from a Protobuf Struct. - * @param metadataStruct The Protobuf Struct containing metadata. * @return The metadata as a map. */ private Document toDocument(ScoredPoint point) { diff --git a/vector-stores/spring-ai-redis/pom.xml b/vector-stores/spring-ai-redis/pom.xml index ebf2cc44ff..2d66f35c73 100644 --- a/vector-stores/spring-ai-redis/pom.xml +++ b/vector-stores/spring-ai-redis/pom.xml @@ -10,8 +10,8 @@ spring-ai-redis jar - spring-ai-redis - Spring AI Redis Vector Store + Spring AI Vector Store - Redis + Spring AI Vector Store - Redis https://github.com/spring-projects/spring-ai diff --git a/vector-stores/spring-ai-weaviate/README.md b/vector-stores/spring-ai-weaviate-store/README.md similarity index 100% rename from vector-stores/spring-ai-weaviate/README.md rename to vector-stores/spring-ai-weaviate-store/README.md diff --git a/vector-stores/spring-ai-weaviate/pom.xml b/vector-stores/spring-ai-weaviate-store/pom.xml similarity index 96% rename from vector-stores/spring-ai-weaviate/pom.xml rename to vector-stores/spring-ai-weaviate-store/pom.xml index 523d341c72..7324f9c7d9 100644 --- a/vector-stores/spring-ai-weaviate/pom.xml +++ b/vector-stores/spring-ai-weaviate-store/pom.xml @@ -10,8 +10,7 @@ spring-ai-weaviate-store jar - spring-ai-weaviate-store - spring-ai-weaviate + Spring AI Vector Store - Weaviate https://github.com/spring-projects/spring-ai diff --git a/vector-stores/spring-ai-weaviate/src/main/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverter.java b/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverter.java similarity index 100% rename from vector-stores/spring-ai-weaviate/src/main/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverter.java rename to vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverter.java diff --git a/vector-stores/spring-ai-weaviate/src/main/java/org/springframework/ai/vectorstore/WeaviateVectorStore.java b/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateVectorStore.java similarity index 100% rename from vector-stores/spring-ai-weaviate/src/main/java/org/springframework/ai/vectorstore/WeaviateVectorStore.java rename to vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateVectorStore.java diff --git a/vector-stores/spring-ai-weaviate/src/test/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverterTests.java b/vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverterTests.java similarity index 100% rename from vector-stores/spring-ai-weaviate/src/test/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverterTests.java rename to vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverterTests.java diff --git a/vector-stores/spring-ai-weaviate/src/test/java/org/springframework/ai/vectorstore/WeaviateVectorStoreIT.java b/vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateVectorStoreIT.java similarity index 100% rename from vector-stores/spring-ai-weaviate/src/test/java/org/springframework/ai/vectorstore/WeaviateVectorStoreIT.java rename to vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateVectorStoreIT.java diff --git a/vector-stores/spring-ai-weaviate/src/test/resources/docker-compose.yml b/vector-stores/spring-ai-weaviate-store/src/test/resources/docker-compose.yml similarity index 100% rename from vector-stores/spring-ai-weaviate/src/test/resources/docker-compose.yml rename to vector-stores/spring-ai-weaviate-store/src/test/resources/docker-compose.yml From 3ee04ed8da61c0f3d0f69eaa3e6012538d2cc432 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 9 May 2024 11:31:37 +0300 Subject: [PATCH 19/25] fix code style and missing dependency renaming --- spring-ai-bom/pom.xml | 2 +- .../ROOT/pages/api/vectordbs/azure.adoc | 2 +- spring-ai-spring-boot-autoconfigure/pom.xml | 4 +- .../spring-ai-starter-azure-store/pom.xml | 2 +- .../vectorstore/azure/AzureVectorStore.java | 26 +++++++++--- .../ai/vectorstore/CassandraVectorStore.java | 14 ++++++- .../CassandraVectorStoreConfig.java | 6 ++- .../ai/vectorstore/ChromaVectorStore.java | 10 +++-- .../ai/vectorstore/JsonUtils.java | 4 -- .../ai/vectorstore/RedisVectorStore.java | 40 +++++++++++++------ 10 files changed, 77 insertions(+), 33 deletions(-) diff --git a/spring-ai-bom/pom.xml b/spring-ai-bom/pom.xml index d1c857f9da..507a8b4014 100644 --- a/spring-ai-bom/pom.xml +++ b/spring-ai-bom/pom.xml @@ -128,7 +128,7 @@ org.springframework.ai - spring-ai-azure-vector-store + spring-ai-azure-store ${project.version} diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/azure.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/azure.adoc index 1ad2d24a1b..b9f303c077 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/azure.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/azure.adoc @@ -77,7 +77,7 @@ Add these dependencies to your project: ---- org.springframework.ai - spring-ai-azure-vector-store + spring-ai-azure-store ---- diff --git a/spring-ai-spring-boot-autoconfigure/pom.xml b/spring-ai-spring-boot-autoconfigure/pom.xml index ee5a26bc45..38e867b1c4 100644 --- a/spring-ai-spring-boot-autoconfigure/pom.xml +++ b/spring-ai-spring-boot-autoconfigure/pom.xml @@ -141,7 +141,7 @@ org.springframework.ai - spring-ai-azure-vector-store + spring-ai-azure-store ${project.parent.version} true @@ -328,7 +328,7 @@ cassandra test - + com.redis testcontainers-redis diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-azure-store/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-azure-store/pom.xml index d688bba563..c6b1d72762 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-azure-store/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-azure-store/pom.xml @@ -34,7 +34,7 @@ org.springframework.ai - spring-ai-azure-vector-store + spring-ai-azure-store ${project.parent.version} diff --git a/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java b/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java index 9a5205cfb9..85e790277f 100644 --- a/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java +++ b/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java @@ -15,16 +15,35 @@ */ package org.springframework.ai.vectorstore.azure; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.TypeReference; import com.azure.core.util.Context; import com.azure.search.documents.SearchClient; import com.azure.search.documents.SearchDocument; import com.azure.search.documents.indexes.SearchIndexClient; -import com.azure.search.documents.indexes.models.*; -import com.azure.search.documents.models.*; +import com.azure.search.documents.indexes.models.HnswAlgorithmConfiguration; +import com.azure.search.documents.indexes.models.HnswParameters; +import com.azure.search.documents.indexes.models.SearchField; +import com.azure.search.documents.indexes.models.SearchFieldDataType; +import com.azure.search.documents.indexes.models.SearchIndex; +import com.azure.search.documents.indexes.models.VectorSearch; +import com.azure.search.documents.indexes.models.VectorSearchAlgorithmMetric; +import com.azure.search.documents.indexes.models.VectorSearchProfile; +import com.azure.search.documents.models.IndexDocumentsResult; +import com.azure.search.documents.models.IndexingResult; +import com.azure.search.documents.models.SearchOptions; +import com.azure.search.documents.models.VectorSearchOptions; +import com.azure.search.documents.models.VectorizedQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.SearchRequest; @@ -36,9 +55,6 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import java.util.*; -import java.util.stream.Collectors; - /** * Uses Azure Cognitive Search as a backing vector store. Documents can be preloaded into * a Cognitive Search index and managed via Azure tools or added and managed through this diff --git a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java index c06cfdfc56..c08f7d09d6 100644 --- a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java +++ b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java @@ -15,7 +15,11 @@ */ package org.springframework.ai.vectorstore; -import com.datastax.oss.driver.api.core.cql.*; +import com.datastax.oss.driver.api.core.cql.BoundStatement; +import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder; +import com.datastax.oss.driver.api.core.cql.PreparedStatement; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.data.CqlVector; import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; import com.datastax.oss.driver.api.querybuilder.QueryBuilder; @@ -32,7 +36,13 @@ import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; import org.springframework.beans.factory.InitializingBean; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; diff --git a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java index aebe211c8a..7218f26973 100644 --- a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java +++ b/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java @@ -38,11 +38,15 @@ import java.net.InetSocketAddress; import java.time.Duration; -import java.util.*; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.function.Function; import java.util.stream.Stream; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; /** * Configuration for the Cassandra vector store. diff --git a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java index e3c03863d7..f51c530e4f 100644 --- a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java +++ b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java @@ -15,6 +15,12 @@ */ package org.springframework.ai.vectorstore; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + import org.springframework.ai.chroma.ChromaApi; import org.springframework.ai.chroma.ChromaApi.AddEmbeddingsRequest; import org.springframework.ai.chroma.ChromaApi.DeleteEmbeddingsRequest; @@ -29,8 +35,6 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import java.util.*; - /** * {@link ChromaVectorStore} is a concrete implementation of the {@link VectorStore} * interface. It is responsible for adding, deleting, and searching documents based on @@ -38,7 +42,7 @@ * for embedding calculations. For more information about how it does this, see the * official Chroma website. * - * + * @author Christian Tzolov */ public class ChromaVectorStore implements VectorStore, ApplicationListener { diff --git a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/JsonUtils.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/JsonUtils.java index 7a05fab286..41f891b492 100644 --- a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/JsonUtils.java +++ b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/JsonUtils.java @@ -21,10 +21,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -/** - * @author Christian Tzolov - */ - /** * Utility class for JSON processing. Provides methods for converting JSON strings to maps * and lists, and for converting between lists of different numeric types. diff --git a/vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java b/vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java index 744411622d..47982bf928 100644 --- a/vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java +++ b/vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java @@ -15,8 +15,35 @@ */ package org.springframework.ai.vectorstore; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Pipeline; +import redis.clients.jedis.json.Path2; +import redis.clients.jedis.search.FTCreateParams; +import redis.clients.jedis.search.IndexDataType; +import redis.clients.jedis.search.Query; +import redis.clients.jedis.search.RediSearchUtil; +import redis.clients.jedis.search.Schema.FieldType; +import redis.clients.jedis.search.SearchResult; +import redis.clients.jedis.search.schemafields.NumericField; +import redis.clients.jedis.search.schemafields.SchemaField; +import redis.clients.jedis.search.schemafields.TagField; +import redis.clients.jedis.search.schemafields.TextField; +import redis.clients.jedis.search.schemafields.VectorField; +import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; + import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; @@ -24,19 +51,6 @@ import org.springframework.context.ApplicationListener; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; -import redis.clients.jedis.JedisPooled; -import redis.clients.jedis.Pipeline; -import redis.clients.jedis.json.Path2; -import redis.clients.jedis.search.*; -import redis.clients.jedis.search.Schema.FieldType; -import redis.clients.jedis.search.schemafields.*; -import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; - -import java.text.MessageFormat; -import java.util.*; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; /** * The RedisVectorStore is for managing and querying vector data in a Redis database. It From 1985824fa906c7b8deab9b92cb1fdcb5c457f022 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 9 May 2024 18:22:24 +0300 Subject: [PATCH 20/25] clean code imports --- .../springframework/ai/vectorstore/azure/AzureVectorStore.java | 3 ++- .../org/springframework/ai/vectorstore/ChromaVectorStore.java | 3 ++- .../test/java/org/springframework/ai/chroma/ChromaApiIT.java | 2 +- .../springframework/ai/vectorstore/BasicAuthChromaWhereIT.java | 1 + .../springframework/ai/vectorstore/ChromaVectorStoreIT.java | 3 +-- .../ai/vectorstore/TokenSecuredChromaWhereIT.java | 1 + 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java b/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java index 85e790277f..36417d6695 100644 --- a/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java +++ b/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java @@ -51,6 +51,7 @@ import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationListener; +import org.springframework.lang.NonNull; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -118,7 +119,7 @@ public class AzureVectorStore implements VectorStore, ApplicationListener filterMetadataFields; @Override - public void onApplicationEvent(ApplicationReadyEvent event) { + public void onApplicationEvent(@NonNull ApplicationReadyEvent event) { int dimensions = this.embeddingClient.dimensions(); diff --git a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java index f51c530e4f..a53d02e9ac 100644 --- a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java +++ b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java @@ -31,6 +31,7 @@ import org.springframework.ai.vectorstore.filter.converter.ChromaFilterExpressionConverter; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationListener; +import org.springframework.lang.NonNull; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -150,7 +151,7 @@ public List similaritySearch(SearchRequest request) { } @Override - public void onApplicationEvent(ApplicationReadyEvent event) { + public void onApplicationEvent(@NonNull ApplicationReadyEvent event) { var collection = this.chromaApi.getCollection(this.collectionName); if (collection == null) { collection = this.chromaApi.createCollection(new ChromaApi.CreateCollectionRequest(this.collectionName)); diff --git a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/chroma/ChromaApiIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/chroma/ChromaApiIT.java index a53814d569..0d1576ea0b 100644 --- a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/chroma/ChromaApiIT.java +++ b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/chroma/ChromaApiIT.java @@ -45,7 +45,7 @@ public class ChromaApiIT { @Container - static ChromaDBContainer chromaContainer = new ChromaDBContainer("ghcr.io/chroma-core/chroma:0.4.22.dev44"); + static ChromaDBContainer chromaContainer = new ChromaDBContainer("ghcr.io/chroma-core/chroma:0.4.22"); @Autowired ChromaApi chroma; diff --git a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java index a7b9ebf12c..8be4b8ac9a 100644 --- a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java +++ b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/BasicAuthChromaWhereIT.java @@ -28,6 +28,7 @@ import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.OpenAiEmbeddingClient; +import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; diff --git a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java index 9c5e8674ed..7ed5eeea84 100644 --- a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java +++ b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreIT.java @@ -28,9 +28,8 @@ import org.springframework.ai.chroma.ChromaApi; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; -import org.springframework.ai.openai.api.OpenAiApi; -import org.springframework.ai.vectorstore.ChromaVectorStore; import org.springframework.ai.openai.OpenAiEmbeddingClient; +import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; diff --git a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java index 4953732c1b..dac6e9bc0a 100644 --- a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java +++ b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/TokenSecuredChromaWhereIT.java @@ -28,6 +28,7 @@ import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.OpenAiEmbeddingClient; +import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; From 3475f17e98c3c6be01792fb5d09ed805d30d37ee Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 17 May 2024 17:05:03 +0200 Subject: [PATCH 21/25] Unify the vector store module and pom names spring-ai-qdrant -> spring-ai-qdrant-store spring-ai-cassandra -> spring-ai-cassandra-store spring-ai-pinecone -> spring-ai-pinecone-store spring-ai-redis -> spring-ai-redis-store spring-ai-qdrant -> spring-ai-qdrant-store spring-ai-gemfire -> spring-ai-gemfire-store spring-ai-azure-vector-store-spring-boot-starter -> spring-ai-azure-store-spring-boot-starter spring-ai-redis-spring-boot-starter -> spring-ai-redis-store-spring-boot-starter --- models/spring-ai-openai/pom.xml | 2 +- pom.xml | 100 +++++++++-------- spring-ai-bom/pom.xml | 14 +-- .../pages/api/vectordbs/apache-cassandra.adoc | 4 +- .../ROOT/pages/api/vectordbs/gemfire.adoc | 2 +- .../ROOT/pages/api/vectordbs/pinecone.adoc | 2 +- .../ROOT/pages/api/vectordbs/qdrant.adoc | 4 +- .../ROOT/pages/api/vectordbs/redis.adoc | 2 +- spring-ai-spring-boot-autoconfigure/pom.xml | 8 +- .../spring-ai-starter-azure-store/pom.xml | 2 +- .../pom.xml | 4 +- .../spring-ai-starter-pinecone-store/pom.xml | 2 +- .../spring-ai-starter-qdrant-store/pom.xml | 2 +- .../pom.xml | 4 +- spring-ai-spring-boot-testcontainers/pom.xml | 4 +- .../vectorstore/azure/AzureVectorStore.java | 103 +++++++++--------- .../README.md | 0 .../pom.xml | 2 +- .../CassandraFilterExpressionConverter.java | 0 .../ai/vectorstore/CassandraVectorStore.java | 0 .../CassandraVectorStoreConfig.java | 0 .../src/main/resources/application.conf | 0 ...ssandraFilterExpressionConverterTests.java | 0 .../CassandraRichSchemaVectorStoreIT.java | 0 .../vectorstore/CassandraVectorStoreIT.java | 0 .../vectorstore/WikiVectorStoreExample.java | 0 .../src/test/resources/application.conf | 0 .../test/resources/test_wiki_full_schema.cql | 0 .../resources/test_wiki_partial_0_schema.cql | 0 .../resources/test_wiki_partial_1_schema.cql | 0 .../resources/test_wiki_partial_2_schema.cql | 0 .../resources/test_wiki_partial_3_schema.cql | 0 .../resources/test_wiki_partial_4_schema.cql | 0 .../ai/vectorstore/ChromaVectorStore.java | 12 +- .../vectorstore/ElasticsearchVectorStore.java | 10 +- .../README.md | 0 .../pom.xml | 2 +- .../ai/vectorstore/GemFireVectorStore.java | 0 .../ai/vectorstore/GemFireVectorStoreIT.java | 0 .../ai/vectorstore/MilvusVectorStore.java | 15 +-- .../vectorstore/MongoDBAtlasVectorStore.java | 26 ++--- .../ai/vectorstore/Neo4jVectorStore.java | 55 +++++----- .../ai/vectorstore/PgVectorStore.java | 67 +++++------- .../README.md | 0 .../pom.xml | 2 +- .../ai/vectorstore/PineconeVectorStore.java | 0 .../ai/vectorstore/PineconeVectorStoreIT.java | 0 .../README.md | 0 .../pom.xml | 2 +- .../QdrantFilterExpressionConverter.java | 0 .../qdrant/QdrantObjectFactory.java | 0 .../qdrant/QdrantValueFactory.java | 0 .../vectorstore/qdrant/QdrantVectorStore.java | 62 +++++------ .../qdrant/QdrantVectorStoreIT.java | 0 .../README.md | 0 .../pom.xml | 2 +- .../RedisFilterExpressionConverter.java | 0 .../ai/vectorstore/RedisVectorStore.java | 51 +++++---- .../RedisFilterExpressionConverterTests.java | 0 .../ai/vectorstore/RedisVectorStoreIT.java | 0 .../ai/vectorstore/WeaviateVectorStore.java | 2 +- 61 files changed, 270 insertions(+), 299 deletions(-) rename spring-ai-spring-boot-starters/{spring-ai-starter-cassandra => spring-ai-starter-cassandra-store}/pom.xml (92%) rename spring-ai-spring-boot-starters/{spring-ai-starter-redis => spring-ai-starter-redis-store}/pom.xml (92%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/README.md (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/pom.xml (97%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/main/resources/application.conf (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverterTests.java (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/java/org/springframework/ai/vectorstore/CassandraRichSchemaVectorStoreIT.java (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/java/org/springframework/ai/vectorstore/CassandraVectorStoreIT.java (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/java/org/springframework/ai/vectorstore/WikiVectorStoreExample.java (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/resources/application.conf (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/resources/test_wiki_full_schema.cql (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/resources/test_wiki_partial_0_schema.cql (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/resources/test_wiki_partial_1_schema.cql (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/resources/test_wiki_partial_2_schema.cql (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/resources/test_wiki_partial_3_schema.cql (100%) rename vector-stores/{spring-ai-cassandra => spring-ai-cassandra-store}/src/test/resources/test_wiki_partial_4_schema.cql (100%) rename vector-stores/{spring-ai-gemfire => spring-ai-gemfire-store}/README.md (100%) rename vector-stores/{spring-ai-gemfire => spring-ai-gemfire-store}/pom.xml (98%) rename vector-stores/{spring-ai-gemfire => spring-ai-gemfire-store}/src/main/java/org/springframework/ai/vectorstore/GemFireVectorStore.java (100%) rename vector-stores/{spring-ai-gemfire => spring-ai-gemfire-store}/src/test/java/org/springframework/ai/vectorstore/GemFireVectorStoreIT.java (100%) rename vector-stores/{spring-ai-pinecone => spring-ai-pinecone-store}/README.md (100%) rename vector-stores/{spring-ai-pinecone => spring-ai-pinecone-store}/pom.xml (98%) rename vector-stores/{spring-ai-pinecone => spring-ai-pinecone-store}/src/main/java/org/springframework/ai/vectorstore/PineconeVectorStore.java (100%) rename vector-stores/{spring-ai-pinecone => spring-ai-pinecone-store}/src/test/java/org/springframework/ai/vectorstore/PineconeVectorStoreIT.java (100%) rename vector-stores/{spring-ai-qdrant => spring-ai-qdrant-store}/README.md (100%) rename vector-stores/{spring-ai-qdrant => spring-ai-qdrant-store}/pom.xml (98%) rename vector-stores/{spring-ai-qdrant => spring-ai-qdrant-store}/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantFilterExpressionConverter.java (100%) rename vector-stores/{spring-ai-qdrant => spring-ai-qdrant-store}/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantObjectFactory.java (100%) rename vector-stores/{spring-ai-qdrant => spring-ai-qdrant-store}/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantValueFactory.java (100%) rename vector-stores/{spring-ai-qdrant => spring-ai-qdrant-store}/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java (95%) rename vector-stores/{spring-ai-qdrant => spring-ai-qdrant-store}/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java (100%) rename vector-stores/{spring-ai-redis => spring-ai-redis-store}/README.md (100%) rename vector-stores/{spring-ai-redis => spring-ai-redis-store}/pom.xml (98%) rename vector-stores/{spring-ai-redis => spring-ai-redis-store}/src/main/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverter.java (100%) rename vector-stores/{spring-ai-redis => spring-ai-redis-store}/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java (97%) rename vector-stores/{spring-ai-redis => spring-ai-redis-store}/src/test/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverterTests.java (100%) rename vector-stores/{spring-ai-redis => spring-ai-redis-store}/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreIT.java (100%) diff --git a/models/spring-ai-openai/pom.xml b/models/spring-ai-openai/pom.xml index 715e3474f7..135b95f140 100644 --- a/models/spring-ai-openai/pom.xml +++ b/models/spring-ai-openai/pom.xml @@ -77,7 +77,7 @@ org.springframework.ai - spring-ai-qdrant + spring-ai-qdrant-store ${project.version} diff --git a/pom.xml b/pom.xml index d9f873b09d..c5e2e767d8 100644 --- a/pom.xml +++ b/pom.xml @@ -13,67 +13,73 @@ Building AI applications with Spring Boot + spring-ai-docs spring-ai-bom spring-ai-core - models/spring-ai-transformers - models/spring-ai-postgresml - models/spring-ai-bedrock + spring-ai-test + spring-ai-spring-boot-autoconfigure + spring-ai-retry + spring-ai-spring-boot-testcontainers + + document-readers/pdf-reader + document-readers/tika-reader + + vector-stores/spring-ai-azure-store + vector-stores/spring-ai-cassandra-store + vector-stores/spring-ai-chroma-store + vector-stores/spring-ai-elasticsearch-store + vector-stores/spring-ai-gemfire-store + vector-stores/spring-ai-hanadb-store + vector-stores/spring-ai-milvus-store + vector-stores/spring-ai-mongodb-atlas-store + vector-stores/spring-ai-neo4j-store + vector-stores/spring-ai-pgvector-store + vector-stores/spring-ai-pinecone-store + vector-stores/spring-ai-qdrant-store + vector-stores/spring-ai-redis-store + vector-stores/spring-ai-weaviate-store + + spring-ai-spring-boot-starters/spring-ai-starter-azure-store + spring-ai-spring-boot-starters/spring-ai-starter-cassandra-store + spring-ai-spring-boot-starters/spring-ai-starter-chroma-store + spring-ai-spring-boot-starters/spring-ai-starter-elasticsearch-store + spring-ai-spring-boot-starters/spring-ai-starter-hanadb-store + spring-ai-spring-boot-starters/spring-ai-starter-milvus-store + spring-ai-spring-boot-starters/spring-ai-starter-mongodb-atlas-store + spring-ai-spring-boot-starters/spring-ai-starter-neo4j-store + spring-ai-spring-boot-starters/spring-ai-starter-pgvector-store + spring-ai-spring-boot-starters/spring-ai-starter-pinecone-store + spring-ai-spring-boot-starters/spring-ai-starter-qdrant-store + spring-ai-spring-boot-starters/spring-ai-starter-redis-store + spring-ai-spring-boot-starters/spring-ai-starter-weaviate-store + + models/spring-ai-anthropic models/spring-ai-azure-openai + models/spring-ai-bedrock models/spring-ai-huggingface + models/spring-ai-mistral-ai models/spring-ai-ollama models/spring-ai-openai + models/spring-ai-postgresml models/spring-ai-stability-ai - models/spring-ai-mistral-ai - models/spring-ai-vertex-ai-palm2 + models/spring-ai-transformers models/spring-ai-vertex-ai-gemini - models/spring-ai-anthropic + models/spring-ai-vertex-ai-palm2 models/spring-ai-watsonx-ai - spring-ai-test - spring-ai-spring-boot-autoconfigure - spring-ai-spring-boot-starters/spring-ai-starter-openai + + spring-ai-spring-boot-starters/spring-ai-starter-anthropic spring-ai-spring-boot-starters/spring-ai-starter-azure-openai + spring-ai-spring-boot-starters/spring-ai-starter-bedrock-ai + spring-ai-spring-boot-starters/spring-ai-starter-mistral-ai spring-ai-spring-boot-starters/spring-ai-starter-ollama - spring-ai-spring-boot-starters/spring-ai-starter-transformers - spring-ai-spring-boot-starters/spring-ai-starter-cassandra - spring-ai-spring-boot-starters/spring-ai-starter-chroma-store - spring-ai-spring-boot-starters/spring-ai-starter-milvus-store - spring-ai-spring-boot-starters/spring-ai-starter-pgvector-store - spring-ai-spring-boot-starters/spring-ai-starter-hanadb-store - spring-ai-spring-boot-starters/spring-ai-starter-pinecone-store - spring-ai-spring-boot-starters/spring-ai-starter-azure-store - spring-ai-spring-boot-starters/spring-ai-starter-weaviate-store - spring-ai-spring-boot-starters/spring-ai-starter-redis - spring-ai-spring-boot-starters/spring-ai-starter-stability-ai - spring-ai-spring-boot-starters/spring-ai-starter-neo4j-store - spring-ai-spring-boot-starters/spring-ai-starter-qdrant-store + spring-ai-spring-boot-starters/spring-ai-starter-openai spring-ai-spring-boot-starters/spring-ai-starter-postgresml-embedding - spring-ai-docs - vector-stores/spring-ai-cassandra - vector-stores/spring-ai-pgvector-store - vector-stores/spring-ai-hanadb-store - vector-stores/spring-ai-milvus-store - vector-stores/spring-ai-neo4j-store - document-readers/pdf-reader - document-readers/tika-reader - vector-stores/spring-ai-pinecone - vector-stores/spring-ai-chroma-store - vector-stores/spring-ai-azure-store - vector-stores/spring-ai-weaviate-store - vector-stores/spring-ai-redis - vector-stores/spring-ai-gemfire - spring-ai-spring-boot-starters/spring-ai-starter-vertex-ai-palm2 + spring-ai-spring-boot-starters/spring-ai-starter-stability-ai + spring-ai-spring-boot-starters/spring-ai-starter-transformers spring-ai-spring-boot-starters/spring-ai-starter-vertex-ai-gemini - vector-stores/spring-ai-qdrant - spring-ai-spring-boot-starters/spring-ai-starter-bedrock-ai - spring-ai-spring-boot-starters/spring-ai-starter-mistral-ai - spring-ai-retry - vector-stores/spring-ai-mongodb-atlas-store - spring-ai-spring-boot-starters/spring-ai-starter-mongodb-atlas-store - spring-ai-spring-boot-testcontainers - spring-ai-spring-boot-starters/spring-ai-starter-anthropic - vector-stores/spring-ai-elasticsearch-store + spring-ai-spring-boot-starters/spring-ai-starter-vertex-ai-palm2 spring-ai-spring-boot-starters/spring-ai-starter-watsonx-ai - spring-ai-spring-boot-starters/spring-ai-starter-elasticsearch-store + diff --git a/spring-ai-bom/pom.xml b/spring-ai-bom/pom.xml index 507a8b4014..0c651b56ec 100644 --- a/spring-ai-bom/pom.xml +++ b/spring-ai-bom/pom.xml @@ -134,7 +134,7 @@ org.springframework.ai - spring-ai-cassandra + spring-ai-cassandra-store ${project.version} @@ -170,13 +170,13 @@ org.springframework.ai - spring-ai-pinecone + spring-ai-pinecone-store ${project.version} org.springframework.ai - spring-ai-redis + spring-ai-redis-store ${project.version} @@ -188,7 +188,7 @@ org.springframework.ai - spring-ai-qdrant + spring-ai-qdrant-store ${project.version} @@ -206,7 +206,7 @@ org.springframework.ai - spring-ai-gemfire + spring-ai-gemfire-store ${project.version} @@ -238,7 +238,7 @@ org.springframework.ai - spring-ai-azure-vector-store-spring-boot-starter + spring-ai-azure-store-spring-boot-starter ${project.version} @@ -316,7 +316,7 @@ org.springframework.ai - spring-ai-redis-spring-boot-starter + spring-ai-redis-store-spring-boot-starter ${project.version} diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/apache-cassandra.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/apache-cassandra.adoc index 51c6110cb0..b1c9d9f0d1 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/apache-cassandra.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/apache-cassandra.adoc @@ -57,7 +57,7 @@ Add these dependencies to your project: ---- org.springframework.ai - spring-ai-cassandra + spring-ai-cassandra-store ---- @@ -67,7 +67,7 @@ Add these dependencies to your project: ---- org.springframework.ai - spring-ai-cassandra-spring-boot-starter + spring-ai-cassandra-store-spring-boot-starter ---- diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/gemfire.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/gemfire.adoc index dff91d5751..c09a45ef74 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/gemfire.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/gemfire.adoc @@ -37,7 +37,7 @@ Add these dependencies to your project: ---- org.springframework.ai - spring-ai-gemfire + spring-ai-gemfire-store ---- diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/pinecone.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/pinecone.adoc index 7cb9132b70..ee7942711f 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/pinecone.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/pinecone.adoc @@ -172,7 +172,7 @@ Add these dependencies to your project: ---- org.springframework.ai - spring-ai-pinecone + spring-ai-pinecone-store ---- diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/qdrant.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/qdrant.adoc index 5ebaf926e4..87aef84f9c 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/qdrant.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/qdrant.adoc @@ -147,13 +147,13 @@ NOTE: These filter expressions are converted into the equivalent Qdrant link:htt == Manual Configuration -Instead of using the Spring Boot auto-configuration, you can manually configure the `QdrantVectorStore`. For this you need to add the `spring-ai-qdrant` dependency to your project: +Instead of using the Spring Boot auto-configuration, you can manually configure the `QdrantVectorStore`. For this you need to add the `spring-ai-qdrant-store` dependency to your project: [source,xml] ---- org.springframework.ai - spring-ai-qdrant + spring-ai-qdrant-store ---- diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/redis.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/redis.adoc index 7adfd60c48..436fd59ce6 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/redis.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/redis.adoc @@ -162,7 +162,7 @@ Add the Redis Vector Store and Jedis dependencies ---- org.springframework.ai - spring-ai-redis + spring-ai-redis-store diff --git a/spring-ai-spring-boot-autoconfigure/pom.xml b/spring-ai-spring-boot-autoconfigure/pom.xml index 38e867b1c4..09b48fc718 100644 --- a/spring-ai-spring-boot-autoconfigure/pom.xml +++ b/spring-ai-spring-boot-autoconfigure/pom.xml @@ -80,7 +80,7 @@ org.springframework.ai - spring-ai-pinecone + spring-ai-pinecone-store ${project.parent.version} true @@ -149,7 +149,7 @@ org.springframework.ai - spring-ai-cassandra + spring-ai-cassandra-store ${project.parent.version} true @@ -165,7 +165,7 @@ org.springframework.ai - spring-ai-redis + spring-ai-redis-store ${project.parent.version} true @@ -242,7 +242,7 @@ org.springframework.ai - spring-ai-qdrant + spring-ai-qdrant-store ${project.parent.version} true diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-azure-store/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-azure-store/pom.xml index c6b1d72762..d9292532ea 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-azure-store/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-azure-store/pom.xml @@ -7,7 +7,7 @@ 1.0.0-SNAPSHOT ../../pom.xml - spring-ai-azure-vector-store-spring-boot-starter + spring-ai-azure-store-spring-boot-starter jar Spring AI Starter - Azure Vector Store Spring AI Azure Vector Store Auto Configuration diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-cassandra/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-cassandra-store/pom.xml similarity index 92% rename from spring-ai-spring-boot-starters/spring-ai-starter-cassandra/pom.xml rename to spring-ai-spring-boot-starters/spring-ai-starter-cassandra-store/pom.xml index a40111ee87..00b58ba758 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-cassandra/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-cassandra-store/pom.xml @@ -7,7 +7,7 @@ 1.0.0-SNAPSHOT ../../pom.xml - spring-ai-cassandra-spring-boot-starter + spring-ai-cassandra-store-spring-boot-starter jar Spring AI Starter - Apache Cassandra Vector Store Spring AI Apache Cassandra Vector Store Auto Configuration @@ -34,7 +34,7 @@ org.springframework.ai - spring-ai-cassandra + spring-ai-cassandra-store ${project.parent.version} diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-pinecone-store/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-pinecone-store/pom.xml index 1a46b3bd7d..aefe6a0a62 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-pinecone-store/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-pinecone-store/pom.xml @@ -34,7 +34,7 @@ org.springframework.ai - spring-ai-pinecone + spring-ai-pinecone-store ${project.parent.version} diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-qdrant-store/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-qdrant-store/pom.xml index 4a578cca65..acbfe9a28d 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-qdrant-store/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-qdrant-store/pom.xml @@ -34,7 +34,7 @@ org.springframework.ai - spring-ai-qdrant + spring-ai-qdrant-store ${project.parent.version} diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-redis/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-redis-store/pom.xml similarity index 92% rename from spring-ai-spring-boot-starters/spring-ai-starter-redis/pom.xml rename to spring-ai-spring-boot-starters/spring-ai-starter-redis-store/pom.xml index 5d6794fb20..09fe4abb2e 100644 --- a/spring-ai-spring-boot-starters/spring-ai-starter-redis/pom.xml +++ b/spring-ai-spring-boot-starters/spring-ai-starter-redis-store/pom.xml @@ -7,7 +7,7 @@ 1.0.0-SNAPSHOT ../../pom.xml - spring-ai-redis-spring-boot-starter + spring-ai-redis-store-spring-boot-starter jar Spring AI Starter - Redis Vector Store Spring AI Redis Vector Store Auto Configuration @@ -34,7 +34,7 @@ org.springframework.ai - spring-ai-redis + spring-ai-redis-store ${project.parent.version} diff --git a/spring-ai-spring-boot-testcontainers/pom.xml b/spring-ai-spring-boot-testcontainers/pom.xml index 9758c4bc7b..ca44ac49fb 100644 --- a/spring-ai-spring-boot-testcontainers/pom.xml +++ b/spring-ai-spring-boot-testcontainers/pom.xml @@ -94,7 +94,7 @@ org.springframework.ai - spring-ai-redis + spring-ai-redis-store ${project.parent.version} true @@ -110,7 +110,7 @@ org.springframework.ai - spring-ai-qdrant + spring-ai-qdrant-store ${project.parent.version} true diff --git a/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java b/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java index 36417d6695..e0d8d9188c 100644 --- a/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java +++ b/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java @@ -49,9 +49,7 @@ import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.lang.NonNull; +import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -65,9 +63,8 @@ * @author Greg Meyer * @author Xiangyang Yu * @author Christian Tzolov - * @author Josh Long */ -public class AzureVectorStore implements VectorStore, ApplicationListener { +public class AzureVectorStore implements VectorStore, InitializingBean { private static final Logger logger = LoggerFactory.getLogger(AzureVectorStore.class); @@ -118,53 +115,6 @@ public class AzureVectorStore implements VectorStore, ApplicationListener filterMetadataFields; - @Override - public void onApplicationEvent(@NonNull ApplicationReadyEvent event) { - - int dimensions = this.embeddingClient.dimensions(); - - List fields = new ArrayList<>(); - - fields.add(new SearchField(ID_FIELD_NAME, SearchFieldDataType.STRING).setKey(true) - .setFilterable(true) - .setSortable(true)); - fields.add(new SearchField(EMBEDDING_FIELD_NAME, SearchFieldDataType.collection(SearchFieldDataType.SINGLE)) - .setSearchable(true) - .setVectorSearchDimensions(dimensions) - // This must match a vector search configuration name. - .setVectorSearchProfileName(SPRING_AI_VECTOR_PROFILE)); - fields.add(new SearchField(CONTENT_FIELD_NAME, SearchFieldDataType.STRING).setSearchable(true) - .setFilterable(true)); - fields.add(new SearchField(METADATA_FIELD_NAME, SearchFieldDataType.STRING).setSearchable(true) - .setFilterable(true)); - - for (MetadataField filterableMetadataField : this.filterMetadataFields) { - fields.add(new SearchField(METADATA_FIELD_PREFIX + filterableMetadataField.name(), - filterableMetadataField.fieldType()) - .setSearchable(false) - .setFacetable(true)); - } - - SearchIndex searchIndex = new SearchIndex(this.indexName).setFields(fields) - // VectorSearch configuration is required for a vector field. The name used - // for the vector search algorithm configuration must match the configuration - // used by the search field used for vector search. - .setVectorSearch(new VectorSearch() - .setProfiles(Collections - .singletonList(new VectorSearchProfile(SPRING_AI_VECTOR_PROFILE, SPRING_AI_VECTOR_CONFIG))) - .setAlgorithms(Collections.singletonList(new HnswAlgorithmConfiguration(SPRING_AI_VECTOR_CONFIG) - .setParameters(new HnswParameters().setM(4) - .setEfConstruction(400) - .setEfSearch(1000) - .setMetric(VectorSearchAlgorithmMetric.COSINE))))); - - SearchIndex index = this.searchIndexClient.createOrUpdateIndex(searchIndex); - - logger.info("Created search index: " + index.getName()); - - this.searchClient = this.searchIndexClient.getSearchClient(this.indexName); - } - public record MetadataField(String name, SearchFieldDataType fieldType) { public static MetadataField text(String name) { @@ -375,4 +325,51 @@ private List toFloatList(List doubleList) { private record AzureSearchDocument(String id, String content, List embedding, String metadata) { } -} + @Override + public void afterPropertiesSet() throws Exception { + + int dimensions = this.embeddingClient.dimensions(); + + List fields = new ArrayList<>(); + + fields.add(new SearchField(ID_FIELD_NAME, SearchFieldDataType.STRING).setKey(true) + .setFilterable(true) + .setSortable(true)); + fields.add(new SearchField(EMBEDDING_FIELD_NAME, SearchFieldDataType.collection(SearchFieldDataType.SINGLE)) + .setSearchable(true) + .setVectorSearchDimensions(dimensions) + // This must match a vector search configuration name. + .setVectorSearchProfileName(SPRING_AI_VECTOR_PROFILE)); + fields.add(new SearchField(CONTENT_FIELD_NAME, SearchFieldDataType.STRING).setSearchable(true) + .setFilterable(true)); + fields.add(new SearchField(METADATA_FIELD_NAME, SearchFieldDataType.STRING).setSearchable(true) + .setFilterable(true)); + + for (MetadataField filterableMetadataField : this.filterMetadataFields) { + fields.add(new SearchField(METADATA_FIELD_PREFIX + filterableMetadataField.name(), + filterableMetadataField.fieldType()) + .setSearchable(false) + .setFacetable(true)); + } + + SearchIndex searchIndex = new SearchIndex(this.indexName).setFields(fields) + // VectorSearch configuration is required for a vector field. The name used + // for the vector search algorithm configuration must match the configuration + // used by the search field used for vector search. + .setVectorSearch(new VectorSearch() + .setProfiles(Collections + .singletonList(new VectorSearchProfile(SPRING_AI_VECTOR_PROFILE, SPRING_AI_VECTOR_CONFIG))) + .setAlgorithms(Collections.singletonList(new HnswAlgorithmConfiguration(SPRING_AI_VECTOR_CONFIG) + .setParameters(new HnswParameters().setM(4) + .setEfConstruction(400) + .setEfSearch(1000) + .setMetric(VectorSearchAlgorithmMetric.COSINE))))); + + SearchIndex index = this.searchIndexClient.createOrUpdateIndex(searchIndex); + + logger.info("Created search index: " + index.getName()); + + this.searchClient = this.searchIndexClient.getSearchClient(this.indexName); + } + +} \ No newline at end of file diff --git a/vector-stores/spring-ai-cassandra/README.md b/vector-stores/spring-ai-cassandra-store/README.md similarity index 100% rename from vector-stores/spring-ai-cassandra/README.md rename to vector-stores/spring-ai-cassandra-store/README.md diff --git a/vector-stores/spring-ai-cassandra/pom.xml b/vector-stores/spring-ai-cassandra-store/pom.xml similarity index 97% rename from vector-stores/spring-ai-cassandra/pom.xml rename to vector-stores/spring-ai-cassandra-store/pom.xml index b01d200f85..fad401cfd5 100644 --- a/vector-stores/spring-ai-cassandra/pom.xml +++ b/vector-stores/spring-ai-cassandra-store/pom.xml @@ -8,7 +8,7 @@ 1.0.0-SNAPSHOT ../../pom.xml - spring-ai-cassandra + spring-ai-cassandra-store jar Spring AI Vector Store – Apache Cassandra Spring AI Vector Store for Apache Cassandra diff --git a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java b/vector-stores/spring-ai-cassandra-store/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java similarity index 100% rename from vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java rename to vector-stores/spring-ai-cassandra-store/src/main/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverter.java diff --git a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java b/vector-stores/spring-ai-cassandra-store/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java similarity index 100% rename from vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java rename to vector-stores/spring-ai-cassandra-store/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStore.java diff --git a/vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java b/vector-stores/spring-ai-cassandra-store/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java similarity index 100% rename from vector-stores/spring-ai-cassandra/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java rename to vector-stores/spring-ai-cassandra-store/src/main/java/org/springframework/ai/vectorstore/CassandraVectorStoreConfig.java diff --git a/vector-stores/spring-ai-cassandra/src/main/resources/application.conf b/vector-stores/spring-ai-cassandra-store/src/main/resources/application.conf similarity index 100% rename from vector-stores/spring-ai-cassandra/src/main/resources/application.conf rename to vector-stores/spring-ai-cassandra-store/src/main/resources/application.conf diff --git a/vector-stores/spring-ai-cassandra/src/test/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverterTests.java b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverterTests.java similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverterTests.java rename to vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/CassandraFilterExpressionConverterTests.java diff --git a/vector-stores/spring-ai-cassandra/src/test/java/org/springframework/ai/vectorstore/CassandraRichSchemaVectorStoreIT.java b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/CassandraRichSchemaVectorStoreIT.java similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/java/org/springframework/ai/vectorstore/CassandraRichSchemaVectorStoreIT.java rename to vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/CassandraRichSchemaVectorStoreIT.java diff --git a/vector-stores/spring-ai-cassandra/src/test/java/org/springframework/ai/vectorstore/CassandraVectorStoreIT.java b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/CassandraVectorStoreIT.java similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/java/org/springframework/ai/vectorstore/CassandraVectorStoreIT.java rename to vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/CassandraVectorStoreIT.java diff --git a/vector-stores/spring-ai-cassandra/src/test/java/org/springframework/ai/vectorstore/WikiVectorStoreExample.java b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/WikiVectorStoreExample.java similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/java/org/springframework/ai/vectorstore/WikiVectorStoreExample.java rename to vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/WikiVectorStoreExample.java diff --git a/vector-stores/spring-ai-cassandra/src/test/resources/application.conf b/vector-stores/spring-ai-cassandra-store/src/test/resources/application.conf similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/resources/application.conf rename to vector-stores/spring-ai-cassandra-store/src/test/resources/application.conf diff --git a/vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_full_schema.cql b/vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_full_schema.cql similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_full_schema.cql rename to vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_full_schema.cql diff --git a/vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_partial_0_schema.cql b/vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_partial_0_schema.cql similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_partial_0_schema.cql rename to vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_partial_0_schema.cql diff --git a/vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_partial_1_schema.cql b/vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_partial_1_schema.cql similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_partial_1_schema.cql rename to vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_partial_1_schema.cql diff --git a/vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_partial_2_schema.cql b/vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_partial_2_schema.cql similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_partial_2_schema.cql rename to vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_partial_2_schema.cql diff --git a/vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_partial_3_schema.cql b/vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_partial_3_schema.cql similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_partial_3_schema.cql rename to vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_partial_3_schema.cql diff --git a/vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_partial_4_schema.cql b/vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_partial_4_schema.cql similarity index 100% rename from vector-stores/spring-ai-cassandra/src/test/resources/test_wiki_partial_4_schema.cql rename to vector-stores/spring-ai-cassandra-store/src/test/resources/test_wiki_partial_4_schema.cql diff --git a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java index a53d02e9ac..40ede3a67e 100644 --- a/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java +++ b/vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/vectorstore/ChromaVectorStore.java @@ -29,9 +29,7 @@ import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; import org.springframework.ai.vectorstore.filter.converter.ChromaFilterExpressionConverter; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.lang.NonNull; +import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -42,10 +40,8 @@ * their similarity to a query, using the {@link ChromaApi} and {@link EmbeddingClient} * for embedding calculations. For more information about how it does this, see the * official Chroma website. - * - * @author Christian Tzolov */ -public class ChromaVectorStore implements VectorStore, ApplicationListener { +public class ChromaVectorStore implements VectorStore, InitializingBean { public static final String DISTANCE_FIELD_NAME = "distance"; @@ -151,7 +147,7 @@ public List similaritySearch(SearchRequest request) { } @Override - public void onApplicationEvent(@NonNull ApplicationReadyEvent event) { + public void afterPropertiesSet() throws Exception { var collection = this.chromaApi.getCollection(this.collectionName); if (collection == null) { collection = this.chromaApi.createCollection(new ChromaApi.CreateCollectionRequest(this.collectionName)); @@ -159,4 +155,4 @@ public void onApplicationEvent(@NonNull ApplicationReadyEvent event) { this.collectionId = collection.id(); } -} +} \ No newline at end of file diff --git a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java index cff3a8efdf..ebc01eae5f 100644 --- a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java +++ b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java @@ -37,8 +37,7 @@ import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.Filter; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationListener; +import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import java.io.IOException; @@ -50,10 +49,9 @@ /** * @author Jemin Huh * @author Wei Jiang - * @author Josh Long * @since 1.0.0 */ -public class ElasticsearchVectorStore implements VectorStore, ApplicationListener { +public class ElasticsearchVectorStore implements VectorStore, InitializingBean { // divided by 2 to get score in the range [0, 1] public static final String COSINE_SIMILARITY_FUNCTION = "(cosineSimilarity(params.query_vector, 'embedding') + 1.0) / 2"; @@ -221,10 +219,10 @@ private CreateIndexResponse createIndexMapping() { } @Override - public void onApplicationEvent(ApplicationReadyEvent event) { + public void afterPropertiesSet() { if (!indexExists()) { createIndexMapping(); } } -} +} \ No newline at end of file diff --git a/vector-stores/spring-ai-gemfire/README.md b/vector-stores/spring-ai-gemfire-store/README.md similarity index 100% rename from vector-stores/spring-ai-gemfire/README.md rename to vector-stores/spring-ai-gemfire-store/README.md diff --git a/vector-stores/spring-ai-gemfire/pom.xml b/vector-stores/spring-ai-gemfire-store/pom.xml similarity index 98% rename from vector-stores/spring-ai-gemfire/pom.xml rename to vector-stores/spring-ai-gemfire-store/pom.xml index 8b4e6407b7..45f7e71637 100644 --- a/vector-stores/spring-ai-gemfire/pom.xml +++ b/vector-stores/spring-ai-gemfire-store/pom.xml @@ -8,7 +8,7 @@ 1.0.0-SNAPSHOT ../../pom.xml - spring-ai-gemfire + spring-ai-gemfire-store jar Spring AI Vector Store - GemFire Spring AI GemFire Vector Store diff --git a/vector-stores/spring-ai-gemfire/src/main/java/org/springframework/ai/vectorstore/GemFireVectorStore.java b/vector-stores/spring-ai-gemfire-store/src/main/java/org/springframework/ai/vectorstore/GemFireVectorStore.java similarity index 100% rename from vector-stores/spring-ai-gemfire/src/main/java/org/springframework/ai/vectorstore/GemFireVectorStore.java rename to vector-stores/spring-ai-gemfire-store/src/main/java/org/springframework/ai/vectorstore/GemFireVectorStore.java diff --git a/vector-stores/spring-ai-gemfire/src/test/java/org/springframework/ai/vectorstore/GemFireVectorStoreIT.java b/vector-stores/spring-ai-gemfire-store/src/test/java/org/springframework/ai/vectorstore/GemFireVectorStoreIT.java similarity index 100% rename from vector-stores/spring-ai-gemfire/src/test/java/org/springframework/ai/vectorstore/GemFireVectorStoreIT.java rename to vector-stores/spring-ai-gemfire-store/src/test/java/org/springframework/ai/vectorstore/GemFireVectorStoreIT.java diff --git a/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java b/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java index b86438b6cd..0a6004a1c0 100644 --- a/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java +++ b/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusVectorStore.java @@ -55,16 +55,14 @@ import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; import org.springframework.ai.vectorstore.filter.converter.MilvusFilterExpressionConverter; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationListener; +import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * @author Christian Tzolov - * @author Josh Long */ -public class MilvusVectorStore implements VectorStore, ApplicationListener { +public class MilvusVectorStore implements VectorStore, InitializingBean { private static final Logger logger = LoggerFactory.getLogger(MilvusVectorStore.class); @@ -98,11 +96,6 @@ public class MilvusVectorStore implements VectorStore, ApplicationListener toFloatList(List embeddingDouble) { // --------------------------------------------------------------------------------- // Initialization // --------------------------------------------------------------------------------- + @Override + public void afterPropertiesSet() throws Exception { + this.createCollection(); + } void releaseCollection() { if (isDatabaseCollectionExists()) { diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStore.java b/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStore.java index 231c196b72..0fbb099ce1 100644 --- a/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStore.java +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/main/java/org/springframework/ai/vectorstore/MongoDBAtlasVectorStore.java @@ -25,8 +25,7 @@ import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationListener; +import org.springframework.beans.factory.InitializingBean; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.query.Criteria; @@ -37,10 +36,9 @@ /** * @author Chris Smith - * @author Josh Long * @since 1.0.0 */ -public class MongoDBAtlasVectorStore implements VectorStore, ApplicationListener { +public class MongoDBAtlasVectorStore implements VectorStore, InitializingBean { public static final String ID_FIELD_NAME = "_id"; @@ -78,6 +76,16 @@ public MongoDBAtlasVectorStore(MongoTemplate mongoTemplate, EmbeddingClient embe } + @Override + public void afterPropertiesSet() throws Exception { + // Create the collection if it does not exist + if (!mongoTemplate.collectionExists(this.config.collectionName)) { + mongoTemplate.createCollection(this.config.collectionName); + } + // Create search index, command doesn't do anything if already existing + mongoTemplate.executeCommand(createSearchIndex()); + } + /** * Provides the Definition for the search index */ @@ -166,16 +174,6 @@ public List similaritySearch(SearchRequest request) { .toList(); } - @Override - public void onApplicationEvent(ApplicationReadyEvent event) { - // Create the collection if it does not exist - if (!mongoTemplate.collectionExists(this.config.collectionName)) { - mongoTemplate.createCollection(this.config.collectionName); - } - // Create search index, command doesn't do anything if already existing - mongoTemplate.executeCommand(createSearchIndex()); - } - public static class MongoDBVectorStoreConfig { private final String collectionName; diff --git a/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java b/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java index 8b5b3024f4..8ac8fd204c 100644 --- a/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java +++ b/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java @@ -22,8 +22,7 @@ import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.Neo4jVectorFilterExpressionConverter; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationListener; +import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import java.util.HashMap; @@ -35,9 +34,8 @@ /** * @author Gerrit Meier * @author Michael Simons - * @author Josh Long */ -public class Neo4jVectorStore implements VectorStore, ApplicationListener { +public class Neo4jVectorStore implements VectorStore, InitializingBean { /** * An enum to configure the distance function used in the Neo4j vector index. @@ -72,7 +70,6 @@ public static final class Neo4jVectorStoreConfig { private final String indexName; // needed for similarity search call - private final String indexNameNotSanitized; private final String idProperty; @@ -256,29 +253,6 @@ public Neo4jVectorStoreConfig build() { } - @Override - public void onApplicationEvent(ApplicationReadyEvent event) { - - try (var session = this.driver.session(this.config.sessionConfig)) { - - session - .run("CREATE CONSTRAINT %s IF NOT EXISTS FOR (n:%s) REQUIRE n.%s IS UNIQUE" - .formatted(this.config.constraintName, this.config.label, this.config.idProperty)) - .consume(); - - var statement = """ - CREATE VECTOR INDEX %s IF NOT EXISTS FOR (n:%s) ON (n.%s) - OPTIONS {indexConfig: { - `vector.dimensions`: %d, - `vector.similarity_function`: '%s' - }} - """.formatted(this.config.indexName, this.config.label, this.config.embeddingProperty, - this.config.embeddingDimension, this.config.distanceType.name); - session.run(statement).consume(); - session.run("CALL db.awaitIndexes()").consume(); - } - } - public static final int DEFAULT_EMBEDDING_DIMENSION = 1536; public static final String DEFAULT_LABEL = "Document"; @@ -374,6 +348,29 @@ public List similaritySearch(SearchRequest request) { } } + @Override + public void afterPropertiesSet() { + + try (var session = this.driver.session(this.config.sessionConfig)) { + + session + .run("CREATE CONSTRAINT %s IF NOT EXISTS FOR (n:%s) REQUIRE n.%s IS UNIQUE" + .formatted(this.config.constraintName, this.config.label, this.config.idProperty)) + .consume(); + + var statement = """ + CREATE VECTOR INDEX %s IF NOT EXISTS FOR (n:%s) ON (n.%s) + OPTIONS {indexConfig: { + `vector.dimensions`: %d, + `vector.similarity_function`: '%s' + }} + """.formatted(this.config.indexName, this.config.label, this.config.embeddingProperty, + this.config.embeddingDimension, this.config.distanceType.name); + session.run(statement).consume(); + session.run("CALL db.awaitIndexes()").consume(); + } + } + private Map documentToRecord(Document document) { var embedding = this.embeddingClient.embed(document); document.setEmbedding(embedding); @@ -416,4 +413,4 @@ private Document recordToDocument(org.neo4j.driver.Record neoRecord) { Map.copyOf(metaData)); } -} +} \ No newline at end of file diff --git a/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java b/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java index 2eb9eefeef..c2b7954d90 100644 --- a/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java +++ b/vector-stores/spring-ai-pgvector-store/src/main/java/org/springframework/ai/vectorstore/PgVectorStore.java @@ -35,8 +35,7 @@ import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; import org.springframework.ai.vectorstore.filter.converter.PgVectorFilterExpressionConverter; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationListener; +import org.springframework.beans.factory.InitializingBean; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -50,9 +49,8 @@ * vector index will be auto-created if not available. * * @author Christian Tzolov - * @author Josh Long */ -public class PgVectorStore implements VectorStore, ApplicationListener { +public class PgVectorStore implements VectorStore, InitializingBean { private static final Logger logger = LoggerFactory.getLogger(PgVectorStore.class); @@ -80,40 +78,6 @@ public class PgVectorStore implements VectorStore, ApplicationListener1.0.0-SNAPSHOT ../../pom.xml - spring-ai-pinecone + spring-ai-pinecone-store jar Spring AI Vector Store - Pinecone https://github.com/spring-projects/spring-ai diff --git a/vector-stores/spring-ai-pinecone/src/main/java/org/springframework/ai/vectorstore/PineconeVectorStore.java b/vector-stores/spring-ai-pinecone-store/src/main/java/org/springframework/ai/vectorstore/PineconeVectorStore.java similarity index 100% rename from vector-stores/spring-ai-pinecone/src/main/java/org/springframework/ai/vectorstore/PineconeVectorStore.java rename to vector-stores/spring-ai-pinecone-store/src/main/java/org/springframework/ai/vectorstore/PineconeVectorStore.java diff --git a/vector-stores/spring-ai-pinecone/src/test/java/org/springframework/ai/vectorstore/PineconeVectorStoreIT.java b/vector-stores/spring-ai-pinecone-store/src/test/java/org/springframework/ai/vectorstore/PineconeVectorStoreIT.java similarity index 100% rename from vector-stores/spring-ai-pinecone/src/test/java/org/springframework/ai/vectorstore/PineconeVectorStoreIT.java rename to vector-stores/spring-ai-pinecone-store/src/test/java/org/springframework/ai/vectorstore/PineconeVectorStoreIT.java diff --git a/vector-stores/spring-ai-qdrant/README.md b/vector-stores/spring-ai-qdrant-store/README.md similarity index 100% rename from vector-stores/spring-ai-qdrant/README.md rename to vector-stores/spring-ai-qdrant-store/README.md diff --git a/vector-stores/spring-ai-qdrant/pom.xml b/vector-stores/spring-ai-qdrant-store/pom.xml similarity index 98% rename from vector-stores/spring-ai-qdrant/pom.xml rename to vector-stores/spring-ai-qdrant-store/pom.xml index 77ec45ffa9..df834e266f 100644 --- a/vector-stores/spring-ai-qdrant/pom.xml +++ b/vector-stores/spring-ai-qdrant-store/pom.xml @@ -8,7 +8,7 @@ 1.0.0-SNAPSHOT ../../pom.xml - spring-ai-qdrant + spring-ai-qdrant-store jar Spring AI Vector Store - QDrant diff --git a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantFilterExpressionConverter.java b/vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantFilterExpressionConverter.java similarity index 100% rename from vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantFilterExpressionConverter.java rename to vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantFilterExpressionConverter.java diff --git a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantObjectFactory.java b/vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantObjectFactory.java similarity index 100% rename from vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantObjectFactory.java rename to vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantObjectFactory.java diff --git a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantValueFactory.java b/vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantValueFactory.java similarity index 100% rename from vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantValueFactory.java rename to vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantValueFactory.java diff --git a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java b/vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java similarity index 95% rename from vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java rename to vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java index 607a1ecb7a..f9d3a250ca 100644 --- a/vector-stores/spring-ai-qdrant/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java +++ b/vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java @@ -15,12 +15,24 @@ */ package org.springframework.ai.vectorstore.qdrant; +import static io.qdrant.client.PointIdFactory.id; +import static io.qdrant.client.ValueFactory.value; +import static io.qdrant.client.VectorsFactory.vectors; +import static io.qdrant.client.WithPayloadSelectorFactory.enable; + import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutionException; +import org.springframework.ai.document.Document; +import org.springframework.ai.embedding.EmbeddingClient; +import org.springframework.ai.vectorstore.SearchRequest; +import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; + import io.qdrant.client.QdrantClient; import io.qdrant.client.grpc.Collections.Distance; import io.qdrant.client.grpc.Collections.VectorParams; @@ -32,19 +44,6 @@ import io.qdrant.client.grpc.Points.SearchPoints; import io.qdrant.client.grpc.Points.UpdateStatus; -import org.springframework.ai.document.Document; -import org.springframework.ai.embedding.EmbeddingClient; -import org.springframework.ai.vectorstore.SearchRequest; -import org.springframework.ai.vectorstore.VectorStore; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.util.Assert; - -import static io.qdrant.client.PointIdFactory.id; -import static io.qdrant.client.ValueFactory.value; -import static io.qdrant.client.VectorsFactory.vectors; -import static io.qdrant.client.WithPayloadSelectorFactory.enable; - /** * Qdrant vectorStore implementation. This store supports creating, updating, deleting, * and similarity searching of documents in a Qdrant collection. @@ -52,10 +51,9 @@ * @author Anush Shetty * @author Christian Tzolov * @author Eddú Meléndez - * @author Josh Long * @since 0.8.1 */ -public class QdrantVectorStore implements VectorStore, ApplicationListener { +public class QdrantVectorStore implements VectorStore, InitializingBean { private static final String CONTENT_FIELD_NAME = "doc_content"; @@ -86,7 +84,6 @@ public static final class QdrantVectorStoreConfig { * * @param builder The configuration builder. */ - private QdrantVectorStoreConfig(Builder builder) { this.collectionName = builder.collectionName; } @@ -161,24 +158,6 @@ public QdrantVectorStore(QdrantClient qdrantClient, String collectionName, Embed this.qdrantClient = qdrantClient; } - @Override - public void onApplicationEvent(ApplicationReadyEvent event) { - - // Create the collection if it does not exist. - if (!isCollectionExists()) { - var vectorParams = VectorParams.newBuilder() - .setDistance(Distance.Cosine) - .setSize(this.embeddingClient.dimensions()) - .build(); - try { - this.qdrantClient.createCollectionAsync(this.collectionName, vectorParams).get(); - } // - catch (Exception e) { - throw new RuntimeException(e); - } - } - } - /** * Adds a list of documents to the vector store. * @param documents The list of documents to be added. @@ -261,6 +240,7 @@ public List similaritySearch(SearchRequest request) { /** * Extracts metadata from a Protobuf Struct. + * @param metadataStruct The Protobuf Struct containing metadata. * @return The metadata as a map. */ private Document toDocument(ScoredPoint point) { @@ -304,6 +284,18 @@ private List toFloatList(List doubleList) { return doubleList.stream().map(d -> d.floatValue()).toList(); } + @Override + public void afterPropertiesSet() throws Exception { + // Create the collection if it does not exist. + if (!isCollectionExists()) { + var vectorParams = VectorParams.newBuilder() + .setDistance(Distance.Cosine) + .setSize(this.embeddingClient.dimensions()) + .build(); + this.qdrantClient.createCollectionAsync(this.collectionName, vectorParams).get(); + } + } + private boolean isCollectionExists() { try { return this.qdrantClient.listCollectionsAsync().get().stream().anyMatch(c -> c.equals(this.collectionName)); @@ -313,4 +305,4 @@ private boolean isCollectionExists() { } } -} +} \ No newline at end of file diff --git a/vector-stores/spring-ai-qdrant/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java b/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java similarity index 100% rename from vector-stores/spring-ai-qdrant/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java rename to vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java diff --git a/vector-stores/spring-ai-redis/README.md b/vector-stores/spring-ai-redis-store/README.md similarity index 100% rename from vector-stores/spring-ai-redis/README.md rename to vector-stores/spring-ai-redis-store/README.md diff --git a/vector-stores/spring-ai-redis/pom.xml b/vector-stores/spring-ai-redis-store/pom.xml similarity index 98% rename from vector-stores/spring-ai-redis/pom.xml rename to vector-stores/spring-ai-redis-store/pom.xml index 2d66f35c73..729ba0fe8b 100644 --- a/vector-stores/spring-ai-redis/pom.xml +++ b/vector-stores/spring-ai-redis-store/pom.xml @@ -8,7 +8,7 @@ 1.0.0-SNAPSHOT ../../pom.xml - spring-ai-redis + spring-ai-redis-store jar Spring AI Vector Store - Redis Spring AI Vector Store - Redis diff --git a/vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverter.java b/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverter.java similarity index 100% rename from vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverter.java rename to vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverter.java diff --git a/vector-stores/spring-ai-redis/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 similarity index 97% rename from vector-stores/spring-ai-redis/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java rename to vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java index 47982bf928..cb34e3b8be 100644 --- a/vector-stores/spring-ai-redis/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 @@ -28,6 +28,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ai.document.Document; +import org.springframework.ai.embedding.EmbeddingClient; +import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + import redis.clients.jedis.JedisPooled; import redis.clients.jedis.Pipeline; import redis.clients.jedis.json.Path2; @@ -44,14 +51,6 @@ import redis.clients.jedis.search.schemafields.VectorField; import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; -import org.springframework.ai.document.Document; -import org.springframework.ai.embedding.EmbeddingClient; -import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; - /** * The RedisVectorStore is for managing and querying vector data in a Redis database. It * offers functionalities like adding, deleting, and performing similarity searches on @@ -69,27 +68,11 @@ * * @author Julien Ruaux * @author Christian Tzolov - * @author Josh Long * @see VectorStore * @see RedisVectorStoreConfig * @see EmbeddingClient */ -public class RedisVectorStore implements VectorStore, ApplicationListener { - - @Override - public void onApplicationEvent(ApplicationReadyEvent event) { - // If index already exists don't do anything - if (this.jedis.ftList().contains(this.config.indexName)) { - return; - } - - String response = this.jedis.ftCreate(this.config.indexName, - FTCreateParams.createParams().on(IndexDataType.JSON).addPrefix(this.config.prefix), schemaFields()); - if (!RESPONSE_OK.test(response)) { - String message = MessageFormat.format("Could not create index: {0}", response); - throw new RuntimeException(message); - } - } +public class RedisVectorStore implements VectorStore, InitializingBean { public enum Algorithm { @@ -419,6 +402,22 @@ private String nativeExpressionFilter(SearchRequest request) { return "(" + this.filterExpressionConverter.convertExpression(request.getFilterExpression()) + ")"; } + @Override + public void afterPropertiesSet() { + + // If index already exists don't do anything + if (this.jedis.ftList().contains(this.config.indexName)) { + return; + } + + String response = this.jedis.ftCreate(this.config.indexName, + FTCreateParams.createParams().on(IndexDataType.JSON).addPrefix(this.config.prefix), schemaFields()); + if (!RESPONSE_OK.test(response)) { + String message = MessageFormat.format("Could not create index: {0}", response); + throw new RuntimeException(message); + } + } + private Iterable schemaFields() { Map vectorAttrs = new HashMap<>(); vectorAttrs.put("DIM", this.embeddingClient.dimensions()); @@ -476,4 +475,4 @@ private static float[] toFloatArray(List embeddingDouble) { return embeddingFloat; } -} +} \ No newline at end of file diff --git a/vector-stores/spring-ai-redis/src/test/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverterTests.java b/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverterTests.java similarity index 100% rename from vector-stores/spring-ai-redis/src/test/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverterTests.java rename to vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverterTests.java diff --git a/vector-stores/spring-ai-redis/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 similarity index 100% rename from vector-stores/spring-ai-redis/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreIT.java rename to vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreIT.java diff --git a/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateVectorStore.java b/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateVectorStore.java index 6e56e8e95f..db676aaddc 100644 --- a/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateVectorStore.java +++ b/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateVectorStore.java @@ -551,4 +551,4 @@ public void afterPropertiesSet() throws Exception { this.delete(List.of(document.getId())); } -} +} \ No newline at end of file From d02661431605f37e4adf2b27c2d65fa0ec9dd492 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 17 May 2024 17:31:48 +0200 Subject: [PATCH 22/25] Update README.md Breaking changes --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 6feaf43c51..1b798a2c0a 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,20 @@ Let's make your `@Beans` intelligent! For further information go to our [Spring AI reference documentation](https://docs.spring.io/spring-ai/reference/). +### Breadking changes +(15.05.2024) +On our march to release 1.0 M1 we have made several breaking changes. Apologies, it is for the best! + +Renamed POM artifact names: + - spring-ai-qdrant -> spring-ai-qdrant-store + - spring-ai-cassandra -> spring-ai-cassandra-store + - spring-ai-pinecone -> spring-ai-pinecone-store + - spring-ai-redis -> spring-ai-redis-store + - spring-ai-qdrant -> spring-ai-qdrant-store + - spring-ai-gemfire -> spring-ai-gemfire-store + - spring-ai-azure-vector-store-spring-boot-starter -> spring-ai-azure-store-spring-boot-starter + - spring-ai-redis-spring-boot-starter -> spring-ai-redis-store-spring-boot-starter + ## Project Links * [Documentation](https://docs.spring.io/spring-ai/reference/) From 517df45cd8782bfb75653167ae60be649dbf136e Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Sat, 18 May 2024 04:50:20 +0200 Subject: [PATCH 23/25] Merge ParametrizedTypeReferenceBeanOutputConverter into BeanOutputConverter Add ParametrizedTypeReference constructoers along the Clas such. Convert the Class into ParametrizedTypeReference internally. Update tests and docs. --- .../ai/openai/chat/OpenAiChatClientIT.java | 3 +- ...ntTypeReferenceBeanOutputConverterIT.java} | 10 +- ...exAiGeminiChatClientFunctionCallingIT.java | 10 +- .../ai/converter/BeanOutputConverter.java | 99 +++++---- ...meterizedTypeReferenceOutputConverter.java | 179 ----------------- .../ai/converter/BeanOutputConverterTest.java | 145 +++++++++++-- .../TypeReferenceOutputConverterTest.java | 190 ------------------ .../images/structured-output-hierarchy4.jpg | Bin 255213 -> 229437 bytes .../api/structured-output-converter.adoc | 27 ++- 9 files changed, 227 insertions(+), 436 deletions(-) rename models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/{OpenAiChatClientParametrizedTypeReferencefOutputConverterIT.java => OpenAiChatClientTypeReferenceBeanOutputConverterIT.java} (87%) delete mode 100644 spring-ai-core/src/main/java/org/springframework/ai/converter/ParameterizedTypeReferenceOutputConverter.java delete mode 100644 spring-ai-core/src/test/java/org/springframework/ai/converter/TypeReferenceOutputConverterTest.java diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java index 0366aa626d..56ac4da9f7 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientIT.java @@ -298,7 +298,8 @@ void streamingMultiModalityImageUrl() throws IOException { .map(AssistantMessage::getContent) .collect(Collectors.joining()); logger.info("Response: {}", content); - assertThat(content).contains("bananas", "apple", "bowl"); + assertThat(content).contains("bananas", "apple"); + assertThat(content).containsAnyOf("bowl", "basket"); } } \ No newline at end of file diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientParametrizedTypeReferencefOutputConverterIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientTypeReferenceBeanOutputConverterIT.java similarity index 87% rename from models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientParametrizedTypeReferencefOutputConverterIT.java rename to models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientTypeReferenceBeanOutputConverterIT.java index 146715f34d..4626af1fae 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientParametrizedTypeReferencefOutputConverterIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatClientTypeReferenceBeanOutputConverterIT.java @@ -29,7 +29,7 @@ import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; -import org.springframework.ai.converter.ParameterizedTypeReferenceOutputConverter; +import org.springframework.ai.converter.BeanOutputConverter; import org.springframework.ai.openai.OpenAiTestConfiguration; import org.springframework.ai.openai.testutils.AbstractIT; import org.springframework.boot.test.context.SpringBootTest; @@ -39,10 +39,10 @@ @SpringBootTest(classes = OpenAiTestConfiguration.class) @EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+") -class OpenAiChatClientParametrizedTypeReferencefOutputConverterIT extends AbstractIT { +class OpenAiChatClientTypeReferenceBeanOutputConverterIT extends AbstractIT { private static final Logger logger = LoggerFactory - .getLogger(OpenAiChatClientParametrizedTypeReferencefOutputConverterIT.class); + .getLogger(OpenAiChatClientTypeReferenceBeanOutputConverterIT.class); record ActorsFilmsRecord(String actor, List movies) { } @@ -50,7 +50,7 @@ record ActorsFilmsRecord(String actor, List movies) { @Test void typeRefOutputConverterRecords() { - ParameterizedTypeReferenceOutputConverter> outputConverter = new ParameterizedTypeReferenceOutputConverter<>( + BeanOutputConverter> outputConverter = new BeanOutputConverter<>( new ParameterizedTypeReference>() { }); @@ -75,7 +75,7 @@ void typeRefOutputConverterRecords() { @Test void typeRefStreamOutputConverterRecords() { - ParameterizedTypeReferenceOutputConverter> outputConverter = new ParameterizedTypeReferenceOutputConverter<>( + BeanOutputConverter> outputConverter = new BeanOutputConverter<>( new ParameterizedTypeReference>() { }); diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java index d5209e82e8..11b8522225 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatClientFunctionCallingIT.java @@ -68,7 +68,7 @@ public void afterEach() { } @Test - // @Disabled("Google Vertex AI degraded support for parallel function calls") + @Disabled("Google Vertex AI degraded support for parallel function calls") public void functionCallExplicitOpenApiSchema() { UserMessage userMessage = new UserMessage( @@ -98,8 +98,8 @@ public void functionCallExplicitOpenApiSchema() { """; var promptOptions = VertexAiGeminiChatOptions.builder() - // .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO) - .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO) + .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO) + // .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO) .withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService()) .withName("get_current_weather") .withDescription("Get the current weather in a given location") @@ -126,8 +126,8 @@ public void functionCallTestInferredOpenApiSchema() { List messages = new ArrayList<>(List.of(userMessage)); var promptOptions = VertexAiGeminiChatOptions.builder() - .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO) - // .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) + // .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO_1_5_PRO) + .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) .withFunctionCallbacks(List.of( FunctionCallbackWrapper.builder(new MockWeatherService()) .withSchemaType(SchemaType.OPEN_API_SCHEMA) diff --git a/spring-ai-core/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java b/spring-ai-core/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java index a26b5e97bd..aca2902b0b 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java @@ -15,7 +15,10 @@ */ package org.springframework.ai.converter; +import java.util.Objects; + import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -27,10 +30,9 @@ import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder; import com.github.victools.jsonschema.module.jackson.JacksonModule; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.lang.NonNull; - -import java.util.Map; -import java.util.Objects; +import java.lang.reflect.Type; import static com.github.victools.jsonschema.generator.OptionPreset.PLAIN_JSON; import static com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_2020_12; @@ -38,23 +40,26 @@ /** * An implementation of {@link StructuredOutputConverter} that transforms the LLM output * to a specific object type using JSON schema. This parser works by generating a JSON - * schema based on a given Java class, which is then used to validate and transform the - * LLM output into the desired type. + * schema based on a given Java class or parameterized type reference, which is then used + * to validate and transform the LLM output into the desired type. * * @param The target type to which the output will be converted. * @author Mark Pollack * @author Christian Tzolov * @author Sebastian Ullrich * @author Kirk Lund + * @author Josh Long */ public class BeanOutputConverter implements StructuredOutputConverter { /** Holds the generated JSON schema for the target type. */ private String jsonSchema; - /** The Java class representing the target type. */ + /** + * The target class type reference to which the output will be converted. + */ @SuppressWarnings({ "FieldMayBeFinal", "rawtypes" }) - private Class clazz; + private TypeReference typeRef; /** The object mapper used for deserialization and other JSON operations. */ @SuppressWarnings("FieldMayBeFinal") @@ -64,8 +69,8 @@ public class BeanOutputConverter implements StructuredOutputConverter { * Constructor to initialize with the target type's class. * @param clazz The target type's class. */ - public BeanOutputConverter(Class clazz) { - this(clazz, null); + public BeanOutputConverter(Class typeClass) { + this(ParameterizedTypeReference.forType(typeClass)); } /** @@ -75,8 +80,53 @@ public BeanOutputConverter(Class clazz) { * @param objectMapper Custom object mapper for JSON operations. endings. */ public BeanOutputConverter(Class clazz, ObjectMapper objectMapper) { - Objects.requireNonNull(clazz, "Java Class cannot be null;"); - this.clazz = clazz; + this(ParameterizedTypeReference.forType(clazz), objectMapper); + } + + /** + * Constructor to initialize with the target class type reference. + * @param typeRef The target class type reference. + */ + public BeanOutputConverter(ParameterizedTypeReference typeRef) { + this(new CustomizedTypeReference<>(typeRef), null); + } + + /** + * Constructor to initialize with the target class type reference, a custom object + * mapper, and a line endings normalizer to ensure consistent line endings on any + * platform. + * @param typeRef The target class type reference. + * @param objectMapper Custom object mapper for JSON operations. endings. + */ + public BeanOutputConverter(ParameterizedTypeReference typeRef, ObjectMapper objectMapper) { + this(new CustomizedTypeReference<>(typeRef), objectMapper); + } + + private static class CustomizedTypeReference extends TypeReference { + + private final Type type; + + CustomizedTypeReference(ParameterizedTypeReference typeRef) { + this.type = typeRef.getType(); + } + + @Override + public Type getType() { + return this.type; + } + + } + + /** + * Constructor to initialize with the target class type reference, a custom object + * mapper, and a line endings normalizer to ensure consistent line endings on any + * platform. + * @param typeRef The target class type reference. + * @param objectMapper Custom object mapper for JSON operations. endings. + */ + private BeanOutputConverter(TypeReference typeRef, ObjectMapper objectMapper) { + Objects.requireNonNull(typeRef, "Type reference cannot be null;"); + this.typeRef = typeRef; this.objectMapper = objectMapper != null ? objectMapper : getObjectMapper(); generateSchema(); } @@ -90,14 +140,14 @@ private void generateSchema() { .with(jacksonModule); SchemaGeneratorConfig config = configBuilder.build(); SchemaGenerator generator = new SchemaGenerator(config); - JsonNode jsonNode = generator.generateSchema(this.clazz); + JsonNode jsonNode = generator.generateSchema(this.typeRef.getType()); ObjectWriter objectWriter = new ObjectMapper().writer(new DefaultPrettyPrinter() .withObjectIndenter(new DefaultIndenter().withLinefeed(System.lineSeparator()))); try { this.jsonSchema = objectWriter.writeValueAsString(jsonNode); } catch (JsonProcessingException e) { - throw new RuntimeException("Could not pretty print json schema for " + this.clazz, e); + throw new RuntimeException("Could not pretty print json schema for " + this.typeRef, e); } } @@ -109,34 +159,13 @@ private void generateSchema() { */ public T convert(@NonNull String text) { try { - // If the response is a JSON Schema, extract the properties and use them as - // the response. - text = this.jsonSchemaToInstance(text); - return (T) this.objectMapper.readValue(text, this.clazz); + return (T) this.objectMapper.readValue(text, this.typeRef); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } - /** - * Converts a JSON Schema to an instance based on a given text. - * @param text The JSON Schema in string format. - * @return The JSON instance generated from the JSON Schema, or the original text if - * the input is not a JSON Schema. - */ - private String jsonSchemaToInstance(String text) { - try { - Map map = this.objectMapper.readValue(text, Map.class); - if (map.containsKey("$schema")) { - return this.objectMapper.writeValueAsString(map.get("properties")); - } - } - catch (Exception e) { - } - return text; - } - /** * Configures and returns an object mapper for JSON operations. * @return Configured object mapper. diff --git a/spring-ai-core/src/main/java/org/springframework/ai/converter/ParameterizedTypeReferenceOutputConverter.java b/spring-ai-core/src/main/java/org/springframework/ai/converter/ParameterizedTypeReferenceOutputConverter.java deleted file mode 100644 index a0b7926195..0000000000 --- a/spring-ai-core/src/main/java/org/springframework/ai/converter/ParameterizedTypeReferenceOutputConverter.java +++ /dev/null @@ -1,179 +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.converter; - -import java.util.Objects; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.core.util.DefaultIndenter; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.github.victools.jsonschema.generator.SchemaGenerator; -import com.github.victools.jsonschema.generator.SchemaGeneratorConfig; -import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder; -import com.github.victools.jsonschema.module.jackson.JacksonModule; - -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.lang.NonNull; -import java.lang.reflect.Type; - -import static com.github.victools.jsonschema.generator.OptionPreset.PLAIN_JSON; -import static com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_2020_12; - -/** - * An implementation of {@link StructuredOutputConverter} that transforms the LLM output - * to a specific object type using JSON schema. This parser works by generating a JSON - * schema based on a given Java class type reference, which is then used to validate and - * transform the LLM output into the desired type. - * - * @param The target type to which the output will be converted. - * @author Mark Pollack - * @author Christian Tzolov - * @author Sebastian Ullrich - * @author Kirk Lund - * @author Josh Long - */ -public class ParameterizedTypeReferenceOutputConverter implements StructuredOutputConverter { - - /** Holds the generated JSON schema for the target type. */ - private String jsonSchema; - - /** - * The target class type reference to which the output will be converted. - */ - @SuppressWarnings({ "FieldMayBeFinal", "rawtypes" }) - private TypeReference typeRef; - - /** The object mapper used for deserialization and other JSON operations. */ - @SuppressWarnings("FieldMayBeFinal") - private ObjectMapper objectMapper; - - /** - * Constructor to initialize with the target class type reference. - * @param typeRef The target type's class. - */ - public ParameterizedTypeReferenceOutputConverter(ParameterizedTypeReference typeRef) { - this(new CustomizedTypeReference<>(typeRef), null); - } - - /** - * Constructor to initialize with the target class type reference, a custom object - * mapper, and a line endings normalizer to ensure consistent line endings on any - * platform. - * @param typeRef The target class type reference. - * @param objectMapper Custom object mapper for JSON operations. endings. - */ - public ParameterizedTypeReferenceOutputConverter(ParameterizedTypeReference typeRef, ObjectMapper objectMapper) { - this(new CustomizedTypeReference<>(typeRef), objectMapper); - } - - private static class CustomizedTypeReference extends TypeReference { - - private final Type type; - - CustomizedTypeReference(ParameterizedTypeReference typeRef) { - this.type = typeRef.getType(); - } - - @Override - public Type getType() { - return this.type; - } - - } - - /** - * Constructor to initialize with the target class type reference, a custom object - * mapper, and a line endings normalizer to ensure consistent line endings on any - * platform. - * @param typeRef The target class type reference. - * @param objectMapper Custom object mapper for JSON operations. endings. - */ - private ParameterizedTypeReferenceOutputConverter(TypeReference typeRef, ObjectMapper objectMapper) { - Objects.requireNonNull(typeRef, "Type reference cannot be null;"); - this.typeRef = typeRef; - this.objectMapper = objectMapper != null ? objectMapper : getObjectMapper(); - generateSchema(); - } - - /** - * Generates the JSON schema for the target type. - */ - private void generateSchema() { - JacksonModule jacksonModule = new JacksonModule(); - SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(DRAFT_2020_12, PLAIN_JSON) - .with(jacksonModule); - SchemaGeneratorConfig config = configBuilder.build(); - SchemaGenerator generator = new SchemaGenerator(config); - JsonNode jsonNode = generator.generateSchema(this.typeRef.getType()); - ObjectWriter objectWriter = new ObjectMapper().writer(new DefaultPrettyPrinter() - .withObjectIndenter(new DefaultIndenter().withLinefeed(System.lineSeparator()))); - try { - this.jsonSchema = objectWriter.writeValueAsString(jsonNode); - } - catch (JsonProcessingException e) { - throw new RuntimeException("Could not pretty print json schema for " + this.typeRef, e); - } - } - - @Override - /** - * Parses the given text to transform it to the desired target type. - * @param text The LLM output in string format. - * @return The parsed output in the desired target type. - */ - public T convert(@NonNull String text) { - try { - return (T) this.objectMapper.readValue(text, this.typeRef); - } - catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - /** - * Configures and returns an object mapper for JSON operations. - * @return Configured object mapper. - */ - protected ObjectMapper getObjectMapper() { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - return mapper; - } - - /** - * Provides the expected format of the response, instructing that it should adhere to - * the generated JSON schema. - * @return The instruction format string. - */ - @Override - public String getFormat() { - String template = """ - Your response should be in JSON format. - Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation. - Do not include markdown code blocks in your response. - Remove the ```json markdown from the output. - Here is the JSON Schema instance your output must adhere to: - ```%s``` - """; - return String.format(template, this.jsonSchema); - } - -} diff --git a/spring-ai-core/src/test/java/org/springframework/ai/converter/BeanOutputConverterTest.java b/spring-ai-core/src/test/java/org/springframework/ai/converter/BeanOutputConverterTest.java index dacad64ffd..24cfd0a5cb 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/converter/BeanOutputConverterTest.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/converter/BeanOutputConverterTest.java @@ -15,9 +15,10 @@ */ package org.springframework.ai.converter; +import java.util.List; + import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Nested; @@ -26,10 +27,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.core.ParameterizedTypeReference; + import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; /** * @author Sebastian Ullrich @@ -44,43 +44,72 @@ class BeanOutputConverterTest { @Test public void shouldHavePreConfiguredDefaultObjectMapper() { - var converter = new BeanOutputConverter<>(TestClass.class); + var converter = new BeanOutputConverter<>(new ParameterizedTypeReference() { + }); var objectMapper = converter.getObjectMapper(); assertThat(objectMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse(); } - @Test - public void shouldUseProvidedObjectMapperForParsing() throws JsonProcessingException { - var testClass = new TestClass("some string"); - when(objectMapperMock.readValue(anyString(), eq(TestClass.class))).thenReturn(testClass); - var converter = new BeanOutputConverter<>(TestClass.class, objectMapperMock); - assertThat(converter.convert("{}")).isEqualTo(testClass); - } - @Nested - class ParserTest { + class ConverterTest { @Test - public void shouldParseFieldNamesFromString() { + public void convertClassType() { var converter = new BeanOutputConverter<>(TestClass.class); var testClass = converter.convert("{ \"someString\": \"some value\" }"); assertThat(testClass.getSomeString()).isEqualTo("some value"); } @Test - public void shouldParseJsonPropertiesFromString() { + public void convertTypeReference() { + var converter = new BeanOutputConverter<>(new ParameterizedTypeReference() { + }); + var testClass = converter.convert("{ \"someString\": \"some value\" }"); + assertThat(testClass.getSomeString()).isEqualTo("some value"); + } + + @Test + public void convertTypeReferenceArray() { + var converter = new BeanOutputConverter<>(new ParameterizedTypeReference>() { + }); + List testClass = converter.convert("[{ \"someString\": \"some value\" }]"); + assertThat(testClass).hasSize(1); + assertThat(testClass.get(0).getSomeString()).isEqualTo("some value"); + } + + @Test + public void convertClassTypeWithJsonAnnotations() { var converter = new BeanOutputConverter<>(TestClassWithJsonAnnotations.class); var testClass = converter.convert("{ \"string_property\": \"some value\" }"); assertThat(testClass.getSomeString()).isEqualTo("some value"); } + @Test + public void convertTypeReferenceWithJsonAnnotations() { + var converter = new BeanOutputConverter<>(new ParameterizedTypeReference() { + }); + var testClass = converter.convert("{ \"string_property\": \"some value\" }"); + assertThat(testClass.getSomeString()).isEqualTo("some value"); + } + + @Test + public void convertTypeReferenceArrayWithJsonAnnotations() { + var converter = new BeanOutputConverter<>( + new ParameterizedTypeReference>() { + }); + List testClass = converter + .convert("[{ \"string_property\": \"some value\" }]"); + assertThat(testClass).hasSize(1); + assertThat(testClass.get(0).getSomeString()).isEqualTo("some value"); + } + } @Nested class FormatTest { @Test - public void shouldReturnFormatContainingResponseInstructionsAndJsonSchema() { + public void formatClassType() { var converter = new BeanOutputConverter<>(TestClass.class); assertThat(converter.getFormat()).isEqualTo( """ @@ -102,7 +131,56 @@ public void shouldReturnFormatContainingResponseInstructionsAndJsonSchema() { } @Test - public void shouldReturnFormatContainingJsonSchemaIncludingPropertyAndPropertyDescription() { + public void formatTypeReference() { + var converter = new BeanOutputConverter<>(new ParameterizedTypeReference() { + }); + assertThat(converter.getFormat()).isEqualTo( + """ + Your response should be in JSON format. + Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation. + Do not include markdown code blocks in your response. + Remove the ```json markdown from the output. + Here is the JSON Schema instance your output must adhere to: + ```{ + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "type" : "object", + "properties" : { + "someString" : { + "type" : "string" + } + } + }``` + """); + } + + @Test + public void formatTypeReferenceArray() { + var converter = new BeanOutputConverter<>(new ParameterizedTypeReference>() { + }); + assertThat(converter.getFormat()).isEqualTo( + """ + Your response should be in JSON format. + Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation. + Do not include markdown code blocks in your response. + Remove the ```json markdown from the output. + Here is the JSON Schema instance your output must adhere to: + ```{ + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "someString" : { + "type" : "string" + } + } + } + }``` + """); + } + + @Test + public void formatClassTypeWithAnnotations() { var converter = new BeanOutputConverter<>(TestClassWithJsonAnnotations.class); assertThat(converter.getFormat()).contains(""" ```{ @@ -119,7 +197,25 @@ public void shouldReturnFormatContainingJsonSchemaIncludingPropertyAndPropertyDe } @Test - void normalizesLineEndings() { + public void formatTypeReferenceWithAnnotations() { + var converter = new BeanOutputConverter<>(new ParameterizedTypeReference() { + }); + assertThat(converter.getFormat()).contains(""" + ```{ + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "type" : "object", + "properties" : { + "string_property" : { + "type" : "string", + "description" : "string_property_description" + } + } + }``` + """); + } + + @Test + void normalizesLineEndingsClassType() { var converter = new BeanOutputConverter<>(TestClass.class); String formatOutput = converter.getFormat(); @@ -128,6 +224,17 @@ void normalizesLineEndings() { assertThat(formatOutput).contains(System.lineSeparator()).doesNotContain("\r\n").doesNotContain("\r"); } + @Test + void normalizesLineEndingsTypeReference() { + var converter = new BeanOutputConverter<>(new ParameterizedTypeReference() { + }); + + String formatOutput = converter.getFormat(); + + // validate that output contains \n line endings + assertThat(formatOutput).contains(System.lineSeparator()).doesNotContain("\r\n").doesNotContain("\r"); + } + } public static class TestClass { diff --git a/spring-ai-core/src/test/java/org/springframework/ai/converter/TypeReferenceOutputConverterTest.java b/spring-ai-core/src/test/java/org/springframework/ai/converter/TypeReferenceOutputConverterTest.java deleted file mode 100644 index abddd15673..0000000000 --- a/spring-ai-core/src/test/java/org/springframework/ai/converter/TypeReferenceOutputConverterTest.java +++ /dev/null @@ -1,190 +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.converter; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.core.ParameterizedTypeReference; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Sebastian Ullrich - * @author Kirk Lund - * @author Christian Tzolov - */ -@ExtendWith(MockitoExtension.class) -class TypeReferenceOutputConverterTest { - - @Mock - private ObjectMapper objectMapperMock; - - @Test - public void shouldHavePreConfiguredDefaultObjectMapper() { - var converter = new ParameterizedTypeReferenceOutputConverter<>(new ParameterizedTypeReference() { - }); - var objectMapper = converter.getObjectMapper(); - assertThat(objectMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse(); - } - - @Nested - class ParserTest { - - @Test - public void shouldParseFieldNamesFromString() { - var converter = new ParameterizedTypeReferenceOutputConverter<>( - new ParameterizedTypeReference() { - }); - var testClass = converter.convert("{ \"someString\": \"some value\" }"); - assertThat(testClass.getSomeString()).isEqualTo("some value"); - } - - @Test - public void shouldParseFieldNamesFromArrayString() { - var converter = new ParameterizedTypeReferenceOutputConverter<>( - new ParameterizedTypeReference>() { - }); - List testClass = converter.convert("[{ \"someString\": \"some value\" }]"); - assertThat(testClass).hasSize(1); - assertThat(testClass.get(0).getSomeString()).isEqualTo("some value"); - } - - @Test - public void shouldParseJsonPropertiesFromString() { - var converter = new ParameterizedTypeReferenceOutputConverter<>( - new ParameterizedTypeReference() { - }); - var testClass = converter.convert("{ \"string_property\": \"some value\" }"); - assertThat(testClass.getSomeString()).isEqualTo("some value"); - } - - @Test - public void shouldParseJsonPropertiesFromArrayString() { - var converter = new ParameterizedTypeReferenceOutputConverter<>( - new ParameterizedTypeReference>() { - }); - List testClass = converter - .convert("[{ \"string_property\": \"some value\" }]"); - assertThat(testClass).hasSize(1); - assertThat(testClass.get(0).getSomeString()).isEqualTo("some value"); - } - - } - - @Nested - class FormatTest { - - @Test - public void shouldReturnFormatContainingResponseInstructionsAndJsonSchema() { - var converter = new ParameterizedTypeReferenceOutputConverter<>( - new ParameterizedTypeReference() { - }); - assertThat(converter.getFormat()).isEqualTo( - """ - Your response should be in JSON format. - Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation. - Do not include markdown code blocks in your response. - Remove the ```json markdown from the output. - Here is the JSON Schema instance your output must adhere to: - ```{ - "$schema" : "https://json-schema.org/draft/2020-12/schema", - "type" : "object", - "properties" : { - "someString" : { - "type" : "string" - } - } - }``` - """); - } - - @Test - public void shouldReturnFormatContainingJsonSchemaIncludingPropertyAndPropertyDescription() { - var converter = new ParameterizedTypeReferenceOutputConverter<>( - new ParameterizedTypeReference() { - }); - assertThat(converter.getFormat()).contains(""" - ```{ - "$schema" : "https://json-schema.org/draft/2020-12/schema", - "type" : "object", - "properties" : { - "string_property" : { - "type" : "string", - "description" : "string_property_description" - } - } - }``` - """); - } - - @Test - void normalizesLineEndings() { - var converter = new ParameterizedTypeReferenceOutputConverter<>( - new ParameterizedTypeReference() { - }); - - String formatOutput = converter.getFormat(); - - // validate that output contains \n line endings - assertThat(formatOutput).contains(System.lineSeparator()).doesNotContain("\r\n").doesNotContain("\r"); - } - - } - - public static class TestClass { - - private String someString; - - @SuppressWarnings("unused") - public TestClass() { - } - - public TestClass(String someString) { - this.someString = someString; - } - - public String getSomeString() { - return someString; - } - - } - - public static class TestClassWithJsonAnnotations { - - @JsonProperty("string_property") - @JsonPropertyDescription("string_property_description") - private String someString; - - public TestClassWithJsonAnnotations() { - } - - public String getSomeString() { - return someString; - } - - } - -} \ No newline at end of file diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/images/structured-output-hierarchy4.jpg b/spring-ai-docs/src/main/antora/modules/ROOT/images/structured-output-hierarchy4.jpg index 6aa6d138da5455265ac711b838f660d9ef49f954..c2226ca330df33b9bd8e0e901c665760c621c055 100644 GIT binary patch literal 229437 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