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
4 changes: 3 additions & 1 deletion strands-py/src/strands/models/sagemaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,9 @@ async def stream(
{"chunk_type": "content_delta", "data_type": "tool", "data": ToolCall(**tool_call)}
)
yield self.format_chunk({"chunk_type": "content_stop", "data_type": "tool"})
message_stop_reason = "tool_calls"
# A tool call cut off by the token limit keeps "length", as in the streaming branch
if message_stop_reason != "length":
message_stop_reason = "tool_calls"

# Message close
yield self.format_chunk({"chunk_type": "message_stop", "data": message_stop_reason})
Expand Down
37 changes: 37 additions & 0 deletions strands-py/tests/strands/models/test_sagemaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,43 @@ async def test_stream_non_streaming_with_tool_calls(self, sagemaker_client, mode
usage_data = metadata["metadata"]["usage"]
assert usage_data["totalTokens"] == 30

@pytest.mark.asyncio
@pytest.mark.parametrize(
("finish_reason", "expected"),
[("length", "max_tokens"), ("tool_calls", "tool_use"), ("stop", "tool_use")],
)
async def test_stream_non_streaming_tool_call_stop_reason(
self, sagemaker_client, model, messages, finish_reason, expected
):
"""A tool call cut off by the token limit reports max_tokens, as the streaming path does."""
model.payload_config["stream"] = False

mock_response = {"Body": unittest.mock.MagicMock()}
mock_response["Body"].read.return_value = json.dumps(
{
"choices": [
{
"message": {
"content": None,
"tool_calls": [
{
"id": "tool123",
"type": "function",
"function": {"name": "get_weather", "arguments": '{"location": "Pa'},
}
],
},
"finish_reason": finish_reason,
}
],
}
).encode("utf-8")
sagemaker_client.invoke_endpoint.return_value = mock_response

response = [chunk async for chunk in model.stream(messages)]

assert [e["messageStop"]["stopReason"] for e in response if "messageStop" in e] == [expected]


class TestDataClasses:
"""Test suite for data classes."""
Expand Down
Loading