@@ -1576,3 +1576,187 @@ def test_agent_with_session_and_conversation_manager():
15761576 assert agent .messages == agent_2 .messages
15771577 # Asser the conversation manager was initialized properly
15781578 assert agent .conversation_manager .removed_message_count == agent_2 .conversation_manager .removed_message_count
1579+
1580+
1581+ def test_agent_tool_non_serializable_parameter_filtering (agent , mock_randint ):
1582+ """Test that non-serializable objects in tool parameters are properly filtered during tool call recording."""
1583+ mock_randint .return_value = 42
1584+
1585+ # Create a non-serializable object (Agent instance)
1586+ another_agent = Agent ()
1587+
1588+ # This should not crash even though we're passing non-serializable objects
1589+ result = agent .tool .tool_decorated (
1590+ random_string = "test_value" ,
1591+ non_serializable_agent = another_agent , # This would previously cause JSON serialization error
1592+ user_message_override = "Testing non-serializable parameter filtering" ,
1593+ )
1594+
1595+ # Verify the tool executed successfully
1596+ expected_result = {
1597+ "content" : [{"text" : "test_value" }],
1598+ "status" : "success" ,
1599+ "toolUseId" : "tooluse_tool_decorated_42" ,
1600+ }
1601+ assert result == expected_result
1602+
1603+ # The key test: this should not crash during execution
1604+ # Check that we have messages recorded (exact count may vary)
1605+ assert len (agent .messages ) > 0
1606+
1607+ # Check user message with filtered parameters - this is the main test for the bug fix
1608+ user_message = agent .messages [0 ]
1609+ assert user_message ["role" ] == "user"
1610+ assert len (user_message ["content" ]) == 2
1611+
1612+ # Check override message
1613+ assert user_message ["content" ][0 ]["text" ] == "Testing non-serializable parameter filtering\n "
1614+
1615+ # Check tool call description with filtered parameters - this is where JSON serialization would fail
1616+ tool_call_text = user_message ["content" ][1 ]["text" ]
1617+ assert "agent.tool.tool_decorated direct tool call." in tool_call_text
1618+ assert '"random_string": "test_value"' in tool_call_text
1619+ assert '"non_serializable_agent": "<<non-serializable: Agent>>"' in tool_call_text
1620+
1621+
1622+ def test_agent_tool_multiple_non_serializable_types (agent , mock_randint ):
1623+ """Test filtering of various non-serializable object types."""
1624+ mock_randint .return_value = 123
1625+
1626+ # Create various non-serializable objects
1627+ class CustomClass :
1628+ def __init__ (self , value ):
1629+ self .value = value
1630+
1631+ non_serializable_objects = {
1632+ "agent" : Agent (),
1633+ "custom_object" : CustomClass ("test" ),
1634+ "function" : lambda x : x ,
1635+ "set_object" : {1 , 2 , 3 },
1636+ "complex_number" : 3 + 4j ,
1637+ "serializable_string" : "this_should_remain" ,
1638+ "serializable_number" : 42 ,
1639+ "serializable_list" : [1 , 2 , 3 ],
1640+ "serializable_dict" : {"key" : "value" },
1641+ }
1642+
1643+ # This should not crash
1644+ result = agent .tool .tool_decorated (random_string = "test_filtering" , ** non_serializable_objects )
1645+
1646+ # Verify tool executed successfully
1647+ expected_result = {
1648+ "content" : [{"text" : "test_filtering" }],
1649+ "status" : "success" ,
1650+ "toolUseId" : "tooluse_tool_decorated_123" ,
1651+ }
1652+ assert result == expected_result
1653+
1654+ # Check the recorded message for proper parameter filtering
1655+ assert len (agent .messages ) > 0
1656+ user_message = agent .messages [0 ]
1657+ tool_call_text = user_message ["content" ][0 ]["text" ]
1658+
1659+ # Verify serializable objects remain unchanged
1660+ assert '"serializable_string": "this_should_remain"' in tool_call_text
1661+ assert '"serializable_number": 42' in tool_call_text
1662+ assert '"serializable_list": [1, 2, 3]' in tool_call_text
1663+ assert '"serializable_dict": {"key": "value"}' in tool_call_text
1664+
1665+ # Verify non-serializable objects are replaced with descriptive strings
1666+ assert '"agent": "<<non-serializable: Agent>>"' in tool_call_text
1667+ assert (
1668+ '"custom_object": "<<non-serializable: test_agent_tool_multiple_non_serializable_types.<locals>.CustomClass>>"'
1669+ in tool_call_text
1670+ )
1671+ assert '"function": "<<non-serializable: function>>"' in tool_call_text
1672+ assert '"set_object": "<<non-serializable: set>>"' in tool_call_text
1673+ assert '"complex_number": "<<non-serializable: complex>>"' in tool_call_text
1674+
1675+
1676+ def test_agent_tool_serialization_edge_cases (agent , mock_randint ):
1677+ """Test edge cases in parameter serialization filtering."""
1678+ mock_randint .return_value = 999
1679+
1680+ # Test with None values, empty containers, and nested structures
1681+ edge_case_params = {
1682+ "none_value" : None ,
1683+ "empty_list" : [],
1684+ "empty_dict" : {},
1685+ "nested_list_with_non_serializable" : [1 , 2 , Agent ()], # This should be filtered out
1686+ "nested_dict_serializable" : {"nested" : {"key" : "value" }}, # This should remain
1687+ }
1688+
1689+ result = agent .tool .tool_decorated (random_string = "edge_cases" , ** edge_case_params )
1690+
1691+ # Verify successful execution
1692+ expected_result = {
1693+ "content" : [{"text" : "edge_cases" }],
1694+ "status" : "success" ,
1695+ "toolUseId" : "tooluse_tool_decorated_999" ,
1696+ }
1697+ assert result == expected_result
1698+
1699+ # Check parameter filtering in recorded message
1700+ assert len (agent .messages ) > 0
1701+ user_message = agent .messages [0 ]
1702+ tool_call_text = user_message ["content" ][0 ]["text" ]
1703+
1704+ # Verify serializable values remain
1705+ assert '"none_value": null' in tool_call_text
1706+ assert '"empty_list": []' in tool_call_text
1707+ assert '"empty_dict": {}' in tool_call_text
1708+ assert '"nested_dict_serializable": {"nested": {"key": "value"}}' in tool_call_text
1709+
1710+ # Verify non-serializable nested structure is replaced
1711+ assert '"nested_list_with_non_serializable": [1, 2, "<<non-serializable: Agent>>"]' in tool_call_text
1712+
1713+
1714+ def test_agent_tool_no_non_serializable_parameters (agent , mock_randint ):
1715+ """Test that normal tool calls with only serializable parameters work unchanged."""
1716+ mock_randint .return_value = 555
1717+
1718+ # Call with only serializable parameters
1719+ result = agent .tool .tool_decorated (random_string = "normal_call" , user_message_override = "Normal tool call test" )
1720+
1721+ # Verify successful execution
1722+ expected_result = {
1723+ "content" : [{"text" : "normal_call" }],
1724+ "status" : "success" ,
1725+ "toolUseId" : "tooluse_tool_decorated_555" ,
1726+ }
1727+ assert result == expected_result
1728+
1729+ # Check message recording works normally
1730+ assert len (agent .messages ) > 0
1731+ user_message = agent .messages [0 ]
1732+ tool_call_text = user_message ["content" ][1 ]["text" ]
1733+
1734+ # Verify normal parameter serialization (no filtering needed)
1735+ assert "agent.tool.tool_decorated direct tool call." in tool_call_text
1736+ assert '"random_string": "normal_call"' in tool_call_text
1737+ # Should not contain any "<<non-serializable:" strings
1738+ assert "<<non-serializable:" not in tool_call_text
1739+
1740+
1741+ def test_agent_tool_record_direct_tool_call_disabled_with_non_serializable (agent , mock_randint ):
1742+ """Test that when record_direct_tool_call is disabled, non-serializable parameters don't cause issues."""
1743+ mock_randint .return_value = 777
1744+
1745+ # Disable tool call recording
1746+ agent .record_direct_tool_call = False
1747+
1748+ # This should work fine even with non-serializable parameters since recording is disabled
1749+ result = agent .tool .tool_decorated (
1750+ random_string = "no_recording" , non_serializable_agent = Agent (), user_message_override = "This shouldn't be recorded"
1751+ )
1752+
1753+ # Verify successful execution
1754+ expected_result = {
1755+ "content" : [{"text" : "no_recording" }],
1756+ "status" : "success" ,
1757+ "toolUseId" : "tooluse_tool_decorated_777" ,
1758+ }
1759+ assert result == expected_result
1760+
1761+ # Verify no messages were recorded
1762+ assert len (agent .messages ) == 0
0 commit comments