diff --git a/src/strands/models/bedrock.py b/src/strands/models/bedrock.py index ace35640a..82fe4aae0 100644 --- a/src/strands/models/bedrock.py +++ b/src/strands/models/bedrock.py @@ -68,6 +68,7 @@ class BedrockConfig(TypedDict, total=False): guardrail_redact_output_message: If a Bedrock Output guardrail triggers, replace output with this message. max_tokens: Maximum number of tokens to generate in the response model_id: The Bedrock model ID (e.g., "us.anthropic.claude-sonnet-4-20250514-v1:0") + remove_tool_result_status: Flag to remove status field from tool results. Defaults to False. stop_sequences: List of sequences that will stop generation when encountered streaming: Flag to enable/disable streaming. Defaults to True. temperature: Controls randomness in generation (higher = more random) @@ -89,6 +90,7 @@ class BedrockConfig(TypedDict, total=False): guardrail_redact_output_message: Optional[str] max_tokens: Optional[int] model_id: str + remove_tool_result_status: Optional[bool] stop_sequences: Optional[list[str]] streaming: Optional[bool] temperature: Optional[float] @@ -276,10 +278,18 @@ def _format_bedrock_messages(self, messages: Messages) -> Messages: # Create a new content block with only the cleaned toolResult tool_result: ToolResult = content_block["toolResult"] - # Keep only the required fields for Bedrock - cleaned_tool_result = ToolResult( - content=tool_result["content"], toolUseId=tool_result["toolUseId"], status=tool_result["status"] - ) + if self.config.get("remove_tool_result_status", False): + # Remove status field when explicitly configured + cleaned_tool_result = ToolResult( + toolUseId=tool_result["toolUseId"], content=tool_result["content"] + ) + else: + # Keep status field by default + cleaned_tool_result = ToolResult( + content=tool_result["content"], + toolUseId=tool_result["toolUseId"], + status=tool_result["status"], + ) cleaned_block: ContentBlock = {"toolResult": cleaned_tool_result} cleaned_content.append(cleaned_block) diff --git a/src/strands/types/tools.py b/src/strands/types/tools.py index bb7c874f6..459e9cea5 100644 --- a/src/strands/types/tools.py +++ b/src/strands/types/tools.py @@ -9,7 +9,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Any, AsyncGenerator, Awaitable, Callable, Literal, Protocol, Union -from typing_extensions import TypedDict +from typing_extensions import NotRequired, TypedDict from .media import DocumentContent, ImageContent @@ -91,7 +91,7 @@ class ToolResult(TypedDict): """ content: list[ToolResultContent] - status: ToolResultStatus + status: NotRequired[ToolResultStatus] toolUseId: str diff --git a/tests/strands/models/test_bedrock.py b/tests/strands/models/test_bedrock.py index 09e508845..1515cc317 100644 --- a/tests/strands/models/test_bedrock.py +++ b/tests/strands/models/test_bedrock.py @@ -1237,3 +1237,58 @@ def test_format_request_cleans_tool_result_content_blocks(model, model_id): assert tool_result == expected assert "extraField" not in tool_result assert "mcpMetadata" not in tool_result + + +def test_format_request_removes_status_field_when_configured(model, model_id): + """Test that format_request removes status field when remove_tool_result_status=True.""" + # Configure model to remove status field + model.update_config(remove_tool_result_status=True) + + messages = [ + { + "role": "user", + "content": [ + { + "toolResult": { + "content": [{"text": "Tool output"}], + "toolUseId": "tool123", + "status": "success", + } + }, + ], + } + ] + + formatted_request = model.format_request(messages) + + # Verify toolResult does not contain status field when configured to remove + tool_result = formatted_request["messages"][0]["content"][0]["toolResult"] + expected = {"toolUseId": "tool123", "content": [{"text": "Tool output"}]} + assert tool_result == expected + assert "status" not in tool_result + + +def test_format_request_keeps_status_field_by_default(model, model_id): + """Test that format_request keeps status field by default.""" + messages = [ + { + "role": "user", + "content": [ + { + "toolResult": { + "content": [{"text": "Tool output"}], + "toolUseId": "tool123", + "status": "success", + } + }, + ], + } + ] + + formatted_request = model.format_request(messages) + + # Verify toolResult contains status field by default + tool_result = formatted_request["messages"][0]["content"][0]["toolResult"] + expected = {"content": [{"text": "Tool output"}], "toolUseId": "tool123", "status": "success"} + assert tool_result == expected + assert "status" in tool_result