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
18 changes: 15 additions & 3 deletions src/strands/models/bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,21 @@ def _format_request_message_content(self, content: ContentBlock) -> dict[str, An

# https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_GuardrailConverseContentBlock.html
if "guardContent" in content:
guard = content["guardContent"]
guard_text = guard["text"]
result = {"text": {"text": guard_text["text"], "qualifiers": guard_text["qualifiers"]}}
# content["guardContent"] can be a dict-like structure. Annotate as Any for mypy.
guard: Any = content["guardContent"]
guard_text: Any = guard.get("text")

# Guard text may be provided either as a plain string or as a dict with 'text' and optional 'qualifiers'.
if isinstance(guard_text, str):
result = {"text": {"text": guard_text}}
else:
# treat as mapping-like when not a string, be defensive using .get()
text_val = guard_text.get("text") if isinstance(guard_text, dict) else None
result = {"text": {"text": text_val}}
qualifiers = guard_text.get("qualifiers") if isinstance(guard_text, dict) else None
if qualifiers is not None:
result["text"]["qualifiers"] = qualifiers

return {"guardContent": result}

# https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ImageBlock.html
Expand Down
42 changes: 42 additions & 0 deletions tests/strands/models/test_bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,48 @@ def test_format_request_filters_document_content_blocks(model, model_id):
assert "metadata" not in document_block


def test_format_request_guard_content_with_qualifiers(model, model_id):
"""Test guardContent formatting when qualifiers are present."""
messages = [
{
"role": "user",
"content": [{"guardContent": {"text": {"text": "guarded text", "qualifiers": ["q1", "q2"]}}}],
}
]

formatted = model.format_request(messages)
guard_block = formatted["messages"][0]["content"][0]["guardContent"]
assert guard_block == {"text": {"text": "guarded text", "qualifiers": ["q1", "q2"]}}


def test_format_request_guard_content_without_qualifiers(model, model_id):
"""Test guardContent formatting when qualifiers are not present."""
messages = [
{
"role": "user",
"content": [{"guardContent": {"text": {"text": "guarded text no qualifiers"}}}],
}
]

formatted = model.format_request(messages)
guard_block = formatted["messages"][0]["content"][0]["guardContent"]
assert guard_block == {"text": {"text": "guarded text no qualifiers"}}


def test_format_request_guard_content_text_as_string(model, model_id):
"""Test guardContent formatting when the text field is a plain string."""
messages = [
{
"role": "user",
"content": [{"guardContent": {"text": "plain guard text"}}],
}
]

formatted = model.format_request(messages)
guard_block = formatted["messages"][0]["content"][0]["guardContent"]
assert guard_block == {"text": {"text": "plain guard text"}}


def test_format_request_filters_nested_reasoning_content(model, model_id):
"""Test deep filtering of nested reasoningText fields."""
messages = [
Expand Down