@@ -1607,6 +1607,87 @@ def test_format_request_cleans_tool_result_content_blocks(model, model_id):
16071607 assert "status" not in tool_result
16081608
16091609
1610+ def test_format_request_message_content_normalizes_empty_tool_result_content (model , model_id ):
1611+ """Test that _format_request_message_content replaces empty toolResult content with a minimal text block.
1612+
1613+ Some model providers (e.g., Nemotron) reject toolResult blocks with content: [] via the
1614+ Converse API, while others (e.g., Claude) accept them. The SDK should normalize empty
1615+ content arrays to ensure cross-model compatibility.
1616+
1617+ See: https://github.com/strands-agents/sdk-python/issues/2122
1618+ """
1619+ messages = [
1620+ {"role" : "user" , "content" : [{"text" : "List tables" }]},
1621+ {
1622+ "role" : "assistant" ,
1623+ "content" : [
1624+ {"text" : "Querying...\n " },
1625+ {"toolUse" : {"toolUseId" : "tool_001" , "name" : "run_query" , "input" : {"sql" : "SELECT 1" }}},
1626+ ],
1627+ },
1628+ {
1629+ "role" : "user" ,
1630+ "content" : [
1631+ {"toolResult" : {"toolUseId" : "tool_001" , "content" : []}},
1632+ ],
1633+ },
1634+ ]
1635+
1636+ formatted_request = model ._format_request (messages )
1637+
1638+ tool_result = formatted_request ["messages" ][2 ]["content" ][0 ]["toolResult" ]
1639+ assert tool_result ["content" ] == [{"text" : "" }], "Empty toolResult content should be normalized to [{'text': ''}]"
1640+
1641+
1642+ def test_format_request_message_content_does_not_mutate_empty_tool_result (model , model_id ):
1643+ """Test that normalizing empty toolResult content does not mutate the original messages."""
1644+ messages = [
1645+ {"role" : "user" , "content" : [{"text" : "List tables" }]},
1646+ {
1647+ "role" : "assistant" ,
1648+ "content" : [
1649+ {"toolUse" : {"toolUseId" : "tool_001" , "name" : "run_query" , "input" : {"sql" : "SELECT 1" }}},
1650+ ],
1651+ },
1652+ {
1653+ "role" : "user" ,
1654+ "content" : [
1655+ {"toolResult" : {"toolUseId" : "tool_001" , "content" : []}},
1656+ ],
1657+ },
1658+ ]
1659+
1660+ original_content = messages [2 ]["content" ][0 ]["toolResult" ]["content" ]
1661+ model ._format_request (messages )
1662+
1663+ assert original_content == [], "Original empty content list should not be mutated"
1664+
1665+
1666+ def test_format_request_message_content_preserves_nonempty_tool_result_content (model , model_id ):
1667+ """Test that _format_request_message_content does not modify non-empty toolResult content."""
1668+ messages = [
1669+ {"role" : "user" , "content" : [{"text" : "List tables" }]},
1670+ {
1671+ "role" : "assistant" ,
1672+ "content" : [
1673+ {"text" : "Querying...\n " },
1674+ {"toolUse" : {"toolUseId" : "tool_001" , "name" : "run_query" , "input" : {"sql" : "SELECT 1" }}},
1675+ ],
1676+ },
1677+ {
1678+ "role" : "user" ,
1679+ "content" : [
1680+ {"toolResult" : {"toolUseId" : "tool_001" , "content" : [{"text" : "some result" }]}},
1681+ ],
1682+ },
1683+ ]
1684+
1685+ formatted_request = model ._format_request (messages )
1686+
1687+ tool_result = formatted_request ["messages" ][2 ]["content" ][0 ]["toolResult" ]
1688+ assert tool_result ["content" ] == [{"text" : "some result" }]
1689+
1690+
16101691def test_format_request_removes_status_field_when_configured (model , model_id ):
16111692 model .update_config (include_tool_result_status = False )
16121693
0 commit comments