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.

Files changed (63) hide show
  1. pydantic_ai/__init__.py +5 -0
  2. pydantic_ai/_a2a.py +6 -4
  3. pydantic_ai/_agent_graph.py +32 -32
  4. pydantic_ai/_cli.py +3 -3
  5. pydantic_ai/_output.py +8 -0
  6. pydantic_ai/_tool_manager.py +3 -0
  7. pydantic_ai/_utils.py +7 -1
  8. pydantic_ai/ag_ui.py +25 -14
  9. pydantic_ai/{agent.py → agent/__init__.py} +217 -1026
  10. pydantic_ai/agent/abstract.py +942 -0
  11. pydantic_ai/agent/wrapper.py +227 -0
  12. pydantic_ai/builtin_tools.py +105 -0
  13. pydantic_ai/direct.py +9 -9
  14. pydantic_ai/durable_exec/__init__.py +0 -0
  15. pydantic_ai/durable_exec/temporal/__init__.py +83 -0
  16. pydantic_ai/durable_exec/temporal/_agent.py +699 -0
  17. pydantic_ai/durable_exec/temporal/_function_toolset.py +92 -0
  18. pydantic_ai/durable_exec/temporal/_logfire.py +48 -0
  19. pydantic_ai/durable_exec/temporal/_mcp_server.py +145 -0
  20. pydantic_ai/durable_exec/temporal/_model.py +168 -0
  21. pydantic_ai/durable_exec/temporal/_run_context.py +50 -0
  22. pydantic_ai/durable_exec/temporal/_toolset.py +77 -0
  23. pydantic_ai/ext/aci.py +10 -9
  24. pydantic_ai/ext/langchain.py +4 -2
  25. pydantic_ai/mcp.py +203 -75
  26. pydantic_ai/messages.py +75 -13
  27. pydantic_ai/models/__init__.py +66 -8
  28. pydantic_ai/models/anthropic.py +135 -18
  29. pydantic_ai/models/bedrock.py +16 -5
  30. pydantic_ai/models/cohere.py +11 -4
  31. pydantic_ai/models/fallback.py +4 -2
  32. pydantic_ai/models/function.py +18 -4
  33. pydantic_ai/models/gemini.py +20 -9
  34. pydantic_ai/models/google.py +53 -15
  35. pydantic_ai/models/groq.py +47 -11
  36. pydantic_ai/models/huggingface.py +26 -11
  37. pydantic_ai/models/instrumented.py +3 -1
  38. pydantic_ai/models/mcp_sampling.py +3 -1
  39. pydantic_ai/models/mistral.py +27 -17
  40. pydantic_ai/models/openai.py +97 -33
  41. pydantic_ai/models/test.py +12 -0
  42. pydantic_ai/models/wrapper.py +6 -2
  43. pydantic_ai/profiles/groq.py +23 -0
  44. pydantic_ai/profiles/openai.py +1 -1
  45. pydantic_ai/providers/google.py +7 -7
  46. pydantic_ai/providers/groq.py +2 -0
  47. pydantic_ai/result.py +21 -55
  48. pydantic_ai/run.py +357 -0
  49. pydantic_ai/tools.py +0 -1
  50. pydantic_ai/toolsets/__init__.py +2 -0
  51. pydantic_ai/toolsets/_dynamic.py +87 -0
  52. pydantic_ai/toolsets/abstract.py +23 -3
  53. pydantic_ai/toolsets/combined.py +19 -4
  54. pydantic_ai/toolsets/deferred.py +10 -2
  55. pydantic_ai/toolsets/function.py +23 -8
  56. pydantic_ai/toolsets/prefixed.py +4 -0
  57. pydantic_ai/toolsets/wrapper.py +14 -1
  58. {pydantic_ai_slim-0.6.1.dist-info → pydantic_ai_slim-0.7.0.dist-info}/METADATA +7 -5
  59. pydantic_ai_slim-0.7.0.dist-info/RECORD +115 -0
  60. pydantic_ai_slim-0.6.1.dist-info/RECORD +0 -100
  61. {pydantic_ai_slim-0.6.1.dist-info → pydantic_ai_slim-0.7.0.dist-info}/WHEEL +0 -0
  62. {pydantic_ai_slim-0.6.1.dist-info → pydantic_ai_slim-0.7.0.dist-info}/entry_points.txt +0 -0
  63. {pydantic_ai_slim-0.6.1.dist-info → pydantic_ai_slim-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,699 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import AsyncIterator, Iterator, Sequence
4
+ from contextlib import AbstractAsyncContextManager, asynccontextmanager, contextmanager
5
+ from contextvars import ContextVar
6
+ from datetime import timedelta
7
+ from typing import Any, Callable, Literal, overload
8
+
9
+ from pydantic.errors import PydanticUserError
10
+ from pydantic_core import PydanticSerializationError
11
+ from temporalio import workflow
12
+ from temporalio.common import RetryPolicy
13
+ from temporalio.workflow import ActivityConfig
14
+ from typing_extensions import Never
15
+
16
+ from pydantic_ai import (
17
+ _utils,
18
+ messages as _messages,
19
+ models,
20
+ usage as _usage,
21
+ )
22
+ from pydantic_ai._run_context import AgentDepsT
23
+ from pydantic_ai.agent import AbstractAgent, AgentRun, AgentRunResult, EventStreamHandler, RunOutputDataT, WrapperAgent
24
+ from pydantic_ai.durable_exec.temporal._run_context import TemporalRunContext
25
+ from pydantic_ai.exceptions import UserError
26
+ from pydantic_ai.models import Model
27
+ from pydantic_ai.output import OutputDataT, OutputSpec
28
+ from pydantic_ai.result import StreamedRunResult
29
+ from pydantic_ai.settings import ModelSettings
30
+ from pydantic_ai.tools import (
31
+ Tool,
32
+ ToolFuncEither,
33
+ )
34
+ from pydantic_ai.toolsets import AbstractToolset
35
+
36
+ from ._model import TemporalModel
37
+ from ._toolset import TemporalWrapperToolset, temporalize_toolset
38
+
39
+
40
+ class TemporalAgent(WrapperAgent[AgentDepsT, OutputDataT]):
41
+ def __init__(
42
+ self,
43
+ wrapped: AbstractAgent[AgentDepsT, OutputDataT],
44
+ *,
45
+ name: str | None = None,
46
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
47
+ activity_config: ActivityConfig | None = None,
48
+ model_activity_config: ActivityConfig | None = None,
49
+ toolset_activity_config: dict[str, ActivityConfig] | None = None,
50
+ tool_activity_config: dict[str, dict[str, ActivityConfig | Literal[False]]] | None = None,
51
+ run_context_type: type[TemporalRunContext[AgentDepsT]] = TemporalRunContext[AgentDepsT],
52
+ temporalize_toolset_func: Callable[
53
+ [
54
+ AbstractToolset[AgentDepsT],
55
+ str,
56
+ ActivityConfig,
57
+ dict[str, ActivityConfig | Literal[False]],
58
+ type[AgentDepsT],
59
+ type[TemporalRunContext[AgentDepsT]],
60
+ ],
61
+ AbstractToolset[AgentDepsT],
62
+ ] = temporalize_toolset,
63
+ ):
64
+ """Wrap an agent to enable it to be used inside a Temporal workflow, by automatically offloading model requests, tool calls, and MCP server communication to Temporal activities.
65
+
66
+ After wrapping, the original agent can still be used as normal outside of the Temporal workflow, but any changes to its model or toolsets after wrapping will not be reflected in the durable agent.
67
+
68
+ Args:
69
+ wrapped: The agent to wrap.
70
+ name: Optional unique agent name to use in the Temporal activities' names. If not provided, the agent's `name` will be used.
71
+ event_stream_handler: Optional event stream handler to use instead of the one set on the wrapped agent.
72
+ activity_config: The base Temporal activity config to use for all activities. If no config is provided, a `start_to_close_timeout` of 60 seconds is used.
73
+ model_activity_config: The Temporal activity config to use for model request activities. This is merged with the base activity config.
74
+ toolset_activity_config: The Temporal activity config to use for get-tools and call-tool activities for specific toolsets identified by ID. This is merged with the base activity config.
75
+ tool_activity_config: The Temporal activity config to use for specific tool call activities identified by toolset ID and tool name.
76
+ This is merged with the base and toolset-specific activity configs.
77
+ If a tool does not use IO, you can specify `False` to disable using an activity.
78
+ Note that the tool is required to be defined as an `async` function as non-async tools are run in threads which are non-deterministic and thus not supported outside of activities.
79
+ run_context_type: The `TemporalRunContext` subclass to use to serialize and deserialize the run context for use inside a Temporal activity.
80
+ By default, only the `deps`, `retries`, `tool_call_id`, `tool_name`, `retry` and `run_step` attributes will be available.
81
+ To make another attribute available, create a `TemporalRunContext` subclass with a custom `serialize_run_context` class method that returns a dictionary that includes the attribute.
82
+ temporalize_toolset_func: Optional function to use to prepare "leaf" toolsets (i.e. those that implement their own tool listing and calling) for Temporal by wrapping them in a `TemporalWrapperToolset` that moves methods that require IO to Temporal activities.
83
+ If not provided, only `FunctionToolset` and `MCPServer` will be prepared for Temporal.
84
+ The function takes the toolset, the activity name prefix, the toolset-specific activity config, the tool-specific activity configs and the run context type.
85
+ """
86
+ super().__init__(wrapped)
87
+
88
+ # start_to_close_timeout is required
89
+ activity_config = activity_config or ActivityConfig(start_to_close_timeout=timedelta(seconds=60))
90
+
91
+ # `pydantic_ai.exceptions.UserError` and `pydantic.errors.PydanticUserError` are not retryable
92
+ retry_policy = activity_config.get('retry_policy') or RetryPolicy()
93
+ retry_policy.non_retryable_error_types = [
94
+ *(retry_policy.non_retryable_error_types or []),
95
+ UserError.__name__,
96
+ PydanticUserError.__name__,
97
+ ]
98
+ activity_config['retry_policy'] = retry_policy
99
+
100
+ model_activity_config = model_activity_config or {}
101
+ toolset_activity_config = toolset_activity_config or {}
102
+ tool_activity_config = tool_activity_config or {}
103
+
104
+ self._name = name or wrapped.name
105
+ if self._name is None:
106
+ raise UserError(
107
+ "An agent needs to have a unique `name` in order to be used with Temporal. The name will be used to identify the agent's activities within the workflow."
108
+ )
109
+
110
+ activity_name_prefix = f'agent__{self.name}'
111
+
112
+ activities: list[Callable[..., Any]] = []
113
+ if not isinstance(wrapped.model, Model):
114
+ raise UserError(
115
+ 'An agent needs to have a `model` in order to be used with Temporal, it cannot be set at agent run time.'
116
+ )
117
+
118
+ temporal_model = TemporalModel(
119
+ wrapped.model,
120
+ activity_name_prefix=activity_name_prefix,
121
+ activity_config=activity_config | model_activity_config,
122
+ deps_type=self.deps_type,
123
+ run_context_type=run_context_type,
124
+ event_stream_handler=event_stream_handler or wrapped.event_stream_handler,
125
+ )
126
+ activities.extend(temporal_model.temporal_activities)
127
+
128
+ def temporalize_toolset(toolset: AbstractToolset[AgentDepsT]) -> AbstractToolset[AgentDepsT]:
129
+ id = toolset.id
130
+ if id is None:
131
+ raise UserError(
132
+ "Toolsets that are 'leaves' (i.e. those that implement their own tool listing and calling) need to have a unique `id` in order to be used with Temporal. The ID will be used to identify the toolset's activities within the workflow."
133
+ )
134
+
135
+ toolset = temporalize_toolset_func(
136
+ toolset,
137
+ activity_name_prefix,
138
+ activity_config | toolset_activity_config.get(id, {}),
139
+ tool_activity_config.get(id, {}),
140
+ self.deps_type,
141
+ run_context_type,
142
+ )
143
+ if isinstance(toolset, TemporalWrapperToolset):
144
+ activities.extend(toolset.temporal_activities)
145
+ return toolset
146
+
147
+ temporal_toolsets = [toolset.visit_and_replace(temporalize_toolset) for toolset in wrapped.toolsets]
148
+
149
+ self._model = temporal_model
150
+ self._toolsets = temporal_toolsets
151
+ self._temporal_activities = activities
152
+
153
+ self._temporal_overrides_active: ContextVar[bool] = ContextVar('_temporal_overrides_active', default=False)
154
+
155
+ @property
156
+ def name(self) -> str | None:
157
+ return self._name
158
+
159
+ @name.setter
160
+ def name(self, value: str | None) -> None: # pragma: no cover
161
+ raise UserError(
162
+ 'The agent name cannot be changed after creation. If you need to change the name, create a new agent.'
163
+ )
164
+
165
+ @property
166
+ def model(self) -> Model:
167
+ return self._model
168
+
169
+ @property
170
+ def toolsets(self) -> Sequence[AbstractToolset[AgentDepsT]]:
171
+ with self._temporal_overrides():
172
+ return super().toolsets
173
+
174
+ @property
175
+ def temporal_activities(self) -> list[Callable[..., Any]]:
176
+ return self._temporal_activities
177
+
178
+ @contextmanager
179
+ def _temporal_overrides(self) -> Iterator[None]:
180
+ # We reset tools here as the temporalized function toolset is already in self._toolsets.
181
+ with super().override(model=self._model, toolsets=self._toolsets, tools=[]):
182
+ token = self._temporal_overrides_active.set(True)
183
+ try:
184
+ yield
185
+ except PydanticSerializationError as e:
186
+ raise UserError(
187
+ "The `deps` object failed to be serialized. Temporal requires all objects that are passed to activities to be serializable using Pydantic's `TypeAdapter`."
188
+ ) from e
189
+ finally:
190
+ self._temporal_overrides_active.reset(token)
191
+
192
+ @overload
193
+ async def run(
194
+ self,
195
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
196
+ *,
197
+ output_type: None = None,
198
+ message_history: list[_messages.ModelMessage] | None = None,
199
+ model: models.Model | models.KnownModelName | str | None = None,
200
+ deps: AgentDepsT = None,
201
+ model_settings: ModelSettings | None = None,
202
+ usage_limits: _usage.UsageLimits | None = None,
203
+ usage: _usage.Usage | None = None,
204
+ infer_name: bool = True,
205
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
206
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
207
+ ) -> AgentRunResult[OutputDataT]: ...
208
+
209
+ @overload
210
+ async def run(
211
+ self,
212
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
213
+ *,
214
+ output_type: OutputSpec[RunOutputDataT],
215
+ message_history: list[_messages.ModelMessage] | None = None,
216
+ model: models.Model | models.KnownModelName | str | None = None,
217
+ deps: AgentDepsT = None,
218
+ model_settings: ModelSettings | None = None,
219
+ usage_limits: _usage.UsageLimits | None = None,
220
+ usage: _usage.Usage | None = None,
221
+ infer_name: bool = True,
222
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
223
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
224
+ ) -> AgentRunResult[RunOutputDataT]: ...
225
+
226
+ async def run(
227
+ self,
228
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
229
+ *,
230
+ output_type: OutputSpec[RunOutputDataT] | None = None,
231
+ message_history: list[_messages.ModelMessage] | None = None,
232
+ model: models.Model | models.KnownModelName | str | None = None,
233
+ deps: AgentDepsT = None,
234
+ model_settings: ModelSettings | None = None,
235
+ usage_limits: _usage.UsageLimits | None = None,
236
+ usage: _usage.Usage | None = None,
237
+ infer_name: bool = True,
238
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
239
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
240
+ **_deprecated_kwargs: Never,
241
+ ) -> AgentRunResult[Any]:
242
+ """Run the agent with a user prompt in async mode.
243
+
244
+ This method builds an internal agent graph (using system prompts, tools and result schemas) and then
245
+ runs the graph to completion. The result of the run is returned.
246
+
247
+ Example:
248
+ ```python
249
+ from pydantic_ai import Agent
250
+
251
+ agent = Agent('openai:gpt-4o')
252
+
253
+ async def main():
254
+ agent_run = await agent.run('What is the capital of France?')
255
+ print(agent_run.output)
256
+ #> The capital of France is Paris.
257
+ ```
258
+
259
+ Args:
260
+ user_prompt: User input to start/continue the conversation.
261
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
262
+ output validators since output validators would expect an argument that matches the agent's output type.
263
+ message_history: History of the conversation so far.
264
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
265
+ deps: Optional dependencies to use for this run.
266
+ model_settings: Optional settings to use for this model's request.
267
+ usage_limits: Optional limits on model request count or token usage.
268
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
269
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
270
+ toolsets: Optional additional toolsets for this run.
271
+ event_stream_handler: Optional event stream handler to use for this run.
272
+
273
+ Returns:
274
+ The result of the run.
275
+ """
276
+ if workflow.in_workflow() and event_stream_handler is not None:
277
+ raise UserError(
278
+ 'Event stream handler cannot be set at agent run time inside a Temporal workflow, it must be set at agent creation time.'
279
+ )
280
+
281
+ with self._temporal_overrides():
282
+ return await super().run(
283
+ user_prompt,
284
+ output_type=output_type,
285
+ message_history=message_history,
286
+ model=model,
287
+ deps=deps,
288
+ model_settings=model_settings,
289
+ usage_limits=usage_limits,
290
+ usage=usage,
291
+ infer_name=infer_name,
292
+ toolsets=toolsets,
293
+ event_stream_handler=event_stream_handler,
294
+ **_deprecated_kwargs,
295
+ )
296
+
297
+ @overload
298
+ def run_sync(
299
+ self,
300
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
301
+ *,
302
+ output_type: None = None,
303
+ message_history: list[_messages.ModelMessage] | None = None,
304
+ model: models.Model | models.KnownModelName | str | None = None,
305
+ deps: AgentDepsT = None,
306
+ model_settings: ModelSettings | None = None,
307
+ usage_limits: _usage.UsageLimits | None = None,
308
+ usage: _usage.Usage | None = None,
309
+ infer_name: bool = True,
310
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
311
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
312
+ ) -> AgentRunResult[OutputDataT]: ...
313
+
314
+ @overload
315
+ def run_sync(
316
+ self,
317
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
318
+ *,
319
+ output_type: OutputSpec[RunOutputDataT],
320
+ message_history: list[_messages.ModelMessage] | None = None,
321
+ model: models.Model | models.KnownModelName | str | None = None,
322
+ deps: AgentDepsT = None,
323
+ model_settings: ModelSettings | None = None,
324
+ usage_limits: _usage.UsageLimits | None = None,
325
+ usage: _usage.Usage | None = None,
326
+ infer_name: bool = True,
327
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | 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: list[_messages.ModelMessage] | None = None,
337
+ model: models.Model | models.KnownModelName | str | None = None,
338
+ deps: AgentDepsT = None,
339
+ model_settings: ModelSettings | None = None,
340
+ usage_limits: _usage.UsageLimits | None = None,
341
+ usage: _usage.Usage | None = None,
342
+ infer_name: bool = True,
343
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
344
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
345
+ **_deprecated_kwargs: Never,
346
+ ) -> AgentRunResult[Any]:
347
+ """Synchronously run the agent with a user prompt.
348
+
349
+ This is a convenience method that wraps [`self.run`][pydantic_ai.agent.AbstractAgent.run] with `loop.run_until_complete(...)`.
350
+ You therefore can't use this method inside async code or if there's an active event loop.
351
+
352
+ Example:
353
+ ```python
354
+ from pydantic_ai import Agent
355
+
356
+ agent = Agent('openai:gpt-4o')
357
+
358
+ result_sync = agent.run_sync('What is the capital of Italy?')
359
+ print(result_sync.output)
360
+ #> The capital of Italy is Rome.
361
+ ```
362
+
363
+ Args:
364
+ user_prompt: User input to start/continue the conversation.
365
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
366
+ output validators since output validators would expect an argument that matches the agent's output type.
367
+ message_history: History of the conversation so far.
368
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
369
+ deps: Optional dependencies to use for this run.
370
+ model_settings: Optional settings to use for this model's request.
371
+ usage_limits: Optional limits on model request count or token usage.
372
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
373
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
374
+ toolsets: Optional additional toolsets for this run.
375
+ event_stream_handler: Optional event stream handler to use for this run.
376
+
377
+ Returns:
378
+ The result of the run.
379
+ """
380
+ if workflow.in_workflow():
381
+ raise UserError(
382
+ '`agent.run_sync()` cannot be used inside a Temporal workflow. Use `await agent.run()` instead.'
383
+ )
384
+
385
+ return super().run_sync(
386
+ user_prompt,
387
+ output_type=output_type,
388
+ message_history=message_history,
389
+ model=model,
390
+ deps=deps,
391
+ model_settings=model_settings,
392
+ usage_limits=usage_limits,
393
+ usage=usage,
394
+ infer_name=infer_name,
395
+ toolsets=toolsets,
396
+ event_stream_handler=event_stream_handler,
397
+ **_deprecated_kwargs,
398
+ )
399
+
400
+ @overload
401
+ def run_stream(
402
+ self,
403
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
404
+ *,
405
+ output_type: None = None,
406
+ message_history: list[_messages.ModelMessage] | None = None,
407
+ model: models.Model | models.KnownModelName | str | None = None,
408
+ deps: AgentDepsT = None,
409
+ model_settings: ModelSettings | None = None,
410
+ usage_limits: _usage.UsageLimits | None = None,
411
+ usage: _usage.Usage | None = None,
412
+ infer_name: bool = True,
413
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
414
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
415
+ ) -> AbstractAsyncContextManager[StreamedRunResult[AgentDepsT, OutputDataT]]: ...
416
+
417
+ @overload
418
+ def run_stream(
419
+ self,
420
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
421
+ *,
422
+ output_type: OutputSpec[RunOutputDataT],
423
+ message_history: list[_messages.ModelMessage] | None = None,
424
+ model: models.Model | models.KnownModelName | str | None = None,
425
+ deps: AgentDepsT = None,
426
+ model_settings: ModelSettings | None = None,
427
+ usage_limits: _usage.UsageLimits | None = None,
428
+ usage: _usage.Usage | None = None,
429
+ infer_name: bool = True,
430
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
431
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
432
+ ) -> AbstractAsyncContextManager[StreamedRunResult[AgentDepsT, RunOutputDataT]]: ...
433
+
434
+ @asynccontextmanager
435
+ async def run_stream(
436
+ self,
437
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
438
+ *,
439
+ output_type: OutputSpec[RunOutputDataT] | None = None,
440
+ message_history: list[_messages.ModelMessage] | 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.Usage | None = None,
446
+ infer_name: bool = True,
447
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
448
+ event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
449
+ **_deprecated_kwargs: Never,
450
+ ) -> AsyncIterator[StreamedRunResult[AgentDepsT, Any]]:
451
+ """Run the agent with a user prompt in async mode, returning a streamed response.
452
+
453
+ Example:
454
+ ```python
455
+ from pydantic_ai import Agent
456
+
457
+ agent = Agent('openai:gpt-4o')
458
+
459
+ async def main():
460
+ async with agent.run_stream('What is the capital of the UK?') as response:
461
+ print(await response.get_output())
462
+ #> The capital of the UK is London.
463
+ ```
464
+
465
+ Args:
466
+ user_prompt: User input to start/continue the conversation.
467
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
468
+ output validators since output validators would expect an argument that matches the agent's output type.
469
+ message_history: History of the conversation so far.
470
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
471
+ deps: Optional dependencies to use for this run.
472
+ model_settings: Optional settings to use for this model's request.
473
+ usage_limits: Optional limits on model request count or token usage.
474
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
475
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
476
+ toolsets: Optional additional toolsets for this run.
477
+ 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.
478
+
479
+ Returns:
480
+ The result of the run.
481
+ """
482
+ if workflow.in_workflow():
483
+ raise UserError(
484
+ '`agent.run_stream()` cannot currently be used inside a Temporal workflow. '
485
+ 'Set an `event_stream_handler` on the agent and use `agent.run()` instead. '
486
+ 'Please file an issue if this is not sufficient for your use case.'
487
+ )
488
+
489
+ async with super().run_stream(
490
+ user_prompt,
491
+ output_type=output_type,
492
+ message_history=message_history,
493
+ model=model,
494
+ deps=deps,
495
+ model_settings=model_settings,
496
+ usage_limits=usage_limits,
497
+ usage=usage,
498
+ infer_name=infer_name,
499
+ toolsets=toolsets,
500
+ event_stream_handler=event_stream_handler,
501
+ **_deprecated_kwargs,
502
+ ) as result:
503
+ yield result
504
+
505
+ @overload
506
+ def iter(
507
+ self,
508
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
509
+ *,
510
+ output_type: None = None,
511
+ message_history: list[_messages.ModelMessage] | None = None,
512
+ model: models.Model | models.KnownModelName | str | None = None,
513
+ deps: AgentDepsT = None,
514
+ model_settings: ModelSettings | None = None,
515
+ usage_limits: _usage.UsageLimits | None = None,
516
+ usage: _usage.Usage | None = None,
517
+ infer_name: bool = True,
518
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
519
+ **_deprecated_kwargs: Never,
520
+ ) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, OutputDataT]]: ...
521
+
522
+ @overload
523
+ def iter(
524
+ self,
525
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
526
+ *,
527
+ output_type: OutputSpec[RunOutputDataT],
528
+ message_history: list[_messages.ModelMessage] | None = None,
529
+ model: models.Model | models.KnownModelName | str | None = None,
530
+ deps: AgentDepsT = None,
531
+ model_settings: ModelSettings | None = None,
532
+ usage_limits: _usage.UsageLimits | None = None,
533
+ usage: _usage.Usage | None = None,
534
+ infer_name: bool = True,
535
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
536
+ **_deprecated_kwargs: Never,
537
+ ) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, RunOutputDataT]]: ...
538
+
539
+ @asynccontextmanager
540
+ async def iter(
541
+ self,
542
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
543
+ *,
544
+ output_type: OutputSpec[RunOutputDataT] | None = None,
545
+ message_history: list[_messages.ModelMessage] | None = None,
546
+ model: models.Model | models.KnownModelName | str | None = None,
547
+ deps: AgentDepsT = None,
548
+ model_settings: ModelSettings | None = None,
549
+ usage_limits: _usage.UsageLimits | None = None,
550
+ usage: _usage.Usage | None = None,
551
+ infer_name: bool = True,
552
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
553
+ **_deprecated_kwargs: Never,
554
+ ) -> AsyncIterator[AgentRun[AgentDepsT, Any]]:
555
+ """A contextmanager which can be used to iterate over the agent graph's nodes as they are executed.
556
+
557
+ This method builds an internal agent graph (using system prompts, tools and output schemas) and then returns an
558
+ `AgentRun` object. The `AgentRun` can be used to async-iterate over the nodes of the graph as they are
559
+ executed. This is the API to use if you want to consume the outputs coming from each LLM model response, or the
560
+ stream of events coming from the execution of tools.
561
+
562
+ The `AgentRun` also provides methods to access the full message history, new messages, and usage statistics,
563
+ and the final result of the run once it has completed.
564
+
565
+ For more details, see the documentation of `AgentRun`.
566
+
567
+ Example:
568
+ ```python
569
+ from pydantic_ai import Agent
570
+
571
+ agent = Agent('openai:gpt-4o')
572
+
573
+ async def main():
574
+ nodes = []
575
+ async with agent.iter('What is the capital of France?') as agent_run:
576
+ async for node in agent_run:
577
+ nodes.append(node)
578
+ print(nodes)
579
+ '''
580
+ [
581
+ UserPromptNode(
582
+ user_prompt='What is the capital of France?',
583
+ instructions=None,
584
+ instructions_functions=[],
585
+ system_prompts=(),
586
+ system_prompt_functions=[],
587
+ system_prompt_dynamic_functions={},
588
+ ),
589
+ ModelRequestNode(
590
+ request=ModelRequest(
591
+ parts=[
592
+ UserPromptPart(
593
+ content='What is the capital of France?',
594
+ timestamp=datetime.datetime(...),
595
+ )
596
+ ]
597
+ )
598
+ ),
599
+ CallToolsNode(
600
+ model_response=ModelResponse(
601
+ parts=[TextPart(content='The capital of France is Paris.')],
602
+ usage=Usage(
603
+ requests=1, request_tokens=56, response_tokens=7, total_tokens=63
604
+ ),
605
+ model_name='gpt-4o',
606
+ timestamp=datetime.datetime(...),
607
+ )
608
+ ),
609
+ End(data=FinalResult(output='The capital of France is Paris.')),
610
+ ]
611
+ '''
612
+ print(agent_run.result.output)
613
+ #> The capital of France is Paris.
614
+ ```
615
+
616
+ Args:
617
+ user_prompt: User input to start/continue the conversation.
618
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
619
+ output validators since output validators would expect an argument that matches the agent's output type.
620
+ message_history: History of the conversation so far.
621
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
622
+ deps: Optional dependencies to use for this run.
623
+ model_settings: Optional settings to use for this model's request.
624
+ usage_limits: Optional limits on model request count or token usage.
625
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
626
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
627
+ toolsets: Optional additional toolsets for this run.
628
+
629
+ Returns:
630
+ The result of the run.
631
+ """
632
+ if workflow.in_workflow():
633
+ if not self._temporal_overrides_active.get():
634
+ raise UserError(
635
+ '`agent.iter()` cannot currently be used inside a Temporal workflow. '
636
+ 'Set an `event_stream_handler` on the agent and use `agent.run()` instead. '
637
+ 'Please file an issue if this is not sufficient for your use case.'
638
+ )
639
+
640
+ if model is not None:
641
+ raise UserError(
642
+ 'Model cannot be set at agent run time inside a Temporal workflow, it must be set at agent creation time.'
643
+ )
644
+ if toolsets is not None:
645
+ raise UserError(
646
+ 'Toolsets cannot be set at agent run time inside a Temporal workflow, it must be set at agent creation time.'
647
+ )
648
+
649
+ async with super().iter(
650
+ user_prompt=user_prompt,
651
+ output_type=output_type,
652
+ message_history=message_history,
653
+ model=model,
654
+ deps=deps,
655
+ model_settings=model_settings,
656
+ usage_limits=usage_limits,
657
+ usage=usage,
658
+ infer_name=infer_name,
659
+ toolsets=toolsets,
660
+ **_deprecated_kwargs,
661
+ ) as run:
662
+ yield run
663
+
664
+ @contextmanager
665
+ def override(
666
+ self,
667
+ *,
668
+ deps: AgentDepsT | _utils.Unset = _utils.UNSET,
669
+ model: models.Model | models.KnownModelName | str | _utils.Unset = _utils.UNSET,
670
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | _utils.Unset = _utils.UNSET,
671
+ tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] | _utils.Unset = _utils.UNSET,
672
+ ) -> Iterator[None]:
673
+ """Context manager to temporarily override agent dependencies, model, toolsets, or tools.
674
+
675
+ This is particularly useful when testing.
676
+ You can find an example of this [here](../testing.md#overriding-model-via-pytest-fixtures).
677
+
678
+ Args:
679
+ deps: The dependencies to use instead of the dependencies passed to the agent run.
680
+ model: The model to use instead of the model passed to the agent run.
681
+ toolsets: The toolsets to use instead of the toolsets passed to the agent constructor and agent run.
682
+ tools: The tools to use instead of the tools registered with the agent.
683
+ """
684
+ if workflow.in_workflow():
685
+ if _utils.is_set(model):
686
+ raise UserError(
687
+ 'Model cannot be contextually overridden inside a Temporal workflow, it must be set at agent creation time.'
688
+ )
689
+ if _utils.is_set(toolsets):
690
+ raise UserError(
691
+ 'Toolsets cannot be contextually overridden inside a Temporal workflow, they must be set at agent creation time.'
692
+ )
693
+ if _utils.is_set(tools):
694
+ raise UserError(
695
+ 'Tools cannot be contextually overridden inside a Temporal workflow, they must be set at agent creation time.'
696
+ )
697
+
698
+ with super().override(deps=deps, model=model, toolsets=toolsets, tools=tools):
699
+ yield