@@ -471,3 +471,215 @@ def _mock_performance_issue_span(is_segment, attributes, **fields) -> SpanEvent:
471471 ** fields ,
472472 },
473473 )
474+
475+
476+ def test_enrich_gen_ai_agent_name_from_immediate_parent () -> None :
477+ """Test that gen_ai.agent.name is inherited from the immediate parent with gen_ai.invoke_agent operation."""
478+ parent_span = build_mock_span (
479+ project_id = 1 ,
480+ is_segment = True ,
481+ span_id = "aaaaaaaaaaaaaaaa" ,
482+ start_timestamp = 1609455600.0 ,
483+ end_timestamp = 1609455605.0 ,
484+ span_op = "gen_ai.invoke_agent" ,
485+ attributes = {
486+ "gen_ai.agent.name" : {"type" : "string" , "value" : "MyAgent" },
487+ },
488+ )
489+
490+ child_span = build_mock_span (
491+ project_id = 1 ,
492+ span_id = "bbbbbbbbbbbbbbbb" ,
493+ parent_span_id = "aaaaaaaaaaaaaaaa" ,
494+ start_timestamp = 1609455601.0 ,
495+ end_timestamp = 1609455602.0 ,
496+ span_op = "gen_ai.execute_tool" ,
497+ )
498+
499+ spans = [parent_span , child_span ]
500+ _ , enriched_spans = TreeEnricher .enrich_spans (spans )
501+ compatible_spans = [make_compatible (span ) for span in enriched_spans ]
502+
503+ parent , child = compatible_spans
504+ assert attribute_value (parent , "gen_ai.agent.name" ) == "MyAgent"
505+ assert attribute_value (child , "gen_ai.agent.name" ) == "MyAgent"
506+
507+
508+ def test_enrich_gen_ai_agent_name_not_overwritten () -> None :
509+ """Test that gen_ai.agent.name is not overwritten if already set on the child."""
510+ parent_span = build_mock_span (
511+ project_id = 1 ,
512+ is_segment = True ,
513+ span_id = "aaaaaaaaaaaaaaaa" ,
514+ start_timestamp = 1609455600.0 ,
515+ end_timestamp = 1609455605.0 ,
516+ span_op = "gen_ai.invoke_agent" ,
517+ attributes = {
518+ "gen_ai.agent.name" : {"type" : "string" , "value" : "ParentAgent" },
519+ },
520+ )
521+
522+ child_span = build_mock_span (
523+ project_id = 1 ,
524+ span_id = "bbbbbbbbbbbbbbbb" ,
525+ parent_span_id = "aaaaaaaaaaaaaaaa" ,
526+ start_timestamp = 1609455601.0 ,
527+ end_timestamp = 1609455602.0 ,
528+ span_op = "gen_ai.handoff" ,
529+ attributes = {
530+ "gen_ai.agent.name" : {"type" : "string" , "value" : "ChildAgent" },
531+ },
532+ )
533+
534+ spans = [parent_span , child_span ]
535+ _ , enriched_spans = TreeEnricher .enrich_spans (spans )
536+ compatible_spans = [make_compatible (span ) for span in enriched_spans ]
537+
538+ parent , child = compatible_spans
539+ assert attribute_value (parent , "gen_ai.agent.name" ) == "ParentAgent"
540+ assert attribute_value (child , "gen_ai.agent.name" ) == "ChildAgent"
541+
542+
543+ def test_enrich_gen_ai_agent_name_not_set_without_ancestor () -> None :
544+ """Test that gen_ai.agent.name is not set if no ancestor has it."""
545+ parent_span = build_mock_span (
546+ project_id = 1 ,
547+ is_segment = True ,
548+ span_id = "aaaaaaaaaaaaaaaa" ,
549+ start_timestamp = 1609455600.0 ,
550+ end_timestamp = 1609455605.0 ,
551+ span_op = "some.operation" ,
552+ )
553+
554+ child_span = build_mock_span (
555+ project_id = 1 ,
556+ span_id = "bbbbbbbbbbbbbbbb" ,
557+ parent_span_id = "aaaaaaaaaaaaaaaa" ,
558+ start_timestamp = 1609455601.0 ,
559+ end_timestamp = 1609455602.0 ,
560+ span_op = "gen_ai.execute_tool" ,
561+ )
562+
563+ spans = [parent_span , child_span ]
564+ _ , enriched_spans = TreeEnricher .enrich_spans (spans )
565+ compatible_spans = [make_compatible (span ) for span in enriched_spans ]
566+
567+ parent , child = compatible_spans
568+ assert attribute_value (parent , "gen_ai.agent.name" ) is None
569+ assert attribute_value (child , "gen_ai.agent.name" ) is None
570+
571+
572+ def test_enrich_gen_ai_agent_name_not_from_sibling () -> None :
573+ """Test that gen_ai.agent.name is not taken from a sibling span."""
574+ parent_span = build_mock_span (
575+ project_id = 1 ,
576+ is_segment = True ,
577+ span_id = "aaaaaaaaaaaaaaaa" ,
578+ start_timestamp = 1609455600.0 ,
579+ end_timestamp = 1609455605.0 ,
580+ span_op = "some.operation" ,
581+ )
582+
583+ sibling_with_agent = build_mock_span (
584+ project_id = 1 ,
585+ span_id = "bbbbbbbbbbbbbbbb" ,
586+ parent_span_id = "aaaaaaaaaaaaaaaa" ,
587+ start_timestamp = 1609455601.0 ,
588+ end_timestamp = 1609455602.0 ,
589+ span_op = "gen_ai.invoke_agent" ,
590+ attributes = {
591+ "gen_ai.agent.name" : {"type" : "string" , "value" : "SiblingAgent" },
592+ },
593+ )
594+
595+ target_child = build_mock_span (
596+ project_id = 1 ,
597+ span_id = "cccccccccccccccc" ,
598+ parent_span_id = "aaaaaaaaaaaaaaaa" ,
599+ start_timestamp = 1609455602.5 ,
600+ end_timestamp = 1609455603.5 ,
601+ span_op = "gen_ai.execute_tool" ,
602+ )
603+
604+ spans = [parent_span , sibling_with_agent , target_child ]
605+ _ , enriched_spans = TreeEnricher .enrich_spans (spans )
606+ compatible_spans = [make_compatible (span ) for span in enriched_spans ]
607+
608+ parent , sibling , target = compatible_spans
609+ assert attribute_value (parent , "gen_ai.agent.name" ) is None
610+ assert attribute_value (sibling , "gen_ai.agent.name" ) == "SiblingAgent"
611+ assert attribute_value (target , "gen_ai.agent.name" ) is None
612+
613+
614+ def test_enrich_gen_ai_agent_name_only_from_invoke_agent_parent () -> None :
615+ """Test that gen_ai.agent.name is only inherited from parent with gen_ai.invoke_agent operation."""
616+ parent_span = build_mock_span (
617+ project_id = 1 ,
618+ is_segment = True ,
619+ span_id = "aaaaaaaaaaaaaaaa" ,
620+ start_timestamp = 1609455600.0 ,
621+ end_timestamp = 1609455605.0 ,
622+ span_op = "gen_ai.create_agent" ,
623+ attributes = {
624+ "gen_ai.agent.name" : {"type" : "string" , "value" : "CreateAgentName" },
625+ },
626+ )
627+
628+ child_span = build_mock_span (
629+ project_id = 1 ,
630+ span_id = "bbbbbbbbbbbbbbbb" ,
631+ parent_span_id = "aaaaaaaaaaaaaaaa" ,
632+ start_timestamp = 1609455601.0 ,
633+ end_timestamp = 1609455602.0 ,
634+ span_op = "gen_ai.run" ,
635+ )
636+
637+ spans = [parent_span , child_span ]
638+ _ , enriched_spans = TreeEnricher .enrich_spans (spans )
639+ compatible_spans = [make_compatible (span ) for span in enriched_spans ]
640+
641+ parent , child = compatible_spans
642+ assert attribute_value (parent , "gen_ai.agent.name" ) == "CreateAgentName"
643+ assert attribute_value (child , "gen_ai.agent.name" ) is None
644+
645+
646+ def test_enrich_gen_ai_agent_name_not_from_grandparent () -> None :
647+ """Test that gen_ai.agent.name is NOT inherited from grandparent, only from immediate parent."""
648+ grandparent_span = build_mock_span (
649+ project_id = 1 ,
650+ is_segment = True ,
651+ span_id = "aaaaaaaaaaaaaaaa" ,
652+ start_timestamp = 1609455600.0 ,
653+ end_timestamp = 1609455605.0 ,
654+ span_op = "gen_ai.invoke_agent" ,
655+ attributes = {
656+ "gen_ai.agent.name" : {"type" : "string" , "value" : "GrandparentAgent" },
657+ },
658+ )
659+
660+ parent_span = build_mock_span (
661+ project_id = 1 ,
662+ span_id = "bbbbbbbbbbbbbbbb" ,
663+ parent_span_id = "aaaaaaaaaaaaaaaa" ,
664+ start_timestamp = 1609455601.0 ,
665+ end_timestamp = 1609455604.0 ,
666+ span_op = "some.operation" ,
667+ )
668+
669+ child_span = build_mock_span (
670+ project_id = 1 ,
671+ span_id = "cccccccccccccccc" ,
672+ parent_span_id = "bbbbbbbbbbbbbbbb" ,
673+ start_timestamp = 1609455602.0 ,
674+ end_timestamp = 1609455603.0 ,
675+ span_op = "gen_ai.run" ,
676+ )
677+
678+ spans = [grandparent_span , parent_span , child_span ]
679+ _ , enriched_spans = TreeEnricher .enrich_spans (spans )
680+ compatible_spans = [make_compatible (span ) for span in enriched_spans ]
681+
682+ grandparent , parent , child = compatible_spans
683+ assert attribute_value (grandparent , "gen_ai.agent.name" ) == "GrandparentAgent"
684+ assert attribute_value (parent , "gen_ai.agent.name" ) is None
685+ assert attribute_value (child , "gen_ai.agent.name" ) is None
0 commit comments