From 19e61bf71a0c11861cce9dd21295c3a9ce284b8a Mon Sep 17 00:00:00 2001 From: Alexandros Pappas Date: Mon, 16 Dec 2024 17:52:07 +0100 Subject: [PATCH] Refactor ChatResponseMetadata Builder methods - Refactor builder methods in `ChatResponseMetadata` to remove `with` prefixes and introduce new methods with updated naming conventions. - Deprecate existing `with*` methods while maintaining backward compatibility. - Ensure dependent files and tests are compatible with the updated `ChatResponseMetadata` class. --- .../ai/anthropic/AnthropicChatModel.java | 13 ++-- .../ai/azure/openai/AzureOpenAiChatModel.java | 21 +++--- .../converse/BedrockProxyChatModel.java | 5 +- .../converse/api/ConverseApiUtils.java | 15 ++-- .../BedrockAnthropic3ChatModel.java | 7 +- .../ai/minimax/MiniMaxChatModel.java | 11 +-- .../ai/mistralai/MistralAiChatModel.java | 17 ++--- .../ai/moonshot/MoonshotChatModel.java | 8 +-- .../ai/oci/cohere/OCICohereChatModel.java | 5 +- .../ai/ollama/OllamaChatModel.java | 21 +++--- .../ai/ollama/OllamaChatModelTests.java | 7 +- .../ai/openai/OpenAiChatModel.java | 21 +++--- .../ai/qianfan/QianFanChatModel.java | 9 +-- .../gemini/VertexAiGeminiChatModel.java | 3 +- .../ai/zhipuai/ZhiPuAiChatModel.java | 11 +-- .../chat/metadata/ChatResponseMetadata.java | 71 +++++++++++++++++-- .../ai/chat/model/ChatResponse.java | 22 +++--- .../ai/chat/model/MessageAggregator.java | 11 +-- .../chat/client/ChatClientAdvisorTests.java | 11 +-- .../client/ChatClientResponseEntityTests.java | 3 +- .../advisor/QuestionAnswerAdvisorTests.java | 12 +--- ...ChatModelMeterObservationHandlerTests.java | 3 +- ...ltChatModelObservationConventionTests.java | 9 +-- 23 files changed, 194 insertions(+), 122 deletions(-) diff --git a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java index babf9a85c..8a20272db 100644 --- a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java +++ b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java @@ -81,6 +81,7 @@ import org.springframework.util.StringUtils; * @author Mariusz Bernacki * @author Thomas Vitale * @author Claudio Silva Junior + * @author Alexandros Pappas * @since 1.0.0 */ public class AnthropicChatModel extends AbstractToolCallSupport implements ChatModel { @@ -357,12 +358,12 @@ public class AnthropicChatModel extends AbstractToolCallSupport implements ChatM private ChatResponseMetadata from(AnthropicApi.ChatCompletionResponse result, Usage usage) { Assert.notNull(result, "Anthropic ChatCompletionResult must not be null"); return ChatResponseMetadata.builder() - .withId(result.id()) - .withModel(result.model()) - .withUsage(usage) - .withKeyValue("stop-reason", result.stopReason()) - .withKeyValue("stop-sequence", result.stopSequence()) - .withKeyValue("type", result.type()) + .id(result.id()) + .model(result.model()) + .usage(usage) + .keyValue("stop-reason", result.stopReason()) + .keyValue("stop-sequence", result.stopSequence()) + .keyValue("type", result.type()) .build(); } diff --git a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java index 48eb26afd..6660dbebf 100644 --- a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java +++ b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java @@ -108,6 +108,7 @@ import org.springframework.util.CollectionUtils; * @author Soby Chacko * @author Jihoon Kim * @author Ilayaperumal Gopinathan + * @author Alexandros Pappas * @see ChatModel * @see com.azure.ai.openai.OpenAIClient * @since 1.0.0 @@ -184,11 +185,11 @@ public class AzureOpenAiChatModel extends AbstractToolCallSupport implements Cha Assert.notNull(chatCompletions, "Azure OpenAI ChatCompletions must not be null"); String id = chatCompletions.getId(); return ChatResponseMetadata.builder() - .withId(id) - .withUsage(usage) - .withModel(chatCompletions.getModel()) - .withPromptMetadata(promptFilterMetadata) - .withKeyValue("system-fingerprint", chatCompletions.getSystemFingerprint()) + .id(id) + .usage(usage) + .model(chatCompletions.getModel()) + .promptMetadata(promptFilterMetadata) + .keyValue("system-fingerprint", chatCompletions.getSystemFingerprint()) .build(); } @@ -206,12 +207,12 @@ public class AzureOpenAiChatModel extends AbstractToolCallSupport implements Cha Assert.notNull(chatResponse, "ChatResponse must not be null"); ChatResponseMetadata chatResponseMetadata = chatResponse.getMetadata(); ChatResponseMetadata.Builder builder = ChatResponseMetadata.builder(); - builder.withId(chatResponseMetadata.getId()) - .withUsage(usage) - .withModel(chatResponseMetadata.getModel()) - .withPromptMetadata(chatResponseMetadata.getPromptMetadata()); + builder.id(chatResponseMetadata.getId()) + .usage(usage) + .model(chatResponseMetadata.getModel()) + .promptMetadata(chatResponseMetadata.getPromptMetadata()); if (chatResponseMetadata.containsKey("system-fingerprint")) { - builder.withKeyValue("system-fingerprint", chatResponseMetadata.get("system-fingerprint")); + builder.keyValue("system-fingerprint", chatResponseMetadata.get("system-fingerprint")); } return builder.build(); } diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java index 116093b09..bec449ee9 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java @@ -128,6 +128,7 @@ import org.springframework.util.StringUtils; * * @author Christian Tzolov * @author Wei Jiang + * @author Alexandros Pappas * @since 1.0.0 */ public class BedrockProxyChatModel extends AbstractToolCallSupport implements ChatModel { @@ -564,8 +565,8 @@ public class BedrockProxyChatModel extends AbstractToolCallSupport implements Ch ConverseMetrics metrics = response.metrics(); var chatResponseMetaData = ChatResponseMetadata.builder() - .withId(response.responseMetadata() != null ? response.responseMetadata().requestId() : "Unknown") - .withUsage(usage) + .id(response.responseMetadata() != null ? response.responseMetadata().requestId() : "Unknown") + .usage(usage) .build(); return new ChatResponse(allGenerations, chatResponseMetaData); diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java index 88047c0cc..84f7cfde4 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java @@ -62,6 +62,7 @@ import org.springframework.util.StringUtils; * * @author Wei Jiang * @author Christian Tzolov + * @author Alexandros Pappas * @since 1.0.0 */ public final class ConverseApiUtils { @@ -144,7 +145,7 @@ public final class ConverseApiUtils { ChatGenerationMetadata.builder().finishReason("tool_use").build()); var chatResponseMetaData = ChatResponseMetadata.builder() - .withUsage(new DefaultUsage(promptTokens, generationTokens, totalTokens)) + .usage(new DefaultUsage(promptTokens, generationTokens, totalTokens)) .build(); return new Aggregation( @@ -210,7 +211,7 @@ public final class ConverseApiUtils { metadataEvent.usage().outputTokens().longValue(), metadataEvent.usage().totalTokens().longValue()); - var chatResponseMetaData = ChatResponseMetadata.builder().withUsage(usage).build(); + var chatResponseMetaData = ChatResponseMetadata.builder().usage(usage).build(); return new Aggregation(newMeta, new ChatResponse(List.of(), chatResponseMetaData)); } @@ -235,10 +236,10 @@ public final class ConverseApiUtils { Long totalTokens = perviousChatResponse.getMetadata().getUsage().getTotalTokens(); if (chatResponse.getMetadata() != null) { - metadataBuilder.withId(chatResponse.getMetadata().getId()); - metadataBuilder.withModel(chatResponse.getMetadata().getModel()); - metadataBuilder.withRateLimit(chatResponse.getMetadata().getRateLimit()); - metadataBuilder.withPromptMetadata(chatResponse.getMetadata().getPromptMetadata()); + metadataBuilder.id(chatResponse.getMetadata().getId()); + metadataBuilder.model(chatResponse.getMetadata().getModel()); + metadataBuilder.rateLimit(chatResponse.getMetadata().getRateLimit()); + metadataBuilder.promptMetadata(chatResponse.getMetadata().getPromptMetadata()); if (chatResponse.getMetadata().getUsage() != null) { promptTokens = promptTokens + chatResponse.getMetadata().getUsage().getPromptTokens(); @@ -248,7 +249,7 @@ public final class ConverseApiUtils { } } - metadataBuilder.withUsage(new DefaultUsage(promptTokens, generationTokens, totalTokens)); + metadataBuilder.usage(new DefaultUsage(promptTokens, generationTokens, totalTokens)); return new ChatResponse(chatResponse.getResults(), metadataBuilder.build()); } diff --git a/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic3/BedrockAnthropic3ChatModel.java b/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic3/BedrockAnthropic3ChatModel.java index c000a88b1..29b1957be 100644 --- a/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic3/BedrockAnthropic3ChatModel.java +++ b/models/spring-ai-bedrock/src/main/java/org/springframework/ai/bedrock/anthropic3/BedrockAnthropic3ChatModel.java @@ -55,6 +55,7 @@ import org.springframework.util.CollectionUtils; * @author Ben Middleton * @author Christian Tzolov * @author Wei Jiang + * @author Alexandros Pappas * @since 1.0.0 */ public class BedrockAnthropic3ChatModel implements ChatModel, StreamingChatModel { @@ -92,9 +93,9 @@ public class BedrockAnthropic3ChatModel implements ChatModel, StreamingChatModel .toList(); ChatResponseMetadata metadata = ChatResponseMetadata.builder() - .withId(response.id()) - .withModel(response.model()) - .withUsage(extractUsage(response)) + .id(response.id()) + .model(response.model()) + .usage(extractUsage(response)) .build(); return new ChatResponse(generations, metadata); diff --git a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatModel.java b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatModel.java index d94ac1716..3c7eb1fff 100644 --- a/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatModel.java +++ b/models/spring-ai-minimax/src/main/java/org/springframework/ai/minimax/MiniMaxChatModel.java @@ -76,6 +76,7 @@ import org.springframework.util.CollectionUtils; * backed by {@link MiniMaxApi}. * * @author Geng Rong + * @author Alexandros Pappas * @see ChatModel * @see StreamingChatModel * @see MiniMaxApi @@ -386,11 +387,11 @@ public class MiniMaxChatModel extends AbstractToolCallSupport implements ChatMod private ChatResponseMetadata from(ChatCompletion result) { Assert.notNull(result, "MiniMax ChatCompletionResult must not be null"); return ChatResponseMetadata.builder() - .withId(result.id() != null ? result.id() : "") - .withUsage(result.usage() != null ? MiniMaxUsage.from(result.usage()) : new EmptyUsage()) - .withModel(result.model() != null ? result.model() : "") - .withKeyValue("created", result.created() != null ? result.created() : 0L) - .withKeyValue("system-fingerprint", result.systemFingerprint() != null ? result.systemFingerprint() : "") + .id(result.id() != null ? result.id() : "") + .usage(result.usage() != null ? MiniMaxUsage.from(result.usage()) : new EmptyUsage()) + .model(result.model() != null ? result.model() : "") + .keyValue("created", result.created() != null ? result.created() : 0L) + .keyValue("system-fingerprint", result.systemFingerprint() != null ? result.systemFingerprint() : "") .build(); } diff --git a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java index 87634bb99..a2a4d7ab6 100644 --- a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java +++ b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java @@ -77,6 +77,7 @@ import org.springframework.util.CollectionUtils; * @author Thomas Vitale * @author luocongqiu * @author Ilayaperumal Gopinathan + * @author Alexandros Pappas * @since 1.0.0 */ public class MistralAiChatModel extends AbstractToolCallSupport implements ChatModel { @@ -151,20 +152,20 @@ public class MistralAiChatModel extends AbstractToolCallSupport implements ChatM Assert.notNull(result, "Mistral AI ChatCompletion must not be null"); MistralAiUsage usage = MistralAiUsage.from(result.usage()); return ChatResponseMetadata.builder() - .withId(result.id()) - .withModel(result.model()) - .withUsage(usage) - .withKeyValue("created", result.created()) + .id(result.id()) + .model(result.model()) + .usage(usage) + .keyValue("created", result.created()) .build(); } public static ChatResponseMetadata from(MistralAiApi.ChatCompletion result, Usage usage) { Assert.notNull(result, "Mistral AI ChatCompletion must not be null"); return ChatResponseMetadata.builder() - .withId(result.id()) - .withModel(result.model()) - .withUsage(usage) - .withKeyValue("created", result.created()) + .id(result.id()) + .model(result.model()) + .usage(usage) + .keyValue("created", result.created()) .build(); } diff --git a/models/spring-ai-moonshot/src/main/java/org/springframework/ai/moonshot/MoonshotChatModel.java b/models/spring-ai-moonshot/src/main/java/org/springframework/ai/moonshot/MoonshotChatModel.java index e94430ad8..0f41afbdd 100644 --- a/models/spring-ai-moonshot/src/main/java/org/springframework/ai/moonshot/MoonshotChatModel.java +++ b/models/spring-ai-moonshot/src/main/java/org/springframework/ai/moonshot/MoonshotChatModel.java @@ -318,10 +318,10 @@ public class MoonshotChatModel extends AbstractToolCallSupport implements ChatMo private ChatResponseMetadata from(ChatCompletion result) { Assert.notNull(result, "Moonshot ChatCompletionResult must not be null"); return ChatResponseMetadata.builder() - .withId(result.id() != null ? result.id() : "") - .withUsage(result.usage() != null ? MoonshotUsage.from(result.usage()) : new EmptyUsage()) - .withModel(result.model() != null ? result.model() : "") - .withKeyValue("created", result.created() != null ? result.created() : 0L) + .id(result.id() != null ? result.id() : "") + .usage(result.usage() != null ? MoonshotUsage.from(result.usage()) : new EmptyUsage()) + .model(result.model() != null ? result.model() : "") + .keyValue("created", result.created() != null ? result.created() : 0L) .build(); } diff --git a/models/spring-ai-oci-genai/src/main/java/org/springframework/ai/oci/cohere/OCICohereChatModel.java b/models/spring-ai-oci-genai/src/main/java/org/springframework/ai/oci/cohere/OCICohereChatModel.java index 236b459d5..d3a23b5d9 100644 --- a/models/spring-ai-oci-genai/src/main/java/org/springframework/ai/oci/cohere/OCICohereChatModel.java +++ b/models/spring-ai-oci-genai/src/main/java/org/springframework/ai/oci/cohere/OCICohereChatModel.java @@ -62,6 +62,7 @@ import org.springframework.util.StringUtils; * {@link ChatModel} implementation that uses the OCI GenAI Chat API. * * @author Anders Swanson + * @author Alexandros Pappas * @since 1.0.0 */ public class OCICohereChatModel implements ChatModel { @@ -138,8 +139,8 @@ public class OCICohereChatModel implements ChatModel { validateChatOptions(options); ChatResponseMetadata metadata = ChatResponseMetadata.builder() - .withModel(options.getModel()) - .withKeyValue("compartment", options.getCompartment()) + .model(options.getModel()) + .keyValue("compartment", options.getCompartment()) .build(); return new ChatResponse(getGenerations(prompt, options), metadata); diff --git a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java index fd2258bcb..3725fecfe 100644 --- a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java +++ b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java @@ -76,6 +76,7 @@ import org.springframework.util.StringUtils; * @author luocongqiu * @author Thomas Vitale * @author Jihoon Kim + * @author Alexandros Pappas * @since 1.0.0 */ public class OllamaChatModel extends AbstractToolCallSupport implements ChatModel { @@ -164,16 +165,16 @@ public class OllamaChatModel extends AbstractToolCallSupport implements ChatMode DefaultUsage aggregatedUsage = new DefaultUsage(promptTokens, generationTokens, totalTokens); return ChatResponseMetadata.builder() - .withUsage(aggregatedUsage) - .withModel(response.model()) - .withKeyValue(METADATA_CREATED_AT, response.createdAt()) - .withKeyValue(METADATA_EVAL_DURATION, evalDuration) - .withKeyValue(METADATA_EVAL_COUNT, aggregatedUsage.getGenerationTokens().intValue()) - .withKeyValue(METADATA_LOAD_DURATION, loadDuration) - .withKeyValue(METADATA_PROMPT_EVAL_DURATION, promptEvalDuration) - .withKeyValue(METADATA_PROMPT_EVAL_COUNT, aggregatedUsage.getPromptTokens().intValue()) - .withKeyValue(METADATA_TOTAL_DURATION, totalDuration) - .withKeyValue(DONE, response.done()) + .usage(aggregatedUsage) + .model(response.model()) + .keyValue(METADATA_CREATED_AT, response.createdAt()) + .keyValue(METADATA_EVAL_DURATION, evalDuration) + .keyValue(METADATA_EVAL_COUNT, aggregatedUsage.getGenerationTokens().intValue()) + .keyValue(METADATA_LOAD_DURATION, loadDuration) + .keyValue(METADATA_PROMPT_EVAL_DURATION, promptEvalDuration) + .keyValue(METADATA_PROMPT_EVAL_COUNT, aggregatedUsage.getPromptTokens().intValue()) + .keyValue(METADATA_TOTAL_DURATION, totalDuration) + .keyValue(DONE, response.done()) .build(); } diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelTests.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelTests.java index efe6da360..d897c6da7 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelTests.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelTests.java @@ -39,6 +39,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; /** * @author Jihoon Kim * @author Christian Tzolov + * @author Alexandros Pappas * @since 1.0.0 */ @ExtendWith(MockitoExtension.class) @@ -99,9 +100,9 @@ public class OllamaChatModelTests { ChatResponse previousChatResponse = ChatResponse.builder() .generations(List.of()) .metadata(ChatResponseMetadata.builder() - .withUsage(new DefaultUsage(66L, 99L)) - .withKeyValue("eval-duration", Duration.ofSeconds(2)) - .withKeyValue("prompt-eval-duration", Duration.ofSeconds(2)) + .usage(new DefaultUsage(66L, 99L)) + .keyValue("eval-duration", Duration.ofSeconds(2)) + .keyValue("prompt-eval-duration", Duration.ofSeconds(2)) .build()) .build(); diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java index 2e574e642..95f15d63a 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java @@ -101,6 +101,7 @@ import org.springframework.util.StringUtils; * @author luocongqiu * @author Thomas Vitale * @author Ilayaperumal Gopinathan + * @author Alexandros Pappas * @see ChatModel * @see StreamingChatModel * @see OpenAiApi @@ -461,13 +462,13 @@ public class OpenAiChatModel extends AbstractToolCallSupport implements ChatMode private ChatResponseMetadata from(OpenAiApi.ChatCompletion result, RateLimit rateLimit, Usage usage) { Assert.notNull(result, "OpenAI ChatCompletionResult must not be null"); var builder = ChatResponseMetadata.builder() - .withId(result.id() != null ? result.id() : "") - .withUsage(usage) - .withModel(result.model() != null ? result.model() : "") - .withKeyValue("created", result.created() != null ? result.created() : 0L) - .withKeyValue("system-fingerprint", result.systemFingerprint() != null ? result.systemFingerprint() : ""); + .id(result.id() != null ? result.id() : "") + .usage(usage) + .model(result.model() != null ? result.model() : "") + .keyValue("created", result.created() != null ? result.created() : 0L) + .keyValue("system-fingerprint", result.systemFingerprint() != null ? result.systemFingerprint() : ""); if (rateLimit != null) { - builder.withRateLimit(rateLimit); + builder.rateLimit(rateLimit); } return builder.build(); } @@ -475,11 +476,11 @@ public class OpenAiChatModel extends AbstractToolCallSupport implements ChatMode private ChatResponseMetadata from(ChatResponseMetadata chatResponseMetadata, Usage usage) { Assert.notNull(chatResponseMetadata, "OpenAI ChatResponseMetadata must not be null"); var builder = ChatResponseMetadata.builder() - .withId(chatResponseMetadata.getId() != null ? chatResponseMetadata.getId() : "") - .withUsage(usage) - .withModel(chatResponseMetadata.getModel() != null ? chatResponseMetadata.getModel() : ""); + .id(chatResponseMetadata.getId() != null ? chatResponseMetadata.getId() : "") + .usage(usage) + .model(chatResponseMetadata.getModel() != null ? chatResponseMetadata.getModel() : ""); if (chatResponseMetadata.getRateLimit() != null) { - builder.withRateLimit(chatResponseMetadata.getRateLimit()); + builder.rateLimit(chatResponseMetadata.getRateLimit()); } return builder.build(); } diff --git a/models/spring-ai-qianfan/src/main/java/org/springframework/ai/qianfan/QianFanChatModel.java b/models/spring-ai-qianfan/src/main/java/org/springframework/ai/qianfan/QianFanChatModel.java index a122480f7..e76e76b06 100644 --- a/models/spring-ai-qianfan/src/main/java/org/springframework/ai/qianfan/QianFanChatModel.java +++ b/models/spring-ai-qianfan/src/main/java/org/springframework/ai/qianfan/QianFanChatModel.java @@ -64,6 +64,7 @@ import org.springframework.util.Assert; * @see ChatModel * @see StreamingChatModel * @see QianFanApi + * @author Alexandros Pappas * @since 1.0 */ public class QianFanChatModel implements ChatModel, StreamingChatModel { @@ -290,10 +291,10 @@ public class QianFanChatModel implements ChatModel, StreamingChatModel { private ChatResponseMetadata from(QianFanApi.ChatCompletion result, String model) { Assert.notNull(result, "QianFan ChatCompletionResult must not be null"); return ChatResponseMetadata.builder() - .withId(result.id() != null ? result.id() : "") - .withUsage(result.usage() != null ? QianFanUsage.from(result.usage()) : new EmptyUsage()) - .withModel(model) - .withKeyValue("created", result.created() != null ? result.created() : 0L) + .id(result.id() != null ? result.id() : "") + .usage(result.usage() != null ? QianFanUsage.from(result.usage()) : new EmptyUsage()) + .model(model) + .keyValue("created", result.created() != null ? result.created() : 0L) .build(); } diff --git a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java index 110b8c7ee..3debc60ba 100644 --- a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java +++ b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java @@ -95,6 +95,7 @@ import org.springframework.util.StringUtils; * @author Mark Pollack * @author Soby Chacko * @author Jihoon Kim + * @author Alexandros Pappas * @since 0.8.1 */ public class VertexAiGeminiChatModel extends AbstractToolCallSupport implements ChatModel, DisposableBean { @@ -427,7 +428,7 @@ public class VertexAiGeminiChatModel extends AbstractToolCallSupport implements } private ChatResponseMetadata toChatResponseMetadata(GenerateContentResponse response) { - return ChatResponseMetadata.builder().withUsage(new VertexAiUsage(response.getUsageMetadata())).build(); + return ChatResponseMetadata.builder().usage(new VertexAiUsage(response.getUsageMetadata())).build(); } private VertexAiGeminiChatOptions vertexAiGeminiChatOptions(Prompt prompt) { diff --git a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java index 3c9163d0f..c85486471 100644 --- a/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java +++ b/models/spring-ai-zhipuai/src/main/java/org/springframework/ai/zhipuai/ZhiPuAiChatModel.java @@ -83,6 +83,7 @@ import org.springframework.util.MimeType; * @see ChatModel * @see StreamingChatModel * @see ZhiPuAiApi + * @author Alexandros Pappas * @since 1.0.0 M1 */ public class ZhiPuAiChatModel extends AbstractToolCallSupport implements ChatModel, StreamingChatModel { @@ -324,11 +325,11 @@ public class ZhiPuAiChatModel extends AbstractToolCallSupport implements ChatMod private ChatResponseMetadata from(ChatCompletion result) { Assert.notNull(result, "ZhiPuAI ChatCompletionResult must not be null"); return ChatResponseMetadata.builder() - .withId(result.id() != null ? result.id() : "") - .withUsage(result.usage() != null ? ZhiPuAiUsage.from(result.usage()) : new EmptyUsage()) - .withModel(result.model() != null ? result.model() : "") - .withKeyValue("created", result.created() != null ? result.created() : 0L) - .withKeyValue("system-fingerprint", result.systemFingerprint() != null ? result.systemFingerprint() : "") + .id(result.id() != null ? result.id() : "") + .usage(result.usage() != null ? ZhiPuAiUsage.from(result.usage()) : new EmptyUsage()) + .model(result.model() != null ? result.model() : "") + .keyValue("created", result.created() != null ? result.created() : 0L) + .keyValue("system-fingerprint", result.systemFingerprint() != null ? result.systemFingerprint() : "") .build(); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/metadata/ChatResponseMetadata.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/metadata/ChatResponseMetadata.java index 20126472b..f9969d9c2 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/metadata/ChatResponseMetadata.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/metadata/ChatResponseMetadata.java @@ -31,6 +31,7 @@ import org.springframework.ai.model.ResponseMetadata; * @author John Blum * @author Thomas Vitale * @author Mark Pollack + * @author Alexandros Pappas * @since 1.0.0 */ public class ChatResponseMetadata extends AbstractResponseMetadata implements ResponseMetadata { @@ -126,12 +127,12 @@ public class ChatResponseMetadata extends AbstractResponseMetadata implements Re this.chatResponseMetadata = new ChatResponseMetadata(); } - public Builder withMetadata(Map mapToCopy) { + public Builder metadata(Map mapToCopy) { this.chatResponseMetadata.map.putAll(mapToCopy); return this; } - public Builder withKeyValue(String key, Object value) { + public Builder keyValue(String key, Object value) { if (key == null) { throw new IllegalArgumentException("Key must not be null"); } @@ -144,31 +145,87 @@ public class ChatResponseMetadata extends AbstractResponseMetadata implements Re return this; } - public Builder withId(String id) { + public Builder id(String id) { this.chatResponseMetadata.id = id; return this; } - public Builder withModel(String model) { + public Builder model(String model) { this.chatResponseMetadata.model = model; return this; } - public Builder withRateLimit(RateLimit rateLimit) { + public Builder rateLimit(RateLimit rateLimit) { this.chatResponseMetadata.rateLimit = rateLimit; return this; } - public Builder withUsage(Usage usage) { + public Builder usage(Usage usage) { this.chatResponseMetadata.usage = usage; return this; } - public Builder withPromptMetadata(PromptMetadata promptMetadata) { + public Builder promptMetadata(PromptMetadata promptMetadata) { this.chatResponseMetadata.promptMetadata = promptMetadata; return this; } + /** + * @deprecated use {@link #metadata(Map)} instead. + */ + @Deprecated(forRemoval = true, since = "1.0.0-M5") + public Builder wihtMetadata(Map mapToCopy) { + return metadata(mapToCopy); + } + + /** + * @deprecated use {@link #keyValue(String, Object)} instead. + */ + @Deprecated(forRemoval = true, since = "1.0.0-M5") + public Builder withKeyValue(String key, Object value) { + return keyValue(key, value); + } + + /** + * @deprecated use {@link #id(String)} instead. + */ + @Deprecated(forRemoval = true, since = "1.0.0-M5") + public Builder withId(String id) { + return id(id); + } + + /** + * @deprecated use {@link #model(String)} instead. + */ + @Deprecated(forRemoval = true, since = "1.0.0-M5") + public Builder withModel(String model) { + return model(model); + } + + /** + * @deprecated use {@link #rateLimit(RateLimit)} instead. + */ + @Deprecated(forRemoval = true, since = "1.0.0-M5") + public Builder withRateLimit(RateLimit rateLimit) { + return rateLimit(rateLimit); + } + + /** + * @deprecated use {@link #usage(Usage)} instead. + */ + @Deprecated(forRemoval = true, since = "1.0.0-M5") + public Builder withUsage(Usage usage) { + return usage(usage); + } + + /** + * @deprecated use {@link #promptMetadata(PromptMetadata)} instead. + */ + @Deprecated(forRemoval = true, since = "1.0.0-M5") + public Builder withPromptMetadata(PromptMetadata promptMetadata) { + return promptMetadata(promptMetadata); + } + public ChatResponseMetadata build() { return this.chatResponseMetadata; } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/model/ChatResponse.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/model/ChatResponse.java index 8346daa11..16e19053e 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/model/ChatResponse.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/model/ChatResponse.java @@ -27,6 +27,12 @@ import org.springframework.util.CollectionUtils; /** * The chat completion (e.g. generation) response returned by an AI provider. + * + * @author Christian Tzolov + * @author Mark Pollack + * @author Soby Chacko + * @author John Blum + * @author Alexandros Pappas */ public class ChatResponse implements ModelResponse { @@ -136,24 +142,24 @@ public class ChatResponse implements ModelResponse { */ @Deprecated public Builder withMetadata(String key, Object value) { - this.chatResponseMetadataBuilder.withKeyValue(key, value); + this.chatResponseMetadataBuilder.keyValue(key, value); return this; } public Builder metadata(String key, Object value) { - this.chatResponseMetadataBuilder.withKeyValue(key, value); + this.chatResponseMetadataBuilder.keyValue(key, value); return this; } public Builder metadata(ChatResponseMetadata other) { - this.chatResponseMetadataBuilder.withModel(other.getModel()); - this.chatResponseMetadataBuilder.withId(other.getId()); - this.chatResponseMetadataBuilder.withRateLimit(other.getRateLimit()); - this.chatResponseMetadataBuilder.withUsage(other.getUsage()); - this.chatResponseMetadataBuilder.withPromptMetadata(other.getPromptMetadata()); + this.chatResponseMetadataBuilder.model(other.getModel()); + this.chatResponseMetadataBuilder.id(other.getId()); + this.chatResponseMetadataBuilder.rateLimit(other.getRateLimit()); + this.chatResponseMetadataBuilder.usage(other.getUsage()); + this.chatResponseMetadataBuilder.promptMetadata(other.getPromptMetadata()); Set> entries = other.entrySet(); for (Map.Entry entry : entries) { - this.chatResponseMetadataBuilder.withKeyValue(entry.getKey(), entry.getValue()); + this.chatResponseMetadataBuilder.keyValue(entry.getKey(), entry.getValue()); } return this; } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/model/MessageAggregator.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/model/MessageAggregator.java index d3f57f270..965b2f6d6 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/model/MessageAggregator.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/model/MessageAggregator.java @@ -41,6 +41,7 @@ import org.springframework.util.StringUtils; * single AssistantMessage. Job is performed in parallel to the chat response processing. * * @author Christian Tzolov + * @author Alexandros Pappas * @since 1.0.0 */ public class MessageAggregator { @@ -146,11 +147,11 @@ public class MessageAggregator { metadataUsageTotalTokensRef.get()); var chatResponseMetadata = ChatResponseMetadata.builder() - .withId(metadataIdRef.get()) - .withModel(metadataModelRef.get()) - .withRateLimit(metadataRateLimitRef.get()) - .withUsage(usage) - .withPromptMetadata(metadataPromptMetadataRef.get()) + .id(metadataIdRef.get()) + .model(metadataModelRef.get()) + .rateLimit(metadataRateLimitRef.get()) + .usage(usage) + .promptMetadata(metadataPromptMetadataRef.get()) .build(); onAggregationComplete.accept(new ChatResponse(List.of(new Generation( diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientAdvisorTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientAdvisorTests.java index cbfc57910..bebe31b8b 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientAdvisorTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientAdvisorTests.java @@ -45,6 +45,7 @@ import static org.mockito.BDDMockito.given; /** * @author Christian Tzolov + * @author Alexandros Pappas */ @ExtendWith(MockitoExtension.class) public class ChatClientAdvisorTests { @@ -63,11 +64,11 @@ public class ChatClientAdvisorTests { public void promptChatMemory() { var builder = ChatResponseMetadata.builder() - .withId("124") - .withUsage(new MessageAggregator.DefaultUsage(1, 2, 3)) - .withModel("gpt4o") - .withKeyValue("created", 0L) - .withKeyValue("system-fingerprint", "john doe"); + .id("124") + .usage(new MessageAggregator.DefaultUsage(1, 2, 3)) + .model("gpt4o") + .keyValue("created", 0L) + .keyValue("system-fingerprint", "john doe"); ChatResponseMetadata chatResponseMetadata = builder.build(); given(this.chatModel.call(this.promptCaptor.capture())) diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientResponseEntityTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientResponseEntityTests.java index 4dfc4a0a8..07fb07d34 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientResponseEntityTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientResponseEntityTests.java @@ -42,6 +42,7 @@ import static org.mockito.BDDMockito.given; /** * @author Christian Tzolov + * @author Alexandros Pappas */ @ExtendWith(MockitoExtension.class) public class ChatClientResponseEntityTests { @@ -55,7 +56,7 @@ public class ChatClientResponseEntityTests { @Test public void responseEntityTest() { - ChatResponseMetadata metadata = ChatResponseMetadata.builder().withKeyValue("key1", "value1").build(); + ChatResponseMetadata metadata = ChatResponseMetadata.builder().keyValue("key1", "value1").build(); var chatResponse = new ChatResponse(List.of(new Generation(new AssistantMessage(""" {"name":"John", "age":30} diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/advisor/QuestionAnswerAdvisorTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/advisor/QuestionAnswerAdvisorTests.java index 7a02fb004..71b04565b 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/advisor/QuestionAnswerAdvisorTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/advisor/QuestionAnswerAdvisorTests.java @@ -50,6 +50,7 @@ import static org.mockito.BDDMockito.given; /** * @author Christian Tzolov * @author Timo Salm + * @author Alexandros Pappas */ @ExtendWith(MockitoExtension.class) public class QuestionAnswerAdvisorTests { @@ -72,13 +73,7 @@ public class QuestionAnswerAdvisorTests { // @formatter:off given(this.chatModel.call(this.promptCaptor.capture())) .willReturn(new ChatResponse(List.of(new Generation(new AssistantMessage("Your answer is ZXY"))), - ChatResponseMetadata.builder() - .withId("678") - .withModel("model1") - .withKeyValue("key6", "value6") - .withMetadata(Map.of("key1", "value1")) - .withPromptMetadata(null) - .withRateLimit(new RateLimit() { + ChatResponseMetadata.builder().id("678").model("model1").keyValue("key6", "value6").metadata(Map.of("key1", "value1")).promptMetadata(null).rateLimit(new RateLimit() { @Override public Long getRequestsLimit() { @@ -109,8 +104,7 @@ public class QuestionAnswerAdvisorTests { public Duration getTokensReset() { return Duration.ofSeconds(9); } - }) - .withUsage(new DefaultUsage(6L, 7L)) + }).usage(new DefaultUsage(6L, 7L)) .build())); // @formatter:on diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/observation/ChatModelMeterObservationHandlerTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/observation/ChatModelMeterObservationHandlerTests.java index ceea12131..5112ed627 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/chat/observation/ChatModelMeterObservationHandlerTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/observation/ChatModelMeterObservationHandlerTests.java @@ -44,6 +44,7 @@ import static org.springframework.ai.chat.observation.ChatModelObservationDocume * Unit tests for {@link ChatModelMeterObservationHandler}. * * @author Thomas Vitale + * @author Alexandros Pappas */ class ChatModelMeterObservationHandlerTests { @@ -68,7 +69,7 @@ class ChatModelMeterObservationHandlerTests { .start(); observationContext.setResponse(new ChatResponse(List.of(new Generation(new AssistantMessage("test"))), - ChatResponseMetadata.builder().withModel("mistral-42").withUsage(new TestUsage()).build())); + ChatResponseMetadata.builder().model("mistral-42").usage(new TestUsage()).build())); observation.stop(); diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/observation/DefaultChatModelObservationConventionTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/observation/DefaultChatModelObservationConventionTests.java index dd4840d07..f847088e1 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/chat/observation/DefaultChatModelObservationConventionTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/observation/DefaultChatModelObservationConventionTests.java @@ -39,6 +39,7 @@ import static org.springframework.ai.chat.observation.ChatModelObservationDocume * Unit tests for {@link DefaultChatModelObservationConvention}. * * @author Thomas Vitale + * @author Alexandros Pappas */ class DefaultChatModelObservationConventionTests { @@ -112,11 +113,7 @@ class DefaultChatModelObservationConventionTests { observationContext.setResponse(new ChatResponse( List.of(new Generation(new AssistantMessage("response"), ChatGenerationMetadata.builder().finishReason("this-is-the-end").build())), - ChatResponseMetadata.builder() - .withId("say33") - .withModel("mistral-42") - .withUsage(new TestUsage()) - .build())); + ChatResponseMetadata.builder().id("say33").model("mistral-42").usage(new TestUsage()).build())); assertThat(this.observationConvention.getLowCardinalityKeyValues(observationContext)) .contains(KeyValue.of(LowCardinalityKeyNames.RESPONSE_MODEL.asString(), "mistral-42")); assertThat(this.observationConvention.getHighCardinalityKeyValues(observationContext)).contains( @@ -170,7 +167,7 @@ class DefaultChatModelObservationConventionTests { observationContext.setResponse(new ChatResponse( List.of(new Generation(new AssistantMessage("response"), ChatGenerationMetadata.builder().finishReason("").build())), - ChatResponseMetadata.builder().withId("").build())); + ChatResponseMetadata.builder().id("").build())); assertThat(this.observationConvention.getHighCardinalityKeyValues(observationContext) .stream() .map(KeyValue::getKey)