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
9 changes: 9 additions & 0 deletions src/strands/models/bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,15 @@ def _format_bedrock_messages(self, messages: Messages) -> list[dict[str, Any]]:

# Create new message with cleaned content (skip if empty)
if cleaned_content:
# Bedrock requires reasoningContent blocks to come first in assistant messages
# when thinking is enabled. Session managers or message reconstruction may
# produce blocks in wrong order, so we defensively reorder here.
if message["role"] == "assistant":
reasoning = [b for b in cleaned_content if "reasoningContent" in b]
other = [b for b in cleaned_content if "reasoningContent" not in b]
if reasoning and other and cleaned_content[0] != reasoning[0]:
cleaned_content = reasoning + other

cleaned_messages.append({"content": cleaned_content, "role": message["role"]})

if filtered_unknown_members:
Expand Down
35 changes: 35 additions & 0 deletions tests/strands/models/test_bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -2817,3 +2817,38 @@ def test_guardrail_latest_message_disabled_does_not_wrap(model):

assert "text" in formatted
assert "guardContent" not in formatted


def test_format_bedrock_messages_reorders_reasoning_blocks_first():
"""Test that reasoning blocks are reordered to come first in assistant messages.

Bedrock requires reasoningContent blocks to precede all other blocks in assistant
messages when thinking is enabled. Session managers may restore messages with blocks
in wrong order, so _format_bedrock_messages must defensively reorder them.

Regression test for https://github.com/strands-agents/sdk-python/issues/1698
"""
model = BedrockModel(model_id="us.anthropic.claude-sonnet-4-20250514-v1:0")

messages = [
{"role": "user", "content": [{"text": "What is the status?"}]},
{
"role": "assistant",
"content": [
{"text": "I'll check the status."},
{"reasoningContent": {"reasoningText": {"text": "Let me think...", "signature": "sig"}}},
{"toolUse": {"toolUseId": "t1", "name": "check", "input": {}}},
],
},
{"role": "user", "content": [{"toolResult": {"toolUseId": "t1", "content": [{"text": "OK"}]}}]},
]

result = model._format_bedrock_messages(messages)

assistant_msg = result[1]
assert assistant_msg["role"] == "assistant"
assert "reasoningContent" in assistant_msg["content"][0], (
"reasoningContent must be the first block in assistant messages"
)
assert "text" in assistant_msg["content"][1]
assert "toolUse" in assistant_msg["content"][2]