Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/strands/event_loop/streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should follow the log format documented here.

)
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
Expand Down
24 changes: 4 additions & 20 deletions src/strands/models/bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't worry about adding this comment here.

callback(chunk)

else:
response = self.client.converse(**request)
Expand Down