@@ -274,6 +274,53 @@ def test_on_tool_start_end(self, mock_context_api):
274274
275275 self .mock_span .set_attribute .assert_any_call ("gen_ai.tool.output" , output )
276276
277+ @patch ("amazon.opentelemetry.distro.opentelemetry.instrumentation.langchain_v2.callback_handler.context_api" )
278+ def test_on_agent_action_and_finish (self , mock_context_api ):
279+ """Test the on_agent_action and on_agent_finish methods."""
280+ mock_context_api .get_value .return_value = False
281+
282+ # Create a mock AgentAction
283+ mock_action = Mock ()
284+ mock_action .tool = "calculator"
285+ mock_action .tool_input = "2 + 2"
286+
287+ # Create a mock AgentFinish
288+ mock_finish = Mock ()
289+ mock_finish .return_values = {"output" : "The answer is 4" }
290+
291+ # Set up the handler with a mocked span
292+ self .handler .span_mapping [self .run_id ] = SpanHolder (self .mock_span , [], time .time (), "gpt-4" )
293+
294+ # Test on_agent_action
295+ self .handler .on_agent_action (action = mock_action , run_id = self .run_id , parent_run_id = self .parent_run_id )
296+
297+ # Verify the expected attributes were set
298+ self .mock_span .set_attribute .assert_any_call ("gen_ai.agent.tool.input" , "2 + 2" )
299+ self .mock_span .set_attribute .assert_any_call ("gen_ai.agent.tool.name" , "calculator" )
300+ self .mock_span .set_attribute .assert_any_call (SpanAttributes .GEN_AI_OPERATION_NAME , "invoke_agent" )
301+
302+ # Test on_agent_finish
303+ self .handler .on_agent_finish (finish = mock_finish , run_id = self .run_id , parent_run_id = self .parent_run_id )
304+
305+ # Verify the output attribute was set
306+ self .mock_span .set_attribute .assert_any_call ("gen_ai.agent.tool.output" , "The answer is 4" )
307+
308+ @patch ("amazon.opentelemetry.distro.opentelemetry.instrumentation.langchain_v2.callback_handler.context_api" )
309+ def test_on_agent_error (self , mock_context_api ):
310+ """Test the on_agent_error method."""
311+ mock_context_api .get_value .return_value = False
312+
313+ # Create a test error
314+ test_error = ValueError ("Something went wrong" )
315+
316+ # Patch the _handle_error method
317+ with patch .object (self .handler , "_handle_error" ) as mock_handle_error :
318+ # Call on_agent_error
319+ self .handler .on_agent_error (error = test_error , run_id = self .run_id , parent_run_id = self .parent_run_id )
320+
321+ # Verify _handle_error was called with the right parameters
322+ mock_handle_error .assert_called_once_with (test_error , self .run_id , self .parent_run_id )
323+
277324
278325class TestLangChainInstrumentor (unittest .TestCase ):
279326 """Test the LangChainInstrumentor class."""
@@ -360,6 +407,49 @@ def test_init_wrapper_handler_already_exists(self):
360407 instance .add_handler .assert_not_called ()
361408
362409
410+ class TestSanitizeMetadataValue (unittest .TestCase ):
411+ """Tests for the _sanitize_metadata_value function."""
412+
413+ def test_sanitize_none (self ):
414+ """Test that None values remain None."""
415+ self .assertIsNone (_sanitize_metadata_value (None ))
416+
417+ def test_sanitize_primitive_types (self ):
418+ """Test that primitive types (bool, str, bytes, int, float) remain unchanged."""
419+ self .assertEqual (_sanitize_metadata_value (True ), True )
420+ self .assertEqual (_sanitize_metadata_value (False ), False )
421+ self .assertEqual (_sanitize_metadata_value ("test_string" ), "test_string" )
422+ self .assertEqual (_sanitize_metadata_value (b"test_bytes" ), b"test_bytes" )
423+ self .assertEqual (_sanitize_metadata_value (123 ), 123 )
424+ self .assertEqual (_sanitize_metadata_value (123.45 ), 123.45 )
425+
426+ def test_sanitize_lists_and_tuples (self ):
427+ """Test that lists and tuples are properly sanitized."""
428+ self .assertEqual (_sanitize_metadata_value ([1 , 2 , 3 ]), ["1" , "2" , "3" ])
429+
430+ self .assertEqual (_sanitize_metadata_value ([1 , "test" , True , None ]), ["1" , "test" , "True" , "None" ])
431+
432+ self .assertEqual (_sanitize_metadata_value ((1 , 2 , 3 )), ["1" , "2" , "3" ])
433+
434+ self .assertEqual (_sanitize_metadata_value ([1 , [2 , 3 ], 4 ]), ["1" , "['2', '3']" , "4" ])
435+
436+ def test_sanitize_complex_objects (self ):
437+ """Test that complex objects are converted to strings."""
438+ self .assertEqual (_sanitize_metadata_value ({"key" : "value" }), "{'key': 'value'}" )
439+
440+ class TestObject :
441+ def __str__ (self ):
442+ return "TestObject"
443+
444+ self .assertEqual (_sanitize_metadata_value (TestObject ()), "TestObject" )
445+
446+ self .assertTrue (_sanitize_metadata_value ({1 , 2 , 3 }).startswith ("{" ))
447+ self .assertTrue (_sanitize_metadata_value ({1 , 2 , 3 }).endswith ("}" ))
448+
449+ complex_struct = {"key1" : [1 , 2 , 3 ], "key2" : {"nested" : "value" }, "key3" : TestObject ()}
450+ self .assertTrue (isinstance (_sanitize_metadata_value (complex_struct ), str ))
451+
452+
363453if __name__ == "__main__" :
364454 import time
365455
0 commit comments