1+ import json
2+ from typing import (
3+ Any ,
4+ Iterable ,
5+ Tuple ,
6+ Dict ,
7+ List ,
8+ )
9+ from opentelemetry .util .types import AttributeValue
10+ from opentelemetry .semconv ._incubating .attributes import (
11+ gen_ai_attributes as GenAIAttributes ,
12+ )
13+
14+ class AgentRunRequestExtractor (object ):
15+
16+ def extract (self , agent : Any , arguments : Dict [Any , Any ]) -> Iterable [Tuple [str , AttributeValue ]]:
17+ if agent .name :
18+ yield GenAIAttributes .GEN_AI_AGENT_NAME , f"{ agent .name } "
19+
20+ if agent .session_id :
21+ yield GenAIAttributes .GEN_AI_AGENT_ID , f"{ agent .session_id } "
22+
23+ if agent .knowledge :
24+ yield f"{ GenAIAttributes .GEN_AI_AGENT_NAME } .knowledge" , f"{ agent .knowledge .__class__ .__name__ } "
25+
26+ if agent .tools :
27+ tool_names = []
28+ from agno .tools .toolkit import Toolkit
29+ from agno .tools .function import Function
30+ for tool in agent .tools :
31+ if isinstance (tool , Function ):
32+ tool_names .append (tool .name )
33+ elif isinstance (tool , Toolkit ):
34+ tool_names .extend ([f for f in tool .functions .keys ()])
35+ elif callable (tool ):
36+ tool_names .append (tool .__name__ )
37+ else :
38+ tool_names .append (str (tool ))
39+ yield GenAIAttributes .GEN_AI_TOOL_NAME , ", " .join (tool_names )
40+
41+ for key in arguments .keys ():
42+ if key == "run_response" :
43+ yield GenAIAttributes .GEN_AI_RESPONSE_ID , f"{ arguments [key ].run_id } "
44+ elif key == "run_messages" :
45+ messages = arguments [key ].messages
46+ for idx in range (len (messages )):
47+ message = messages [idx ]
48+ yield f"{ GenAIAttributes .GEN_AI_PROMPT } .{ idx } .message" , f"{ json .dumps (message .to_dict (), indent = 2 )} "
49+ elif key == "response_format" :
50+ yield GenAIAttributes .GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT , f"{ arguments [key ]} "
51+
52+ class AgentRunResponseExtractor (object ):
53+
54+ def extract (self , response : Any ) -> Iterable [Tuple [str , AttributeValue ]]:
55+ yield GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS , f"{ response .to_json ()} "
56+
57+ class FunctionCallRequestExtractor (object ):
58+
59+ def extract (self , function_call : Any ) -> Iterable [Tuple [str , AttributeValue ]]:
60+
61+ if function_call .function .name :
62+ yield GenAIAttributes .GEN_AI_TOOL_NAME , f"{ function_call .function .name } "
63+
64+ if function_call .function .description :
65+ yield GenAIAttributes .GEN_AI_TOOL_DESCRIPTION , f"{ function_call .function .description } "
66+
67+ if function_call .call_id :
68+ yield GenAIAttributes .GEN_AI_TOOL_CALL_ID , f"{ function_call .call_id } "
69+
70+ if function_call .arguments :
71+ yield f"{ GenAIAttributes .GEN_AI_TOOL_TYPE } .arguments" , f"{ json .dumps (function_call .arguments , indent = 2 )} "
72+
73+ class FunctionCallResponseExtractor (object ):
74+
75+ def extract (self , response : Any ) -> Iterable [Tuple [str , AttributeValue ]]:
76+ yield f"{ GenAIAttributes .GEN_AI_TOOL_TYPE } .response" , f"{ response .result } "
77+
78+ class ModelRequestExtractor (object ):
79+
80+ def extract (self , model : Any , arguments : Dict [Any , Any ]) -> Iterable [Tuple [str , AttributeValue ]]:
81+
82+ request_kwargs = {}
83+ if getattr (model , "request_kwargs" , None ):
84+ request_kwargs = model .request_kwargs
85+ if getattr (model , "request_params" , None ):
86+ request_kwargs = model .request_params
87+ if getattr (model , "get_request_kwargs" , None ):
88+ request_kwargs = model .get_request_kwargs ()
89+ if getattr (model , "get_request_params" , None ):
90+ request_kwargs = model .get_request_params ()
91+
92+ if request_kwargs :
93+ yield GenAIAttributes .GEN_AI_REQUEST_MODEL , f"{ json .dumps (request_kwargs , indent = 2 )} "
94+
95+ for key in arguments .keys ():
96+ if key == "response_format" :
97+ yield GenAIAttributes .GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT ,f"{ arguments [key ]} "
98+ elif key == "messages" :
99+ messages = arguments ["messages" ]
100+ for idx in range (len (messages )):
101+ message = messages [idx ]
102+ yield f"{ GenAIAttributes .GEN_AI_PROMPT } .{ idx } .message" , f"{ json .dumps (message .to_dict (), indent = 2 )} "
103+ elif key == "tools" :
104+ tools = arguments ["tools" ]
105+ for idx in range (len (tools )):
106+ yield f"{ GenAIAttributes .GEN_AI_TOOL_DESCRIPTION } .{ idx } " , f"{ json .dumps (tools [idx ], indent = 2 )} "
107+
108+ class ModelResponseExtractor (object ):
109+
110+ def extract (self , responses : List [Any ]) -> Iterable [Tuple [str , AttributeValue ]]:
111+ content = ""
112+ for response in responses :
113+ # basic response fields
114+ if getattr (response , "role" , None ):
115+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .role" , response .role
116+ if getattr (response , "content" , None ):
117+ content += response .content
118+ if getattr (response , "audio" , None ):
119+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .audio" , json .dumps (response .audio .to_dict (), indent = 2 )
120+ if getattr (response , "image" , None ):
121+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .image" , json .dumps (response .image .to_dict (), indent = 2 )
122+ for idx , exec in enumerate (getattr (response , "tool_executions" , []) or []):
123+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .tool_executions.{ idx } " , json .dumps (exec .to_dict (), indent = 2 )
124+ # other metadata
125+ if getattr (response , "event" , None ):
126+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .event" , response .event
127+ if getattr (response , "provider_data" , None ):
128+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .provider_data" , json .dumps (response .provider_data , indent = 2 )
129+ if getattr (response , "thinking" , None ):
130+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .thinking" , response .thinking
131+ if getattr (response , "redacted_thinking" , None ):
132+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .redacted_thinking" , response .redacted_thinking
133+ if getattr (response , "reasoning_content" , None ):
134+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .reasoning_content" , response .reasoning_content
135+ if getattr (response , "extra" , None ):
136+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .extra" , json .dumps (response .extra , indent = 2 )
137+ if len (content ):
138+ yield f"{ GenAIAttributes .GEN_AI_RESPONSE_FINISH_REASONS } .content" , f"{ content } "
139+
0 commit comments