12
12
from typing_extensions import assert_never
13
13
14
14
from . import messages as _messages
15
+ from ._instrumentation import InstrumentationNames
15
16
from ._run_context import AgentDepsT , RunContext
16
17
from .exceptions import ModelRetry , ToolRetryError , UnexpectedModelBehavior
17
18
from .messages import ToolCallPart
@@ -115,6 +116,7 @@ async def handle_call(
115
116
wrap_validation_errors ,
116
117
self .ctx .tracer ,
117
118
self .ctx .trace_include_content ,
119
+ self .ctx .instrumentation_version ,
118
120
usage_limits ,
119
121
)
120
122
@@ -203,15 +205,18 @@ async def _call_tool_traced(
203
205
allow_partial : bool ,
204
206
wrap_validation_errors : bool ,
205
207
tracer : Tracer ,
206
- include_content : bool = False ,
208
+ include_content : bool ,
209
+ instrumentation_version : int ,
207
210
usage_limits : UsageLimits | None = None ,
208
211
) -> Any :
209
212
"""See <https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/#execute-tool-span>."""
213
+ instrumentation_names = InstrumentationNames .for_version (instrumentation_version )
214
+
210
215
span_attributes = {
211
216
'gen_ai.tool.name' : call .tool_name ,
212
217
# NOTE: this means `gen_ai.tool.call.id` will be included even if it was generated by pydantic-ai
213
218
'gen_ai.tool.call.id' : call .tool_call_id ,
214
- ** ({'tool_arguments' : call .args_as_json_str ()} if include_content else {}),
219
+ ** ({instrumentation_names . tool_arguments_attr : call .args_as_json_str ()} if include_content else {}),
215
220
'logfire.msg' : f'running tool: { call .tool_name } ' ,
216
221
# add the JSON schema so these attributes are formatted nicely in Logfire
217
222
'logfire.json_schema' : json .dumps (
@@ -220,8 +225,8 @@ async def _call_tool_traced(
220
225
'properties' : {
221
226
** (
222
227
{
223
- 'tool_arguments' : {'type' : 'object' },
224
- 'tool_response' : {'type' : 'object' },
228
+ instrumentation_names . tool_arguments_attr : {'type' : 'object' },
229
+ instrumentation_names . tool_result_attr : {'type' : 'object' },
225
230
}
226
231
if include_content
227
232
else {}
@@ -232,18 +237,21 @@ async def _call_tool_traced(
232
237
}
233
238
),
234
239
}
235
- with tracer .start_as_current_span ('running tool' , attributes = span_attributes ) as span :
240
+ with tracer .start_as_current_span (
241
+ instrumentation_names .get_tool_span_name (call .tool_name ),
242
+ attributes = span_attributes ,
243
+ ) as span :
236
244
try :
237
245
tool_result = await self ._call_tool (call , allow_partial , wrap_validation_errors , usage_limits )
238
246
except ToolRetryError as e :
239
247
part = e .tool_retry
240
248
if include_content and span .is_recording ():
241
- span .set_attribute ('tool_response' , part .model_response ())
249
+ span .set_attribute (instrumentation_names . tool_result_attr , part .model_response ())
242
250
raise e
243
251
244
252
if include_content and span .is_recording ():
245
253
span .set_attribute (
246
- 'tool_response' ,
254
+ instrumentation_names . tool_result_attr ,
247
255
tool_result
248
256
if isinstance (tool_result , str )
249
257
else _messages .tool_return_ta .dump_json (tool_result ).decode (),
0 commit comments