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
27 changes: 23 additions & 4 deletions src/strands/models/bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -926,11 +926,14 @@ def _convert_non_streaming_to_streaming(self, response: dict[str, Any]) -> Itera
yield {"messageStart": {"role": response["output"]["message"]["role"]}}

# Process content blocks
for content in cast(list[ContentBlock], response["output"]["message"]["content"]):
# Yield contentBlockStart event if needed
for content_block_index, content in enumerate(
cast(list[ContentBlock], response["output"]["message"]["content"])
):
# Yield contentBlockStart for all content block types to match streaming behavior
if "toolUse" in content:
yield {
"contentBlockStart": {
"contentBlockIndex": content_block_index,
"start": {
"toolUse": {
"toolUseId": content["toolUse"]["toolUseId"],
Expand All @@ -945,14 +948,24 @@ def _convert_non_streaming_to_streaming(self, response: dict[str, Any]) -> Itera

yield {"contentBlockDelta": {"delta": {"toolUse": {"input": input_value}}}}
elif "text" in content:
# Then yield the text as a delta
yield {
"contentBlockStart": {
"contentBlockIndex": content_block_index,
"start": {},
}
}
yield {
"contentBlockDelta": {
"delta": {"text": content["text"]},
}
}
elif "reasoningContent" in content:
# Then yield the reasoning content as a delta
yield {
"contentBlockStart": {
"contentBlockIndex": content_block_index,
"start": {},
}
}
yield {
"contentBlockDelta": {
"delta": {"reasoningContent": {"text": content["reasoningContent"]["reasoningText"]["text"]}}
Expand All @@ -970,6 +983,12 @@ def _convert_non_streaming_to_streaming(self, response: dict[str, Any]) -> Itera
}
}
elif "citationsContent" in content:
yield {
"contentBlockStart": {
"contentBlockIndex": content_block_index,
"start": {},
}
}
# For non-streaming citations, emit text and metadata deltas in sequence
# to match streaming behavior where they flow naturally
if "content" in content["citationsContent"]:
Expand Down
14 changes: 13 additions & 1 deletion tests/strands/models/test_bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,7 @@ async def test_stream_with_streaming_false(bedrock_client, alist, messages):
tru_events = await alist(response)
exp_events = [
{"messageStart": {"role": "assistant"}},
{"contentBlockStart": {"contentBlockIndex": 0, "start": {}}},
{"contentBlockDelta": {"delta": {"text": "test"}}},
{"contentBlockStop": {}},
{"messageStop": {"stopReason": "end_turn", "additionalModelResponseFields": None}},
Expand Down Expand Up @@ -1122,7 +1123,12 @@ async def test_stream_with_streaming_false_and_tool_use(bedrock_client, alist, m
tru_events = await alist(response)
exp_events = [
{"messageStart": {"role": "assistant"}},
{"contentBlockStart": {"start": {"toolUse": {"toolUseId": "123", "name": "dummyTool"}}}},
{
"contentBlockStart": {
"contentBlockIndex": 0,
"start": {"toolUse": {"toolUseId": "123", "name": "dummyTool"}},
}
},
{"contentBlockDelta": {"delta": {"toolUse": {"input": '{"hello": "world!"}'}}}},
{"contentBlockStop": {}},
{"messageStop": {"stopReason": "tool_use", "additionalModelResponseFields": None}},
Expand Down Expand Up @@ -1159,6 +1165,7 @@ async def test_stream_with_streaming_false_and_reasoning(bedrock_client, alist,
tru_events = await alist(response)
exp_events = [
{"messageStart": {"role": "assistant"}},
{"contentBlockStart": {"contentBlockIndex": 0, "start": {}}},
{"contentBlockDelta": {"delta": {"reasoningContent": {"text": "Thinking really hard...."}}}},
{"contentBlockDelta": {"delta": {"reasoningContent": {"signature": "123"}}}},
{"contentBlockStop": {}},
Expand Down Expand Up @@ -1197,6 +1204,7 @@ async def test_stream_and_reasoning_no_signature(bedrock_client, alist, messages
tru_events = await alist(response)
exp_events = [
{"messageStart": {"role": "assistant"}},
{"contentBlockStart": {"contentBlockIndex": 0, "start": {}}},
{"contentBlockDelta": {"delta": {"reasoningContent": {"text": "Thinking really hard...."}}}},
{"contentBlockStop": {}},
{"messageStop": {"stopReason": "tool_use", "additionalModelResponseFields": None}},
Expand Down Expand Up @@ -1224,6 +1232,7 @@ async def test_stream_with_streaming_false_with_metrics_and_usage(bedrock_client
tru_events = await alist(response)
exp_events = [
{"messageStart": {"role": "assistant"}},
{"contentBlockStart": {"contentBlockIndex": 0, "start": {}}},
{"contentBlockDelta": {"delta": {"text": "test"}}},
{"contentBlockStop": {}},
{"messageStop": {"stopReason": "tool_use", "additionalModelResponseFields": None}},
Expand Down Expand Up @@ -1265,6 +1274,7 @@ async def test_stream_input_guardrails(bedrock_client, alist, messages):
tru_events = await alist(response)
exp_events = [
{"messageStart": {"role": "assistant"}},
{"contentBlockStart": {"contentBlockIndex": 0, "start": {}}},
{"contentBlockDelta": {"delta": {"text": "test"}}},
{"contentBlockStop": {}},
{"messageStop": {"stopReason": "end_turn", "additionalModelResponseFields": None}},
Expand Down Expand Up @@ -1316,6 +1326,7 @@ async def test_stream_output_guardrails(bedrock_client, alist, messages):
tru_events = await alist(response)
exp_events = [
{"messageStart": {"role": "assistant"}},
{"contentBlockStart": {"contentBlockIndex": 0, "start": {}}},
{"contentBlockDelta": {"delta": {"text": "test"}}},
{"contentBlockStop": {}},
{"messageStop": {"stopReason": "end_turn", "additionalModelResponseFields": None}},
Expand Down Expand Up @@ -1369,6 +1380,7 @@ async def test_stream_output_guardrails_redacts_output(bedrock_client, alist, me
tru_events = await alist(response)
exp_events = [
{"messageStart": {"role": "assistant"}},
{"contentBlockStart": {"contentBlockIndex": 0, "start": {}}},
{"contentBlockDelta": {"delta": {"text": "test"}}},
{"contentBlockStop": {}},
{"messageStop": {"stopReason": "end_turn", "additionalModelResponseFields": None}},
Expand Down