@@ -274,6 +274,53 @@ def test_on_tool_start_end(self, mock_context_api):
274
274
275
275
self .mock_span .set_attribute .assert_any_call ("gen_ai.tool.output" , output )
276
276
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
+
277
324
278
325
class TestLangChainInstrumentor (unittest .TestCase ):
279
326
"""Test the LangChainInstrumentor class."""
@@ -360,6 +407,49 @@ def test_init_wrapper_handler_already_exists(self):
360
407
instance .add_handler .assert_not_called ()
361
408
362
409
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
+
363
453
if __name__ == "__main__" :
364
454
import time
365
455
0 commit comments