pydantic-ai-slim 1.0.18__py3-none-any.whl → 1.1.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.

@@ -0,0 +1,833 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import AsyncIterable, AsyncIterator, Callable, Iterator, Sequence
4
+ from contextlib import AbstractAsyncContextManager, asynccontextmanager, contextmanager
5
+ from contextvars import ContextVar
6
+ from typing import Any, overload
7
+
8
+ from prefect import flow, task
9
+ from prefect.context import FlowRunContext
10
+ from prefect.utilities.asyncutils import run_coro_as_sync
11
+ from typing_extensions import Never
12
+
13
+ from pydantic_ai import (
14
+ AbstractToolset,
15
+ AgentRunResultEvent,
16
+ _utils,
17
+ messages as _messages,
18
+ models,
19
+ usage as _usage,
20
+ )
21
+ from pydantic_ai.agent import AbstractAgent, AgentRun, AgentRunResult, EventStreamHandler, WrapperAgent
22
+ from pydantic_ai.agent.abstract import Instructions, RunOutputDataT
23
+ from pydantic_ai.builtin_tools import AbstractBuiltinTool
24
+ from pydantic_ai.exceptions import UserError
25
+ from pydantic_ai.models import Model
26
+ from pydantic_ai.output import OutputDataT, OutputSpec
27
+ from pydantic_ai.result import StreamedRunResult
28
+ from pydantic_ai.settings import ModelSettings
29
+ from pydantic_ai.tools import (
30
+ AgentDepsT,
31
+ DeferredToolResults,
32
+ RunContext,
33
+ Tool,
34
+ ToolFuncEither,
35
+ )
36
+
37
+ from ._model import PrefectModel
38
+ from ._toolset import prefectify_toolset
39
+ from ._types import TaskConfig, default_task_config
40
+
41
+
42
+ class PrefectAgent(WrapperAgent[AgentDepsT, OutputDataT]):
43
+ def __init__(
44
+ self,
45
+ wrapped: AbstractAgent[AgentDepsT, OutputDataT],
46
+ *,
47
+ name: str | None = None,
48
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
49
+ mcp_task_config: TaskConfig | None = None,
50
+ model_task_config: TaskConfig | None = None,
51
+ tool_task_config: TaskConfig | None = None,
52
+ tool_task_config_by_name: dict[str, TaskConfig | None] | None = None,
53
+ event_stream_handler_task_config: TaskConfig | None = None,
54
+ prefectify_toolset_func: Callable[
55
+ [AbstractToolset[AgentDepsT], TaskConfig, TaskConfig, dict[str, TaskConfig | None]],
56
+ AbstractToolset[AgentDepsT],
57
+ ] = prefectify_toolset,
58
+ ):
59
+ """Wrap an agent to enable it with Prefect durable flows, by automatically offloading model requests, tool calls, and MCP server communication to Prefect tasks.
60
+
61
+ After wrapping, the original agent can still be used as normal outside of the Prefect flow.
62
+
63
+ Args:
64
+ wrapped: The agent to wrap.
65
+ name: Optional unique agent name to use as the Prefect flow name prefix. If not provided, the agent's `name` will be used.
66
+ event_stream_handler: Optional event stream handler to use instead of the one set on the wrapped agent.
67
+ mcp_task_config: The base Prefect task config to use for MCP server tasks. If no config is provided, use the default settings of Prefect.
68
+ model_task_config: The Prefect task config to use for model request tasks. If no config is provided, use the default settings of Prefect.
69
+ tool_task_config: The default Prefect task config to use for tool calls. If no config is provided, use the default settings of Prefect.
70
+ tool_task_config_by_name: Per-tool task configuration. Keys are tool names, values are TaskConfig or None (None disables task wrapping for that tool).
71
+ event_stream_handler_task_config: The Prefect task config to use for the event stream handler task. If no config is provided, use the default settings of Prefect.
72
+ prefectify_toolset_func: Optional function to use to prepare toolsets for Prefect by wrapping them in a `PrefectWrapperToolset` that moves methods that require IO to Prefect tasks.
73
+ If not provided, only `FunctionToolset` and `MCPServer` will be prepared for Prefect.
74
+ The function takes the toolset, the task config, the tool-specific task config, and the tool-specific task config by name.
75
+ """
76
+ super().__init__(wrapped)
77
+
78
+ self._name = name or wrapped.name
79
+ self._event_stream_handler = event_stream_handler
80
+ if self._name is None:
81
+ raise UserError(
82
+ "An agent needs to have a unique `name` in order to be used with Prefect. The name will be used to identify the agent's flows and tasks."
83
+ )
84
+
85
+ # Merge the config with the default Prefect config
86
+ self._mcp_task_config = default_task_config | (mcp_task_config or {})
87
+ self._model_task_config = default_task_config | (model_task_config or {})
88
+ self._tool_task_config = default_task_config | (tool_task_config or {})
89
+ self._tool_task_config_by_name = tool_task_config_by_name or {}
90
+ self._event_stream_handler_task_config = default_task_config | (event_stream_handler_task_config or {})
91
+
92
+ if not isinstance(wrapped.model, Model):
93
+ raise UserError(
94
+ 'An agent needs to have a `model` in order to be used with Prefect, it cannot be set at agent run time.'
95
+ )
96
+
97
+ prefect_model = PrefectModel(
98
+ wrapped.model,
99
+ task_config=self._model_task_config,
100
+ event_stream_handler=self.event_stream_handler,
101
+ )
102
+ self._model = prefect_model
103
+
104
+ def _prefectify_toolset(toolset: AbstractToolset[AgentDepsT]) -> AbstractToolset[AgentDepsT]:
105
+ """Convert a toolset to its Prefect equivalent."""
106
+ return prefectify_toolset_func(
107
+ toolset,
108
+ self._mcp_task_config,
109
+ self._tool_task_config,
110
+ self._tool_task_config_by_name,
111
+ )
112
+
113
+ prefect_toolsets = [toolset.visit_and_replace(_prefectify_toolset) for toolset in wrapped.toolsets]
114
+ self._toolsets = prefect_toolsets
115
+
116
+ # Context variable to track when we're inside this agent's Prefect flow
117
+ self._in_prefect_agent_flow: ContextVar[bool] = ContextVar(
118
+ f'_in_prefect_agent_flow_{self._name}', default=False
119
+ )
120
+
121
+ @property
122
+ def name(self) -> str | None:
123
+ return self._name
124
+
125
+ @name.setter
126
+ def name(self, value: str | None) -> None: # pragma: no cover
127
+ raise UserError(
128
+ 'The agent name cannot be changed after creation. If you need to change the name, create a new agent.'
129
+ )
130
+
131
+ @property
132
+ def model(self) -> Model:
133
+ return self._model
134
+
135
+ @property
136
+ def event_stream_handler(self) -> EventStreamHandler[AgentDepsT] | None:
137
+ handler = self._event_stream_handler or super().event_stream_handler
138
+ if handler is None:
139
+ return None
140
+ elif FlowRunContext.get() is not None:
141
+ # Special case if it's in a Prefect flow, we need to iterate through all events and call the handler.
142
+ return self._call_event_stream_handler_in_flow
143
+ else:
144
+ return handler
145
+
146
+ async def _call_event_stream_handler_in_flow(
147
+ self, ctx: RunContext[AgentDepsT], stream: AsyncIterable[_messages.AgentStreamEvent]
148
+ ) -> None:
149
+ handler = self._event_stream_handler or super().event_stream_handler
150
+ assert handler is not None
151
+
152
+ # Create a task to handle each event
153
+ @task(name='Handle Stream Event', **self._event_stream_handler_task_config)
154
+ async def event_stream_handler_task(event: _messages.AgentStreamEvent) -> None:
155
+ async def streamed_response():
156
+ yield event
157
+
158
+ await handler(ctx, streamed_response())
159
+
160
+ async for event in stream:
161
+ await event_stream_handler_task(event)
162
+
163
+ @property
164
+ def toolsets(self) -> Sequence[AbstractToolset[AgentDepsT]]:
165
+ with self._prefect_overrides():
166
+ return super().toolsets
167
+
168
+ @contextmanager
169
+ def _prefect_overrides(self) -> Iterator[None]:
170
+ # Override with PrefectModel and PrefectMCPServer in the toolsets.
171
+ with super().override(model=self._model, toolsets=self._toolsets, tools=[]):
172
+ yield
173
+
174
+ @overload
175
+ async def run(
176
+ self,
177
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
178
+ *,
179
+ output_type: None = None,
180
+ message_history: Sequence[_messages.ModelMessage] | None = None,
181
+ deferred_tool_results: DeferredToolResults | None = None,
182
+ model: models.Model | models.KnownModelName | str | None = None,
183
+ deps: AgentDepsT = None,
184
+ model_settings: ModelSettings | None = None,
185
+ usage_limits: _usage.UsageLimits | None = None,
186
+ usage: _usage.RunUsage | None = None,
187
+ infer_name: bool = True,
188
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
189
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
190
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
191
+ ) -> AgentRunResult[OutputDataT]: ...
192
+
193
+ @overload
194
+ async def run(
195
+ self,
196
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
197
+ *,
198
+ output_type: OutputSpec[RunOutputDataT],
199
+ message_history: Sequence[_messages.ModelMessage] | None = None,
200
+ deferred_tool_results: DeferredToolResults | None = None,
201
+ model: models.Model | models.KnownModelName | str | None = None,
202
+ deps: AgentDepsT = None,
203
+ model_settings: ModelSettings | None = None,
204
+ usage_limits: _usage.UsageLimits | None = None,
205
+ usage: _usage.RunUsage | None = None,
206
+ infer_name: bool = True,
207
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
208
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
209
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
210
+ ) -> AgentRunResult[RunOutputDataT]: ...
211
+
212
+ async def run(
213
+ self,
214
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
215
+ *,
216
+ output_type: OutputSpec[RunOutputDataT] | None = None,
217
+ message_history: Sequence[_messages.ModelMessage] | None = None,
218
+ deferred_tool_results: DeferredToolResults | None = None,
219
+ model: models.Model | models.KnownModelName | str | None = None,
220
+ deps: AgentDepsT = None,
221
+ model_settings: ModelSettings | None = None,
222
+ usage_limits: _usage.UsageLimits | None = None,
223
+ usage: _usage.RunUsage | None = None,
224
+ infer_name: bool = True,
225
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
226
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
227
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
228
+ **_deprecated_kwargs: Never,
229
+ ) -> AgentRunResult[Any]:
230
+ """Run the agent with a user prompt in async mode.
231
+
232
+ This method builds an internal agent graph (using system prompts, tools and result schemas) and then
233
+ runs the graph to completion. The result of the run is returned.
234
+
235
+ Example:
236
+ ```python
237
+ from pydantic_ai import Agent
238
+
239
+ agent = Agent('openai:gpt-4o')
240
+
241
+ async def main():
242
+ agent_run = await agent.run('What is the capital of France?')
243
+ print(agent_run.output)
244
+ #> The capital of France is Paris.
245
+ ```
246
+
247
+ Args:
248
+ user_prompt: User input to start/continue the conversation.
249
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
250
+ output validators since output validators would expect an argument that matches the agent's output type.
251
+ message_history: History of the conversation so far.
252
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
253
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
254
+ deps: Optional dependencies to use for this run.
255
+ model_settings: Optional settings to use for this model's request.
256
+ usage_limits: Optional limits on model request count or token usage.
257
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
258
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
259
+ toolsets: Optional additional toolsets for this run.
260
+ event_stream_handler: Optional event stream handler to use for this run.
261
+ builtin_tools: Optional additional builtin tools for this run.
262
+
263
+ Returns:
264
+ The result of the run.
265
+ """
266
+
267
+ @flow(name=f'{self._name} Run')
268
+ async def wrapped_run_flow() -> AgentRunResult[Any]:
269
+ # Mark that we're inside a PrefectAgent flow
270
+ token = self._in_prefect_agent_flow.set(True)
271
+ try:
272
+ with self._prefect_overrides():
273
+ result = await super(WrapperAgent, self).run(
274
+ user_prompt,
275
+ output_type=output_type,
276
+ message_history=message_history,
277
+ deferred_tool_results=deferred_tool_results,
278
+ model=model,
279
+ deps=deps,
280
+ model_settings=model_settings,
281
+ usage_limits=usage_limits,
282
+ usage=usage,
283
+ infer_name=infer_name,
284
+ toolsets=toolsets,
285
+ event_stream_handler=event_stream_handler,
286
+ )
287
+ return result
288
+ finally:
289
+ self._in_prefect_agent_flow.reset(token)
290
+
291
+ return await wrapped_run_flow()
292
+
293
+ @overload
294
+ def run_sync(
295
+ self,
296
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
297
+ *,
298
+ output_type: None = None,
299
+ message_history: Sequence[_messages.ModelMessage] | None = None,
300
+ deferred_tool_results: DeferredToolResults | None = None,
301
+ model: models.Model | models.KnownModelName | str | None = None,
302
+ deps: AgentDepsT = None,
303
+ model_settings: ModelSettings | None = None,
304
+ usage_limits: _usage.UsageLimits | None = None,
305
+ usage: _usage.RunUsage | None = None,
306
+ infer_name: bool = True,
307
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
308
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
309
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
310
+ ) -> AgentRunResult[OutputDataT]: ...
311
+
312
+ @overload
313
+ def run_sync(
314
+ self,
315
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
316
+ *,
317
+ output_type: OutputSpec[RunOutputDataT],
318
+ message_history: Sequence[_messages.ModelMessage] | None = None,
319
+ deferred_tool_results: DeferredToolResults | None = None,
320
+ model: models.Model | models.KnownModelName | str | None = None,
321
+ deps: AgentDepsT = None,
322
+ model_settings: ModelSettings | None = None,
323
+ usage_limits: _usage.UsageLimits | None = None,
324
+ usage: _usage.RunUsage | None = None,
325
+ infer_name: bool = True,
326
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
327
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
328
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
329
+ ) -> AgentRunResult[RunOutputDataT]: ...
330
+
331
+ def run_sync(
332
+ self,
333
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
334
+ *,
335
+ output_type: OutputSpec[RunOutputDataT] | None = None,
336
+ message_history: Sequence[_messages.ModelMessage] | None = None,
337
+ deferred_tool_results: DeferredToolResults | None = None,
338
+ model: models.Model | models.KnownModelName | str | None = None,
339
+ deps: AgentDepsT = None,
340
+ model_settings: ModelSettings | None = None,
341
+ usage_limits: _usage.UsageLimits | None = None,
342
+ usage: _usage.RunUsage | None = None,
343
+ infer_name: bool = True,
344
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
345
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
346
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
347
+ **_deprecated_kwargs: Never,
348
+ ) -> AgentRunResult[Any]:
349
+ """Synchronously run the agent with a user prompt.
350
+
351
+ This is a convenience method that wraps [`self.run`][pydantic_ai.agent.AbstractAgent.run] with `loop.run_until_complete(...)`.
352
+ You therefore can't use this method inside async code or if there's an active event loop.
353
+
354
+ Example:
355
+ ```python
356
+ from pydantic_ai import Agent
357
+
358
+ agent = Agent('openai:gpt-4o')
359
+
360
+ result_sync = agent.run_sync('What is the capital of Italy?')
361
+ print(result_sync.output)
362
+ #> The capital of Italy is Rome.
363
+ ```
364
+
365
+ Args:
366
+ user_prompt: User input to start/continue the conversation.
367
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
368
+ output validators since output validators would expect an argument that matches the agent's output type.
369
+ message_history: History of the conversation so far.
370
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
371
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
372
+ deps: Optional dependencies to use for this run.
373
+ model_settings: Optional settings to use for this model's request.
374
+ usage_limits: Optional limits on model request count or token usage.
375
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
376
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
377
+ toolsets: Optional additional toolsets for this run.
378
+ event_stream_handler: Optional event stream handler to use for this run.
379
+ builtin_tools: Optional additional builtin tools for this run.
380
+
381
+ Returns:
382
+ The result of the run.
383
+ """
384
+
385
+ @flow(name=f'{self._name} Sync Run')
386
+ def wrapped_run_sync_flow() -> AgentRunResult[Any]:
387
+ # Mark that we're inside a PrefectAgent flow
388
+ token = self._in_prefect_agent_flow.set(True)
389
+ try:
390
+ with self._prefect_overrides():
391
+ # Using `run_coro_as_sync` from Prefect with async `run` to avoid event loop conflicts.
392
+ result = run_coro_as_sync(
393
+ super(PrefectAgent, self).run(
394
+ user_prompt,
395
+ output_type=output_type,
396
+ message_history=message_history,
397
+ deferred_tool_results=deferred_tool_results,
398
+ model=model,
399
+ deps=deps,
400
+ model_settings=model_settings,
401
+ usage_limits=usage_limits,
402
+ usage=usage,
403
+ infer_name=infer_name,
404
+ toolsets=toolsets,
405
+ event_stream_handler=event_stream_handler,
406
+ )
407
+ )
408
+ return result
409
+ finally:
410
+ self._in_prefect_agent_flow.reset(token)
411
+
412
+ return wrapped_run_sync_flow()
413
+
414
+ @overload
415
+ def run_stream(
416
+ self,
417
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
418
+ *,
419
+ output_type: None = None,
420
+ message_history: Sequence[_messages.ModelMessage] | None = None,
421
+ deferred_tool_results: DeferredToolResults | None = None,
422
+ model: models.Model | models.KnownModelName | str | None = None,
423
+ deps: AgentDepsT = None,
424
+ model_settings: ModelSettings | None = None,
425
+ usage_limits: _usage.UsageLimits | None = None,
426
+ usage: _usage.RunUsage | None = None,
427
+ infer_name: bool = True,
428
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
429
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
430
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
431
+ ) -> AbstractAsyncContextManager[StreamedRunResult[AgentDepsT, OutputDataT]]: ...
432
+
433
+ @overload
434
+ def run_stream(
435
+ self,
436
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
437
+ *,
438
+ output_type: OutputSpec[RunOutputDataT],
439
+ message_history: Sequence[_messages.ModelMessage] | None = None,
440
+ deferred_tool_results: DeferredToolResults | None = None,
441
+ model: models.Model | models.KnownModelName | str | None = None,
442
+ deps: AgentDepsT = None,
443
+ model_settings: ModelSettings | None = None,
444
+ usage_limits: _usage.UsageLimits | None = None,
445
+ usage: _usage.RunUsage | None = None,
446
+ infer_name: bool = True,
447
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
448
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
449
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
450
+ ) -> AbstractAsyncContextManager[StreamedRunResult[AgentDepsT, RunOutputDataT]]: ...
451
+
452
+ @asynccontextmanager
453
+ async def run_stream(
454
+ self,
455
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
456
+ *,
457
+ output_type: OutputSpec[RunOutputDataT] | None = None,
458
+ message_history: Sequence[_messages.ModelMessage] | None = None,
459
+ deferred_tool_results: DeferredToolResults | None = None,
460
+ model: models.Model | models.KnownModelName | str | None = None,
461
+ deps: AgentDepsT = None,
462
+ model_settings: ModelSettings | None = None,
463
+ usage_limits: _usage.UsageLimits | None = None,
464
+ usage: _usage.RunUsage | None = None,
465
+ infer_name: bool = True,
466
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
467
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
468
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
469
+ **_deprecated_kwargs: Never,
470
+ ) -> AsyncIterator[StreamedRunResult[AgentDepsT, Any]]:
471
+ """Run the agent with a user prompt in async mode, returning a streamed response.
472
+
473
+ Example:
474
+ ```python
475
+ from pydantic_ai import Agent
476
+
477
+ agent = Agent('openai:gpt-4o')
478
+
479
+ async def main():
480
+ async with agent.run_stream('What is the capital of the UK?') as response:
481
+ print(await response.get_output())
482
+ #> The capital of the UK is London.
483
+ ```
484
+
485
+ Args:
486
+ user_prompt: User input to start/continue the conversation.
487
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
488
+ output validators since output validators would expect an argument that matches the agent's output type.
489
+ message_history: History of the conversation so far.
490
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
491
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
492
+ deps: Optional dependencies to use for this run.
493
+ model_settings: Optional settings to use for this model's request.
494
+ usage_limits: Optional limits on model request count or token usage.
495
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
496
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
497
+ toolsets: Optional additional toolsets for this run.
498
+ builtin_tools: Optional additional builtin tools for this run.
499
+ event_stream_handler: Optional event stream handler to use for this run. 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.
500
+
501
+ Returns:
502
+ The result of the run.
503
+ """
504
+ if FlowRunContext.get() is not None:
505
+ raise UserError(
506
+ '`agent.run_stream()` cannot be used inside a Prefect flow. '
507
+ 'Set an `event_stream_handler` on the agent and use `agent.run()` instead.'
508
+ )
509
+
510
+ async with super().run_stream(
511
+ user_prompt,
512
+ output_type=output_type,
513
+ message_history=message_history,
514
+ deferred_tool_results=deferred_tool_results,
515
+ model=model,
516
+ deps=deps,
517
+ model_settings=model_settings,
518
+ usage_limits=usage_limits,
519
+ usage=usage,
520
+ infer_name=infer_name,
521
+ toolsets=toolsets,
522
+ event_stream_handler=event_stream_handler,
523
+ builtin_tools=builtin_tools,
524
+ **_deprecated_kwargs,
525
+ ) as result:
526
+ yield result
527
+
528
+ @overload
529
+ def run_stream_events(
530
+ self,
531
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
532
+ *,
533
+ output_type: None = None,
534
+ message_history: Sequence[_messages.ModelMessage] | None = None,
535
+ deferred_tool_results: DeferredToolResults | None = None,
536
+ model: models.Model | models.KnownModelName | str | None = None,
537
+ deps: AgentDepsT = None,
538
+ model_settings: ModelSettings | None = None,
539
+ usage_limits: _usage.UsageLimits | None = None,
540
+ usage: _usage.RunUsage | None = None,
541
+ infer_name: bool = True,
542
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
543
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
544
+ ) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[OutputDataT]]: ...
545
+
546
+ @overload
547
+ def run_stream_events(
548
+ self,
549
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
550
+ *,
551
+ output_type: OutputSpec[RunOutputDataT],
552
+ message_history: Sequence[_messages.ModelMessage] | None = None,
553
+ deferred_tool_results: DeferredToolResults | None = None,
554
+ model: models.Model | models.KnownModelName | str | None = None,
555
+ deps: AgentDepsT = None,
556
+ model_settings: ModelSettings | None = None,
557
+ usage_limits: _usage.UsageLimits | None = None,
558
+ usage: _usage.RunUsage | None = None,
559
+ infer_name: bool = True,
560
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
561
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
562
+ ) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[RunOutputDataT]]: ...
563
+
564
+ def run_stream_events(
565
+ self,
566
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
567
+ *,
568
+ output_type: OutputSpec[RunOutputDataT] | None = None,
569
+ message_history: Sequence[_messages.ModelMessage] | None = None,
570
+ deferred_tool_results: DeferredToolResults | None = None,
571
+ model: models.Model | models.KnownModelName | str | None = None,
572
+ deps: AgentDepsT = None,
573
+ model_settings: ModelSettings | None = None,
574
+ usage_limits: _usage.UsageLimits | None = None,
575
+ usage: _usage.RunUsage | None = None,
576
+ infer_name: bool = True,
577
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
578
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
579
+ ) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[Any]]:
580
+ """Run the agent with a user prompt in async mode and stream events from the run.
581
+
582
+ This is a convenience method that wraps [`self.run`][pydantic_ai.agent.AbstractAgent.run] and
583
+ uses the `event_stream_handler` kwarg to get a stream of events from the run.
584
+
585
+ Example:
586
+ ```python
587
+ from pydantic_ai import Agent, AgentRunResultEvent, AgentStreamEvent
588
+
589
+ agent = Agent('openai:gpt-4o')
590
+
591
+ async def main():
592
+ events: list[AgentStreamEvent | AgentRunResultEvent] = []
593
+ async for event in agent.run_stream_events('What is the capital of France?'):
594
+ events.append(event)
595
+ print(events)
596
+ '''
597
+ [
598
+ PartStartEvent(index=0, part=TextPart(content='The capital of ')),
599
+ FinalResultEvent(tool_name=None, tool_call_id=None),
600
+ PartDeltaEvent(index=0, delta=TextPartDelta(content_delta='France is Paris. ')),
601
+ AgentRunResultEvent(
602
+ result=AgentRunResult(output='The capital of France is Paris. ')
603
+ ),
604
+ ]
605
+ '''
606
+ ```
607
+
608
+ Arguments are the same as for [`self.run`][pydantic_ai.agent.AbstractAgent.run],
609
+ except that `event_stream_handler` is now allowed.
610
+
611
+ Args:
612
+ user_prompt: User input to start/continue the conversation.
613
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
614
+ output validators since output validators would expect an argument that matches the agent's output type.
615
+ message_history: History of the conversation so far.
616
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
617
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
618
+ deps: Optional dependencies to use for this run.
619
+ model_settings: Optional settings to use for this model's request.
620
+ usage_limits: Optional limits on model request count or token usage.
621
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
622
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
623
+ toolsets: Optional additional toolsets for this run.
624
+ builtin_tools: Optional additional builtin tools for this run.
625
+
626
+ Returns:
627
+ An async iterable of stream events `AgentStreamEvent` and finally a `AgentRunResultEvent` with the final
628
+ run result.
629
+ """
630
+ if FlowRunContext.get() is not None:
631
+ raise UserError(
632
+ '`agent.run_stream_events()` cannot be used inside a Prefect flow. '
633
+ 'Set an `event_stream_handler` on the agent and use `agent.run()` instead.'
634
+ )
635
+
636
+ return super().run_stream_events(
637
+ user_prompt,
638
+ output_type=output_type,
639
+ message_history=message_history,
640
+ deferred_tool_results=deferred_tool_results,
641
+ model=model,
642
+ deps=deps,
643
+ model_settings=model_settings,
644
+ usage_limits=usage_limits,
645
+ usage=usage,
646
+ infer_name=infer_name,
647
+ toolsets=toolsets,
648
+ builtin_tools=builtin_tools,
649
+ )
650
+
651
+ @overload
652
+ def iter(
653
+ self,
654
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
655
+ *,
656
+ output_type: None = None,
657
+ message_history: Sequence[_messages.ModelMessage] | None = None,
658
+ deferred_tool_results: DeferredToolResults | None = None,
659
+ model: models.Model | models.KnownModelName | str | None = None,
660
+ deps: AgentDepsT = None,
661
+ model_settings: ModelSettings | None = None,
662
+ usage_limits: _usage.UsageLimits | None = None,
663
+ usage: _usage.RunUsage | None = None,
664
+ infer_name: bool = True,
665
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
666
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
667
+ ) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, OutputDataT]]: ...
668
+
669
+ @overload
670
+ def iter(
671
+ self,
672
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
673
+ *,
674
+ output_type: OutputSpec[RunOutputDataT],
675
+ message_history: Sequence[_messages.ModelMessage] | None = None,
676
+ deferred_tool_results: DeferredToolResults | None = None,
677
+ model: models.Model | models.KnownModelName | str | None = None,
678
+ deps: AgentDepsT = None,
679
+ model_settings: ModelSettings | None = None,
680
+ usage_limits: _usage.UsageLimits | None = None,
681
+ usage: _usage.RunUsage | None = None,
682
+ infer_name: bool = True,
683
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
684
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
685
+ ) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, RunOutputDataT]]: ...
686
+
687
+ @asynccontextmanager
688
+ async def iter(
689
+ self,
690
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
691
+ *,
692
+ output_type: OutputSpec[RunOutputDataT] | None = None,
693
+ message_history: Sequence[_messages.ModelMessage] | None = None,
694
+ deferred_tool_results: DeferredToolResults | None = None,
695
+ model: models.Model | models.KnownModelName | str | None = None,
696
+ deps: AgentDepsT = None,
697
+ model_settings: ModelSettings | None = None,
698
+ usage_limits: _usage.UsageLimits | None = None,
699
+ usage: _usage.RunUsage | None = None,
700
+ infer_name: bool = True,
701
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
702
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
703
+ ) -> AsyncIterator[AgentRun[AgentDepsT, Any]]:
704
+ """A contextmanager which can be used to iterate over the agent graph's nodes as they are executed.
705
+
706
+ This method builds an internal agent graph (using system prompts, tools and output schemas) and then returns an
707
+ `AgentRun` object. The `AgentRun` can be used to async-iterate over the nodes of the graph as they are
708
+ executed. This is the API to use if you want to consume the outputs coming from each LLM model response, or the
709
+ stream of events coming from the execution of tools.
710
+
711
+ The `AgentRun` also provides methods to access the full message history, new messages, and usage statistics,
712
+ and the final result of the run once it has completed.
713
+
714
+ For more details, see the documentation of `AgentRun`.
715
+
716
+ Example:
717
+ ```python
718
+ from pydantic_ai import Agent
719
+
720
+ agent = Agent('openai:gpt-4o')
721
+
722
+ async def main():
723
+ nodes = []
724
+ async with agent.iter('What is the capital of France?') as agent_run:
725
+ async for node in agent_run:
726
+ nodes.append(node)
727
+ print(nodes)
728
+ '''
729
+ [
730
+ UserPromptNode(
731
+ user_prompt='What is the capital of France?',
732
+ instructions_functions=[],
733
+ system_prompts=(),
734
+ system_prompt_functions=[],
735
+ system_prompt_dynamic_functions={},
736
+ ),
737
+ ModelRequestNode(
738
+ request=ModelRequest(
739
+ parts=[
740
+ UserPromptPart(
741
+ content='What is the capital of France?',
742
+ timestamp=datetime.datetime(...),
743
+ )
744
+ ]
745
+ )
746
+ ),
747
+ CallToolsNode(
748
+ model_response=ModelResponse(
749
+ parts=[TextPart(content='The capital of France is Paris.')],
750
+ usage=RequestUsage(input_tokens=56, output_tokens=7),
751
+ model_name='gpt-4o',
752
+ timestamp=datetime.datetime(...),
753
+ )
754
+ ),
755
+ End(data=FinalResult(output='The capital of France is Paris.')),
756
+ ]
757
+ '''
758
+ print(agent_run.result.output)
759
+ #> The capital of France is Paris.
760
+ ```
761
+
762
+ Args:
763
+ user_prompt: User input to start/continue the conversation.
764
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
765
+ output validators since output validators would expect an argument that matches the agent's output type.
766
+ message_history: History of the conversation so far.
767
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
768
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
769
+ deps: Optional dependencies to use for this run.
770
+ model_settings: Optional settings to use for this model's request.
771
+ usage_limits: Optional limits on model request count or token usage.
772
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
773
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
774
+ toolsets: Optional additional toolsets for this run.
775
+ builtin_tools: Optional additional builtin tools for this run.
776
+
777
+ Returns:
778
+ The result of the run.
779
+ """
780
+ if model is not None and not isinstance(model, PrefectModel):
781
+ raise UserError(
782
+ 'Non-Prefect model cannot be set at agent run time inside a Prefect flow, it must be set at agent creation time.'
783
+ )
784
+
785
+ with self._prefect_overrides():
786
+ async with super().iter(
787
+ user_prompt=user_prompt,
788
+ output_type=output_type,
789
+ message_history=message_history,
790
+ deferred_tool_results=deferred_tool_results,
791
+ model=model,
792
+ deps=deps,
793
+ model_settings=model_settings,
794
+ usage_limits=usage_limits,
795
+ usage=usage,
796
+ infer_name=infer_name,
797
+ toolsets=toolsets,
798
+ ) as run:
799
+ yield run
800
+
801
+ @contextmanager
802
+ def override(
803
+ self,
804
+ *,
805
+ name: str | _utils.Unset = _utils.UNSET,
806
+ deps: AgentDepsT | _utils.Unset = _utils.UNSET,
807
+ model: models.Model | models.KnownModelName | str | _utils.Unset = _utils.UNSET,
808
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | _utils.Unset = _utils.UNSET,
809
+ tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] | _utils.Unset = _utils.UNSET,
810
+ instructions: Instructions[AgentDepsT] | _utils.Unset = _utils.UNSET,
811
+ ) -> Iterator[None]:
812
+ """Context manager to temporarily override agent dependencies, model, toolsets, tools, or instructions.
813
+
814
+ This is particularly useful when testing.
815
+ You can find an example of this [here](../testing.md#overriding-model-via-pytest-fixtures).
816
+
817
+ Args:
818
+ name: The name to use instead of the name passed to the agent constructor and agent run.
819
+ deps: The dependencies to use instead of the dependencies passed to the agent run.
820
+ model: The model to use instead of the model passed to the agent run.
821
+ toolsets: The toolsets to use instead of the toolsets passed to the agent constructor and agent run.
822
+ tools: The tools to use instead of the tools registered with the agent.
823
+ instructions: The instructions to use instead of the instructions registered with the agent.
824
+ """
825
+ if _utils.is_set(model) and not isinstance(model, PrefectModel):
826
+ raise UserError(
827
+ 'Non-Prefect model cannot be contextually overridden inside a Prefect flow, it must be set at agent creation time.'
828
+ )
829
+
830
+ with super().override(
831
+ name=name, deps=deps, model=model, toolsets=toolsets, tools=tools, instructions=instructions
832
+ ):
833
+ yield