Skip to content

Commit 83d14b1

Browse files
authored
Merge branch 'pydantic:main' into main
2 parents 6f99fb2 + 78fb707 commit 83d14b1

File tree

11 files changed

+298
-14
lines changed

11 files changed

+298
-14
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/__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,

pydantic_ai_slim/pydantic_ai/agent/abstract.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
usage as _usage,
2626
)
2727
from .._tool_manager import ToolManager
28+
from ..builtin_tools import AbstractBuiltinTool
2829
from ..output import OutputDataT, OutputSpec
2930
from ..result import AgentStream, FinalResult, StreamedRunResult
3031
from ..run import AgentRun, AgentRunResult, AgentRunResultEvent
@@ -137,6 +138,7 @@ async def run(
137138
usage: _usage.RunUsage | None = None,
138139
infer_name: bool = True,
139140
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
141+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
140142
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
141143
) -> AgentRunResult[OutputDataT]: ...
142144

@@ -155,6 +157,7 @@ async def run(
155157
usage: _usage.RunUsage | None = None,
156158
infer_name: bool = True,
157159
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
160+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
158161
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
159162
) -> AgentRunResult[RunOutputDataT]: ...
160163

@@ -172,6 +175,7 @@ async def run(
172175
usage: _usage.RunUsage | None = None,
173176
infer_name: bool = True,
174177
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
178+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
175179
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
176180
) -> AgentRunResult[Any]:
177181
"""Run the agent with a user prompt in async mode.
@@ -205,6 +209,7 @@ async def main():
205209
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
206210
toolsets: Optional additional toolsets for this run.
207211
event_stream_handler: Optional handler for events from the model's streaming response and the agent's execution of tools to use for this run.
212+
builtin_tools: Optional additional builtin tools for this run.
208213
209214
Returns:
210215
The result of the run.
@@ -225,6 +230,7 @@ async def main():
225230
usage_limits=usage_limits,
226231
usage=usage,
227232
toolsets=toolsets,
233+
builtin_tools=builtin_tools,
228234
) as agent_run:
229235
async for node in agent_run:
230236
if event_stream_handler is not None and (
@@ -251,6 +257,7 @@ def run_sync(
251257
usage: _usage.RunUsage | None = None,
252258
infer_name: bool = True,
253259
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
260+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
254261
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
255262
) -> AgentRunResult[OutputDataT]: ...
256263

@@ -269,6 +276,7 @@ def run_sync(
269276
usage: _usage.RunUsage | None = None,
270277
infer_name: bool = True,
271278
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
279+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
272280
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
273281
) -> AgentRunResult[RunOutputDataT]: ...
274282

@@ -286,6 +294,7 @@ def run_sync(
286294
usage: _usage.RunUsage | None = None,
287295
infer_name: bool = True,
288296
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
297+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
289298
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
290299
) -> AgentRunResult[Any]:
291300
"""Synchronously run the agent with a user prompt.
@@ -318,6 +327,7 @@ def run_sync(
318327
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
319328
toolsets: Optional additional toolsets for this run.
320329
event_stream_handler: Optional handler for events from the model's streaming response and the agent's execution of tools to use for this run.
330+
builtin_tools: Optional additional builtin tools for this run.
321331
322332
Returns:
323333
The result of the run.
@@ -338,6 +348,7 @@ def run_sync(
338348
usage=usage,
339349
infer_name=False,
340350
toolsets=toolsets,
351+
builtin_tools=builtin_tools,
341352
event_stream_handler=event_stream_handler,
342353
)
343354
)
@@ -357,6 +368,7 @@ def run_stream(
357368
usage: _usage.RunUsage | None = None,
358369
infer_name: bool = True,
359370
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
371+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
360372
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
361373
) -> AbstractAsyncContextManager[result.StreamedRunResult[AgentDepsT, OutputDataT]]: ...
362374

@@ -375,6 +387,7 @@ def run_stream(
375387
usage: _usage.RunUsage | None = None,
376388
infer_name: bool = True,
377389
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
390+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
378391
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
379392
) -> AbstractAsyncContextManager[result.StreamedRunResult[AgentDepsT, RunOutputDataT]]: ...
380393

@@ -393,6 +406,7 @@ async def run_stream( # noqa C901
393406
usage: _usage.RunUsage | None = None,
394407
infer_name: bool = True,
395408
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
409+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
396410
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
397411
) -> AsyncIterator[result.StreamedRunResult[AgentDepsT, Any]]:
398412
"""Run the agent with a user prompt in async streaming mode.
@@ -432,6 +446,7 @@ async def main():
432446
usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
433447
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
434448
toolsets: Optional additional toolsets for this run.
449+
builtin_tools: Optional additional builtin tools for this run.
435450
event_stream_handler: Optional handler for events from the model's streaming response and the agent's execution of tools to use for this run.
436451
It will receive all the events up until the final result is found, which you can then read or stream from inside the context manager.
437452
Note that it does _not_ receive any events after the final result is found.
@@ -459,6 +474,7 @@ async def main():
459474
usage=usage,
460475
infer_name=False,
461476
toolsets=toolsets,
477+
builtin_tools=builtin_tools,
462478
) as agent_run:
463479
first_node = agent_run.next_node # start with the first node
464480
assert isinstance(first_node, _agent_graph.UserPromptNode) # the first node should be a user prompt node
@@ -568,6 +584,7 @@ def run_stream_events(
568584
usage: _usage.RunUsage | None = None,
569585
infer_name: bool = True,
570586
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
587+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
571588
) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[OutputDataT]]: ...
572589

573590
@overload
@@ -585,6 +602,7 @@ def run_stream_events(
585602
usage: _usage.RunUsage | None = None,
586603
infer_name: bool = True,
587604
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
605+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
588606
) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[RunOutputDataT]]: ...
589607

590608
def run_stream_events(
@@ -601,6 +619,7 @@ def run_stream_events(
601619
usage: _usage.RunUsage | None = None,
602620
infer_name: bool = True,
603621
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
622+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
604623
) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[Any]]:
605624
"""Run the agent with a user prompt in async mode and stream events from the run.
606625
@@ -646,6 +665,7 @@ async def main():
646665
usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
647666
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
648667
toolsets: Optional additional toolsets for this run.
668+
builtin_tools: Optional additional builtin tools for this run.
649669
650670
Returns:
651671
An async iterable of stream events `AgentStreamEvent` and finally a `AgentRunResultEvent` with the final
@@ -666,6 +686,7 @@ async def main():
666686
usage=usage,
667687
infer_name=infer_name,
668688
toolsets=toolsets,
689+
builtin_tools=builtin_tools,
669690
)
670691

671692
async def _run_stream_events(
@@ -682,6 +703,7 @@ async def _run_stream_events(
682703
usage: _usage.RunUsage | None = None,
683704
infer_name: bool = True,
684705
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
706+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
685707
) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[Any]]:
686708
send_stream, receive_stream = anyio.create_memory_object_stream[
687709
_messages.AgentStreamEvent | AgentRunResultEvent[Any]
@@ -707,6 +729,7 @@ async def run_agent() -> AgentRunResult[Any]:
707729
usage=usage,
708730
infer_name=infer_name,
709731
toolsets=toolsets,
732+
builtin_tools=builtin_tools,
710733
event_stream_handler=event_stream_handler,
711734
)
712735

@@ -734,6 +757,7 @@ def iter(
734757
usage: _usage.RunUsage | None = None,
735758
infer_name: bool = True,
736759
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
760+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
737761
) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, OutputDataT]]: ...
738762

739763
@overload
@@ -751,6 +775,7 @@ def iter(
751775
usage: _usage.RunUsage | None = None,
752776
infer_name: bool = True,
753777
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
778+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
754779
) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, RunOutputDataT]]: ...
755780

756781
@asynccontextmanager
@@ -769,6 +794,7 @@ async def iter(
769794
usage: _usage.RunUsage | None = None,
770795
infer_name: bool = True,
771796
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
797+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
772798
) -> AsyncIterator[AgentRun[AgentDepsT, Any]]:
773799
"""A contextmanager which can be used to iterate over the agent graph's nodes as they are executed.
774800
@@ -841,6 +867,7 @@ async def main():
841867
usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
842868
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
843869
toolsets: Optional additional toolsets for this run.
870+
builtin_tools: Optional additional builtin tools for this run.
844871
845872
Returns:
846873
The result of the run.

pydantic_ai_slim/pydantic_ai/agent/wrapper.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
models,
1111
usage as _usage,
1212
)
13+
from ..builtin_tools import AbstractBuiltinTool
1314
from ..output import OutputDataT, OutputSpec
1415
from ..run import AgentRun
1516
from ..settings import ModelSettings
@@ -81,6 +82,7 @@ def iter(
8182
usage: _usage.RunUsage | None = None,
8283
infer_name: bool = True,
8384
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
85+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
8486
) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, OutputDataT]]: ...
8587

8688
@overload
@@ -98,6 +100,7 @@ def iter(
98100
usage: _usage.RunUsage | None = None,
99101
infer_name: bool = True,
100102
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
103+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
101104
) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, RunOutputDataT]]: ...
102105

103106
@asynccontextmanager
@@ -115,6 +118,7 @@ async def iter(
115118
usage: _usage.RunUsage | None = None,
116119
infer_name: bool = True,
117120
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
121+
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
118122
) -> AsyncIterator[AgentRun[AgentDepsT, Any]]:
119123
"""A contextmanager which can be used to iterate over the agent graph's nodes as they are executed.
120124
@@ -187,6 +191,7 @@ async def main():
187191
usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
188192
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
189193
toolsets: Optional additional toolsets for this run.
194+
builtin_tools: Optional additional builtin tools for this run.
190195
191196
Returns:
192197
The result of the run.
@@ -203,6 +208,7 @@ async def main():
203208
usage=usage,
204209
infer_name=infer_name,
205210
toolsets=toolsets,
211+
builtin_tools=builtin_tools,
206212
) as run:
207213
yield run
208214

pydantic_ai_slim/pydantic_ai/builtin_tools.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
from abc import ABC
44
from dataclasses import dataclass
5-
from typing import Literal
5+
from typing import TYPE_CHECKING, Literal
66

77
from typing_extensions import TypedDict
88

9+
if TYPE_CHECKING:
10+
from .builtin_tools import AbstractBuiltinTool
11+
912
__all__ = (
1013
'AbstractBuiltinTool',
1114
'WebSearchTool',

0 commit comments

Comments
 (0)