diff --git a/README.md b/README.md index 4212bcdd5..439d5b495 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,19 @@ Let's make your `@Beans` intelligent! :warning: +### Breaking Changes +December 19, 2023 Update + +Renaming of AiClient and related classes and packagenames + +* Rename AiClient to ChatClient +* Rename AiResponse to ChatResponse +* Rename AiStreamClient to StreamingChatClient +* Rename package org.sf.ai.client to org.sf.ai.chat +:warning: + +December 1, 2023 + We are transitioning the project's Group ID: * **FROM**: `org.springframework.experimental.ai` diff --git a/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/client/AzureOpenAiClient.java b/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/client/AzureOpenAiClient.java index 3ccaaf06c..2e39e1bcd 100644 --- a/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/client/AzureOpenAiClient.java +++ b/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/client/AzureOpenAiClient.java @@ -31,9 +31,9 @@ import com.azure.ai.openai.models.PromptFilterResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.azure.openai.metadata.AzureOpenAiGenerationMetadata; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatClient; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; import org.springframework.ai.metadata.ChoiceMetadata; import org.springframework.ai.metadata.PromptMetadata; import org.springframework.ai.metadata.PromptMetadata.PromptFilterMetadata; @@ -42,16 +42,16 @@ import org.springframework.ai.prompt.messages.Message; import org.springframework.util.Assert; /** - * {@link AiClient} implementation for {@literal Microsoft Azure AI} backed by + * {@link ChatClient} implementation for {@literal Microsoft Azure AI} backed by * {@link OpenAIClient}. * * @author Mark Pollack * @author Ueibin Kim * @author John Blum - * @see org.springframework.ai.client.AiClient + * @see ChatClient * @see com.azure.ai.openai.OpenAIClient */ -public class AzureOpenAiClient implements AiClient { +public class AzureOpenAiClient implements ChatClient { private Double temperature = 0.7; @@ -108,7 +108,7 @@ public class AzureOpenAiClient implements AiClient { } @Override - public AiResponse generate(Prompt prompt) { + public ChatResponse generate(Prompt prompt) { List messages = prompt.getMessages(); List azureMessages = new ArrayList<>(); @@ -136,7 +136,7 @@ public class AzureOpenAiClient implements AiClient { generations.add(generation); } - return new AiResponse(generations, AzureOpenAiGenerationMetadata.from(chatCompletions)) + return new ChatResponse(generations, AzureOpenAiGenerationMetadata.from(chatCompletions)) .withPromptMetadata(generatePromptMetadata(chatCompletions)); } diff --git a/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/client/AzureOpenAiClientMetadataTests.java b/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/client/AzureOpenAiClientMetadataTests.java index 2a330859e..a001f0b87 100644 --- a/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/client/AzureOpenAiClientMetadataTests.java +++ b/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/client/AzureOpenAiClientMetadataTests.java @@ -27,8 +27,8 @@ import com.azure.ai.openai.models.ContentFilterSeverity; import org.junit.jupiter.api.Test; import org.springframework.ai.azure.openai.MockAzureOpenAiTestConfiguration; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; import org.springframework.ai.metadata.ChoiceMetadata; import org.springframework.ai.metadata.GenerationMetadata; import org.springframework.ai.metadata.PromptMetadata; @@ -73,7 +73,7 @@ class AzureOpenAiClientMetadataTests { Prompt prompt = new Prompt("Can I fly like a bird?"); - AiResponse response = this.aiClient.generate(prompt); + ChatResponse response = this.aiClient.generate(prompt); assertThat(response).isNotNull(); @@ -88,7 +88,7 @@ class AzureOpenAiClientMetadataTests { assertChoiceMetadata(generation); } - private void assertPromptMetadata(AiResponse response) { + private void assertPromptMetadata(ChatResponse response) { PromptMetadata promptMetadata = response.getPromptMetadata(); @@ -101,7 +101,7 @@ class AzureOpenAiClientMetadataTests { assertContentFilterResults(promptFilterMetadata.getContentFilterMetadata(), ContentFilterSeverity.HIGH); } - private void assertGenerationMetadata(AiResponse response) { + private void assertGenerationMetadata(ChatResponse response) { GenerationMetadata generationMetadata = response.getGenerationMetadata(); diff --git a/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClient.java b/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClient.java index de1e8795c..6c7dd7f39 100644 --- a/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClient.java +++ b/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClient.java @@ -18,26 +18,27 @@ package org.springframework.ai.bedrock.anthropic; import java.util.List; +import org.springframework.ai.chat.ChatClient; +import org.springframework.ai.chat.ChatResponse; import reactor.core.publisher.Flux; import org.springframework.ai.bedrock.MessageToPromptConverter; import org.springframework.ai.bedrock.anthropic.api.AnthropicChatBedrockApi; import org.springframework.ai.bedrock.anthropic.api.AnthropicChatBedrockApi.AnthropicChatRequest; import org.springframework.ai.bedrock.anthropic.api.AnthropicChatBedrockApi.AnthropicChatResponse; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.AiStreamClient; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.StreamingChatClient; +import org.springframework.ai.chat.Generation; import org.springframework.ai.metadata.ChoiceMetadata; import org.springframework.ai.prompt.Prompt; /** - * Java {@link AiClient} and {@link AiStreamClient} for the Bedrock Anthropic chat model. + * Java {@link ChatClient} and {@link StreamingChatClient} for the Bedrock Anthropic chat + * model. * * @author Christian Tzolov * @since 0.8.0 */ -public class BedrockAnthropicChatClient implements AiClient, AiStreamClient { +public class BedrockAnthropicChatClient implements ChatClient, StreamingChatClient { private final AnthropicChatBedrockApi anthropicChatApi; @@ -88,7 +89,7 @@ public class BedrockAnthropicChatClient implements AiClient, AiStreamClient { } @Override - public AiResponse generate(Prompt prompt) { + public ChatResponse generate(Prompt prompt) { final String promptValue = MessageToPromptConverter.create().toPrompt(prompt.getMessages()); AnthropicChatRequest request = AnthropicChatRequest.builder(promptValue) @@ -102,11 +103,11 @@ public class BedrockAnthropicChatClient implements AiClient, AiStreamClient { AnthropicChatResponse response = this.anthropicChatApi.chatCompletion(request); - return new AiResponse(List.of(new Generation(response.completion()))); + return new ChatResponse(List.of(new Generation(response.completion()))); } @Override - public Flux generateStream(Prompt prompt) { + public Flux generateStream(Prompt prompt) { final String promptValue = MessageToPromptConverter.create().toPrompt(prompt.getMessages()); @@ -128,7 +129,7 @@ public class BedrockAnthropicChatClient implements AiClient, AiStreamClient { generation = generation .withChoiceMetadata(ChoiceMetadata.from(stopReason, response.amazonBedrockInvocationMetrics())); } - return new AiResponse(List.of(generation)); + return new ChatResponse(List.of(generation)); }); } diff --git a/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClient.java b/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClient.java index 5befb9dce..500124807 100644 --- a/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClient.java +++ b/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClient.java @@ -18,6 +18,7 @@ package org.springframework.ai.bedrock.cohere; import java.util.List; +import org.springframework.ai.chat.ChatResponse; import reactor.core.publisher.Flux; import org.springframework.ai.bedrock.BedrockUsage; @@ -28,10 +29,9 @@ import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChat import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest.LogitBias; import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest.ReturnLikelihoods; import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest.Truncate; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.AiStreamClient; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatClient; +import org.springframework.ai.chat.StreamingChatClient; +import org.springframework.ai.chat.Generation; import org.springframework.ai.metadata.ChoiceMetadata; import org.springframework.ai.metadata.Usage; import org.springframework.ai.prompt.Prompt; @@ -40,7 +40,7 @@ import org.springframework.ai.prompt.Prompt; * @author Christian Tzolov * @since 0.8.0 */ -public class BedrockCohereChatClient implements AiClient, AiStreamClient { +public class BedrockCohereChatClient implements ChatClient, StreamingChatClient { private final CohereChatBedrockApi chatApi; @@ -112,25 +112,25 @@ public class BedrockCohereChatClient implements AiClient, AiStreamClient { } @Override - public AiResponse generate(Prompt prompt) { + public ChatResponse generate(Prompt prompt) { CohereChatResponse response = this.chatApi.chatCompletion(this.createRequest(prompt, false)); List generations = response.generations().stream().map(g -> { return new Generation(g.text()); }).toList(); - return new AiResponse(generations); + return new ChatResponse(generations); } @Override - public Flux generateStream(Prompt prompt) { + public Flux generateStream(Prompt prompt) { return this.chatApi.chatCompletionStream(this.createRequest(prompt, true)).map(g -> { if (g.isFinished()) { String finishReason = g.finishReason().name(); Usage usage = BedrockUsage.from(g.amazonBedrockInvocationMetrics()); - return new AiResponse( + return new ChatResponse( List.of(new Generation("").withChoiceMetadata(ChoiceMetadata.from(finishReason, usage)))); } - return new AiResponse(List.of(new Generation(g.text()))); + return new ChatResponse(List.of(new Generation(g.text()))); }); } diff --git a/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClient.java b/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClient.java index 6255ff5d0..e4fc8d69d 100644 --- a/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClient.java +++ b/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClient.java @@ -18,27 +18,28 @@ package org.springframework.ai.bedrock.llama2; import java.util.List; +import org.springframework.ai.chat.ChatResponse; import reactor.core.publisher.Flux; import org.springframework.ai.bedrock.MessageToPromptConverter; import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi; import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi.Llama2ChatRequest; import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi.Llama2ChatResponse; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.AiStreamClient; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatClient; +import org.springframework.ai.chat.StreamingChatClient; +import org.springframework.ai.chat.Generation; import org.springframework.ai.metadata.ChoiceMetadata; import org.springframework.ai.metadata.Usage; import org.springframework.ai.prompt.Prompt; /** - * Java {@link AiClient} and {@link AiStreamClient} for the Bedrock Llama2 chat model. + * Java {@link ChatClient} and {@link StreamingChatClient} for the Bedrock Llama2 chat + * model. * * @author Christian Tzolov * @since 0.8.0 */ -public class BedrockLlama2ChatClient implements AiClient, AiStreamClient { +public class BedrockLlama2ChatClient implements ChatClient, StreamingChatClient { private final Llama2ChatBedrockApi chatApi; @@ -68,7 +69,7 @@ public class BedrockLlama2ChatClient implements AiClient, AiStreamClient { } @Override - public AiResponse generate(Prompt prompt) { + public ChatResponse generate(Prompt prompt) { final String promptValue = MessageToPromptConverter.create().toPrompt(prompt.getMessages()); var request = Llama2ChatRequest.builder(promptValue) @@ -79,12 +80,12 @@ public class BedrockLlama2ChatClient implements AiClient, AiStreamClient { Llama2ChatResponse response = this.chatApi.chatCompletion(request); - return new AiResponse(List.of(new Generation(response.generation()) + return new ChatResponse(List.of(new Generation(response.generation()) .withChoiceMetadata(ChoiceMetadata.from(response.stopReason().name(), extractUsage(response))))); } @Override - public Flux generateStream(Prompt prompt) { + public Flux generateStream(Prompt prompt) { final String promptValue = MessageToPromptConverter.create().toPrompt(prompt.getMessages()); @@ -98,7 +99,7 @@ public class BedrockLlama2ChatClient implements AiClient, AiStreamClient { return fluxResponse.map(response -> { String stopReason = response.stopReason() != null ? response.stopReason().name() : null; - return new AiResponse(List.of(new Generation(response.generation()) + return new ChatResponse(List.of(new Generation(response.generation()) .withChoiceMetadata(ChoiceMetadata.from(stopReason, extractUsage(response))))); }); } diff --git a/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClient.java b/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClient.java index 919f6399a..14a95d04a 100644 --- a/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClient.java +++ b/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClient.java @@ -18,6 +18,7 @@ package org.springframework.ai.bedrock.titan; import java.util.List; +import org.springframework.ai.chat.ChatClient; import reactor.core.publisher.Flux; import org.springframework.ai.bedrock.MessageToPromptConverter; @@ -25,10 +26,9 @@ import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi; import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi.TitanChatRequest; import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi.TitanChatResponse; import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi.TitanChatResponseChunk; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.AiStreamClient; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.StreamingChatClient; +import org.springframework.ai.chat.Generation; import org.springframework.ai.metadata.ChoiceMetadata; import org.springframework.ai.metadata.Usage; import org.springframework.ai.prompt.Prompt; @@ -37,7 +37,7 @@ import org.springframework.ai.prompt.Prompt; * @author Christian Tzolov * @since 0.8.0 */ -public class BedrockTitanChatClient implements AiClient, AiStreamClient { +public class BedrockTitanChatClient implements ChatClient, StreamingChatClient { private final TitanChatBedrockApi chatApi; @@ -74,17 +74,17 @@ public class BedrockTitanChatClient implements AiClient, AiStreamClient { } @Override - public AiResponse generate(Prompt prompt) { + public ChatResponse generate(Prompt prompt) { TitanChatResponse response = this.chatApi.chatCompletion(this.createRequest(prompt, false)); List generations = response.results().stream().map(result -> { return new Generation(result.outputText()); }).toList(); - return new AiResponse(generations); + return new ChatResponse(generations); } @Override - public Flux generateStream(Prompt prompt) { + public Flux generateStream(Prompt prompt) { return this.chatApi.chatCompletionStream(this.createRequest(prompt, true)).map(chunk -> { Generation generation = new Generation(chunk.outputText()); @@ -99,7 +99,7 @@ public class BedrockTitanChatClient implements AiClient, AiStreamClient { generation = generation.withChoiceMetadata(ChoiceMetadata.from(completionReason, extractUsage(chunk))); } - return new AiResponse(List.of(generation)); + return new ChatResponse(List.of(generation)); }); } diff --git a/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClientIT.java b/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClientIT.java index 3615d83e9..14ecad3f9 100644 --- a/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClientIT.java +++ b/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/anthropic/BedrockAnthropicChatClientIT.java @@ -8,12 +8,12 @@ import java.util.stream.Collectors; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.chat.ChatResponse; import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.regions.Region; import org.springframework.ai.bedrock.anthropic.api.AnthropicChatBedrockApi; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.Generation; import org.springframework.ai.parser.BeanOutputParser; import org.springframework.ai.parser.ListOutputParser; import org.springframework.ai.parser.MapOutputParser; @@ -52,7 +52,7 @@ class BedrockAnthropicChatClientIT { Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); - AiResponse response = client.generate(prompt); + ChatResponse response = client.generate(prompt); assertThat(response.getGeneration().getContent()).contains("Blackbeard"); } @@ -137,7 +137,7 @@ class BedrockAnthropicChatClientIT { .collectList() .block() .stream() - .map(AiResponse::getGenerations) + .map(ChatResponse::getGenerations) .flatMap(List::stream) .map(Generation::getContent) .collect(Collectors.joining()); diff --git a/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClientIT.java b/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClientIT.java index 5900ec9eb..bb4ffc32f 100644 --- a/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClientIT.java +++ b/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/cohere/BedrockCohereChatClientIT.java @@ -13,8 +13,8 @@ import software.amazon.awssdk.regions.Region; import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi; import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatModel; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; import org.springframework.ai.parser.BeanOutputParser; import org.springframework.ai.parser.ListOutputParser; import org.springframework.ai.parser.MapOutputParser; @@ -53,7 +53,7 @@ class BedrockCohereChatClientIT { SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice)); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); - AiResponse response = client.generate(prompt); + ChatResponse response = client.generate(prompt); assertThat(response.getGeneration().getContent()).contains("Blackbeard"); } @@ -137,7 +137,7 @@ class BedrockCohereChatClientIT { .collectList() .block() .stream() - .map(AiResponse::getGenerations) + .map(ChatResponse::getGenerations) .flatMap(List::stream) .map(Generation::getContent) .collect(Collectors.joining()); diff --git a/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClientIT.java b/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClientIT.java index 56fcd3c39..a5fe9dd14 100644 --- a/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClientIT.java +++ b/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/llama2/BedrockLlama2ChatClientIT.java @@ -9,13 +9,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.chat.ChatResponse; import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.regions.Region; import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi; import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi.Llama2ChatCompletionModel; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.Generation; import org.springframework.ai.parser.BeanOutputParser; import org.springframework.ai.parser.ListOutputParser; import org.springframework.ai.parser.MapOutputParser; @@ -54,7 +54,7 @@ class BedrockLlama2ChatClientIT { Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); - AiResponse response = client.generate(prompt); + ChatResponse response = client.generate(prompt); assertThat(response.getGeneration().getContent()).contains("Blackbeard"); } @@ -142,7 +142,7 @@ class BedrockLlama2ChatClientIT { .collectList() .block() .stream() - .map(AiResponse::getGenerations) + .map(ChatResponse::getGenerations) .flatMap(List::stream) .map(Generation::getContent) .collect(Collectors.joining()); diff --git a/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClientIT.java b/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClientIT.java index 87dd7b7d2..2f96bb84f 100644 --- a/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClientIT.java +++ b/spring-ai-bedrock/src/test/java/org/springframework/ai/bedrock/titan/BedrockTitanChatClientIT.java @@ -9,13 +9,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.chat.ChatResponse; import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; import software.amazon.awssdk.regions.Region; import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi; import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi.TitanChatModel; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.Generation; import org.springframework.ai.parser.BeanOutputParser; import org.springframework.ai.parser.ListOutputParser; import org.springframework.ai.parser.MapOutputParser; @@ -54,7 +54,7 @@ class BedrockTitanChatClientIT { SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice)); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); - AiResponse response = client.generate(prompt); + ChatResponse response = client.generate(prompt); assertThat(response.getGeneration().getContent()).contains("Blackbeard"); } @@ -143,7 +143,7 @@ class BedrockTitanChatClientIT { .collectList() .block() .stream() - .map(AiResponse::getGenerations) + .map(ChatResponse::getGenerations) .flatMap(List::stream) .map(Generation::getContent) .collect(Collectors.joining()); diff --git a/spring-ai-core/src/main/java/org/springframework/ai/client/AiClient.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java similarity index 89% rename from spring-ai-core/src/main/java/org/springframework/ai/client/AiClient.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java index d41a768e7..373339467 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/client/AiClient.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatClient.java @@ -14,19 +14,19 @@ * limitations under the License. */ -package org.springframework.ai.client; +package org.springframework.ai.chat; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.messages.UserMessage; @FunctionalInterface -public interface AiClient { +public interface ChatClient { default String generate(String message) { Prompt prompt = new Prompt(new UserMessage(message)); return generate(prompt).getGeneration().getContent(); } - AiResponse generate(Prompt prompt); + ChatResponse generate(Prompt prompt); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/client/AiResponse.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatResponse.java similarity index 86% rename from spring-ai-core/src/main/java/org/springframework/ai/client/AiResponse.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/ChatResponse.java index c8984ca72..f68f6bb31 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/client/AiResponse.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/ChatResponse.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.ai.client; +package org.springframework.ai.chat; import java.util.List; @@ -24,7 +24,7 @@ import org.springframework.lang.Nullable; /** * The chat completion (e.g. generation) response returned by an AI provider. */ -public class AiResponse { +public class ChatResponse { private final GenerationMetadata metadata; @@ -36,22 +36,22 @@ public class AiResponse { private PromptMetadata promptMetadata; /** - * Construct a new {@link AiResponse} instance without metadata. + * Construct a new {@link ChatResponse} instance without metadata. * @param generations the {@link List} of {@link Generation} returned by the AI * provider. */ - public AiResponse(List generations) { + public ChatResponse(List generations) { this(generations, GenerationMetadata.NULL); } /** - * Construct a new {@link AiResponse} instance. + * Construct a new {@link ChatResponse} instance. * @param generations the {@link List} of {@link Generation} returned by the AI * provider. * @param metadata {@link GenerationMetadata} containing information about the use of * the AI provider's API. */ - public AiResponse(List generations, GenerationMetadata metadata) { + public ChatResponse(List generations, GenerationMetadata metadata) { this.metadata = metadata; this.generations = List.copyOf(generations); } @@ -96,10 +96,10 @@ public class AiResponse { * when processing the prompt. * @param promptMetadata {@link PromptMetadata} returned by the AI in the response * when processing the prompt. - * @return this {@link AiResponse}. + * @return this {@link ChatResponse}. * @see #getPromptMetadata() */ - public AiResponse withPromptMetadata(@Nullable PromptMetadata promptMetadata) { + public ChatResponse withPromptMetadata(@Nullable PromptMetadata promptMetadata) { this.promptMetadata = promptMetadata; return this; } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/client/Generation.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/Generation.java similarity index 97% rename from spring-ai-core/src/main/java/org/springframework/ai/client/Generation.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/Generation.java index adcedfe52..71fd3455a 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/client/Generation.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/Generation.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.client; +package org.springframework.ai.chat; import java.util.Collections; import java.util.Map; diff --git a/spring-ai-core/src/main/java/org/springframework/ai/client/AiStreamClient.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/StreamingChatClient.java similarity index 84% rename from spring-ai-core/src/main/java/org/springframework/ai/client/AiStreamClient.java rename to spring-ai-core/src/main/java/org/springframework/ai/chat/StreamingChatClient.java index 6a655dc92..b441ade99 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/client/AiStreamClient.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/StreamingChatClient.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package org.springframework.ai.client; +package org.springframework.ai.chat; import reactor.core.publisher.Flux; import org.springframework.ai.prompt.Prompt; @FunctionalInterface -public interface AiStreamClient { +public interface StreamingChatClient { - public Flux generateStream(Prompt prompt); + public Flux generateStream(Prompt prompt); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/transformer/KeywordMetadataEnricher.java b/spring-ai-core/src/main/java/org/springframework/ai/transformer/KeywordMetadataEnricher.java index f5039b5f3..9387be744 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/transformer/KeywordMetadataEnricher.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/transformer/KeywordMetadataEnricher.java @@ -19,7 +19,7 @@ package org.springframework.ai.transformer; import java.util.List; import java.util.Map; -import org.springframework.ai.client.AiClient; +import org.springframework.ai.chat.ChatClient; import org.springframework.ai.document.Document; import org.springframework.ai.document.DocumentTransformer; import org.springframework.ai.prompt.Prompt; @@ -44,18 +44,18 @@ public class KeywordMetadataEnricher implements DocumentTransformer { /** * Model predictor */ - private final AiClient aiClient; + private final ChatClient chatClient; /** * The number of keywords to extract. */ private final int keywordCount; - public KeywordMetadataEnricher(AiClient aiClient, int keywordCount) { - Assert.notNull(aiClient, "AiClient must not be null"); + public KeywordMetadataEnricher(ChatClient chatClient, int keywordCount) { + Assert.notNull(chatClient, "ChatClient must not be null"); Assert.isTrue(keywordCount >= 1, "Document count must be >= 1"); - this.aiClient = aiClient; + this.chatClient = chatClient; this.keywordCount = keywordCount; } @@ -65,7 +65,7 @@ public class KeywordMetadataEnricher implements DocumentTransformer { var template = new PromptTemplate(String.format(KEYWORDS_TEMPLATE, keywordCount)); Prompt prompt = template.create(Map.of(CONTEXT_STR_PLACEHOLDER, document.getContent())); - String keywords = this.aiClient.generate(prompt).getGeneration().getContent(); + String keywords = this.chatClient.generate(prompt).getGeneration().getContent(); document.getMetadata().putAll(Map.of(EXCERPT_KEYWORDS_METADATA_KEY, keywords)); } return documents; diff --git a/spring-ai-core/src/main/java/org/springframework/ai/transformer/SummaryMetadataEnricher.java b/spring-ai-core/src/main/java/org/springframework/ai/transformer/SummaryMetadataEnricher.java index f43904a0b..e5162e1b7 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/transformer/SummaryMetadataEnricher.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/transformer/SummaryMetadataEnricher.java @@ -21,7 +21,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.springframework.ai.client.AiClient; +import org.springframework.ai.chat.ChatClient; import org.springframework.ai.document.Document; import org.springframework.ai.document.DocumentTransformer; import org.springframework.ai.document.MetadataMode; @@ -63,7 +63,7 @@ public class SummaryMetadataEnricher implements DocumentTransformer { /** * AI client. */ - private final AiClient aiClient; + private final ChatClient chatClient; /** * Number of documents from front to use for title extraction. @@ -77,16 +77,16 @@ public class SummaryMetadataEnricher implements DocumentTransformer { */ private final String summaryTemplate; - public SummaryMetadataEnricher(AiClient aiClient, List summaryTypes) { - this(aiClient, summaryTypes, DEFAULT_SUMMARY_EXTRACT_TEMPLATE, MetadataMode.ALL); + public SummaryMetadataEnricher(ChatClient chatClient, List summaryTypes) { + this(chatClient, summaryTypes, DEFAULT_SUMMARY_EXTRACT_TEMPLATE, MetadataMode.ALL); } - public SummaryMetadataEnricher(AiClient aiClient, List summaryTypes, String summaryTemplate, + public SummaryMetadataEnricher(ChatClient chatClient, List summaryTypes, String summaryTemplate, MetadataMode metadataMode) { - Assert.notNull(aiClient, "AiClient must not be null"); + Assert.notNull(chatClient, "ChatClient must not be null"); Assert.hasText(summaryTemplate, "Summary template must not be empty"); - this.aiClient = aiClient; + this.chatClient = chatClient; this.summaryTypes = CollectionUtils.isEmpty(summaryTypes) ? List.of(SummaryType.CURRENT) : summaryTypes; this.metadataMode = metadataMode; this.summaryTemplate = summaryTemplate; @@ -102,7 +102,7 @@ public class SummaryMetadataEnricher implements DocumentTransformer { Prompt prompt = new PromptTemplate(this.summaryTemplate) .create(Map.of(CONTEXT_STR_PLACEHOLDER, documentContext)); - documentSummaries.add(this.aiClient.generate(prompt).getGeneration().getContent()); + documentSummaries.add(this.chatClient.generate(prompt).getGeneration().getContent()); } for (int i = 0; i < documentSummaries.size(); i++) { diff --git a/spring-ai-core/src/test/java/org/springframework/ai/client/AiClientTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/AiClientTests.java similarity index 90% rename from spring-ai-core/src/test/java/org/springframework/ai/client/AiClientTests.java rename to spring-ai-core/src/test/java/org/springframework/ai/chat/AiClientTests.java index f673e1543..08642a859 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/client/AiClientTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/AiClientTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.client; +package org.springframework.ai.chat; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -33,10 +33,11 @@ import java.util.Collections; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.ai.prompt.Prompt; /** - * Unit Tests for {@link AiClient}. + * Unit Tests for {@link ChatClient}. * * @author John Blum * @since 0.2.0 @@ -49,9 +50,9 @@ class AiClientTests { String userMessage = "Zero Wing"; String responseMessage = "All your bases are belong to us"; - AiClient mockClient = mock(AiClient.class); + ChatClient mockClient = Mockito.mock(ChatClient.class); Generation generation = spy(new Generation(responseMessage)); - AiResponse response = spy(new AiResponse(Collections.singletonList(generation))); + ChatResponse response = spy(new ChatResponse(Collections.singletonList(generation))); doCallRealMethod().when(mockClient).generate(anyString()); diff --git a/spring-ai-huggingface/src/main/java/org/springframework/ai/huggingface/client/HuggingfaceAiClient.java b/spring-ai-huggingface/src/main/java/org/springframework/ai/huggingface/client/HuggingfaceAiClient.java index 6ccd47c3c..9f686317f 100644 --- a/spring-ai-huggingface/src/main/java/org/springframework/ai/huggingface/client/HuggingfaceAiClient.java +++ b/spring-ai-huggingface/src/main/java/org/springframework/ai/huggingface/client/HuggingfaceAiClient.java @@ -23,9 +23,9 @@ import java.util.Map; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatClient; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; import org.springframework.ai.huggingface.api.TextGenerationInferenceApi; import org.springframework.ai.huggingface.invoker.ApiClient; import org.springframework.ai.huggingface.model.AllOfGenerateResponseDetails; @@ -35,12 +35,12 @@ import org.springframework.ai.huggingface.model.GenerateResponse; import org.springframework.ai.prompt.Prompt; /** - * An implementation of {@link AiClient} that interfaces with HuggingFace Inference + * An implementation of {@link ChatClient} that interfaces with HuggingFace Inference * Endpoints for text generation. * * @author Mark Pollack */ -public class HuggingfaceAiClient implements AiClient { +public class HuggingfaceAiClient implements ChatClient { /** * Token required for authenticating with the HuggingFace Inference API. @@ -83,10 +83,10 @@ public class HuggingfaceAiClient implements AiClient { /** * Generate text based on the provided prompt. * @param prompt The input prompt based on which text is to be generated. - * @return AiResponse containing the generated text and other related details. + * @return ChatResponse containing the generated text and other related details. */ @Override - public AiResponse generate(Prompt prompt) { + public ChatResponse generate(Prompt prompt) { GenerateRequest generateRequest = new GenerateRequest(); generateRequest.setInputs(prompt.getContents()); GenerateParameters generateParameters = new GenerateParameters(); @@ -102,7 +102,7 @@ public class HuggingfaceAiClient implements AiClient { }); Generation generation = new Generation(generatedText, detailsMap); generations.add(generation); - return new AiResponse(generations); + return new ChatResponse(generations); } /** diff --git a/spring-ai-huggingface/src/test/java/org/springframework/ai/huggingface/client/ClientIT.java b/spring-ai-huggingface/src/test/java/org/springframework/ai/huggingface/client/ClientIT.java index 59153fce7..d03592398 100644 --- a/spring-ai-huggingface/src/test/java/org/springframework/ai/huggingface/client/ClientIT.java +++ b/spring-ai-huggingface/src/test/java/org/springframework/ai/huggingface/client/ClientIT.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.springframework.ai.client.AiResponse; +import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.prompt.Prompt; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -46,8 +46,8 @@ public class ClientIT { [/INST] """; Prompt prompt = new Prompt(mistral7bInstruct); - AiResponse aiResponse = huggingfaceAiClient.generate(prompt); - assertThat(aiResponse.getGeneration().getContent()).isNotEmpty(); + ChatResponse chatResponse = huggingfaceAiClient.generate(prompt); + assertThat(chatResponse.getGeneration().getContent()).isNotEmpty(); String expectedResponse = """ ```json { @@ -56,9 +56,9 @@ public class ClientIT { "address": "#1 Samuel St." } ```"""; - assertThat(aiResponse.getGeneration().getContent()).isEqualTo(expectedResponse); - assertThat(aiResponse.getGeneration().getProperties()).containsKey("generated_tokens"); - assertThat(aiResponse.getGeneration().getProperties()).containsEntry("generated_tokens", 39); + assertThat(chatResponse.getGeneration().getContent()).isEqualTo(expectedResponse); + assertThat(chatResponse.getGeneration().getProperties()).containsKey("generated_tokens"); + assertThat(chatResponse.getGeneration().getProperties()).containsEntry("generated_tokens", 39); } diff --git a/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/client/OllamaClient.java b/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/client/OllamaClient.java index b30c4386c..11b10282e 100644 --- a/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/client/OllamaClient.java +++ b/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/client/OllamaClient.java @@ -4,9 +4,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatClient; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; import org.springframework.ai.prompt.Prompt; import org.springframework.util.CollectionUtils; @@ -30,7 +30,7 @@ import java.util.stream.Collectors; * * @author nullptr */ -public class OllamaClient implements AiClient { +public class OllamaClient implements ChatClient { /** Logger for logging the events and messages. */ private static final Logger log = LoggerFactory.getLogger(OllamaClient.class); @@ -72,7 +72,7 @@ public class OllamaClient implements AiClient { } @Override - public AiResponse generate(Prompt prompt) { + public ChatResponse generate(Prompt prompt) { validatePrompt(prompt); HttpRequest request = buildHttpRequest(prompt); @@ -196,11 +196,11 @@ public class OllamaClient implements AiClient { } /** - * Converts the list of OllamaGenerateResult into a structured AiResponse. + * Converts the list of OllamaGenerateResult into a structured ChatResponse. * @param results List of OllamaGenerateResult. - * @return Formulated AiResponse. + * @return Formulated ChatResponse. */ - protected AiResponse getAiResponse(List results) { + protected ChatResponse getAiResponse(List results) { var ollamaResponse = results.stream() .filter(Objects::nonNull) .filter(it -> it.getResponse() != null && !it.getResponse().isBlank()) @@ -211,7 +211,7 @@ public class OllamaClient implements AiClient { var generation = new Generation(ollamaResponse); // TODO investigate mapping of additional metadata/runtime info to the response. - return new AiResponse(Collections.singletonList(generation)); + return new ChatResponse(Collections.singletonList(generation)); } /** diff --git a/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/client/OllamaClientTests.java b/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/client/OllamaClientTests.java index 4abb94230..839c33cc8 100644 --- a/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/client/OllamaClientTests.java +++ b/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/client/OllamaClientTests.java @@ -3,7 +3,7 @@ package org.springframework.ai.ollama.client; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.springframework.ai.client.AiResponse; +import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.prompt.Prompt; import org.springframework.util.CollectionUtils; @@ -17,11 +17,11 @@ public class OllamaClientTests { OllamaClient ollama2 = getOllamaClient(); Prompt prompt = new Prompt("Hello"); - AiResponse aiResponse = ollama2.generate(prompt); - Assertions.assertNotNull(aiResponse); - Assertions.assertFalse(CollectionUtils.isEmpty(aiResponse.getGenerations())); - Assertions.assertNotNull(aiResponse.getGeneration()); - Assertions.assertNotNull(aiResponse.getGeneration().getContent()); + ChatResponse chatResponse = ollama2.generate(prompt); + Assertions.assertNotNull(chatResponse); + Assertions.assertFalse(CollectionUtils.isEmpty(chatResponse.getGenerations())); + Assertions.assertNotNull(chatResponse.getGeneration()); + Assertions.assertNotNull(chatResponse.getGeneration().getContent()); } private static OllamaClient getOllamaClient() { diff --git a/spring-ai-openai/src/main/java/org/springframework/ai/openai/client/OpenAiClient.java b/spring-ai-openai/src/main/java/org/springframework/ai/openai/client/OpenAiClient.java index 858ac4776..a7123f45d 100644 --- a/spring-ai-openai/src/main/java/org/springframework/ai/openai/client/OpenAiClient.java +++ b/spring-ai-openai/src/main/java/org/springframework/ai/openai/client/OpenAiClient.java @@ -22,12 +22,12 @@ import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ai.chat.ChatClient; +import org.springframework.ai.chat.ChatResponse; import reactor.core.publisher.Flux; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.AiStreamClient; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.StreamingChatClient; +import org.springframework.ai.chat.Generation; import org.springframework.ai.metadata.ChoiceMetadata; import org.springframework.ai.metadata.RateLimit; import org.springframework.ai.openai.api.OpenAiApi; @@ -43,7 +43,7 @@ import org.springframework.retry.support.RetryTemplate; import org.springframework.util.Assert; /** - * {@link AiClient} implementation for {@literal OpenAI} backed by {@link OpenAiApi}. + * {@link ChatClient} implementation for {@literal OpenAI} backed by {@link OpenAiApi}. * * @author Mark Pollack * @author Christian Tzolov @@ -51,11 +51,11 @@ import org.springframework.util.Assert; * @author John Blum * @author Josh Long * @author Jemin Huh - * @see org.springframework.ai.client.AiClient - * @see org.springframework.ai.client.AiStreamClient + * @see ChatClient + * @see StreamingChatClient * @see OpenAiApi */ -public class OpenAiClient implements AiClient, AiStreamClient { +public class OpenAiClient implements ChatClient, StreamingChatClient { private Double temperature = 0.7; @@ -93,7 +93,7 @@ public class OpenAiClient implements AiClient, AiStreamClient { } @Override - public AiResponse generate(Prompt prompt) { + public ChatResponse generate(Prompt prompt) { return this.retryTemplate.execute(ctx -> { List messages = prompt.getMessages(); @@ -110,7 +110,7 @@ public class OpenAiClient implements AiClient, AiStreamClient { var chatCompletion = completionEntity.getBody(); if (chatCompletion == null) { logger.warn("No chat completion returned for request: {}", chatCompletionMessages); - return new AiResponse(List.of()); + return new ChatResponse(List.of()); } RateLimit rateLimits = OpenAiResponseHeaderExtractor.extractAiResponseHeaders(completionEntity); @@ -120,13 +120,13 @@ public class OpenAiClient implements AiClient, AiStreamClient { .withChoiceMetadata(ChoiceMetadata.from(choice.finishReason().name(), null)); }).toList(); - return new AiResponse(generations, + return new ChatResponse(generations, OpenAiGenerationMetadata.from(completionEntity.getBody()).withRateLimit(rateLimits)); }); } @Override - public Flux generateStream(Prompt prompt) { + public Flux generateStream(Prompt prompt) { return this.retryTemplate.execute(ctx -> { List messages = prompt.getMessages(); @@ -144,7 +144,7 @@ public class OpenAiClient implements AiClient, AiStreamClient { ConcurrentHashMap roleMap = new ConcurrentHashMap<>(); // An alternative implementation that returns Flux instead of - // Flux. + // Flux. // Flux generationFlux = completionChunks.map(chunk -> { // String chunkId = chunk.id(); // return chunk.choices().stream() @@ -172,7 +172,7 @@ public class OpenAiClient implements AiClient, AiStreamClient { } return generation; }).toList(); - return new AiResponse(generations); + return new ChatResponse(generations); }); }); } diff --git a/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java b/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java index 0cf980b3f..b78e25d4b 100644 --- a/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java +++ b/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ai.client.AiResponse; +import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.document.Document; import org.springframework.ai.openai.OpenAiTestConfiguration; import org.springframework.ai.openai.client.OpenAiClient; @@ -96,7 +96,7 @@ public class AcmeIT extends AbstractIT { logger.info("Asking AI model to reply to question."); Prompt prompt = new Prompt(List.of(systemMessage, userMessage)); logger.info("AI responded."); - AiResponse response = aiClient.generate(prompt); + ChatResponse response = aiClient.generate(prompt); evaluateQuestionAndAnswer(userQuery, response, true); } diff --git a/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/OpenAiClientIT.java b/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/OpenAiClientIT.java index 277ce7c17..7b1b97ab3 100644 --- a/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/OpenAiClientIT.java +++ b/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/OpenAiClientIT.java @@ -8,8 +8,8 @@ import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; import org.springframework.ai.openai.OpenAiTestConfiguration; import org.springframework.ai.openai.testutils.AbstractIT; import org.springframework.ai.parser.BeanOutputParser; @@ -41,7 +41,7 @@ class OpenAiClientIT extends AbstractIT { SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", "Bob", "voice", "pirate")); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); - AiResponse response = openAiClient.generate(prompt); + ChatResponse response = openAiClient.generate(prompt); assertThat(response.getGenerations()).hasSize(1); assertThat(response.getGenerations().get(0).getContent()).contains("Blackbeard"); // needs fine tuning... evaluateQuestionAndAnswer(request, response, false); @@ -139,11 +139,11 @@ class OpenAiClientIT extends AbstractIT { PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format)); Prompt prompt = new Prompt(promptTemplate.createMessage()); - String generationTextFromStream = openAiStreamClient.generateStream(prompt) + String generationTextFromStream = openStreamingChatClient.generateStream(prompt) .collectList() .block() .stream() - .map(AiResponse::getGenerations) + .map(ChatResponse::getGenerations) .flatMap(List::stream) .map(Generation::getContent) .collect(Collectors.joining()); diff --git a/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/OpenAiClientWithGenerationMetadata2Tests.java b/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/OpenAiClientWithGenerationMetadata2Tests.java index 83011d74b..0e2dc81d3 100644 --- a/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/OpenAiClientWithGenerationMetadata2Tests.java +++ b/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/OpenAiClientWithGenerationMetadata2Tests.java @@ -21,7 +21,7 @@ import java.time.Duration; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.springframework.ai.client.AiResponse; +import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.metadata.ChoiceMetadata; import org.springframework.ai.metadata.GenerationMetadata; import org.springframework.ai.metadata.PromptMetadata; @@ -74,7 +74,7 @@ public class OpenAiClientWithGenerationMetadata2Tests { Prompt prompt = new Prompt("Reach for the sky."); - AiResponse response = this.openAiClient.generate(prompt); + ChatResponse response = this.openAiClient.generate(prompt); assertThat(response).isNotNull(); diff --git a/spring-ai-openai/src/test/java/org/springframework/ai/openai/testutils/AbstractIT.java b/spring-ai-openai/src/test/java/org/springframework/ai/openai/testutils/AbstractIT.java index 8f22e2c92..facd66c53 100644 --- a/spring-ai-openai/src/test/java/org/springframework/ai/openai/testutils/AbstractIT.java +++ b/spring-ai-openai/src/test/java/org/springframework/ai/openai/testutils/AbstractIT.java @@ -6,9 +6,9 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.AiStreamClient; +import org.springframework.ai.chat.ChatClient; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.StreamingChatClient; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.PromptTemplate; import org.springframework.ai.prompt.messages.Message; @@ -25,10 +25,10 @@ public abstract class AbstractIT { private static final Logger logger = LoggerFactory.getLogger(AbstractIT.class); @Autowired - protected AiClient openAiClient; + protected ChatClient openAiClient; @Autowired - protected AiStreamClient openAiStreamClient; + protected StreamingChatClient openStreamingChatClient; @Value("classpath:/prompts/eval/qa-evaluator-accurate-answer.st") protected Resource qaEvaluatorAccurateAnswerResource; @@ -42,7 +42,7 @@ public abstract class AbstractIT { @Value("classpath:/prompts/eval/user-evaluator-message.st") protected Resource userEvaluatorResource; - protected void evaluateQuestionAndAnswer(String question, AiResponse response, boolean factBased) { + protected void evaluateQuestionAndAnswer(String question, ChatResponse response, boolean factBased) { assertThat(response).isNotNull(); String answer = response.getGeneration().getContent(); logger.info("Question: " + question); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/anthropic/BedrockAnthropicChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/anthropic/BedrockAnthropicChatAutoConfigurationIT.java index 2895ae6c7..416bb8676 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/anthropic/BedrockAnthropicChatAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/anthropic/BedrockAnthropicChatAutoConfigurationIT.java @@ -28,8 +28,8 @@ import software.amazon.awssdk.regions.Region; import org.springframework.ai.autoconfigure.bedrock.BedrockAwsConnectionProperties; import org.springframework.ai.bedrock.anthropic.BedrockAnthropicChatClient; import org.springframework.ai.bedrock.anthropic.api.AnthropicChatBedrockApi.AnthropicChatModel; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.SystemPromptTemplate; import org.springframework.ai.prompt.messages.Message; @@ -70,7 +70,7 @@ public class BedrockAnthropicChatAutoConfigurationIT { public void chatCompletion() { contextRunner.run(context -> { BedrockAnthropicChatClient anthropicChatClient = context.getBean(BedrockAnthropicChatClient.class); - AiResponse response = anthropicChatClient.generate(new Prompt(List.of(userMessage, systemMessage))); + ChatResponse response = anthropicChatClient.generate(new Prompt(List.of(userMessage, systemMessage))); assertThat(response.getGeneration().getContent()).contains("Blackbeard"); }); } @@ -81,14 +81,14 @@ public class BedrockAnthropicChatAutoConfigurationIT { BedrockAnthropicChatClient anthropicChatClient = context.getBean(BedrockAnthropicChatClient.class); - Flux response = anthropicChatClient + Flux response = anthropicChatClient .generateStream(new Prompt(List.of(userMessage, systemMessage))); - List responses = response.collectList().block(); + List responses = response.collectList().block(); assertThat(responses.size()).isGreaterThan(2); String stitchedResponseContent = responses.stream() - .map(AiResponse::getGenerations) + .map(ChatResponse::getGenerations) .flatMap(List::stream) .map(Generation::getContent) .collect(Collectors.joining()); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/cohere/BedrockCohereChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/cohere/BedrockCohereChatAutoConfigurationIT.java index b7041d1f1..f80a9b78d 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/cohere/BedrockCohereChatAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/cohere/BedrockCohereChatAutoConfigurationIT.java @@ -22,6 +22,7 @@ import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.chat.ChatResponse; import reactor.core.publisher.Flux; import software.amazon.awssdk.regions.Region; @@ -30,8 +31,7 @@ import org.springframework.ai.bedrock.cohere.BedrockCohereChatClient; import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatModel; import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest.ReturnLikelihoods; import org.springframework.ai.bedrock.cohere.api.CohereChatBedrockApi.CohereChatRequest.Truncate; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.Generation; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.SystemPromptTemplate; import org.springframework.ai.prompt.messages.Message; @@ -72,7 +72,7 @@ public class BedrockCohereChatAutoConfigurationIT { public void chatCompletion() { contextRunner.run(context -> { BedrockCohereChatClient cohereChatClient = context.getBean(BedrockCohereChatClient.class); - AiResponse response = cohereChatClient.generate(new Prompt(List.of(userMessage, systemMessage))); + ChatResponse response = cohereChatClient.generate(new Prompt(List.of(userMessage, systemMessage))); assertThat(response.getGeneration().getContent()).contains("Blackbeard"); }); } @@ -83,14 +83,14 @@ public class BedrockCohereChatAutoConfigurationIT { BedrockCohereChatClient cohereChatClient = context.getBean(BedrockCohereChatClient.class); - Flux response = cohereChatClient + Flux response = cohereChatClient .generateStream(new Prompt(List.of(userMessage, systemMessage))); - List responses = response.collectList().block(); + List responses = response.collectList().block(); assertThat(responses.size()).isGreaterThan(2); String stitchedResponseContent = responses.stream() - .map(AiResponse::getGenerations) + .map(ChatResponse::getGenerations) .flatMap(List::stream) .map(Generation::getContent) .collect(Collectors.joining()); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/llama2/BedrockLlama2ChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/llama2/BedrockLlama2ChatAutoConfigurationIT.java index d85b6bf5a..1377d31fc 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/llama2/BedrockLlama2ChatAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/llama2/BedrockLlama2ChatAutoConfigurationIT.java @@ -22,14 +22,14 @@ import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.chat.ChatResponse; import reactor.core.publisher.Flux; import software.amazon.awssdk.regions.Region; import org.springframework.ai.autoconfigure.bedrock.BedrockAwsConnectionProperties; import org.springframework.ai.bedrock.llama2.BedrockLlama2ChatClient; import org.springframework.ai.bedrock.llama2.api.Llama2ChatBedrockApi.Llama2ChatCompletionModel; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.Generation; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.SystemPromptTemplate; import org.springframework.ai.prompt.messages.Message; @@ -70,7 +70,7 @@ public class BedrockLlama2ChatAutoConfigurationIT { public void chatCompletion() { contextRunner.run(context -> { BedrockLlama2ChatClient llama2ChatClient = context.getBean(BedrockLlama2ChatClient.class); - AiResponse response = llama2ChatClient.generate(new Prompt(List.of(userMessage, systemMessage))); + ChatResponse response = llama2ChatClient.generate(new Prompt(List.of(userMessage, systemMessage))); assertThat(response.getGeneration().getContent()).contains("Blackbeard"); }); } @@ -81,14 +81,14 @@ public class BedrockLlama2ChatAutoConfigurationIT { BedrockLlama2ChatClient llama2ChatClient = context.getBean(BedrockLlama2ChatClient.class); - Flux response = llama2ChatClient + Flux response = llama2ChatClient .generateStream(new Prompt(List.of(userMessage, systemMessage))); - List responses = response.collectList().block(); + List responses = response.collectList().block(); assertThat(responses.size()).isGreaterThan(2); String stitchedResponseContent = responses.stream() - .map(AiResponse::getGenerations) + .map(ChatResponse::getGenerations) .flatMap(List::stream) .map(Generation::getContent) .collect(Collectors.joining()); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/titan/BedrockTitanChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/titan/BedrockTitanChatAutoConfigurationIT.java index 6bb3f1998..835217e60 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/titan/BedrockTitanChatAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/titan/BedrockTitanChatAutoConfigurationIT.java @@ -22,14 +22,14 @@ import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.springframework.ai.chat.ChatResponse; import reactor.core.publisher.Flux; import software.amazon.awssdk.regions.Region; import org.springframework.ai.autoconfigure.bedrock.BedrockAwsConnectionProperties; import org.springframework.ai.bedrock.titan.BedrockTitanChatClient; import org.springframework.ai.bedrock.titan.api.TitanChatBedrockApi.TitanChatModel; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.Generation; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.SystemPromptTemplate; import org.springframework.ai.prompt.messages.Message; @@ -70,7 +70,7 @@ public class BedrockTitanChatAutoConfigurationIT { public void chatCompletion() { contextRunner.run(context -> { BedrockTitanChatClient chatClient = context.getBean(BedrockTitanChatClient.class); - AiResponse response = chatClient.generate(new Prompt(List.of(userMessage, systemMessage))); + ChatResponse response = chatClient.generate(new Prompt(List.of(userMessage, systemMessage))); assertThat(response.getGeneration().getContent()).contains("Blackbeard"); }); } @@ -81,13 +81,13 @@ public class BedrockTitanChatAutoConfigurationIT { BedrockTitanChatClient chatClient = context.getBean(BedrockTitanChatClient.class); - Flux response = chatClient.generateStream(new Prompt(List.of(userMessage, systemMessage))); + Flux response = chatClient.generateStream(new Prompt(List.of(userMessage, systemMessage))); - List responses = response.collectList().block(); + List responses = response.collectList().block(); assertThat(responses.size()).isGreaterThan(1); String stitchedResponseContent = responses.stream() - .map(AiResponse::getGenerations) + .map(ChatResponse::getGenerations) .flatMap(List::stream) .map(Generation::getContent) .collect(Collectors.joining()); diff --git a/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BasicEvaluationTest.java b/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BasicEvaluationTest.java index c65e2cce5..d7e99d34e 100644 --- a/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BasicEvaluationTest.java +++ b/spring-ai-test/src/main/java/org/springframework/ai/evaluation/BasicEvaluationTest.java @@ -18,8 +18,8 @@ package org.springframework.ai.evaluation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; +import org.springframework.ai.chat.ChatClient; +import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.PromptTemplate; import org.springframework.ai.prompt.messages.Message; @@ -39,7 +39,7 @@ public class BasicEvaluationTest { private static final Logger logger = LoggerFactory.getLogger(BasicEvaluationTest.class); @Autowired - protected AiClient openAiClient; + protected ChatClient openAiClient; @Value("classpath:/prompts/spring/test/evaluation/qa-evaluator-accurate-answer.st") protected Resource qaEvaluatorAccurateAnswerResource; @@ -53,7 +53,7 @@ public class BasicEvaluationTest { @Value("classpath:/prompts/spring/test/evaluation/user-evaluator-message.st") protected Resource userEvaluatorResource; - protected void evaluateQuestionAndAnswer(String question, AiResponse response, boolean factBased) { + protected void evaluateQuestionAndAnswer(String question, ChatResponse response, boolean factBased) { assertThat(response).isNotNull(); String answer = response.getGeneration().getContent(); logger.info("Question: " + question); diff --git a/spring-ai-vertex-ai/src/main/java/org/springframework/ai/vertex/generation/VertexAiChatClient.java b/spring-ai-vertex-ai/src/main/java/org/springframework/ai/vertex/generation/VertexAiChatClient.java index 5a2ac9432..76a9a8b7b 100644 --- a/spring-ai-vertex-ai/src/main/java/org/springframework/ai/vertex/generation/VertexAiChatClient.java +++ b/spring-ai-vertex-ai/src/main/java/org/springframework/ai/vertex/generation/VertexAiChatClient.java @@ -19,9 +19,9 @@ package org.springframework.ai.vertex.generation; import java.util.List; import java.util.stream.Collectors; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatClient; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.messages.MessageType; import org.springframework.ai.vertex.api.VertexAiApi; @@ -34,7 +34,7 @@ import org.springframework.util.CollectionUtils; /** * @author Christian Tzolov */ -public class VertexAiChatClient implements AiClient { +public class VertexAiChatClient implements ChatClient { private final VertexAiApi vertexAiApi; @@ -71,7 +71,7 @@ public class VertexAiChatClient implements AiClient { } @Override - public AiResponse generate(Prompt prompt) { + public ChatResponse generate(Prompt prompt) { String vertexContext = prompt.getMessages() .stream() @@ -99,7 +99,7 @@ public class VertexAiChatClient implements AiClient { .map(vmsg -> new Generation(vmsg.content())) .toList(); - return new AiResponse(generations); + return new ChatResponse(generations); } } diff --git a/spring-ai-vertex-ai/src/test/java/org/springframework/ai/vertex/generation/VertexAiChatGenerationClientIT.java b/spring-ai-vertex-ai/src/test/java/org/springframework/ai/vertex/generation/VertexAiChatGenerationClientIT.java index fc3bb2cfc..d85e380d7 100644 --- a/spring-ai-vertex-ai/src/test/java/org/springframework/ai/vertex/generation/VertexAiChatGenerationClientIT.java +++ b/spring-ai-vertex-ai/src/test/java/org/springframework/ai/vertex/generation/VertexAiChatGenerationClientIT.java @@ -7,8 +7,8 @@ import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; +import org.springframework.ai.chat.ChatResponse; +import org.springframework.ai.chat.Generation; import org.springframework.ai.parser.BeanOutputParser; import org.springframework.ai.parser.ListOutputParser; import org.springframework.ai.parser.MapOutputParser; @@ -47,7 +47,7 @@ class VertexAiChatGenerationClientIT { SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource); Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice)); Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); - AiResponse response = client.generate(prompt); + ChatResponse response = client.generate(prompt); assertThat(response.getGeneration().getContent()).contains("Bartholomew"); }