@@ -316,7 +316,7 @@ def end_model_invoke_span(
316
316
[
317
317
{
318
318
"role" : message ["role" ],
319
- "parts" : [{ "type" : "text" , "content" : message ["content" ]}] ,
319
+ "parts" : self . _map_content_blocks_to_otel_parts ( message ["content" ]) ,
320
320
"finish_reason" : str (stop_reason ),
321
321
}
322
322
]
@@ -371,7 +371,7 @@ def start_tool_call_span(self, tool: ToolUse, parent_span: Optional[Span] = None
371
371
"type" : "tool_call" ,
372
372
"name" : tool ["name" ],
373
373
"id" : tool ["toolUseId" ],
374
- "arguments" : [{ "content" : tool ["input" ]} ],
374
+ "arguments" : tool ["input" ],
375
375
}
376
376
],
377
377
}
@@ -426,7 +426,7 @@ def end_tool_call_span(
426
426
{
427
427
"type" : "tool_call_response" ,
428
428
"id" : tool_result .get ("toolUseId" , "" ),
429
- "result " : tool_result .get ("content" ),
429
+ "response " : tool_result .get ("content" ),
430
430
}
431
431
],
432
432
}
@@ -513,7 +513,7 @@ def end_event_loop_cycle_span(
513
513
[
514
514
{
515
515
"role" : tool_result_message ["role" ],
516
- "parts" : [{ "type" : "text" , "content" : tool_result_message ["content" ]}] ,
516
+ "parts" : self . _map_content_blocks_to_otel_parts ( tool_result_message ["content" ]) ,
517
517
}
518
518
]
519
519
)
@@ -643,19 +643,23 @@ def start_multiagent_span(
643
643
)
644
644
645
645
span = self ._start_span (operation , attributes = attributes , span_kind = trace_api .SpanKind .CLIENT )
646
- content = serialize (task ) if isinstance (task , list ) else task
647
646
648
647
if self .use_latest_genai_conventions :
648
+ parts : list [dict [str , Any ]] = []
649
+ if isinstance (task , list ):
650
+ parts = self ._map_content_blocks_to_otel_parts (task )
651
+ else :
652
+ parts = [{"type" : "text" , "content" : task }]
649
653
self ._add_event (
650
654
span ,
651
655
"gen_ai.client.inference.operation.details" ,
652
- {"gen_ai.input.messages" : serialize ([{"role" : "user" , "parts" : [{ "type" : "text" , "content" : task }] }])},
656
+ {"gen_ai.input.messages" : serialize ([{"role" : "user" , "parts" : parts }])},
653
657
)
654
658
else :
655
659
self ._add_event (
656
660
span ,
657
661
"gen_ai.user.message" ,
658
- event_attributes = {"content" : content },
662
+ event_attributes = {"content" : serialize ( task ) if isinstance ( task , list ) else task },
659
663
)
660
664
661
665
return span
@@ -727,7 +731,7 @@ def _add_event_messages(self, span: Span, messages: Messages) -> None:
727
731
input_messages : list = []
728
732
for message in messages :
729
733
input_messages .append (
730
- {"role" : message ["role" ], "parts" : [{ "type" : "text" , "content" : message ["content" ]}] }
734
+ {"role" : message ["role" ], "parts" : self . _map_content_blocks_to_otel_parts ( message ["content" ]) }
731
735
)
732
736
self ._add_event (
733
737
span , "gen_ai.client.inference.operation.details" , {"gen_ai.input.messages" : serialize (input_messages )}
@@ -740,6 +744,41 @@ def _add_event_messages(self, span: Span, messages: Messages) -> None:
740
744
{"content" : serialize (message ["content" ])},
741
745
)
742
746
747
+ def _map_content_blocks_to_otel_parts (self , content_blocks : list [ContentBlock ]) -> list [dict [str , Any ]]:
748
+ """Map ContentBlock objects to OpenTelemetry parts format."""
749
+ parts : list [dict [str , Any ]] = []
750
+
751
+ for block in content_blocks :
752
+ if "text" in block :
753
+ # Standard TextPart
754
+ parts .append ({"type" : "text" , "content" : block ["text" ]})
755
+ elif "toolUse" in block :
756
+ # Standard ToolCallRequestPart
757
+ tool_use = block ["toolUse" ]
758
+ parts .append (
759
+ {
760
+ "type" : "tool_call" ,
761
+ "name" : tool_use ["name" ],
762
+ "id" : tool_use ["toolUseId" ],
763
+ "arguments" : tool_use ["input" ],
764
+ }
765
+ )
766
+ elif "toolResult" in block :
767
+ # Standard ToolCallResponsePart
768
+ tool_result = block ["toolResult" ]
769
+ parts .append (
770
+ {
771
+ "type" : "tool_call_response" ,
772
+ "id" : tool_result ["toolUseId" ],
773
+ "response" : tool_result ["content" ],
774
+ }
775
+ )
776
+ else :
777
+ # For all other ContentBlock types, use the key as type and value as content
778
+ for key , value in block .items ():
779
+ parts .append ({"type" : key , "content" : value })
780
+ return parts
781
+
743
782
744
783
# Singleton instance for global access
745
784
_tracer_instance = None
0 commit comments