1818
1919import logging
2020import threading
21- from typing import Any , Optional
21+ from typing import Any , Mapping , Optional
2222
2323from opentelemetry .context import Context
2424from opentelemetry .sdk .trace import SpanProcessor
2525from opentelemetry .trace import Span as OtelSpan
2626from opentelemetry .trace import SpanKind
2727
2828from .semantic_conventions import (
29+ AUTOGEN_LIVE_SPAN_MARKER ,
2930 AUTOGEN_PROVIDER_NAME ,
31+ GEN_AI_AGENT_DESCRIPTION ,
32+ GEN_AI_AGENT_ID ,
33+ GEN_AI_AGENT_NAME ,
3034 GEN_AI_OPERATION_NAME ,
3135 GEN_AI_PROVIDER_NAME ,
3236 GEN_AI_SPAN_KIND ,
4246 f"{ GenAIOperation .EXECUTE_TOOL } " ,
4347 f"{ GenAIOperation .INVOKE_AGENT } " ,
4448)
49+ _AUTOGEN_PRIVATE_ATTRS = frozenset (
50+ {
51+ "autogen.code.block_count" ,
52+ "autogen.code.exit_code" ,
53+ "autogen.code.retry_attempt" ,
54+ "autogen.handoff.context_count" ,
55+ "autogen.handoff.source" ,
56+ "autogen.handoff.target" ,
57+ "autogen.memory.result_count" ,
58+ "autogen.team.message_count" ,
59+ "autogen.team.participants" ,
60+ "autogen.team.stop_reason" ,
61+ "autogen.team.type" ,
62+ "autogen.user_input.request_id" ,
63+ }
64+ )
65+ _CREATE_AGENT_LIFECYCLE_ATTRS = (
66+ GEN_AI_AGENT_DESCRIPTION ,
67+ GEN_AI_AGENT_ID ,
68+ GEN_AI_AGENT_NAME ,
69+ GEN_AI_OPERATION_NAME ,
70+ GEN_AI_PROVIDER_NAME ,
71+ GEN_AI_SPAN_KIND ,
72+ GEN_AI_SYSTEM ,
73+ "gen_ai.agent.version" ,
74+ "gen_ai.system_instructions" ,
75+ "gen_ai.tool.definitions" ,
76+ )
4577
4678
4779def _attr_value (span : Any , key : str ) -> Any :
80+ if isinstance (span , Mapping ):
81+ return span .get (key )
4882 attrs = getattr (span , "_attributes" , None )
4983 if attrs is not None :
5084 try :
@@ -57,6 +91,46 @@ def _attr_value(span: Any, key: str) -> Any:
5791 return None
5892
5993
94+ def _attr_values (value : Any ) -> tuple [str , ...]:
95+ if value is None :
96+ return ()
97+ if isinstance (value , (list , tuple )):
98+ values = value
99+ else :
100+ values = (value ,)
101+ return tuple (str (item ).lower () for item in values if item is not None )
102+
103+
104+ def _span_attrs (span : Any ) -> Mapping [Any , Any ]:
105+ if isinstance (span , Mapping ):
106+ return span
107+ attrs = getattr (span , "_attributes" , None )
108+ if attrs is not None :
109+ return attrs
110+ attrs = getattr (span , "attributes" , None )
111+ if attrs is not None :
112+ return attrs
113+ return {}
114+
115+
116+ def _has_live_autogen_marker (live : Any | None ) -> bool :
117+ if live is None :
118+ return False
119+ return (
120+ getattr (live , AUTOGEN_LIVE_SPAN_MARKER , None ) == AUTOGEN_PROVIDER_NAME
121+ )
122+
123+
124+ def _has_autogen_marker (readable : Any , live : Any | None = None ) -> bool :
125+ if _has_live_autogen_marker (live ):
126+ return True
127+ for key in (GEN_AI_SYSTEM , GEN_AI_PROVIDER_NAME ):
128+ if AUTOGEN_PROVIDER_NAME in _attr_values (_attr_value (readable , key )):
129+ return True
130+ attrs = _span_attrs (readable )
131+ return any (str (key ) in _AUTOGEN_PRIVATE_ATTRS for key in attrs )
132+
133+
60134def _set_attr (live_span : OtelSpan , key : str , value : Any ) -> None :
61135 if value is None :
62136 return
@@ -138,19 +212,9 @@ def _set_otel_span_kind(
138212
139213
140214def _is_autogen_span (
141- name : str , operation : Optional [str ], readable : Any
215+ _name : str , _operation : Optional [str ], readable : Any , live : Any | None
142216) -> bool :
143- if _attr_value (readable , GEN_AI_SYSTEM ) == AUTOGEN_PROVIDER_NAME :
144- return True
145- if _attr_value (readable , GEN_AI_PROVIDER_NAME ) == AUTOGEN_PROVIDER_NAME :
146- return True
147- if operation in {
148- GenAIOperation .CREATE_AGENT ,
149- GenAIOperation .EXECUTE_TOOL ,
150- GenAIOperation .INVOKE_AGENT ,
151- }:
152- return True
153- return name .startswith (_AUTOGEN_NAME_PREFIXES )
217+ return _has_autogen_marker (readable , live )
154218
155219
156220def _classify_span (
@@ -208,10 +272,15 @@ def on_end(self, span: Any) -> None:
208272 name = span .name or ""
209273 existing_op = _attr_value (span , GEN_AI_OPERATION_NAME )
210274 operation = existing_op if isinstance (existing_op , str ) else None
211- if not _is_autogen_span (name , operation , span ):
275+ if not _is_autogen_span (name , operation , span , live ):
212276 return
213277
214278 span_kind , op_name = _classify_span (name , operation )
279+ if op_name == GenAIOperation .CREATE_AGENT :
280+ for attr_name in _CREATE_AGENT_LIFECYCLE_ATTRS :
281+ _delete_attr_on_both (live , span , attr_name )
282+ return
283+
215284 if span_kind and not _attr_value (span , GEN_AI_SPAN_KIND ):
216285 _set_attr_on_both (live , span , GEN_AI_SPAN_KIND , span_kind )
217286 if not operation :
@@ -235,7 +304,11 @@ def on_end(self, span: Any) -> None:
235304
236305 if span_kind == GenAISpanKind .LLM :
237306 _set_otel_span_kind (live , span , SpanKind .CLIENT )
238- elif span_kind in {GenAISpanKind .AGENT , GenAISpanKind .TOOL }:
307+ elif span_kind == GenAISpanKind .AGENT and op_name == (
308+ GenAIOperation .INVOKE_AGENT
309+ ):
310+ _set_otel_span_kind (live , span , SpanKind .INTERNAL )
311+ elif span_kind == GenAISpanKind .TOOL :
239312 _set_otel_span_kind (live , span , SpanKind .INTERNAL )
240313 except Exception as exc : # pragma: no cover - defensive
241314 logger .warning ("AutoGenSemanticProcessor.on_end failed: %s" , exc )
0 commit comments