Skip to content

Commit c6f2c29

Browse files
committed
Issue 3890
Signed-off-by: Mattia Pasetto <[email protected]>
1 parent c122fe1 commit c6f2c29

File tree

5 files changed

+86
-1
lines changed

5 files changed

+86
-1
lines changed

models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ public class OllamaChatModel implements ChatModel {
113113

114114
private static final String METADATA_EVAL_DURATION = "eval-duration";
115115

116+
private static final String METADATA_SOURCE = "source";
117+
118+
private static final String SOURCE_MODEL = "model";
119+
120+
private static final String SOURCE_TOOL = "tool";
121+
116122
private static final ChatModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultChatModelObservationConvention();
117123

118124
private static final ToolCallingManager DEFAULT_TOOL_CALLING_MANAGER = ToolCallingManager.builder().build();
@@ -264,6 +270,7 @@ private ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespon
264270
if (ollamaResponse.promptEvalCount() != null && ollamaResponse.evalCount() != null) {
265271
generationMetadata = ChatGenerationMetadata.builder()
266272
.finishReason(ollamaResponse.doneReason())
273+
.metadata(METADATA_SOURCE, SOURCE_MODEL)
267274
.build();
268275
}
269276

@@ -340,7 +347,10 @@ private Flux<ChatResponse> internalStream(Prompt prompt, ChatResponse previousCh
340347

341348
ChatGenerationMetadata generationMetadata = ChatGenerationMetadata.NULL;
342349
if (chunk.promptEvalCount() != null && chunk.evalCount() != null) {
343-
generationMetadata = ChatGenerationMetadata.builder().finishReason(chunk.doneReason()).build();
350+
generationMetadata = ChatGenerationMetadata.builder()
351+
.finishReason(chunk.doneReason())
352+
.metadata(METADATA_SOURCE, SOURCE_MODEL)
353+
.build();
344354
}
345355

346356
var generator = new Generation(assistantMessage, generationMetadata);

models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelIT.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,23 @@ void chatMemoryWithTools() {
341341
assertThat(newResponse.getResult().getOutput().getText()).contains("6").contains("8");
342342
}
343343

344+
@Test
345+
void shouldProvideSourceMetadataInRealResponses() {
346+
String userMessageContent = "Hello, can you respond with a simple greeting?";
347+
ChatResponse response = this.chatModel.call(new Prompt(userMessageContent));
348+
349+
assertThat(response).isNotNull();
350+
assertThat(response.getResults()).hasSize(1);
351+
352+
Generation generation = response.getResult();
353+
assertThat(generation).isNotNull();
354+
assertThat(generation.getOutput().getText()).isNotEmpty();
355+
356+
// Verify that source metadata is present and set to "model"
357+
String source = (String) generation.getMetadata().get("source");
358+
assertThat(source).isEqualTo("model");
359+
}
360+
344361
static class MathTools {
345362

346363
@Tool(description = "Multiply the two numbers")

models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,32 @@ void buildChatResponseMetadataAggregationWithNonEmptyMetadataButEmptyEval() {
171171

172172
}
173173

174+
@Test
175+
void shouldAddSourceMetadataForModelResponses() {
176+
// Create a mock OllamaApi.ChatResponse with eval counts to trigger metadata
177+
// creation
178+
Long evalDuration = 1000L;
179+
Integer evalCount = 101;
180+
Integer promptEvalCount = 808;
181+
Long promptEvalDuration = 8L;
182+
Long loadDuration = 100L;
183+
Long totalDuration = 2000L;
184+
185+
OllamaApi.ChatResponse response = new OllamaApi.ChatResponse("model", Instant.now(),
186+
new OllamaApi.Message(OllamaApi.Message.Role.ASSISTANT, "Test response", null, null), "stop", true,
187+
totalDuration, loadDuration, promptEvalCount, promptEvalDuration, evalCount, evalDuration);
188+
189+
ChatResponseMetadata metadata = OllamaChatModel.from(response, null);
190+
191+
// Verify that basic metadata fields are present
192+
assertEquals(Duration.ofNanos(evalDuration), metadata.get("eval-duration"));
193+
assertEquals(evalCount, metadata.get("eval-count"));
194+
195+
// Test that source metadata is NOT added by the from() method (this is handled in
196+
// Generation creation)
197+
// The from() method only creates response-level metadata, not generation-level
198+
// metadata
199+
assertNull(metadata.get("source"));
200+
}
201+
174202
}

spring-ai-model/src/main/java/org/springframework/ai/model/tool/ToolExecutionResult.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ public interface ToolExecutionResult {
3939

4040
String METADATA_TOOL_NAME = "toolName";
4141

42+
String METADATA_SOURCE = "source";
43+
44+
String SOURCE_TOOL = "tool";
45+
4246
/**
4347
* The history of messages exchanged during the conversation, including the tool
4448
* execution result.
@@ -75,6 +79,7 @@ static List<Generation> buildGenerations(ToolExecutionResult toolExecutionResult
7579
ChatGenerationMetadata.builder()
7680
.metadata(METADATA_TOOL_ID, response.id())
7781
.metadata(METADATA_TOOL_NAME, response.name())
82+
.metadata(METADATA_SOURCE, SOURCE_TOOL)
7883
.finishReason(FINISH_REASON)
7984
.build());
8085
generations.add(generation);

spring-ai-model/src/test/java/org/springframework/ai/model/tool/ToolExecutionResultTests.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,29 @@ void whenMultipleToolCallsThenMultipleGenerations() {
8080
assertThat(generations.get(1).getMetadata().getFinishReason()).isEqualTo(ToolExecutionResult.FINISH_REASON);
8181
}
8282

83+
@Test
84+
void shouldAddSourceMetadataForToolResponses() {
85+
var toolExecutionResult = ToolExecutionResult.builder()
86+
.conversationHistory(List.of(new AssistantMessage("Hello, how can I help you?"),
87+
new UserMessage("I would like to know the weather in London"),
88+
new AssistantMessage("Call the weather tool"),
89+
new ToolResponseMessage(List.of(new ToolResponseMessage.ToolResponse("42", "weather",
90+
"The weather in London is 20 degrees Celsius")))))
91+
.build();
92+
93+
var generations = ToolExecutionResult.buildGenerations(toolExecutionResult);
94+
95+
assertThat(generations).hasSize(1);
96+
var generation = generations.get(0);
97+
98+
// Verify existing metadata fields are still present
99+
assertThat((String) generation.getMetadata().get(ToolExecutionResult.METADATA_TOOL_NAME)).isEqualTo("weather");
100+
assertThat((String) generation.getMetadata().get(ToolExecutionResult.METADATA_TOOL_ID)).isEqualTo("42");
101+
assertThat(generation.getMetadata().getFinishReason()).isEqualTo(ToolExecutionResult.FINISH_REASON);
102+
103+
// Verify new source metadata is added
104+
assertThat((String) generation.getMetadata().get(ToolExecutionResult.METADATA_SOURCE))
105+
.isEqualTo(ToolExecutionResult.SOURCE_TOOL);
106+
}
107+
83108
}

0 commit comments

Comments
 (0)