Skip to content

Commit a753b0a

Browse files
authored
Merge branch 'main' into use-click-for-cli
2 parents 615c1d5 + 6cf43ea commit a753b0a

File tree

24 files changed

+2162
-128
lines changed

24 files changed

+2162
-128
lines changed

docs/logfire.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,14 @@ The following providers have dedicated documentation on Pydantic AI:
268268

269269
### Configuring data format
270270

271-
Pydantic AI follows the [OpenTelemetry Semantic Conventions for Generative AI systems](https://opentelemetry.io/docs/specs/semconv/gen-ai/). Specifically, it follows version 1.37.0 of the conventions by default. To use [version 1.36.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.36.0/docs/gen-ai/README.md) or older, pass [`InstrumentationSettings(version=1)`][pydantic_ai.models.instrumented.InstrumentationSettings] (the default is `version=2`). Moreover, those semantic conventions specify that messages should be captured as individual events (logs) that are children of the request span, whereas by default, Pydantic AI instead collects these events into a JSON array which is set as a single large attribute called `events` on the request span. To change this, use `event_mode='logs'`:
271+
Pydantic AI follows the [OpenTelemetry Semantic Conventions for Generative AI systems](https://opentelemetry.io/docs/specs/semconv/gen-ai/). Specifically, it follows version 1.37.0 of the conventions by default, with a few exceptions. Certain span and attribute names are not spec compliant by default for compatibility reasons, but can be made compliant by passing [`InstrumentationSettings(version=3)`][pydantic_ai.models.instrumented.InstrumentationSettings] (the default is currently `version=2`). This will change the following:
272+
273+
- The span name `agent run` becomes `invoke_agent {gen_ai.agent.name}` (with the agent name filled in)
274+
- The span name `running tool` becomes `execute_tool {gen_ai.tool.name}` (with the tool name filled in)
275+
- The attribute name `tool_arguments` becomes `gen_ai.tool.call.arguments`
276+
- The attribute name `tool_response` becomes `gen_ai.tool.call.result`
277+
278+
To use [OpenTelemetry semantic conventions version 1.36.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.36.0/docs/gen-ai/README.md) or older, pass [`InstrumentationSettings(version=1)`][pydantic_ai.models.instrumented.InstrumentationSettings]. Moreover, those semantic conventions specify that messages should be captured as individual events (logs) that are children of the request span, whereas by default, Pydantic AI instead collects these events into a JSON array which is set as a single large attribute called `events` on the request span. To change this, use `event_mode='logs'`:
272279

273280
```python {title="instrumentation_settings_event_mode.py"}
274281
import logfire

pydantic_ai_slim/pydantic_ai/_agent_graph.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -795,16 +795,14 @@ async def process_tool_calls( # noqa: C901
795795
# Then, we handle function tool calls
796796
calls_to_run: list[_messages.ToolCallPart] = []
797797
if final_result and ctx.deps.end_strategy == 'early':
798-
output_parts.extend(
799-
[
798+
for call in tool_calls_by_kind['function']:
799+
output_parts.append(
800800
_messages.ToolReturnPart(
801801
tool_name=call.tool_name,
802802
content='Tool not executed - a final result was already processed.',
803803
tool_call_id=call.tool_call_id,
804804
)
805-
for call in tool_calls_by_kind['function']
806-
]
807-
)
805+
)
808806
else:
809807
calls_to_run.extend(tool_calls_by_kind['function'])
810808

@@ -850,14 +848,17 @@ async def process_tool_calls( # noqa: C901
850848
if tool_call_results is None:
851849
calls = [*tool_calls_by_kind['external'], *tool_calls_by_kind['unapproved']]
852850
if final_result:
853-
for call in calls:
854-
output_parts.append(
855-
_messages.ToolReturnPart(
856-
tool_name=call.tool_name,
857-
content='Tool not executed - a final result was already processed.',
858-
tool_call_id=call.tool_call_id,
851+
# If the run was already determined to end on deferred tool calls,
852+
# we shouldn't insert return parts as the deferred tools will still get a real result.
853+
if not isinstance(final_result.output, _output.DeferredToolRequests):
854+
for call in calls:
855+
output_parts.append(
856+
_messages.ToolReturnPart(
857+
tool_name=call.tool_name,
858+
content='Tool not executed - a final result was already processed.',
859+
tool_call_id=call.tool_call_id,
860+
)
859861
)
860-
)
861862
elif calls:
862863
deferred_calls['external'].extend(tool_calls_by_kind['external'])
863864
deferred_calls['unapproved'].extend(tool_calls_by_kind['unapproved'])

pydantic_ai_slim/pydantic_ai/agent/__init__.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ def iter(
426426
usage: _usage.RunUsage | None = None,
427427
infer_name: bool = True,
428428
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
429+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
429430
) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, OutputDataT]]: ...
430431

431432
@overload
@@ -443,6 +444,7 @@ def iter(
443444
usage: _usage.RunUsage | None = None,
444445
infer_name: bool = True,
445446
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
447+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
446448
) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, RunOutputDataT]]: ...
447449

448450
@asynccontextmanager
@@ -460,6 +462,7 @@ async def iter(
460462
usage: _usage.RunUsage | None = None,
461463
infer_name: bool = True,
462464
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
465+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
463466
) -> AsyncIterator[AgentRun[AgentDepsT, Any]]:
464467
"""A contextmanager which can be used to iterate over the agent graph's nodes as they are executed.
465468
@@ -532,6 +535,7 @@ async def main():
532535
usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
533536
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
534537
toolsets: Optional additional toolsets for this run.
538+
builtin_tools: Optional additional builtin tools for this run.
535539
536540
Returns:
537541
The result of the run.
@@ -603,7 +607,16 @@ async def get_instructions(run_context: RunContext[AgentDepsT]) -> str | None:
603607
else:
604608
instrumentation_settings = None
605609
tracer = NoOpTracer()
606-
610+
if builtin_tools:
611+
# Deduplicate builtin tools passed to the agent and the run based on type
612+
builtin_tools = list(
613+
{
614+
**({type(tool): tool for tool in self._builtin_tools or []}),
615+
**({type(tool): tool for tool in builtin_tools}),
616+
}.values()
617+
)
618+
else:
619+
builtin_tools = list(self._builtin_tools)
607620
graph_deps = _agent_graph.GraphAgentDeps[AgentDepsT, RunOutputDataT](
608621
user_deps=deps,
609622
prompt=user_prompt,
@@ -616,7 +629,7 @@ async def get_instructions(run_context: RunContext[AgentDepsT]) -> str | None:
616629
output_schema=output_schema,
617630
output_validators=output_validators,
618631
history_processors=self.history_processors,
619-
builtin_tools=list(self._builtin_tools),
632+
builtin_tools=builtin_tools,
620633
tool_manager=tool_manager,
621634
tracer=tracer,
622635
get_instructions=get_instructions,

0 commit comments

Comments
 (0)