Skip to content

Invalid finish reason when max_tokens is the stop reason for Bedrock Converse Stream #4374

@emopti-jrufer

Description

@emopti-jrufer

Bug description
The finish reason is hard coded to tool_use when that are tool calls even if a stop reason of max_tokens is encountered.

Environment
Spring AI 1.0.1

Steps to reproduce
Set max output tokens to a low value with a prompt that should use a tool call.

Expected behavior
Finish reason should be max_tokens.

Minimal Complete Reproducible example

Index: models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModelIT.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModelIT.java b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModelIT.java
--- a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModelIT.java	(revision c2103b0e6c5030abf28c3a9b088d408bf9af8347)
+++ b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModelIT.java	(date 1757625472524)
@@ -335,6 +335,35 @@
 		assertThat(content).contains("30", "10", "15");
 	}
 
+	@ParameterizedTest(name = "{displayName} - {0} ")
+	@ValueSource(ints = { 50, 200 })
+	void streamFunctionCallTestWithMaxTokens(int maxTokens) {
+
+		UserMessage userMessage = new UserMessage(
+				// "What's the weather like in San Francisco? Return the result in
+				// Celsius.");
+				"What's the weather like in San Francisco, Tokyo and Paris? Return the result in Celsius.");
+
+		List<Message> messages = new ArrayList<>(List.of(userMessage));
+
+		var promptOptions = BedrockChatOptions.builder()
+			.maxTokens(maxTokens)
+			.model("anthropic.claude-3-5-sonnet-20240620-v1:0")
+			.toolCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
+				.description(
+						"Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
+				.inputType(MockWeatherService.Request.class)
+				.build()))
+			.build();
+
+		Flux<ChatResponse> response = this.chatModel.stream(new Prompt(messages, promptOptions));
+		ChatResponse lastResponse = response.blockLast();
+		String finishReason = lastResponse.getResult().getMetadata().getFinishReason();
+
+		logger.info("Finish reason: {}", finishReason);
+		assertThat(finishReason).isEqualTo("max_tokens");
+	}
+
 	@Test
 	void validateCallResponseMetadata() {
 		String model = "anthropic.claude-3-5-sonnet-20240620-v1:0";

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions