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.
This commit is contained in:
Alexandros Pappas
2024-12-16 17:52:07 +01:00
committed by Ilayaperumal Gopinathan
parent 8c51f7a84c
commit 19e61bf71a
23 changed files with 194 additions and 122 deletions

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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());
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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();
}

View File

@@ -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<String, Object> mapToCopy) {
public Builder metadata(Map<String, Object> 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<String, Object> 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;
}

View File

@@ -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<Generation> {
@@ -136,24 +142,24 @@ public class ChatResponse implements ModelResponse<Generation> {
*/
@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<Map.Entry<String, Object>> entries = other.entrySet();
for (Map.Entry<String, Object> entry : entries) {
this.chatResponseMetadataBuilder.withKeyValue(entry.getKey(), entry.getValue());
this.chatResponseMetadataBuilder.keyValue(entry.getKey(), entry.getValue());
}
return this;
}

View File

@@ -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(

View File

@@ -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()))

View File

@@ -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}

View File

@@ -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

View File

@@ -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();

View File

@@ -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)