diff --git a/src/strands/event_loop/streaming.py b/src/strands/event_loop/streaming.py index b157f740e..4a4201be4 100644 --- a/src/strands/event_loop/streaming.py +++ b/src/strands/event_loop/streaming.py @@ -411,6 +411,16 @@ async def process_stream( state = handle_content_block_stop(state) elif "messageStop" in chunk: stop_reason = handle_message_stop(chunk["messageStop"]) + # Some models return "end_turn" even when tool calls are present, + # which prevents the event loop from processing those tool calls. + # Override to "tool_use" so tool execution proceeds correctly. + if stop_reason == "end_turn": + content = state["message"].get("content", []) + if any("toolUse" in item for item in content): + logger.warning( + "stop_reason override: end_turn -> tool_use (response contains toolUse blocks)" + ) + stop_reason = "tool_use" elif "metadata" in chunk: time_to_first_byte_ms = ( int(1000 * (first_byte_time - start_time)) if (start_time and first_byte_time) else None diff --git a/src/strands/models/bedrock.py b/src/strands/models/bedrock.py index 3fa907995..fc9a353b6 100644 --- a/src/strands/models/bedrock.py +++ b/src/strands/models/bedrock.py @@ -823,8 +823,6 @@ def _stream( logger.debug("got response from model") if streaming: response = self.client.converse_stream(**request) - # Track tool use events to fix stopReason for streaming responses - has_tool_use = False for chunk in response["stream"]: if ( "metadata" in chunk @@ -836,24 +834,10 @@ def _stream( for event in self._generate_redaction_events(): callback(event) - # Track if we see tool use events - if "contentBlockStart" in chunk and chunk["contentBlockStart"].get("start", {}).get("toolUse"): - has_tool_use = True - - # Fix stopReason for streaming responses that contain tool use - if ( - has_tool_use - and "messageStop" in chunk - and (message_stop := chunk["messageStop"]).get("stopReason") == "end_turn" - ): - # Create corrected chunk with tool_use stopReason - modified_chunk = chunk.copy() - modified_chunk["messageStop"] = message_stop.copy() - modified_chunk["messageStop"]["stopReason"] = "tool_use" - logger.warning("Override stop reason from end_turn to tool_use") - callback(modified_chunk) - else: - callback(chunk) + # The stop_reason override for end_turn -> tool_use is now + # handled centrally in streaming.py so no model-specific + # fixup is needed here. + callback(chunk) else: response = self.client.converse(**request)