-
Notifications
You must be signed in to change notification settings - Fork 1.4k
trace the output tool call but not count it #3398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f1cd425
419d5ac
0b287bd
11f6fe7
b8ad3df
159cb21
7bbb7e3
e26e10d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -108,24 +108,21 @@ async def handle_call( | |
| raise ValueError('ToolManager has not been prepared for a run step yet') # pragma: no cover | ||
|
|
||
| if (tool := self.tools.get(call.tool_name)) and tool.tool_def.kind == 'output': | ||
| # Output tool calls are not traced and not counted | ||
| return await self._call_tool( | ||
| call, | ||
| allow_partial=allow_partial, | ||
| wrap_validation_errors=wrap_validation_errors, | ||
| approved=approved, | ||
| ) | ||
| output_tool_flag = True | ||
| else: | ||
| return await self._call_function_tool( | ||
| call, | ||
| allow_partial=allow_partial, | ||
| wrap_validation_errors=wrap_validation_errors, | ||
| approved=approved, | ||
| tracer=self.ctx.tracer, | ||
| include_content=self.ctx.trace_include_content, | ||
| instrumentation_version=self.ctx.instrumentation_version, | ||
| usage=self.ctx.usage, | ||
| ) | ||
| output_tool_flag = False | ||
|
|
||
| return await self._call_function_tool( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This name is no longer accurate, because we also use it for output tools now. Can we either merge the 2 methods (as there's no need for a separate |
||
| call, | ||
| allow_partial=allow_partial, | ||
| wrap_validation_errors=wrap_validation_errors, | ||
| approved=approved, | ||
| tracer=self.ctx.tracer, | ||
| include_content=self.ctx.trace_include_content, | ||
| instrumentation_version=self.ctx.instrumentation_version, | ||
| usage=self.ctx.usage, | ||
| output_tool_flag=output_tool_flag, | ||
| ) | ||
|
|
||
| async def _call_tool( | ||
| self, | ||
|
|
@@ -213,16 +210,22 @@ async def _call_function_tool( | |
| include_content: bool, | ||
| instrumentation_version: int, | ||
| usage: RunUsage, | ||
| output_tool_flag: bool = False, | ||
| ) -> Any: | ||
| """See <https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/#execute-tool-span>.""" | ||
| instrumentation_names = InstrumentationNames.for_version(instrumentation_version) | ||
|
|
||
| if output_tool_flag: | ||
| tool_name = 'output tool' | ||
| else: | ||
| tool_name = call.tool_name | ||
|
|
||
| span_attributes = { | ||
| 'gen_ai.tool.name': call.tool_name, | ||
| 'gen_ai.tool.name': tool_name, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This arg should always have the actual tool name, so please change this back |
||
| # NOTE: this means `gen_ai.tool.call.id` will be included even if it was generated by pydantic-ai | ||
| 'gen_ai.tool.call.id': call.tool_call_id, | ||
| **({instrumentation_names.tool_arguments_attr: call.args_as_json_str()} if include_content else {}), | ||
| 'logfire.msg': f'running tool: {call.tool_name}', | ||
| 'logfire.msg': f'running tool: {tool_name}', | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where I think we should say |
||
| # add the JSON schema so these attributes are formatted nicely in Logfire | ||
| 'logfire.json_schema': json.dumps( | ||
| { | ||
|
|
@@ -243,7 +246,7 @@ async def _call_function_tool( | |
| ), | ||
| } | ||
| with tracer.start_as_current_span( | ||
| instrumentation_names.get_tool_span_name(call.tool_name), | ||
| instrumentation_names.get_tool_span_name(tool_name), | ||
| attributes=span_attributes, | ||
| ) as span: | ||
| try: | ||
|
|
@@ -253,7 +256,9 @@ async def _call_function_tool( | |
| wrap_validation_errors=wrap_validation_errors, | ||
| approved=approved, | ||
| ) | ||
| usage.tool_calls += 1 | ||
| if not output_tool_flag: | ||
| # Output tool calls are not counted | ||
| usage.tool_calls += 1 | ||
|
|
||
| except ToolRetryError as e: | ||
| part = e.tool_retry | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be simplified as
is_output_tool = ..., without theif/else, but I think we don't need this check here anymore since we can also do it inside the call method, now that we always use that. So please move this check into the method.