pydantic-ai-slim 0.6.1__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pydantic-ai-slim might be problematic. Click here for more details.
- pydantic_ai/__init__.py +5 -0
- pydantic_ai/_a2a.py +6 -4
- pydantic_ai/_agent_graph.py +32 -32
- pydantic_ai/_cli.py +3 -3
- pydantic_ai/_output.py +8 -0
- pydantic_ai/_tool_manager.py +3 -0
- pydantic_ai/_utils.py +7 -1
- pydantic_ai/ag_ui.py +25 -14
- pydantic_ai/{agent.py → agent/__init__.py} +217 -1026
- pydantic_ai/agent/abstract.py +942 -0
- pydantic_ai/agent/wrapper.py +227 -0
- pydantic_ai/builtin_tools.py +105 -0
- pydantic_ai/direct.py +9 -9
- pydantic_ai/durable_exec/__init__.py +0 -0
- pydantic_ai/durable_exec/temporal/__init__.py +83 -0
- pydantic_ai/durable_exec/temporal/_agent.py +699 -0
- pydantic_ai/durable_exec/temporal/_function_toolset.py +92 -0
- pydantic_ai/durable_exec/temporal/_logfire.py +48 -0
- pydantic_ai/durable_exec/temporal/_mcp_server.py +145 -0
- pydantic_ai/durable_exec/temporal/_model.py +168 -0
- pydantic_ai/durable_exec/temporal/_run_context.py +50 -0
- pydantic_ai/durable_exec/temporal/_toolset.py +77 -0
- pydantic_ai/ext/aci.py +10 -9
- pydantic_ai/ext/langchain.py +4 -2
- pydantic_ai/mcp.py +203 -75
- pydantic_ai/messages.py +75 -13
- pydantic_ai/models/__init__.py +66 -8
- pydantic_ai/models/anthropic.py +135 -18
- pydantic_ai/models/bedrock.py +16 -5
- pydantic_ai/models/cohere.py +11 -4
- pydantic_ai/models/fallback.py +4 -2
- pydantic_ai/models/function.py +18 -4
- pydantic_ai/models/gemini.py +20 -9
- pydantic_ai/models/google.py +53 -15
- pydantic_ai/models/groq.py +47 -11
- pydantic_ai/models/huggingface.py +26 -11
- pydantic_ai/models/instrumented.py +3 -1
- pydantic_ai/models/mcp_sampling.py +3 -1
- pydantic_ai/models/mistral.py +27 -17
- pydantic_ai/models/openai.py +97 -33
- pydantic_ai/models/test.py +12 -0
- pydantic_ai/models/wrapper.py +6 -2
- pydantic_ai/profiles/groq.py +23 -0
- pydantic_ai/profiles/openai.py +1 -1
- pydantic_ai/providers/google.py +7 -7
- pydantic_ai/providers/groq.py +2 -0
- pydantic_ai/result.py +21 -55
- pydantic_ai/run.py +357 -0
- pydantic_ai/tools.py +0 -1
- pydantic_ai/toolsets/__init__.py +2 -0
- pydantic_ai/toolsets/_dynamic.py +87 -0
- pydantic_ai/toolsets/abstract.py +23 -3
- pydantic_ai/toolsets/combined.py +19 -4
- pydantic_ai/toolsets/deferred.py +10 -2
- pydantic_ai/toolsets/function.py +23 -8
- pydantic_ai/toolsets/prefixed.py +4 -0
- pydantic_ai/toolsets/wrapper.py +14 -1
- {pydantic_ai_slim-0.6.1.dist-info → pydantic_ai_slim-0.7.0.dist-info}/METADATA +7 -5
- pydantic_ai_slim-0.7.0.dist-info/RECORD +115 -0
- pydantic_ai_slim-0.6.1.dist-info/RECORD +0 -100
- {pydantic_ai_slim-0.6.1.dist-info → pydantic_ai_slim-0.7.0.dist-info}/WHEEL +0 -0
- {pydantic_ai_slim-0.6.1.dist-info → pydantic_ai_slim-0.7.0.dist-info}/entry_points.txt +0 -0
- {pydantic_ai_slim-0.6.1.dist-info → pydantic_ai_slim-0.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,21 +5,18 @@ import inspect
|
|
|
5
5
|
import json
|
|
6
6
|
import warnings
|
|
7
7
|
from asyncio import Lock
|
|
8
|
-
from collections.abc import AsyncIterator, Awaitable, Iterator,
|
|
8
|
+
from collections.abc import AsyncIterator, Awaitable, Iterator, Sequence
|
|
9
9
|
from contextlib import AbstractAsyncContextManager, AsyncExitStack, asynccontextmanager, contextmanager
|
|
10
10
|
from contextvars import ContextVar
|
|
11
|
-
from
|
|
12
|
-
from types import FrameType
|
|
13
|
-
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, cast, final, overload
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Callable, ClassVar, cast, overload
|
|
14
12
|
|
|
15
13
|
from opentelemetry.trace import NoOpTracer, use_span
|
|
16
14
|
from pydantic.json_schema import GenerateJsonSchema
|
|
17
|
-
from typing_extensions import
|
|
15
|
+
from typing_extensions import TypeVar, deprecated
|
|
18
16
|
|
|
19
|
-
from pydantic_graph import
|
|
20
|
-
from pydantic_graph._utils import get_event_loop
|
|
17
|
+
from pydantic_graph import Graph
|
|
21
18
|
|
|
22
|
-
from
|
|
19
|
+
from .. import (
|
|
23
20
|
_agent_graph,
|
|
24
21
|
_output,
|
|
25
22
|
_system_prompt,
|
|
@@ -27,18 +24,19 @@ from . import (
|
|
|
27
24
|
exceptions,
|
|
28
25
|
messages as _messages,
|
|
29
26
|
models,
|
|
30
|
-
result,
|
|
31
27
|
usage as _usage,
|
|
32
28
|
)
|
|
33
|
-
from
|
|
34
|
-
from
|
|
35
|
-
from
|
|
36
|
-
from
|
|
37
|
-
from .
|
|
38
|
-
from
|
|
39
|
-
from
|
|
40
|
-
from
|
|
41
|
-
from
|
|
29
|
+
from .._agent_graph import HistoryProcessor
|
|
30
|
+
from .._output import OutputToolset
|
|
31
|
+
from .._tool_manager import ToolManager
|
|
32
|
+
from ..builtin_tools import AbstractBuiltinTool
|
|
33
|
+
from ..models.instrumented import InstrumentationSettings, InstrumentedModel, instrument_model
|
|
34
|
+
from ..output import OutputDataT, OutputSpec
|
|
35
|
+
from ..profiles import ModelProfile
|
|
36
|
+
from ..result import FinalResult
|
|
37
|
+
from ..run import AgentRun, AgentRunResult
|
|
38
|
+
from ..settings import ModelSettings, merge_model_settings
|
|
39
|
+
from ..tools import (
|
|
42
40
|
AgentDepsT,
|
|
43
41
|
DocstringFormat,
|
|
44
42
|
GenerateToolJsonSchema,
|
|
@@ -51,11 +49,16 @@ from .tools import (
|
|
|
51
49
|
ToolPrepareFunc,
|
|
52
50
|
ToolsPrepareFunc,
|
|
53
51
|
)
|
|
54
|
-
from
|
|
55
|
-
from
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
from ..toolsets import AbstractToolset
|
|
53
|
+
from ..toolsets._dynamic import (
|
|
54
|
+
DynamicToolset,
|
|
55
|
+
ToolsetFunc,
|
|
56
|
+
)
|
|
57
|
+
from ..toolsets.combined import CombinedToolset
|
|
58
|
+
from ..toolsets.function import FunctionToolset
|
|
59
|
+
from ..toolsets.prepared import PreparedToolset
|
|
60
|
+
from .abstract import AbstractAgent, EventStreamHandler, RunOutputDataT
|
|
61
|
+
from .wrapper import WrapperAgent
|
|
59
62
|
|
|
60
63
|
# Re-exporting like this improves auto-import behavior in PyCharm
|
|
61
64
|
capture_run_messages = _agent_graph.capture_run_messages
|
|
@@ -65,17 +68,7 @@ ModelRequestNode = _agent_graph.ModelRequestNode
|
|
|
65
68
|
UserPromptNode = _agent_graph.UserPromptNode
|
|
66
69
|
|
|
67
70
|
if TYPE_CHECKING:
|
|
68
|
-
from
|
|
69
|
-
from fasta2a.broker import Broker
|
|
70
|
-
from fasta2a.schema import AgentProvider, Skill
|
|
71
|
-
from fasta2a.storage import Storage
|
|
72
|
-
from starlette.middleware import Middleware
|
|
73
|
-
from starlette.routing import BaseRoute, Route
|
|
74
|
-
from starlette.types import ExceptionHandler, Lifespan
|
|
75
|
-
|
|
76
|
-
from pydantic_ai.mcp import MCPServer
|
|
77
|
-
|
|
78
|
-
from .ag_ui import AGUIApp
|
|
71
|
+
from ..mcp import MCPServer
|
|
79
72
|
|
|
80
73
|
__all__ = (
|
|
81
74
|
'Agent',
|
|
@@ -87,19 +80,19 @@ __all__ = (
|
|
|
87
80
|
'ModelRequestNode',
|
|
88
81
|
'UserPromptNode',
|
|
89
82
|
'InstrumentationSettings',
|
|
83
|
+
'WrapperAgent',
|
|
84
|
+
'AbstractAgent',
|
|
85
|
+
'EventStreamHandler',
|
|
90
86
|
)
|
|
91
87
|
|
|
92
88
|
|
|
93
89
|
T = TypeVar('T')
|
|
94
90
|
S = TypeVar('S')
|
|
95
91
|
NoneType = type(None)
|
|
96
|
-
RunOutputDataT = TypeVar('RunOutputDataT')
|
|
97
|
-
"""Type variable for the result data of a run where `output_type` was customized on the run call."""
|
|
98
92
|
|
|
99
93
|
|
|
100
|
-
@final
|
|
101
94
|
@dataclasses.dataclass(init=False)
|
|
102
|
-
class Agent(
|
|
95
|
+
class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
|
|
103
96
|
"""Class for defining "agents" - a way to have a specific type of "conversation" with an LLM.
|
|
104
97
|
|
|
105
98
|
Agents are generic in the dependency type they take [`AgentDepsT`][pydantic_ai.tools.AgentDepsT]
|
|
@@ -115,21 +108,13 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
115
108
|
agent = Agent('openai:gpt-4o')
|
|
116
109
|
result = agent.run_sync('What is the capital of France?')
|
|
117
110
|
print(result.output)
|
|
118
|
-
#> Paris
|
|
111
|
+
#> The capital of France is Paris.
|
|
119
112
|
```
|
|
120
113
|
"""
|
|
121
114
|
|
|
122
|
-
|
|
123
|
-
"""The default model configured for this agent.
|
|
124
|
-
|
|
125
|
-
We allow `str` here since the actual list of allowed models changes frequently.
|
|
126
|
-
"""
|
|
127
|
-
|
|
128
|
-
name: str | None
|
|
129
|
-
"""The name of the agent, used for logging.
|
|
115
|
+
_model: models.Model | models.KnownModelName | str | None
|
|
130
116
|
|
|
131
|
-
|
|
132
|
-
"""
|
|
117
|
+
_name: str | None
|
|
133
118
|
end_strategy: EndStrategy
|
|
134
119
|
"""Strategy for handling tool calls when a final result is found."""
|
|
135
120
|
|
|
@@ -140,10 +125,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
140
125
|
be merged with this value, with the runtime argument taking priority.
|
|
141
126
|
"""
|
|
142
127
|
|
|
143
|
-
|
|
144
|
-
"""
|
|
145
|
-
The type of data output by agent runs, used to validate the data returned by the model, defaults to `str`.
|
|
146
|
-
"""
|
|
128
|
+
_output_type: OutputSpec[OutputDataT]
|
|
147
129
|
|
|
148
130
|
instrument: InstrumentationSettings | bool | None
|
|
149
131
|
"""Options to automatically instrument with OpenTelemetry."""
|
|
@@ -162,10 +144,13 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
162
144
|
)
|
|
163
145
|
_function_toolset: FunctionToolset[AgentDepsT] = dataclasses.field(repr=False)
|
|
164
146
|
_output_toolset: OutputToolset[AgentDepsT] | None = dataclasses.field(repr=False)
|
|
165
|
-
_user_toolsets:
|
|
147
|
+
_user_toolsets: list[AbstractToolset[AgentDepsT]] = dataclasses.field(repr=False)
|
|
166
148
|
_prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = dataclasses.field(repr=False)
|
|
167
149
|
_prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = dataclasses.field(repr=False)
|
|
168
150
|
_max_result_retries: int = dataclasses.field(repr=False)
|
|
151
|
+
_max_tool_retries: int = dataclasses.field(repr=False)
|
|
152
|
+
|
|
153
|
+
_event_stream_handler: EventStreamHandler[AgentDepsT] | None = dataclasses.field(repr=False)
|
|
169
154
|
|
|
170
155
|
_enter_lock: Lock = dataclasses.field(repr=False)
|
|
171
156
|
_entered_count: int = dataclasses.field(repr=False)
|
|
@@ -188,13 +173,15 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
188
173
|
retries: int = 1,
|
|
189
174
|
output_retries: int | None = None,
|
|
190
175
|
tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (),
|
|
176
|
+
builtin_tools: Sequence[AbstractBuiltinTool] = (),
|
|
191
177
|
prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
|
|
192
178
|
prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
|
|
193
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
179
|
+
toolsets: Sequence[AbstractToolset[AgentDepsT] | ToolsetFunc[AgentDepsT]] | None = None,
|
|
194
180
|
defer_model_check: bool = False,
|
|
195
181
|
end_strategy: EndStrategy = 'early',
|
|
196
182
|
instrument: InstrumentationSettings | bool | None = None,
|
|
197
183
|
history_processors: Sequence[HistoryProcessor[AgentDepsT]] | None = None,
|
|
184
|
+
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
|
|
198
185
|
) -> None: ...
|
|
199
186
|
|
|
200
187
|
@overload
|
|
@@ -215,6 +202,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
215
202
|
retries: int = 1,
|
|
216
203
|
output_retries: int | None = None,
|
|
217
204
|
tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (),
|
|
205
|
+
builtin_tools: Sequence[AbstractBuiltinTool] = (),
|
|
218
206
|
prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
|
|
219
207
|
prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
|
|
220
208
|
mcp_servers: Sequence[MCPServer] = (),
|
|
@@ -222,6 +210,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
222
210
|
end_strategy: EndStrategy = 'early',
|
|
223
211
|
instrument: InstrumentationSettings | bool | None = None,
|
|
224
212
|
history_processors: Sequence[HistoryProcessor[AgentDepsT]] | None = None,
|
|
213
|
+
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
|
|
225
214
|
) -> None: ...
|
|
226
215
|
|
|
227
216
|
def __init__(
|
|
@@ -240,13 +229,15 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
240
229
|
retries: int = 1,
|
|
241
230
|
output_retries: int | None = None,
|
|
242
231
|
tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (),
|
|
232
|
+
builtin_tools: Sequence[AbstractBuiltinTool] = (),
|
|
243
233
|
prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
|
|
244
234
|
prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
|
|
245
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
235
|
+
toolsets: Sequence[AbstractToolset[AgentDepsT] | ToolsetFunc[AgentDepsT]] | None = None,
|
|
246
236
|
defer_model_check: bool = False,
|
|
247
237
|
end_strategy: EndStrategy = 'early',
|
|
248
238
|
instrument: InstrumentationSettings | bool | None = None,
|
|
249
239
|
history_processors: Sequence[HistoryProcessor[AgentDepsT]] | None = None,
|
|
240
|
+
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
|
|
250
241
|
**_deprecated_kwargs: Any,
|
|
251
242
|
):
|
|
252
243
|
"""Create an agent.
|
|
@@ -271,13 +262,16 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
271
262
|
output_retries: The maximum number of retries to allow for output validation, defaults to `retries`.
|
|
272
263
|
tools: Tools to register with the agent, you can also register tools via the decorators
|
|
273
264
|
[`@agent.tool`][pydantic_ai.Agent.tool] and [`@agent.tool_plain`][pydantic_ai.Agent.tool_plain].
|
|
265
|
+
builtin_tools: The builtin tools that the agent will use. This depends on the model, as some models may not
|
|
266
|
+
support certain tools. If the model doesn't support the builtin tools, an error will be raised.
|
|
274
267
|
prepare_tools: Custom function to prepare the tool definition of all tools for each step, except output tools.
|
|
275
268
|
This is useful if you want to customize the definition of multiple tools or you want to register
|
|
276
269
|
a subset of tools for a given step. See [`ToolsPrepareFunc`][pydantic_ai.tools.ToolsPrepareFunc]
|
|
277
270
|
prepare_output_tools: Custom function to prepare the tool definition of all output tools for each step.
|
|
278
271
|
This is useful if you want to customize the definition of multiple output tools or you want to register
|
|
279
272
|
a subset of output tools for a given step. See [`ToolsPrepareFunc`][pydantic_ai.tools.ToolsPrepareFunc]
|
|
280
|
-
toolsets: Toolsets to register with the agent, including MCP servers
|
|
273
|
+
toolsets: Toolsets to register with the agent, including MCP servers and functions which take a run context
|
|
274
|
+
and return a toolset. See [`ToolsetFunc`][pydantic_ai.toolsets.ToolsetFunc] for more information.
|
|
281
275
|
defer_model_check: by default, if you provide a [named][pydantic_ai.models.KnownModelName] model,
|
|
282
276
|
it's evaluated to create a [`Model`][pydantic_ai.models.Model] instance immediately,
|
|
283
277
|
which checks for the necessary environment variables. Set this to `false`
|
|
@@ -295,17 +289,18 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
295
289
|
history_processors: Optional list of callables to process the message history before sending it to the model.
|
|
296
290
|
Each processor takes a list of messages and returns a modified list of messages.
|
|
297
291
|
Processors can be sync or async and are applied in sequence.
|
|
292
|
+
event_stream_handler: Optional handler for events from the model's streaming response and the agent's execution of tools.
|
|
298
293
|
"""
|
|
299
294
|
if model is None or defer_model_check:
|
|
300
|
-
self.
|
|
295
|
+
self._model = model
|
|
301
296
|
else:
|
|
302
|
-
self.
|
|
297
|
+
self._model = models.infer_model(model)
|
|
303
298
|
|
|
299
|
+
self._name = name
|
|
304
300
|
self.end_strategy = end_strategy
|
|
305
|
-
self.name = name
|
|
306
301
|
self.model_settings = model_settings
|
|
307
302
|
|
|
308
|
-
self.
|
|
303
|
+
self._output_type = output_type
|
|
309
304
|
self.instrument = instrument
|
|
310
305
|
self._deps_type = deps_type
|
|
311
306
|
|
|
@@ -340,6 +335,10 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
340
335
|
self._system_prompt_dynamic_functions = {}
|
|
341
336
|
|
|
342
337
|
self._max_result_retries = output_retries if output_retries is not None else retries
|
|
338
|
+
self._max_tool_retries = retries
|
|
339
|
+
|
|
340
|
+
self._builtin_tools = builtin_tools
|
|
341
|
+
|
|
343
342
|
self._prepare_tools = prepare_tools
|
|
344
343
|
self._prepare_output_tools = prepare_output_tools
|
|
345
344
|
|
|
@@ -347,16 +346,26 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
347
346
|
if self._output_toolset:
|
|
348
347
|
self._output_toolset.max_retries = self._max_result_retries
|
|
349
348
|
|
|
350
|
-
self._function_toolset =
|
|
351
|
-
self.
|
|
349
|
+
self._function_toolset = _AgentFunctionToolset(tools, max_retries=self._max_tool_retries)
|
|
350
|
+
self._dynamic_toolsets = [
|
|
351
|
+
DynamicToolset[AgentDepsT](toolset_func=toolset)
|
|
352
|
+
for toolset in toolsets or []
|
|
353
|
+
if not isinstance(toolset, AbstractToolset)
|
|
354
|
+
]
|
|
355
|
+
self._user_toolsets = [toolset for toolset in toolsets or [] if isinstance(toolset, AbstractToolset)]
|
|
352
356
|
|
|
353
357
|
self.history_processors = history_processors or []
|
|
354
358
|
|
|
359
|
+
self._event_stream_handler = event_stream_handler
|
|
360
|
+
|
|
355
361
|
self._override_deps: ContextVar[_utils.Option[AgentDepsT]] = ContextVar('_override_deps', default=None)
|
|
356
362
|
self._override_model: ContextVar[_utils.Option[models.Model]] = ContextVar('_override_model', default=None)
|
|
357
363
|
self._override_toolsets: ContextVar[_utils.Option[Sequence[AbstractToolset[AgentDepsT]]]] = ContextVar(
|
|
358
364
|
'_override_toolsets', default=None
|
|
359
365
|
)
|
|
366
|
+
self._override_tools: ContextVar[
|
|
367
|
+
_utils.Option[Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]]]
|
|
368
|
+
] = ContextVar('_override_tools', default=None)
|
|
360
369
|
|
|
361
370
|
self._enter_lock = _utils.get_async_lock()
|
|
362
371
|
self._entered_count = 0
|
|
@@ -367,107 +376,49 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
367
376
|
"""Set the instrumentation options for all agents where `instrument` is not set."""
|
|
368
377
|
Agent._instrument_default = instrument
|
|
369
378
|
|
|
370
|
-
@
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
*,
|
|
375
|
-
output_type: None = None,
|
|
376
|
-
message_history: list[_messages.ModelMessage] | None = None,
|
|
377
|
-
model: models.Model | models.KnownModelName | str | None = None,
|
|
378
|
-
deps: AgentDepsT = None,
|
|
379
|
-
model_settings: ModelSettings | None = None,
|
|
380
|
-
usage_limits: _usage.UsageLimits | None = None,
|
|
381
|
-
usage: _usage.Usage | None = None,
|
|
382
|
-
infer_name: bool = True,
|
|
383
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
384
|
-
) -> AgentRunResult[OutputDataT]: ...
|
|
385
|
-
|
|
386
|
-
@overload
|
|
387
|
-
async def run(
|
|
388
|
-
self,
|
|
389
|
-
user_prompt: str | Sequence[_messages.UserContent] | None = None,
|
|
390
|
-
*,
|
|
391
|
-
output_type: OutputSpec[RunOutputDataT],
|
|
392
|
-
message_history: list[_messages.ModelMessage] | None = None,
|
|
393
|
-
model: models.Model | models.KnownModelName | str | None = None,
|
|
394
|
-
deps: AgentDepsT = None,
|
|
395
|
-
model_settings: ModelSettings | None = None,
|
|
396
|
-
usage_limits: _usage.UsageLimits | None = None,
|
|
397
|
-
usage: _usage.Usage | None = None,
|
|
398
|
-
infer_name: bool = True,
|
|
399
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
400
|
-
) -> AgentRunResult[RunOutputDataT]: ...
|
|
401
|
-
|
|
402
|
-
async def run(
|
|
403
|
-
self,
|
|
404
|
-
user_prompt: str | Sequence[_messages.UserContent] | None = None,
|
|
405
|
-
*,
|
|
406
|
-
output_type: OutputSpec[RunOutputDataT] | None = None,
|
|
407
|
-
message_history: list[_messages.ModelMessage] | None = None,
|
|
408
|
-
model: models.Model | models.KnownModelName | str | None = None,
|
|
409
|
-
deps: AgentDepsT = None,
|
|
410
|
-
model_settings: ModelSettings | None = None,
|
|
411
|
-
usage_limits: _usage.UsageLimits | None = None,
|
|
412
|
-
usage: _usage.Usage | None = None,
|
|
413
|
-
infer_name: bool = True,
|
|
414
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
415
|
-
**_deprecated_kwargs: Never,
|
|
416
|
-
) -> AgentRunResult[Any]:
|
|
417
|
-
"""Run the agent with a user prompt in async mode.
|
|
379
|
+
@property
|
|
380
|
+
def model(self) -> models.Model | models.KnownModelName | str | None:
|
|
381
|
+
"""The default model configured for this agent."""
|
|
382
|
+
return self._model
|
|
418
383
|
|
|
419
|
-
|
|
420
|
-
|
|
384
|
+
@model.setter
|
|
385
|
+
def model(self, value: models.Model | models.KnownModelName | str | None) -> None:
|
|
386
|
+
"""Set the default model configured for this agent.
|
|
421
387
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
388
|
+
We allow `str` here since the actual list of allowed models changes frequently.
|
|
389
|
+
"""
|
|
390
|
+
self._model = value
|
|
425
391
|
|
|
426
|
-
|
|
392
|
+
@property
|
|
393
|
+
def name(self) -> str | None:
|
|
394
|
+
"""The name of the agent, used for logging.
|
|
427
395
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
#> Paris
|
|
432
|
-
```
|
|
396
|
+
If `None`, we try to infer the agent name from the call frame when the agent is first run.
|
|
397
|
+
"""
|
|
398
|
+
return self._name
|
|
433
399
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
message_history: History of the conversation so far.
|
|
439
|
-
model: Optional model to use for this run, required if `model` was not set when creating the agent.
|
|
440
|
-
deps: Optional dependencies to use for this run.
|
|
441
|
-
model_settings: Optional settings to use for this model's request.
|
|
442
|
-
usage_limits: Optional limits on model request count or token usage.
|
|
443
|
-
usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
|
|
444
|
-
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
|
|
445
|
-
toolsets: Optional additional toolsets for this run.
|
|
400
|
+
@name.setter
|
|
401
|
+
def name(self, value: str | None) -> None:
|
|
402
|
+
"""Set the name of the agent, used for logging."""
|
|
403
|
+
self._name = value
|
|
446
404
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
"""
|
|
450
|
-
|
|
451
|
-
self._infer_name(inspect.currentframe())
|
|
405
|
+
@property
|
|
406
|
+
def deps_type(self) -> type:
|
|
407
|
+
"""The type of dependencies used by the agent."""
|
|
408
|
+
return self._deps_type
|
|
452
409
|
|
|
453
|
-
|
|
410
|
+
@property
|
|
411
|
+
def output_type(self) -> OutputSpec[OutputDataT]:
|
|
412
|
+
"""The type of data output by agent runs, used to validate the data returned by the model, defaults to `str`."""
|
|
413
|
+
return self._output_type
|
|
454
414
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
model=model,
|
|
460
|
-
deps=deps,
|
|
461
|
-
model_settings=model_settings,
|
|
462
|
-
usage_limits=usage_limits,
|
|
463
|
-
usage=usage,
|
|
464
|
-
toolsets=toolsets,
|
|
465
|
-
) as agent_run:
|
|
466
|
-
async for _ in agent_run:
|
|
467
|
-
pass
|
|
415
|
+
@property
|
|
416
|
+
def event_stream_handler(self) -> EventStreamHandler[AgentDepsT] | None:
|
|
417
|
+
"""Optional handler for events from the model's streaming response and the agent's execution of tools."""
|
|
418
|
+
return self._event_stream_handler
|
|
468
419
|
|
|
469
|
-
|
|
470
|
-
return
|
|
420
|
+
def __repr__(self) -> str:
|
|
421
|
+
return f'{type(self).__name__}(model={self.model!r}, name={self.name!r}, end_strategy={self.end_strategy!r}, model_settings={self.model_settings!r}, output_type={self.output_type!r}, instrument={self.instrument!r})'
|
|
471
422
|
|
|
472
423
|
@overload
|
|
473
424
|
def iter(
|
|
@@ -483,7 +434,6 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
483
434
|
usage: _usage.Usage | None = None,
|
|
484
435
|
infer_name: bool = True,
|
|
485
436
|
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
486
|
-
**_deprecated_kwargs: Never,
|
|
487
437
|
) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, OutputDataT]]: ...
|
|
488
438
|
|
|
489
439
|
@overload
|
|
@@ -500,7 +450,6 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
500
450
|
usage: _usage.Usage | None = None,
|
|
501
451
|
infer_name: bool = True,
|
|
502
452
|
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
503
|
-
**_deprecated_kwargs: Never,
|
|
504
453
|
) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, RunOutputDataT]]: ...
|
|
505
454
|
|
|
506
455
|
@asynccontextmanager
|
|
@@ -517,7 +466,6 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
517
466
|
usage: _usage.Usage | None = None,
|
|
518
467
|
infer_name: bool = True,
|
|
519
468
|
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
520
|
-
**_deprecated_kwargs: Never,
|
|
521
469
|
) -> AsyncIterator[AgentRun[AgentDepsT, Any]]:
|
|
522
470
|
"""A contextmanager which can be used to iterate over the agent graph's nodes as they are executed.
|
|
523
471
|
|
|
@@ -565,19 +513,19 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
565
513
|
),
|
|
566
514
|
CallToolsNode(
|
|
567
515
|
model_response=ModelResponse(
|
|
568
|
-
parts=[TextPart(content='Paris')],
|
|
516
|
+
parts=[TextPart(content='The capital of France is Paris.')],
|
|
569
517
|
usage=Usage(
|
|
570
|
-
requests=1, request_tokens=56, response_tokens=
|
|
518
|
+
requests=1, request_tokens=56, response_tokens=7, total_tokens=63
|
|
571
519
|
),
|
|
572
520
|
model_name='gpt-4o',
|
|
573
521
|
timestamp=datetime.datetime(...),
|
|
574
522
|
)
|
|
575
523
|
),
|
|
576
|
-
End(data=FinalResult(output='Paris')),
|
|
524
|
+
End(data=FinalResult(output='The capital of France is Paris.')),
|
|
577
525
|
]
|
|
578
526
|
'''
|
|
579
527
|
print(agent_run.result.output)
|
|
580
|
-
#> Paris
|
|
528
|
+
#> The capital of France is Paris.
|
|
581
529
|
```
|
|
582
530
|
|
|
583
531
|
Args:
|
|
@@ -601,8 +549,6 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
601
549
|
model_used = self._get_model(model)
|
|
602
550
|
del model
|
|
603
551
|
|
|
604
|
-
_utils.validate_empty_kwargs(_deprecated_kwargs)
|
|
605
|
-
|
|
606
552
|
deps = self._get_deps(deps)
|
|
607
553
|
new_message_index = len(message_history) if message_history else 0
|
|
608
554
|
output_schema = self._prepare_output_schema(output_type, model_used.profile)
|
|
@@ -654,9 +600,10 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
654
600
|
)
|
|
655
601
|
|
|
656
602
|
toolset = self._get_toolset(output_toolset=output_toolset, additional_toolsets=toolsets)
|
|
657
|
-
|
|
603
|
+
|
|
658
604
|
async with toolset:
|
|
659
|
-
|
|
605
|
+
# This will raise errors for any name conflicts
|
|
606
|
+
tool_manager = await ToolManager[AgentDepsT].build(toolset, run_context)
|
|
660
607
|
|
|
661
608
|
# Merge model settings in order of precedence: run > agent > model
|
|
662
609
|
merged_settings = merge_model_settings(model_used.settings, self.model_settings)
|
|
@@ -700,7 +647,8 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
700
647
|
output_schema=output_schema,
|
|
701
648
|
output_validators=output_validators,
|
|
702
649
|
history_processors=self.history_processors,
|
|
703
|
-
|
|
650
|
+
builtin_tools=list(self._builtin_tools),
|
|
651
|
+
tool_manager=tool_manager,
|
|
704
652
|
tracer=tracer,
|
|
705
653
|
get_instructions=get_instructions,
|
|
706
654
|
instrumentation_settings=instrumentation_settings,
|
|
@@ -760,266 +708,6 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
760
708
|
),
|
|
761
709
|
}
|
|
762
710
|
|
|
763
|
-
@overload
|
|
764
|
-
def run_sync(
|
|
765
|
-
self,
|
|
766
|
-
user_prompt: str | Sequence[_messages.UserContent] | None = None,
|
|
767
|
-
*,
|
|
768
|
-
message_history: list[_messages.ModelMessage] | None = None,
|
|
769
|
-
model: models.Model | models.KnownModelName | str | None = None,
|
|
770
|
-
deps: AgentDepsT = None,
|
|
771
|
-
model_settings: ModelSettings | None = None,
|
|
772
|
-
usage_limits: _usage.UsageLimits | None = None,
|
|
773
|
-
usage: _usage.Usage | None = None,
|
|
774
|
-
infer_name: bool = True,
|
|
775
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
776
|
-
) -> AgentRunResult[OutputDataT]: ...
|
|
777
|
-
|
|
778
|
-
@overload
|
|
779
|
-
def run_sync(
|
|
780
|
-
self,
|
|
781
|
-
user_prompt: str | Sequence[_messages.UserContent] | None = None,
|
|
782
|
-
*,
|
|
783
|
-
output_type: OutputSpec[RunOutputDataT] | None = None,
|
|
784
|
-
message_history: list[_messages.ModelMessage] | None = None,
|
|
785
|
-
model: models.Model | models.KnownModelName | str | None = None,
|
|
786
|
-
deps: AgentDepsT = None,
|
|
787
|
-
model_settings: ModelSettings | None = None,
|
|
788
|
-
usage_limits: _usage.UsageLimits | None = None,
|
|
789
|
-
usage: _usage.Usage | None = None,
|
|
790
|
-
infer_name: bool = True,
|
|
791
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
792
|
-
) -> AgentRunResult[RunOutputDataT]: ...
|
|
793
|
-
|
|
794
|
-
def run_sync(
|
|
795
|
-
self,
|
|
796
|
-
user_prompt: str | Sequence[_messages.UserContent] | None = None,
|
|
797
|
-
*,
|
|
798
|
-
output_type: OutputSpec[RunOutputDataT] | None = None,
|
|
799
|
-
message_history: list[_messages.ModelMessage] | None = None,
|
|
800
|
-
model: models.Model | models.KnownModelName | str | None = None,
|
|
801
|
-
deps: AgentDepsT = None,
|
|
802
|
-
model_settings: ModelSettings | None = None,
|
|
803
|
-
usage_limits: _usage.UsageLimits | None = None,
|
|
804
|
-
usage: _usage.Usage | None = None,
|
|
805
|
-
infer_name: bool = True,
|
|
806
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
807
|
-
**_deprecated_kwargs: Never,
|
|
808
|
-
) -> AgentRunResult[Any]:
|
|
809
|
-
"""Synchronously run the agent with a user prompt.
|
|
810
|
-
|
|
811
|
-
This is a convenience method that wraps [`self.run`][pydantic_ai.Agent.run] with `loop.run_until_complete(...)`.
|
|
812
|
-
You therefore can't use this method inside async code or if there's an active event loop.
|
|
813
|
-
|
|
814
|
-
Example:
|
|
815
|
-
```python
|
|
816
|
-
from pydantic_ai import Agent
|
|
817
|
-
|
|
818
|
-
agent = Agent('openai:gpt-4o')
|
|
819
|
-
|
|
820
|
-
result_sync = agent.run_sync('What is the capital of Italy?')
|
|
821
|
-
print(result_sync.output)
|
|
822
|
-
#> Rome
|
|
823
|
-
```
|
|
824
|
-
|
|
825
|
-
Args:
|
|
826
|
-
user_prompt: User input to start/continue the conversation.
|
|
827
|
-
output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
|
|
828
|
-
output validators since output validators would expect an argument that matches the agent's output type.
|
|
829
|
-
message_history: History of the conversation so far.
|
|
830
|
-
model: Optional model to use for this run, required if `model` was not set when creating the agent.
|
|
831
|
-
deps: Optional dependencies to use for this run.
|
|
832
|
-
model_settings: Optional settings to use for this model's request.
|
|
833
|
-
usage_limits: Optional limits on model request count or token usage.
|
|
834
|
-
usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
|
|
835
|
-
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
|
|
836
|
-
toolsets: Optional additional toolsets for this run.
|
|
837
|
-
|
|
838
|
-
Returns:
|
|
839
|
-
The result of the run.
|
|
840
|
-
"""
|
|
841
|
-
if infer_name and self.name is None:
|
|
842
|
-
self._infer_name(inspect.currentframe())
|
|
843
|
-
|
|
844
|
-
_utils.validate_empty_kwargs(_deprecated_kwargs)
|
|
845
|
-
|
|
846
|
-
return get_event_loop().run_until_complete(
|
|
847
|
-
self.run(
|
|
848
|
-
user_prompt,
|
|
849
|
-
output_type=output_type,
|
|
850
|
-
message_history=message_history,
|
|
851
|
-
model=model,
|
|
852
|
-
deps=deps,
|
|
853
|
-
model_settings=model_settings,
|
|
854
|
-
usage_limits=usage_limits,
|
|
855
|
-
usage=usage,
|
|
856
|
-
infer_name=False,
|
|
857
|
-
toolsets=toolsets,
|
|
858
|
-
)
|
|
859
|
-
)
|
|
860
|
-
|
|
861
|
-
@overload
|
|
862
|
-
def run_stream(
|
|
863
|
-
self,
|
|
864
|
-
user_prompt: str | Sequence[_messages.UserContent] | None = None,
|
|
865
|
-
*,
|
|
866
|
-
message_history: list[_messages.ModelMessage] | None = None,
|
|
867
|
-
model: models.Model | models.KnownModelName | None = None,
|
|
868
|
-
deps: AgentDepsT = None,
|
|
869
|
-
model_settings: ModelSettings | None = None,
|
|
870
|
-
usage_limits: _usage.UsageLimits | None = None,
|
|
871
|
-
usage: _usage.Usage | None = None,
|
|
872
|
-
infer_name: bool = True,
|
|
873
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
874
|
-
) -> AbstractAsyncContextManager[result.StreamedRunResult[AgentDepsT, OutputDataT]]: ...
|
|
875
|
-
|
|
876
|
-
@overload
|
|
877
|
-
def run_stream(
|
|
878
|
-
self,
|
|
879
|
-
user_prompt: str | Sequence[_messages.UserContent],
|
|
880
|
-
*,
|
|
881
|
-
output_type: OutputSpec[RunOutputDataT],
|
|
882
|
-
message_history: list[_messages.ModelMessage] | None = None,
|
|
883
|
-
model: models.Model | models.KnownModelName | str | None = None,
|
|
884
|
-
deps: AgentDepsT = None,
|
|
885
|
-
model_settings: ModelSettings | None = None,
|
|
886
|
-
usage_limits: _usage.UsageLimits | None = None,
|
|
887
|
-
usage: _usage.Usage | None = None,
|
|
888
|
-
infer_name: bool = True,
|
|
889
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
890
|
-
) -> AbstractAsyncContextManager[result.StreamedRunResult[AgentDepsT, RunOutputDataT]]: ...
|
|
891
|
-
|
|
892
|
-
@asynccontextmanager
|
|
893
|
-
async def run_stream(
|
|
894
|
-
self,
|
|
895
|
-
user_prompt: str | Sequence[_messages.UserContent] | None = None,
|
|
896
|
-
*,
|
|
897
|
-
output_type: OutputSpec[RunOutputDataT] | None = None,
|
|
898
|
-
message_history: list[_messages.ModelMessage] | None = None,
|
|
899
|
-
model: models.Model | models.KnownModelName | str | None = None,
|
|
900
|
-
deps: AgentDepsT = None,
|
|
901
|
-
model_settings: ModelSettings | None = None,
|
|
902
|
-
usage_limits: _usage.UsageLimits | None = None,
|
|
903
|
-
usage: _usage.Usage | None = None,
|
|
904
|
-
infer_name: bool = True,
|
|
905
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
906
|
-
**_deprecated_kwargs: Never,
|
|
907
|
-
) -> AsyncIterator[result.StreamedRunResult[AgentDepsT, Any]]:
|
|
908
|
-
"""Run the agent with a user prompt in async mode, returning a streamed response.
|
|
909
|
-
|
|
910
|
-
Example:
|
|
911
|
-
```python
|
|
912
|
-
from pydantic_ai import Agent
|
|
913
|
-
|
|
914
|
-
agent = Agent('openai:gpt-4o')
|
|
915
|
-
|
|
916
|
-
async def main():
|
|
917
|
-
async with agent.run_stream('What is the capital of the UK?') as response:
|
|
918
|
-
print(await response.get_output())
|
|
919
|
-
#> London
|
|
920
|
-
```
|
|
921
|
-
|
|
922
|
-
Args:
|
|
923
|
-
user_prompt: User input to start/continue the conversation.
|
|
924
|
-
output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
|
|
925
|
-
output validators since output validators would expect an argument that matches the agent's output type.
|
|
926
|
-
message_history: History of the conversation so far.
|
|
927
|
-
model: Optional model to use for this run, required if `model` was not set when creating the agent.
|
|
928
|
-
deps: Optional dependencies to use for this run.
|
|
929
|
-
model_settings: Optional settings to use for this model's request.
|
|
930
|
-
usage_limits: Optional limits on model request count or token usage.
|
|
931
|
-
usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
|
|
932
|
-
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
|
|
933
|
-
toolsets: Optional additional toolsets for this run.
|
|
934
|
-
|
|
935
|
-
Returns:
|
|
936
|
-
The result of the run.
|
|
937
|
-
"""
|
|
938
|
-
# TODO: We need to deprecate this now that we have the `iter` method.
|
|
939
|
-
# Before that, though, we should add an event for when we reach the final result of the stream.
|
|
940
|
-
if infer_name and self.name is None:
|
|
941
|
-
# f_back because `asynccontextmanager` adds one frame
|
|
942
|
-
if frame := inspect.currentframe(): # pragma: no branch
|
|
943
|
-
self._infer_name(frame.f_back)
|
|
944
|
-
|
|
945
|
-
_utils.validate_empty_kwargs(_deprecated_kwargs)
|
|
946
|
-
|
|
947
|
-
yielded = False
|
|
948
|
-
async with self.iter(
|
|
949
|
-
user_prompt,
|
|
950
|
-
output_type=output_type,
|
|
951
|
-
message_history=message_history,
|
|
952
|
-
model=model,
|
|
953
|
-
deps=deps,
|
|
954
|
-
model_settings=model_settings,
|
|
955
|
-
usage_limits=usage_limits,
|
|
956
|
-
usage=usage,
|
|
957
|
-
infer_name=False,
|
|
958
|
-
toolsets=toolsets,
|
|
959
|
-
) as agent_run:
|
|
960
|
-
first_node = agent_run.next_node # start with the first node
|
|
961
|
-
assert isinstance(first_node, _agent_graph.UserPromptNode) # the first node should be a user prompt node
|
|
962
|
-
node = first_node
|
|
963
|
-
while True:
|
|
964
|
-
if self.is_model_request_node(node):
|
|
965
|
-
graph_ctx = agent_run.ctx
|
|
966
|
-
async with node.stream(graph_ctx) as stream:
|
|
967
|
-
|
|
968
|
-
async def stream_to_final(s: AgentStream) -> FinalResult[AgentStream] | None:
|
|
969
|
-
async for event in stream:
|
|
970
|
-
if isinstance(event, _messages.FinalResultEvent):
|
|
971
|
-
return FinalResult(s, event.tool_name, event.tool_call_id)
|
|
972
|
-
return None
|
|
973
|
-
|
|
974
|
-
final_result = await stream_to_final(stream)
|
|
975
|
-
if final_result is not None:
|
|
976
|
-
if yielded:
|
|
977
|
-
raise exceptions.AgentRunError('Agent run produced final results') # pragma: no cover
|
|
978
|
-
yielded = True
|
|
979
|
-
|
|
980
|
-
messages = graph_ctx.state.message_history.copy()
|
|
981
|
-
|
|
982
|
-
async def on_complete() -> None:
|
|
983
|
-
"""Called when the stream has completed.
|
|
984
|
-
|
|
985
|
-
The model response will have been added to messages by now
|
|
986
|
-
by `StreamedRunResult._marked_completed`.
|
|
987
|
-
"""
|
|
988
|
-
last_message = messages[-1]
|
|
989
|
-
assert isinstance(last_message, _messages.ModelResponse)
|
|
990
|
-
tool_calls = [
|
|
991
|
-
part for part in last_message.parts if isinstance(part, _messages.ToolCallPart)
|
|
992
|
-
]
|
|
993
|
-
|
|
994
|
-
parts: list[_messages.ModelRequestPart] = []
|
|
995
|
-
async for _event in _agent_graph.process_function_tools(
|
|
996
|
-
graph_ctx.deps.tool_manager,
|
|
997
|
-
tool_calls,
|
|
998
|
-
final_result,
|
|
999
|
-
graph_ctx,
|
|
1000
|
-
parts,
|
|
1001
|
-
):
|
|
1002
|
-
pass
|
|
1003
|
-
if parts:
|
|
1004
|
-
messages.append(_messages.ModelRequest(parts))
|
|
1005
|
-
|
|
1006
|
-
yield StreamedRunResult(
|
|
1007
|
-
messages,
|
|
1008
|
-
graph_ctx.deps.new_message_index,
|
|
1009
|
-
stream,
|
|
1010
|
-
on_complete,
|
|
1011
|
-
)
|
|
1012
|
-
break
|
|
1013
|
-
next_node = await agent_run.next(node)
|
|
1014
|
-
if not isinstance(next_node, _agent_graph.AgentNode):
|
|
1015
|
-
raise exceptions.AgentRunError( # pragma: no cover
|
|
1016
|
-
'Should have produced a StreamedRunResult before getting here'
|
|
1017
|
-
)
|
|
1018
|
-
node = cast(_agent_graph.AgentNode[Any, Any], next_node)
|
|
1019
|
-
|
|
1020
|
-
if not yielded:
|
|
1021
|
-
raise exceptions.AgentRunError('Agent run finished without producing a final result') # pragma: no cover
|
|
1022
|
-
|
|
1023
711
|
@contextmanager
|
|
1024
712
|
def override(
|
|
1025
713
|
self,
|
|
@@ -1027,8 +715,9 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1027
715
|
deps: AgentDepsT | _utils.Unset = _utils.UNSET,
|
|
1028
716
|
model: models.Model | models.KnownModelName | str | _utils.Unset = _utils.UNSET,
|
|
1029
717
|
toolsets: Sequence[AbstractToolset[AgentDepsT]] | _utils.Unset = _utils.UNSET,
|
|
718
|
+
tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] | _utils.Unset = _utils.UNSET,
|
|
1030
719
|
) -> Iterator[None]:
|
|
1031
|
-
"""Context manager to temporarily override agent dependencies, model, or
|
|
720
|
+
"""Context manager to temporarily override agent dependencies, model, toolsets, or tools.
|
|
1032
721
|
|
|
1033
722
|
This is particularly useful when testing.
|
|
1034
723
|
You can find an example of this [here](../testing.md#overriding-model-via-pytest-fixtures).
|
|
@@ -1037,6 +726,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1037
726
|
deps: The dependencies to use instead of the dependencies passed to the agent run.
|
|
1038
727
|
model: The model to use instead of the model passed to the agent run.
|
|
1039
728
|
toolsets: The toolsets to use instead of the toolsets passed to the agent constructor and agent run.
|
|
729
|
+
tools: The tools to use instead of the tools registered with the agent.
|
|
1040
730
|
"""
|
|
1041
731
|
if _utils.is_set(deps):
|
|
1042
732
|
deps_token = self._override_deps.set(_utils.Some(deps))
|
|
@@ -1053,6 +743,11 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1053
743
|
else:
|
|
1054
744
|
toolsets_token = None
|
|
1055
745
|
|
|
746
|
+
if _utils.is_set(tools):
|
|
747
|
+
tools_token = self._override_tools.set(_utils.Some(tools))
|
|
748
|
+
else:
|
|
749
|
+
tools_token = None
|
|
750
|
+
|
|
1056
751
|
try:
|
|
1057
752
|
yield
|
|
1058
753
|
finally:
|
|
@@ -1062,6 +757,8 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1062
757
|
self._override_model.reset(model_token)
|
|
1063
758
|
if toolsets_token is not None:
|
|
1064
759
|
self._override_toolsets.reset(toolsets_token)
|
|
760
|
+
if tools_token is not None:
|
|
761
|
+
self._override_tools.reset(tools_token)
|
|
1065
762
|
|
|
1066
763
|
@overload
|
|
1067
764
|
def instructions(
|
|
@@ -1452,6 +1149,53 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1452
1149
|
|
|
1453
1150
|
return tool_decorator if func is None else tool_decorator(func)
|
|
1454
1151
|
|
|
1152
|
+
@overload
|
|
1153
|
+
def toolset(self, func: ToolsetFunc[AgentDepsT], /) -> ToolsetFunc[AgentDepsT]: ...
|
|
1154
|
+
|
|
1155
|
+
@overload
|
|
1156
|
+
def toolset(
|
|
1157
|
+
self,
|
|
1158
|
+
/,
|
|
1159
|
+
*,
|
|
1160
|
+
per_run_step: bool = True,
|
|
1161
|
+
) -> Callable[[ToolsetFunc[AgentDepsT]], ToolsetFunc[AgentDepsT]]: ...
|
|
1162
|
+
|
|
1163
|
+
def toolset(
|
|
1164
|
+
self,
|
|
1165
|
+
func: ToolsetFunc[AgentDepsT] | None = None,
|
|
1166
|
+
/,
|
|
1167
|
+
*,
|
|
1168
|
+
per_run_step: bool = True,
|
|
1169
|
+
) -> Any:
|
|
1170
|
+
"""Decorator to register a toolset function which takes [`RunContext`][pydantic_ai.tools.RunContext] as its only argument.
|
|
1171
|
+
|
|
1172
|
+
Can decorate a sync or async functions.
|
|
1173
|
+
|
|
1174
|
+
The decorator can be used bare (`agent.toolset`).
|
|
1175
|
+
|
|
1176
|
+
Example:
|
|
1177
|
+
```python
|
|
1178
|
+
from pydantic_ai import Agent, RunContext
|
|
1179
|
+
from pydantic_ai.toolsets import AbstractToolset, FunctionToolset
|
|
1180
|
+
|
|
1181
|
+
agent = Agent('test', deps_type=str)
|
|
1182
|
+
|
|
1183
|
+
@agent.toolset
|
|
1184
|
+
async def simple_toolset(ctx: RunContext[str]) -> AbstractToolset[str]:
|
|
1185
|
+
return FunctionToolset()
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
Args:
|
|
1189
|
+
func: The toolset function to register.
|
|
1190
|
+
per_run_step: Whether to re-evaluate the toolset for each run step. Defaults to True.
|
|
1191
|
+
"""
|
|
1192
|
+
|
|
1193
|
+
def toolset_decorator(func_: ToolsetFunc[AgentDepsT]) -> ToolsetFunc[AgentDepsT]:
|
|
1194
|
+
self._dynamic_toolsets.append(DynamicToolset(func_, per_run_step=per_run_step))
|
|
1195
|
+
return func_
|
|
1196
|
+
|
|
1197
|
+
return toolset_decorator if func is None else toolset_decorator(func)
|
|
1198
|
+
|
|
1455
1199
|
def _get_model(self, model: models.Model | models.KnownModelName | str | None) -> models.Model:
|
|
1456
1200
|
"""Create a model configured for this agent.
|
|
1457
1201
|
|
|
@@ -1505,46 +1249,56 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1505
1249
|
|
|
1506
1250
|
Args:
|
|
1507
1251
|
output_toolset: The output toolset to use instead of the one built at agent construction time.
|
|
1508
|
-
additional_toolsets: Additional toolsets to add.
|
|
1252
|
+
additional_toolsets: Additional toolsets to add, unless toolsets have been overridden.
|
|
1509
1253
|
"""
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
else:
|
|
1515
|
-
user_toolsets = self._user_toolsets
|
|
1254
|
+
toolsets = self.toolsets
|
|
1255
|
+
# Don't add additional toolsets if the toolsets have been overridden
|
|
1256
|
+
if additional_toolsets and self._override_toolsets.get() is None:
|
|
1257
|
+
toolsets = [*toolsets, *additional_toolsets]
|
|
1516
1258
|
|
|
1517
|
-
|
|
1259
|
+
toolset = CombinedToolset(toolsets)
|
|
1260
|
+
|
|
1261
|
+
# Copy the dynamic toolsets to ensure each run has its own instances
|
|
1262
|
+
def copy_dynamic_toolsets(toolset: AbstractToolset[AgentDepsT]) -> AbstractToolset[AgentDepsT]:
|
|
1263
|
+
if isinstance(toolset, DynamicToolset):
|
|
1264
|
+
return dataclasses.replace(toolset)
|
|
1265
|
+
else:
|
|
1266
|
+
return toolset
|
|
1267
|
+
|
|
1268
|
+
toolset = toolset.visit_and_replace(copy_dynamic_toolsets)
|
|
1518
1269
|
|
|
1519
1270
|
if self._prepare_tools:
|
|
1520
|
-
|
|
1271
|
+
toolset = PreparedToolset(toolset, self._prepare_tools)
|
|
1521
1272
|
|
|
1522
1273
|
output_toolset = output_toolset if _utils.is_set(output_toolset) else self._output_toolset
|
|
1523
1274
|
if output_toolset is not None:
|
|
1524
1275
|
if self._prepare_output_tools:
|
|
1525
1276
|
output_toolset = PreparedToolset(output_toolset, self._prepare_output_tools)
|
|
1526
|
-
|
|
1277
|
+
toolset = CombinedToolset([output_toolset, toolset])
|
|
1527
1278
|
|
|
1528
|
-
return
|
|
1279
|
+
return toolset
|
|
1529
1280
|
|
|
1530
|
-
|
|
1531
|
-
|
|
1281
|
+
@property
|
|
1282
|
+
def toolsets(self) -> Sequence[AbstractToolset[AgentDepsT]]:
|
|
1283
|
+
"""All toolsets registered on the agent, including a function toolset holding tools that were registered on the agent directly.
|
|
1532
1284
|
|
|
1533
|
-
|
|
1285
|
+
Output tools are not included.
|
|
1534
1286
|
"""
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1287
|
+
toolsets: list[AbstractToolset[AgentDepsT]] = []
|
|
1288
|
+
|
|
1289
|
+
if some_tools := self._override_tools.get():
|
|
1290
|
+
function_toolset = _AgentFunctionToolset(some_tools.value, max_retries=self._max_tool_retries)
|
|
1291
|
+
else:
|
|
1292
|
+
function_toolset = self._function_toolset
|
|
1293
|
+
toolsets.append(function_toolset)
|
|
1294
|
+
|
|
1295
|
+
if some_user_toolsets := self._override_toolsets.get():
|
|
1296
|
+
user_toolsets = some_user_toolsets.value
|
|
1297
|
+
else:
|
|
1298
|
+
user_toolsets = [*self._user_toolsets, *self._dynamic_toolsets]
|
|
1299
|
+
toolsets.extend(user_toolsets)
|
|
1300
|
+
|
|
1301
|
+
return toolsets
|
|
1548
1302
|
|
|
1549
1303
|
def _prepare_output_schema(
|
|
1550
1304
|
self, output_type: OutputSpec[RunOutputDataT] | None, model_profile: ModelProfile
|
|
@@ -1562,47 +1316,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1562
1316
|
|
|
1563
1317
|
return schema # pyright: ignore[reportReturnType]
|
|
1564
1318
|
|
|
1565
|
-
|
|
1566
|
-
def is_model_request_node(
|
|
1567
|
-
node: _agent_graph.AgentNode[T, S] | End[result.FinalResult[S]],
|
|
1568
|
-
) -> TypeIs[_agent_graph.ModelRequestNode[T, S]]:
|
|
1569
|
-
"""Check if the node is a `ModelRequestNode`, narrowing the type if it is.
|
|
1570
|
-
|
|
1571
|
-
This method preserves the generic parameters while narrowing the type, unlike a direct call to `isinstance`.
|
|
1572
|
-
"""
|
|
1573
|
-
return isinstance(node, _agent_graph.ModelRequestNode)
|
|
1574
|
-
|
|
1575
|
-
@staticmethod
|
|
1576
|
-
def is_call_tools_node(
|
|
1577
|
-
node: _agent_graph.AgentNode[T, S] | End[result.FinalResult[S]],
|
|
1578
|
-
) -> TypeIs[_agent_graph.CallToolsNode[T, S]]:
|
|
1579
|
-
"""Check if the node is a `CallToolsNode`, narrowing the type if it is.
|
|
1580
|
-
|
|
1581
|
-
This method preserves the generic parameters while narrowing the type, unlike a direct call to `isinstance`.
|
|
1582
|
-
"""
|
|
1583
|
-
return isinstance(node, _agent_graph.CallToolsNode)
|
|
1584
|
-
|
|
1585
|
-
@staticmethod
|
|
1586
|
-
def is_user_prompt_node(
|
|
1587
|
-
node: _agent_graph.AgentNode[T, S] | End[result.FinalResult[S]],
|
|
1588
|
-
) -> TypeIs[_agent_graph.UserPromptNode[T, S]]:
|
|
1589
|
-
"""Check if the node is a `UserPromptNode`, narrowing the type if it is.
|
|
1590
|
-
|
|
1591
|
-
This method preserves the generic parameters while narrowing the type, unlike a direct call to `isinstance`.
|
|
1592
|
-
"""
|
|
1593
|
-
return isinstance(node, _agent_graph.UserPromptNode)
|
|
1594
|
-
|
|
1595
|
-
@staticmethod
|
|
1596
|
-
def is_end_node(
|
|
1597
|
-
node: _agent_graph.AgentNode[T, S] | End[result.FinalResult[S]],
|
|
1598
|
-
) -> TypeIs[End[result.FinalResult[S]]]:
|
|
1599
|
-
"""Check if the node is a `End`, narrowing the type if it is.
|
|
1600
|
-
|
|
1601
|
-
This method preserves the generic parameters while narrowing the type, unlike a direct call to `isinstance`.
|
|
1602
|
-
"""
|
|
1603
|
-
return isinstance(node, End)
|
|
1604
|
-
|
|
1605
|
-
async def __aenter__(self) -> Self:
|
|
1319
|
+
async def __aenter__(self) -> AbstractAgent[AgentDepsT, OutputDataT]:
|
|
1606
1320
|
"""Enter the agent context.
|
|
1607
1321
|
|
|
1608
1322
|
This will start all [`MCPServerStdio`s][pydantic_ai.mcp.MCPServerStdio] registered as `toolsets` so they are ready to be used.
|
|
@@ -1636,7 +1350,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1636
1350
|
except exceptions.UserError as e:
|
|
1637
1351
|
raise exceptions.UserError('No sampling model provided and no model set on the agent.') from e
|
|
1638
1352
|
|
|
1639
|
-
from
|
|
1353
|
+
from ..mcp import MCPServer
|
|
1640
1354
|
|
|
1641
1355
|
def _set_sampling_model(toolset: AbstractToolset[AgentDepsT]) -> None:
|
|
1642
1356
|
if isinstance(toolset, MCPServer):
|
|
@@ -1667,536 +1381,13 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1667
1381
|
async with self:
|
|
1668
1382
|
yield
|
|
1669
1383
|
|
|
1670
|
-
def to_ag_ui(
|
|
1671
|
-
self,
|
|
1672
|
-
*,
|
|
1673
|
-
# Agent.iter parameters
|
|
1674
|
-
output_type: OutputSpec[OutputDataT] | None = None,
|
|
1675
|
-
model: models.Model | models.KnownModelName | str | None = None,
|
|
1676
|
-
deps: AgentDepsT = None,
|
|
1677
|
-
model_settings: ModelSettings | None = None,
|
|
1678
|
-
usage_limits: UsageLimits | None = None,
|
|
1679
|
-
usage: Usage | None = None,
|
|
1680
|
-
infer_name: bool = True,
|
|
1681
|
-
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
|
|
1682
|
-
# Starlette
|
|
1683
|
-
debug: bool = False,
|
|
1684
|
-
routes: Sequence[BaseRoute] | None = None,
|
|
1685
|
-
middleware: Sequence[Middleware] | None = None,
|
|
1686
|
-
exception_handlers: Mapping[Any, ExceptionHandler] | None = None,
|
|
1687
|
-
on_startup: Sequence[Callable[[], Any]] | None = None,
|
|
1688
|
-
on_shutdown: Sequence[Callable[[], Any]] | None = None,
|
|
1689
|
-
lifespan: Lifespan[AGUIApp[AgentDepsT, OutputDataT]] | None = None,
|
|
1690
|
-
) -> AGUIApp[AgentDepsT, OutputDataT]:
|
|
1691
|
-
"""Returns an ASGI application that handles every AG-UI request by running the agent.
|
|
1692
|
-
|
|
1693
|
-
Note that the `deps` will be the same for each request, with the exception of the AG-UI state that's
|
|
1694
|
-
injected into the `state` field of a `deps` object that implements the [`StateHandler`][pydantic_ai.ag_ui.StateHandler] protocol.
|
|
1695
|
-
To provide different `deps` for each request (e.g. based on the authenticated user),
|
|
1696
|
-
use [`pydantic_ai.ag_ui.run_ag_ui`][pydantic_ai.ag_ui.run_ag_ui] or
|
|
1697
|
-
[`pydantic_ai.ag_ui.handle_ag_ui_request`][pydantic_ai.ag_ui.handle_ag_ui_request] instead.
|
|
1698
|
-
|
|
1699
|
-
Example:
|
|
1700
|
-
```python
|
|
1701
|
-
from pydantic_ai import Agent
|
|
1702
|
-
|
|
1703
|
-
agent = Agent('openai:gpt-4o')
|
|
1704
|
-
app = agent.to_ag_ui()
|
|
1705
|
-
```
|
|
1706
|
-
|
|
1707
|
-
To run the application, you can use the following command:
|
|
1708
|
-
|
|
1709
|
-
```bash
|
|
1710
|
-
uvicorn app:app --host 0.0.0.0 --port 8000
|
|
1711
|
-
```
|
|
1712
|
-
|
|
1713
|
-
See [AG-UI docs](../ag-ui.md) for more information.
|
|
1714
|
-
|
|
1715
|
-
Args:
|
|
1716
|
-
output_type: Custom output type to use for this run, `output_type` may only be used if the agent has
|
|
1717
|
-
no output validators since output validators would expect an argument that matches the agent's
|
|
1718
|
-
output type.
|
|
1719
|
-
model: Optional model to use for this run, required if `model` was not set when creating the agent.
|
|
1720
|
-
deps: Optional dependencies to use for this run.
|
|
1721
|
-
model_settings: Optional settings to use for this model's request.
|
|
1722
|
-
usage_limits: Optional limits on model request count or token usage.
|
|
1723
|
-
usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
|
|
1724
|
-
infer_name: Whether to try to infer the agent name from the call frame if it's not set.
|
|
1725
|
-
toolsets: Optional additional toolsets for this run.
|
|
1726
|
-
|
|
1727
|
-
debug: Boolean indicating if debug tracebacks should be returned on errors.
|
|
1728
|
-
routes: A list of routes to serve incoming HTTP and WebSocket requests.
|
|
1729
|
-
middleware: A list of middleware to run for every request. A starlette application will always
|
|
1730
|
-
automatically include two middleware classes. `ServerErrorMiddleware` is added as the very
|
|
1731
|
-
outermost middleware, to handle any uncaught errors occurring anywhere in the entire stack.
|
|
1732
|
-
`ExceptionMiddleware` is added as the very innermost middleware, to deal with handled
|
|
1733
|
-
exception cases occurring in the routing or endpoints.
|
|
1734
|
-
exception_handlers: A mapping of either integer status codes, or exception class types onto
|
|
1735
|
-
callables which handle the exceptions. Exception handler callables should be of the form
|
|
1736
|
-
`handler(request, exc) -> response` and may be either standard functions, or async functions.
|
|
1737
|
-
on_startup: A list of callables to run on application startup. Startup handler callables do not
|
|
1738
|
-
take any arguments, and may be either standard functions, or async functions.
|
|
1739
|
-
on_shutdown: A list of callables to run on application shutdown. Shutdown handler callables do
|
|
1740
|
-
not take any arguments, and may be either standard functions, or async functions.
|
|
1741
|
-
lifespan: A lifespan context function, which can be used to perform startup and shutdown tasks.
|
|
1742
|
-
This is a newer style that replaces the `on_startup` and `on_shutdown` handlers. Use one or
|
|
1743
|
-
the other, not both.
|
|
1744
|
-
|
|
1745
|
-
Returns:
|
|
1746
|
-
An ASGI application for running Pydantic AI agents with AG-UI protocol support.
|
|
1747
|
-
"""
|
|
1748
|
-
from .ag_ui import AGUIApp
|
|
1749
|
-
|
|
1750
|
-
return AGUIApp(
|
|
1751
|
-
agent=self,
|
|
1752
|
-
# Agent.iter parameters
|
|
1753
|
-
output_type=output_type,
|
|
1754
|
-
model=model,
|
|
1755
|
-
deps=deps,
|
|
1756
|
-
model_settings=model_settings,
|
|
1757
|
-
usage_limits=usage_limits,
|
|
1758
|
-
usage=usage,
|
|
1759
|
-
infer_name=infer_name,
|
|
1760
|
-
toolsets=toolsets,
|
|
1761
|
-
# Starlette
|
|
1762
|
-
debug=debug,
|
|
1763
|
-
routes=routes,
|
|
1764
|
-
middleware=middleware,
|
|
1765
|
-
exception_handlers=exception_handlers,
|
|
1766
|
-
on_startup=on_startup,
|
|
1767
|
-
on_shutdown=on_shutdown,
|
|
1768
|
-
lifespan=lifespan,
|
|
1769
|
-
)
|
|
1770
|
-
|
|
1771
|
-
def to_a2a(
|
|
1772
|
-
self,
|
|
1773
|
-
*,
|
|
1774
|
-
storage: Storage | None = None,
|
|
1775
|
-
broker: Broker | None = None,
|
|
1776
|
-
# Agent card
|
|
1777
|
-
name: str | None = None,
|
|
1778
|
-
url: str = 'http://localhost:8000',
|
|
1779
|
-
version: str = '1.0.0',
|
|
1780
|
-
description: str | None = None,
|
|
1781
|
-
provider: AgentProvider | None = None,
|
|
1782
|
-
skills: list[Skill] | None = None,
|
|
1783
|
-
# Starlette
|
|
1784
|
-
debug: bool = False,
|
|
1785
|
-
routes: Sequence[Route] | None = None,
|
|
1786
|
-
middleware: Sequence[Middleware] | None = None,
|
|
1787
|
-
exception_handlers: dict[Any, ExceptionHandler] | None = None,
|
|
1788
|
-
lifespan: Lifespan[FastA2A] | None = None,
|
|
1789
|
-
) -> FastA2A:
|
|
1790
|
-
"""Convert the agent to a FastA2A application.
|
|
1791
|
-
|
|
1792
|
-
Example:
|
|
1793
|
-
```python
|
|
1794
|
-
from pydantic_ai import Agent
|
|
1795
|
-
|
|
1796
|
-
agent = Agent('openai:gpt-4o')
|
|
1797
|
-
app = agent.to_a2a()
|
|
1798
|
-
```
|
|
1799
|
-
|
|
1800
|
-
The `app` is an ASGI application that can be used with any ASGI server.
|
|
1801
|
-
|
|
1802
|
-
To run the application, you can use the following command:
|
|
1803
|
-
|
|
1804
|
-
```bash
|
|
1805
|
-
uvicorn app:app --host 0.0.0.0 --port 8000
|
|
1806
|
-
```
|
|
1807
|
-
"""
|
|
1808
|
-
from ._a2a import agent_to_a2a
|
|
1809
|
-
|
|
1810
|
-
return agent_to_a2a(
|
|
1811
|
-
self,
|
|
1812
|
-
storage=storage,
|
|
1813
|
-
broker=broker,
|
|
1814
|
-
name=name,
|
|
1815
|
-
url=url,
|
|
1816
|
-
version=version,
|
|
1817
|
-
description=description,
|
|
1818
|
-
provider=provider,
|
|
1819
|
-
skills=skills,
|
|
1820
|
-
debug=debug,
|
|
1821
|
-
routes=routes,
|
|
1822
|
-
middleware=middleware,
|
|
1823
|
-
exception_handlers=exception_handlers,
|
|
1824
|
-
lifespan=lifespan,
|
|
1825
|
-
)
|
|
1826
|
-
|
|
1827
|
-
async def to_cli(self: Self, deps: AgentDepsT = None, prog_name: str = 'pydantic-ai') -> None:
|
|
1828
|
-
"""Run the agent in a CLI chat interface.
|
|
1829
|
-
|
|
1830
|
-
Args:
|
|
1831
|
-
deps: The dependencies to pass to the agent.
|
|
1832
|
-
prog_name: The name of the program to use for the CLI. Defaults to 'pydantic-ai'.
|
|
1833
|
-
|
|
1834
|
-
Example:
|
|
1835
|
-
```python {title="agent_to_cli.py" test="skip"}
|
|
1836
|
-
from pydantic_ai import Agent
|
|
1837
|
-
|
|
1838
|
-
agent = Agent('openai:gpt-4o', instructions='You always respond in Italian.')
|
|
1839
|
-
|
|
1840
|
-
async def main():
|
|
1841
|
-
await agent.to_cli()
|
|
1842
|
-
```
|
|
1843
|
-
"""
|
|
1844
|
-
from rich.console import Console
|
|
1845
|
-
|
|
1846
|
-
from pydantic_ai._cli import run_chat
|
|
1847
|
-
|
|
1848
|
-
await run_chat(stream=True, agent=self, deps=deps, console=Console(), code_theme='monokai', prog_name=prog_name)
|
|
1849
|
-
|
|
1850
|
-
def to_cli_sync(self: Self, deps: AgentDepsT = None, prog_name: str = 'pydantic-ai') -> None:
|
|
1851
|
-
"""Run the agent in a CLI chat interface with the non-async interface.
|
|
1852
|
-
|
|
1853
|
-
Args:
|
|
1854
|
-
deps: The dependencies to pass to the agent.
|
|
1855
|
-
prog_name: The name of the program to use for the CLI. Defaults to 'pydantic-ai'.
|
|
1856
|
-
|
|
1857
|
-
```python {title="agent_to_cli_sync.py" test="skip"}
|
|
1858
|
-
from pydantic_ai import Agent
|
|
1859
|
-
|
|
1860
|
-
agent = Agent('openai:gpt-4o', instructions='You always respond in Italian.')
|
|
1861
|
-
agent.to_cli_sync()
|
|
1862
|
-
agent.to_cli_sync(prog_name='assistant')
|
|
1863
|
-
```
|
|
1864
|
-
"""
|
|
1865
|
-
return get_event_loop().run_until_complete(self.to_cli(deps=deps, prog_name=prog_name))
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
@dataclasses.dataclass(repr=False)
|
|
1869
|
-
class AgentRun(Generic[AgentDepsT, OutputDataT]):
|
|
1870
|
-
"""A stateful, async-iterable run of an [`Agent`][pydantic_ai.agent.Agent].
|
|
1871
|
-
|
|
1872
|
-
You generally obtain an `AgentRun` instance by calling `async with my_agent.iter(...) as agent_run:`.
|
|
1873
|
-
|
|
1874
|
-
Once you have an instance, you can use it to iterate through the run's nodes as they execute. When an
|
|
1875
|
-
[`End`][pydantic_graph.nodes.End] is reached, the run finishes and [`result`][pydantic_ai.agent.AgentRun.result]
|
|
1876
|
-
becomes available.
|
|
1877
|
-
|
|
1878
|
-
Example:
|
|
1879
|
-
```python
|
|
1880
|
-
from pydantic_ai import Agent
|
|
1881
|
-
|
|
1882
|
-
agent = Agent('openai:gpt-4o')
|
|
1883
|
-
|
|
1884
|
-
async def main():
|
|
1885
|
-
nodes = []
|
|
1886
|
-
# Iterate through the run, recording each node along the way:
|
|
1887
|
-
async with agent.iter('What is the capital of France?') as agent_run:
|
|
1888
|
-
async for node in agent_run:
|
|
1889
|
-
nodes.append(node)
|
|
1890
|
-
print(nodes)
|
|
1891
|
-
'''
|
|
1892
|
-
[
|
|
1893
|
-
UserPromptNode(
|
|
1894
|
-
user_prompt='What is the capital of France?',
|
|
1895
|
-
instructions=None,
|
|
1896
|
-
instructions_functions=[],
|
|
1897
|
-
system_prompts=(),
|
|
1898
|
-
system_prompt_functions=[],
|
|
1899
|
-
system_prompt_dynamic_functions={},
|
|
1900
|
-
),
|
|
1901
|
-
ModelRequestNode(
|
|
1902
|
-
request=ModelRequest(
|
|
1903
|
-
parts=[
|
|
1904
|
-
UserPromptPart(
|
|
1905
|
-
content='What is the capital of France?',
|
|
1906
|
-
timestamp=datetime.datetime(...),
|
|
1907
|
-
)
|
|
1908
|
-
]
|
|
1909
|
-
)
|
|
1910
|
-
),
|
|
1911
|
-
CallToolsNode(
|
|
1912
|
-
model_response=ModelResponse(
|
|
1913
|
-
parts=[TextPart(content='Paris')],
|
|
1914
|
-
usage=Usage(
|
|
1915
|
-
requests=1, request_tokens=56, response_tokens=1, total_tokens=57
|
|
1916
|
-
),
|
|
1917
|
-
model_name='gpt-4o',
|
|
1918
|
-
timestamp=datetime.datetime(...),
|
|
1919
|
-
)
|
|
1920
|
-
),
|
|
1921
|
-
End(data=FinalResult(output='Paris')),
|
|
1922
|
-
]
|
|
1923
|
-
'''
|
|
1924
|
-
print(agent_run.result.output)
|
|
1925
|
-
#> Paris
|
|
1926
|
-
```
|
|
1927
|
-
|
|
1928
|
-
You can also manually drive the iteration using the [`next`][pydantic_ai.agent.AgentRun.next] method for
|
|
1929
|
-
more granular control.
|
|
1930
|
-
"""
|
|
1931
|
-
|
|
1932
|
-
_graph_run: GraphRun[
|
|
1933
|
-
_agent_graph.GraphAgentState, _agent_graph.GraphAgentDeps[AgentDepsT, Any], FinalResult[OutputDataT]
|
|
1934
|
-
]
|
|
1935
|
-
|
|
1936
|
-
@overload
|
|
1937
|
-
def _traceparent(self, *, required: Literal[False]) -> str | None: ...
|
|
1938
|
-
@overload
|
|
1939
|
-
def _traceparent(self) -> str: ...
|
|
1940
|
-
def _traceparent(self, *, required: bool = True) -> str | None:
|
|
1941
|
-
traceparent = self._graph_run._traceparent(required=False) # type: ignore[reportPrivateUsage]
|
|
1942
|
-
if traceparent is None and required: # pragma: no cover
|
|
1943
|
-
raise AttributeError('No span was created for this agent run')
|
|
1944
|
-
return traceparent
|
|
1945
|
-
|
|
1946
|
-
@property
|
|
1947
|
-
def ctx(self) -> GraphRunContext[_agent_graph.GraphAgentState, _agent_graph.GraphAgentDeps[AgentDepsT, Any]]:
|
|
1948
|
-
"""The current context of the agent run."""
|
|
1949
|
-
return GraphRunContext[_agent_graph.GraphAgentState, _agent_graph.GraphAgentDeps[AgentDepsT, Any]](
|
|
1950
|
-
self._graph_run.state, self._graph_run.deps
|
|
1951
|
-
)
|
|
1952
1384
|
|
|
1385
|
+
@dataclasses.dataclass(init=False)
|
|
1386
|
+
class _AgentFunctionToolset(FunctionToolset[AgentDepsT]):
|
|
1953
1387
|
@property
|
|
1954
|
-
def
|
|
1955
|
-
|
|
1956
|
-
) -> _agent_graph.AgentNode[AgentDepsT, OutputDataT] | End[FinalResult[OutputDataT]]:
|
|
1957
|
-
"""The next node that will be run in the agent graph.
|
|
1958
|
-
|
|
1959
|
-
This is the next node that will be used during async iteration, or if a node is not passed to `self.next(...)`.
|
|
1960
|
-
"""
|
|
1961
|
-
next_node = self._graph_run.next_node
|
|
1962
|
-
if isinstance(next_node, End):
|
|
1963
|
-
return next_node
|
|
1964
|
-
if _agent_graph.is_agent_node(next_node):
|
|
1965
|
-
return next_node
|
|
1966
|
-
raise exceptions.AgentRunError(f'Unexpected node type: {type(next_node)}') # pragma: no cover
|
|
1388
|
+
def id(self) -> str:
|
|
1389
|
+
return '<agent>'
|
|
1967
1390
|
|
|
1968
1391
|
@property
|
|
1969
|
-
def
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
Once the run returns an [`End`][pydantic_graph.nodes.End] node, `result` is populated
|
|
1973
|
-
with an [`AgentRunResult`][pydantic_ai.agent.AgentRunResult].
|
|
1974
|
-
"""
|
|
1975
|
-
graph_run_result = self._graph_run.result
|
|
1976
|
-
if graph_run_result is None:
|
|
1977
|
-
return None
|
|
1978
|
-
return AgentRunResult(
|
|
1979
|
-
graph_run_result.output.output,
|
|
1980
|
-
graph_run_result.output.tool_name,
|
|
1981
|
-
graph_run_result.state,
|
|
1982
|
-
self._graph_run.deps.new_message_index,
|
|
1983
|
-
self._traceparent(required=False),
|
|
1984
|
-
)
|
|
1985
|
-
|
|
1986
|
-
def __aiter__(
|
|
1987
|
-
self,
|
|
1988
|
-
) -> AsyncIterator[_agent_graph.AgentNode[AgentDepsT, OutputDataT] | End[FinalResult[OutputDataT]]]:
|
|
1989
|
-
"""Provide async-iteration over the nodes in the agent run."""
|
|
1990
|
-
return self
|
|
1991
|
-
|
|
1992
|
-
async def __anext__(
|
|
1993
|
-
self,
|
|
1994
|
-
) -> _agent_graph.AgentNode[AgentDepsT, OutputDataT] | End[FinalResult[OutputDataT]]:
|
|
1995
|
-
"""Advance to the next node automatically based on the last returned node."""
|
|
1996
|
-
next_node = await self._graph_run.__anext__()
|
|
1997
|
-
if _agent_graph.is_agent_node(node=next_node):
|
|
1998
|
-
return next_node
|
|
1999
|
-
assert isinstance(next_node, End), f'Unexpected node type: {type(next_node)}'
|
|
2000
|
-
return next_node
|
|
2001
|
-
|
|
2002
|
-
async def next(
|
|
2003
|
-
self,
|
|
2004
|
-
node: _agent_graph.AgentNode[AgentDepsT, OutputDataT],
|
|
2005
|
-
) -> _agent_graph.AgentNode[AgentDepsT, OutputDataT] | End[FinalResult[OutputDataT]]:
|
|
2006
|
-
"""Manually drive the agent run by passing in the node you want to run next.
|
|
2007
|
-
|
|
2008
|
-
This lets you inspect or mutate the node before continuing execution, or skip certain nodes
|
|
2009
|
-
under dynamic conditions. The agent run should be stopped when you return an [`End`][pydantic_graph.nodes.End]
|
|
2010
|
-
node.
|
|
2011
|
-
|
|
2012
|
-
Example:
|
|
2013
|
-
```python
|
|
2014
|
-
from pydantic_ai import Agent
|
|
2015
|
-
from pydantic_graph import End
|
|
2016
|
-
|
|
2017
|
-
agent = Agent('openai:gpt-4o')
|
|
2018
|
-
|
|
2019
|
-
async def main():
|
|
2020
|
-
async with agent.iter('What is the capital of France?') as agent_run:
|
|
2021
|
-
next_node = agent_run.next_node # start with the first node
|
|
2022
|
-
nodes = [next_node]
|
|
2023
|
-
while not isinstance(next_node, End):
|
|
2024
|
-
next_node = await agent_run.next(next_node)
|
|
2025
|
-
nodes.append(next_node)
|
|
2026
|
-
# Once `next_node` is an End, we've finished:
|
|
2027
|
-
print(nodes)
|
|
2028
|
-
'''
|
|
2029
|
-
[
|
|
2030
|
-
UserPromptNode(
|
|
2031
|
-
user_prompt='What is the capital of France?',
|
|
2032
|
-
instructions=None,
|
|
2033
|
-
instructions_functions=[],
|
|
2034
|
-
system_prompts=(),
|
|
2035
|
-
system_prompt_functions=[],
|
|
2036
|
-
system_prompt_dynamic_functions={},
|
|
2037
|
-
),
|
|
2038
|
-
ModelRequestNode(
|
|
2039
|
-
request=ModelRequest(
|
|
2040
|
-
parts=[
|
|
2041
|
-
UserPromptPart(
|
|
2042
|
-
content='What is the capital of France?',
|
|
2043
|
-
timestamp=datetime.datetime(...),
|
|
2044
|
-
)
|
|
2045
|
-
]
|
|
2046
|
-
)
|
|
2047
|
-
),
|
|
2048
|
-
CallToolsNode(
|
|
2049
|
-
model_response=ModelResponse(
|
|
2050
|
-
parts=[TextPart(content='Paris')],
|
|
2051
|
-
usage=Usage(
|
|
2052
|
-
requests=1,
|
|
2053
|
-
request_tokens=56,
|
|
2054
|
-
response_tokens=1,
|
|
2055
|
-
total_tokens=57,
|
|
2056
|
-
),
|
|
2057
|
-
model_name='gpt-4o',
|
|
2058
|
-
timestamp=datetime.datetime(...),
|
|
2059
|
-
)
|
|
2060
|
-
),
|
|
2061
|
-
End(data=FinalResult(output='Paris')),
|
|
2062
|
-
]
|
|
2063
|
-
'''
|
|
2064
|
-
print('Final result:', agent_run.result.output)
|
|
2065
|
-
#> Final result: Paris
|
|
2066
|
-
```
|
|
2067
|
-
|
|
2068
|
-
Args:
|
|
2069
|
-
node: The node to run next in the graph.
|
|
2070
|
-
|
|
2071
|
-
Returns:
|
|
2072
|
-
The next node returned by the graph logic, or an [`End`][pydantic_graph.nodes.End] node if
|
|
2073
|
-
the run has completed.
|
|
2074
|
-
"""
|
|
2075
|
-
# Note: It might be nice to expose a synchronous interface for iteration, but we shouldn't do it
|
|
2076
|
-
# on this class, or else IDEs won't warn you if you accidentally use `for` instead of `async for` to iterate.
|
|
2077
|
-
next_node = await self._graph_run.next(node)
|
|
2078
|
-
if _agent_graph.is_agent_node(next_node):
|
|
2079
|
-
return next_node
|
|
2080
|
-
assert isinstance(next_node, End), f'Unexpected node type: {type(next_node)}'
|
|
2081
|
-
return next_node
|
|
2082
|
-
|
|
2083
|
-
def usage(self) -> _usage.Usage:
|
|
2084
|
-
"""Get usage statistics for the run so far, including token usage, model requests, and so on."""
|
|
2085
|
-
return self._graph_run.state.usage
|
|
2086
|
-
|
|
2087
|
-
def __repr__(self) -> str: # pragma: no cover
|
|
2088
|
-
result = self._graph_run.result
|
|
2089
|
-
result_repr = '<run not finished>' if result is None else repr(result.output)
|
|
2090
|
-
return f'<{type(self).__name__} result={result_repr} usage={self.usage()}>'
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
@dataclasses.dataclass
|
|
2094
|
-
class AgentRunResult(Generic[OutputDataT]):
|
|
2095
|
-
"""The final result of an agent run."""
|
|
2096
|
-
|
|
2097
|
-
output: OutputDataT
|
|
2098
|
-
"""The output data from the agent run."""
|
|
2099
|
-
|
|
2100
|
-
_output_tool_name: str | None = dataclasses.field(repr=False)
|
|
2101
|
-
_state: _agent_graph.GraphAgentState = dataclasses.field(repr=False)
|
|
2102
|
-
_new_message_index: int = dataclasses.field(repr=False)
|
|
2103
|
-
_traceparent_value: str | None = dataclasses.field(repr=False)
|
|
2104
|
-
|
|
2105
|
-
@overload
|
|
2106
|
-
def _traceparent(self, *, required: Literal[False]) -> str | None: ...
|
|
2107
|
-
@overload
|
|
2108
|
-
def _traceparent(self) -> str: ...
|
|
2109
|
-
def _traceparent(self, *, required: bool = True) -> str | None:
|
|
2110
|
-
if self._traceparent_value is None and required: # pragma: no cover
|
|
2111
|
-
raise AttributeError('No span was created for this agent run')
|
|
2112
|
-
return self._traceparent_value
|
|
2113
|
-
|
|
2114
|
-
def _set_output_tool_return(self, return_content: str) -> list[_messages.ModelMessage]:
|
|
2115
|
-
"""Set return content for the output tool.
|
|
2116
|
-
|
|
2117
|
-
Useful if you want to continue the conversation and want to set the response to the output tool call.
|
|
2118
|
-
"""
|
|
2119
|
-
if not self._output_tool_name:
|
|
2120
|
-
raise ValueError('Cannot set output tool return content when the return type is `str`.')
|
|
2121
|
-
|
|
2122
|
-
messages = self._state.message_history
|
|
2123
|
-
last_message = messages[-1]
|
|
2124
|
-
for idx, part in enumerate(last_message.parts):
|
|
2125
|
-
if isinstance(part, _messages.ToolReturnPart) and part.tool_name == self._output_tool_name:
|
|
2126
|
-
# Only do deepcopy when we have to modify
|
|
2127
|
-
copied_messages = list(messages)
|
|
2128
|
-
copied_last = deepcopy(last_message)
|
|
2129
|
-
copied_last.parts[idx].content = return_content # type: ignore[misc]
|
|
2130
|
-
copied_messages[-1] = copied_last
|
|
2131
|
-
return copied_messages
|
|
2132
|
-
|
|
2133
|
-
raise LookupError(f'No tool call found with tool name {self._output_tool_name!r}.')
|
|
2134
|
-
|
|
2135
|
-
def all_messages(self, *, output_tool_return_content: str | None = None) -> list[_messages.ModelMessage]:
|
|
2136
|
-
"""Return the history of _messages.
|
|
2137
|
-
|
|
2138
|
-
Args:
|
|
2139
|
-
output_tool_return_content: The return content of the tool call to set in the last message.
|
|
2140
|
-
This provides a convenient way to modify the content of the output tool call if you want to continue
|
|
2141
|
-
the conversation and want to set the response to the output tool call. If `None`, the last message will
|
|
2142
|
-
not be modified.
|
|
2143
|
-
|
|
2144
|
-
Returns:
|
|
2145
|
-
List of messages.
|
|
2146
|
-
"""
|
|
2147
|
-
if output_tool_return_content is not None:
|
|
2148
|
-
return self._set_output_tool_return(output_tool_return_content)
|
|
2149
|
-
else:
|
|
2150
|
-
return self._state.message_history
|
|
2151
|
-
|
|
2152
|
-
def all_messages_json(self, *, output_tool_return_content: str | None = None) -> bytes:
|
|
2153
|
-
"""Return all messages from [`all_messages`][pydantic_ai.agent.AgentRunResult.all_messages] as JSON bytes.
|
|
2154
|
-
|
|
2155
|
-
Args:
|
|
2156
|
-
output_tool_return_content: The return content of the tool call to set in the last message.
|
|
2157
|
-
This provides a convenient way to modify the content of the output tool call if you want to continue
|
|
2158
|
-
the conversation and want to set the response to the output tool call. If `None`, the last message will
|
|
2159
|
-
not be modified.
|
|
2160
|
-
|
|
2161
|
-
Returns:
|
|
2162
|
-
JSON bytes representing the messages.
|
|
2163
|
-
"""
|
|
2164
|
-
return _messages.ModelMessagesTypeAdapter.dump_json(
|
|
2165
|
-
self.all_messages(output_tool_return_content=output_tool_return_content)
|
|
2166
|
-
)
|
|
2167
|
-
|
|
2168
|
-
def new_messages(self, *, output_tool_return_content: str | None = None) -> list[_messages.ModelMessage]:
|
|
2169
|
-
"""Return new messages associated with this run.
|
|
2170
|
-
|
|
2171
|
-
Messages from older runs are excluded.
|
|
2172
|
-
|
|
2173
|
-
Args:
|
|
2174
|
-
output_tool_return_content: The return content of the tool call to set in the last message.
|
|
2175
|
-
This provides a convenient way to modify the content of the output tool call if you want to continue
|
|
2176
|
-
the conversation and want to set the response to the output tool call. If `None`, the last message will
|
|
2177
|
-
not be modified.
|
|
2178
|
-
|
|
2179
|
-
Returns:
|
|
2180
|
-
List of new messages.
|
|
2181
|
-
"""
|
|
2182
|
-
return self.all_messages(output_tool_return_content=output_tool_return_content)[self._new_message_index :]
|
|
2183
|
-
|
|
2184
|
-
def new_messages_json(self, *, output_tool_return_content: str | None = None) -> bytes:
|
|
2185
|
-
"""Return new messages from [`new_messages`][pydantic_ai.agent.AgentRunResult.new_messages] as JSON bytes.
|
|
2186
|
-
|
|
2187
|
-
Args:
|
|
2188
|
-
output_tool_return_content: The return content of the tool call to set in the last message.
|
|
2189
|
-
This provides a convenient way to modify the content of the output tool call if you want to continue
|
|
2190
|
-
the conversation and want to set the response to the output tool call. If `None`, the last message will
|
|
2191
|
-
not be modified.
|
|
2192
|
-
|
|
2193
|
-
Returns:
|
|
2194
|
-
JSON bytes representing the new messages.
|
|
2195
|
-
"""
|
|
2196
|
-
return _messages.ModelMessagesTypeAdapter.dump_json(
|
|
2197
|
-
self.new_messages(output_tool_return_content=output_tool_return_content)
|
|
2198
|
-
)
|
|
2199
|
-
|
|
2200
|
-
def usage(self) -> _usage.Usage:
|
|
2201
|
-
"""Return the usage of the whole run."""
|
|
2202
|
-
return self._state.usage
|
|
1392
|
+
def label(self) -> str:
|
|
1393
|
+
return 'the agent'
|