pydantic-ai-slim 0.8.1__py3-none-any.whl → 1.0.0b1__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 (70) hide show
  1. pydantic_ai/__init__.py +28 -2
  2. pydantic_ai/_agent_graph.py +310 -140
  3. pydantic_ai/_function_schema.py +5 -5
  4. pydantic_ai/_griffe.py +2 -1
  5. pydantic_ai/_otel_messages.py +2 -2
  6. pydantic_ai/_output.py +31 -35
  7. pydantic_ai/_parts_manager.py +4 -4
  8. pydantic_ai/_run_context.py +3 -1
  9. pydantic_ai/_system_prompt.py +2 -2
  10. pydantic_ai/_tool_manager.py +3 -22
  11. pydantic_ai/_utils.py +14 -26
  12. pydantic_ai/ag_ui.py +7 -8
  13. pydantic_ai/agent/__init__.py +70 -9
  14. pydantic_ai/agent/abstract.py +35 -4
  15. pydantic_ai/agent/wrapper.py +6 -0
  16. pydantic_ai/builtin_tools.py +2 -2
  17. pydantic_ai/common_tools/duckduckgo.py +4 -2
  18. pydantic_ai/durable_exec/temporal/__init__.py +4 -2
  19. pydantic_ai/durable_exec/temporal/_agent.py +23 -2
  20. pydantic_ai/durable_exec/temporal/_function_toolset.py +53 -6
  21. pydantic_ai/durable_exec/temporal/_logfire.py +1 -1
  22. pydantic_ai/durable_exec/temporal/_mcp_server.py +2 -1
  23. pydantic_ai/durable_exec/temporal/_model.py +2 -2
  24. pydantic_ai/durable_exec/temporal/_run_context.py +2 -1
  25. pydantic_ai/durable_exec/temporal/_toolset.py +2 -1
  26. pydantic_ai/exceptions.py +45 -2
  27. pydantic_ai/format_prompt.py +2 -2
  28. pydantic_ai/mcp.py +2 -2
  29. pydantic_ai/messages.py +73 -25
  30. pydantic_ai/models/__init__.py +5 -4
  31. pydantic_ai/models/anthropic.py +5 -5
  32. pydantic_ai/models/bedrock.py +58 -56
  33. pydantic_ai/models/cohere.py +3 -3
  34. pydantic_ai/models/fallback.py +2 -2
  35. pydantic_ai/models/function.py +25 -23
  36. pydantic_ai/models/gemini.py +9 -12
  37. pydantic_ai/models/google.py +3 -3
  38. pydantic_ai/models/groq.py +4 -4
  39. pydantic_ai/models/huggingface.py +4 -4
  40. pydantic_ai/models/instrumented.py +30 -16
  41. pydantic_ai/models/mcp_sampling.py +3 -1
  42. pydantic_ai/models/mistral.py +6 -6
  43. pydantic_ai/models/openai.py +18 -27
  44. pydantic_ai/models/test.py +24 -4
  45. pydantic_ai/output.py +27 -32
  46. pydantic_ai/profiles/__init__.py +3 -3
  47. pydantic_ai/profiles/groq.py +1 -1
  48. pydantic_ai/profiles/openai.py +25 -4
  49. pydantic_ai/providers/anthropic.py +2 -3
  50. pydantic_ai/providers/bedrock.py +3 -2
  51. pydantic_ai/result.py +144 -41
  52. pydantic_ai/retries.py +10 -29
  53. pydantic_ai/run.py +12 -5
  54. pydantic_ai/tools.py +126 -22
  55. pydantic_ai/toolsets/__init__.py +4 -1
  56. pydantic_ai/toolsets/_dynamic.py +4 -4
  57. pydantic_ai/toolsets/abstract.py +18 -2
  58. pydantic_ai/toolsets/approval_required.py +32 -0
  59. pydantic_ai/toolsets/combined.py +7 -12
  60. pydantic_ai/toolsets/{deferred.py → external.py} +11 -5
  61. pydantic_ai/toolsets/filtered.py +1 -1
  62. pydantic_ai/toolsets/function.py +13 -4
  63. pydantic_ai/toolsets/wrapper.py +2 -1
  64. pydantic_ai/usage.py +7 -5
  65. {pydantic_ai_slim-0.8.1.dist-info → pydantic_ai_slim-1.0.0b1.dist-info}/METADATA +5 -6
  66. pydantic_ai_slim-1.0.0b1.dist-info/RECORD +120 -0
  67. pydantic_ai_slim-0.8.1.dist-info/RECORD +0 -119
  68. {pydantic_ai_slim-0.8.1.dist-info → pydantic_ai_slim-1.0.0b1.dist-info}/WHEEL +0 -0
  69. {pydantic_ai_slim-0.8.1.dist-info → pydantic_ai_slim-1.0.0b1.dist-info}/entry_points.txt +0 -0
  70. {pydantic_ai_slim-0.8.1.dist-info → pydantic_ai_slim-1.0.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -4,12 +4,12 @@ import dataclasses
4
4
  import inspect
5
5
  import json
6
6
  import warnings
7
- from asyncio import Lock
8
- from collections.abc import AsyncIterator, Awaitable, Iterator, Sequence
7
+ from collections.abc import AsyncIterator, Awaitable, Callable, Iterator, Sequence
9
8
  from contextlib import AbstractAsyncContextManager, AsyncExitStack, asynccontextmanager, contextmanager
10
9
  from contextvars import ContextVar
11
- from typing import TYPE_CHECKING, Any, Callable, ClassVar, cast, overload
10
+ from typing import TYPE_CHECKING, Any, ClassVar, cast, overload
12
11
 
12
+ import anyio
13
13
  from opentelemetry.trace import NoOpTracer, use_span
14
14
  from pydantic.json_schema import GenerateJsonSchema
15
15
  from typing_extensions import TypeVar, deprecated
@@ -45,10 +45,15 @@ from ..run import AgentRun, AgentRunResult
45
45
  from ..settings import ModelSettings, merge_model_settings
46
46
  from ..tools import (
47
47
  AgentDepsT,
48
+ DeferredToolCallResult,
49
+ DeferredToolResult,
50
+ DeferredToolResults,
48
51
  DocstringFormat,
49
52
  GenerateToolJsonSchema,
50
53
  RunContext,
51
54
  Tool,
55
+ ToolApproved,
56
+ ToolDenied,
52
57
  ToolFuncContext,
53
58
  ToolFuncEither,
54
59
  ToolFuncPlain,
@@ -152,7 +157,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
152
157
 
153
158
  _event_stream_handler: EventStreamHandler[AgentDepsT] | None = dataclasses.field(repr=False)
154
159
 
155
- _enter_lock: Lock = dataclasses.field(repr=False)
160
+ _enter_lock: anyio.Lock = dataclasses.field(repr=False)
156
161
  _entered_count: int = dataclasses.field(repr=False)
157
162
  _exit_stack: AsyncExitStack | None = dataclasses.field(repr=False)
158
163
 
@@ -321,7 +326,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
321
326
 
322
327
  self._instructions = ''
323
328
  self._instructions_functions = []
324
- if isinstance(instructions, (str, Callable)):
329
+ if isinstance(instructions, str | Callable):
325
330
  instructions = [instructions]
326
331
  for instruction in instructions or []:
327
332
  if isinstance(instruction, str):
@@ -346,7 +351,9 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
346
351
  if self._output_toolset:
347
352
  self._output_toolset.max_retries = self._max_result_retries
348
353
 
349
- self._function_toolset = _AgentFunctionToolset(tools, max_retries=self._max_tool_retries)
354
+ self._function_toolset = _AgentFunctionToolset(
355
+ tools, max_retries=self._max_tool_retries, output_schema=self._output_schema
356
+ )
350
357
  self._dynamic_toolsets = [
351
358
  DynamicToolset[AgentDepsT](toolset_func=toolset)
352
359
  for toolset in toolsets or []
@@ -367,7 +374,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
367
374
  _utils.Option[Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]]]
368
375
  ] = ContextVar('_override_tools', default=None)
369
376
 
370
- self._enter_lock = _utils.get_async_lock()
377
+ self._enter_lock = anyio.Lock()
371
378
  self._entered_count = 0
372
379
  self._exit_stack = None
373
380
 
@@ -427,6 +434,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
427
434
  *,
428
435
  output_type: None = None,
429
436
  message_history: list[_messages.ModelMessage] | None = None,
437
+ deferred_tool_results: DeferredToolResults | None = None,
430
438
  model: models.Model | models.KnownModelName | str | None = None,
431
439
  deps: AgentDepsT = None,
432
440
  model_settings: ModelSettings | None = None,
@@ -443,6 +451,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
443
451
  *,
444
452
  output_type: OutputSpec[RunOutputDataT],
445
453
  message_history: list[_messages.ModelMessage] | None = None,
454
+ deferred_tool_results: DeferredToolResults | None = None,
446
455
  model: models.Model | models.KnownModelName | str | None = None,
447
456
  deps: AgentDepsT = None,
448
457
  model_settings: ModelSettings | None = None,
@@ -453,12 +462,13 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
453
462
  ) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, RunOutputDataT]]: ...
454
463
 
455
464
  @asynccontextmanager
456
- async def iter(
465
+ async def iter( # noqa: C901
457
466
  self,
458
467
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
459
468
  *,
460
469
  output_type: OutputSpec[RunOutputDataT] | None = None,
461
470
  message_history: list[_messages.ModelMessage] | None = None,
471
+ deferred_tool_results: DeferredToolResults | None = None,
462
472
  model: models.Model | models.KnownModelName | str | None = None,
463
473
  deps: AgentDepsT = None,
464
474
  model_settings: ModelSettings | None = None,
@@ -531,6 +541,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
531
541
  output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
532
542
  output validators since output validators would expect an argument that matches the agent's output type.
533
543
  message_history: History of the conversation so far.
544
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
534
545
  model: Optional model to use for this run, required if `model` was not set when creating the agent.
535
546
  deps: Optional dependencies to use for this run.
536
547
  model_settings: Optional settings to use for this model's request.
@@ -609,6 +620,23 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
609
620
  instrumentation_settings = None
610
621
  tracer = NoOpTracer()
611
622
 
623
+ tool_call_results: dict[str, DeferredToolResult] | None = None
624
+ if deferred_tool_results is not None:
625
+ tool_call_results = {}
626
+ for tool_call_id, approval in deferred_tool_results.approvals.items():
627
+ if approval is True:
628
+ approval = ToolApproved()
629
+ elif approval is False:
630
+ approval = ToolDenied()
631
+ tool_call_results[tool_call_id] = approval
632
+
633
+ if calls := deferred_tool_results.calls:
634
+ call_result_types = _utils.get_union_args(DeferredToolCallResult)
635
+ for tool_call_id, result in calls.items():
636
+ if not isinstance(result, call_result_types):
637
+ result = _messages.ToolReturn(result)
638
+ tool_call_results[tool_call_id] = result
639
+
612
640
  graph_deps = _agent_graph.GraphAgentDeps[AgentDepsT, RunOutputDataT](
613
641
  user_deps=deps,
614
642
  prompt=user_prompt,
@@ -623,6 +651,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
623
651
  history_processors=self.history_processors,
624
652
  builtin_tools=list(self._builtin_tools),
625
653
  tool_manager=tool_manager,
654
+ tool_call_results=tool_call_results,
626
655
  tracer=tracer,
627
656
  get_instructions=get_instructions,
628
657
  instrumentation_settings=instrumentation_settings,
@@ -976,6 +1005,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
976
1005
  require_parameter_descriptions: bool = False,
977
1006
  schema_generator: type[GenerateJsonSchema] = GenerateToolJsonSchema,
978
1007
  strict: bool | None = None,
1008
+ requires_approval: bool = False,
979
1009
  ) -> Callable[[ToolFuncContext[AgentDepsT, ToolParams]], ToolFuncContext[AgentDepsT, ToolParams]]: ...
980
1010
 
981
1011
  def tool(
@@ -990,6 +1020,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
990
1020
  require_parameter_descriptions: bool = False,
991
1021
  schema_generator: type[GenerateJsonSchema] = GenerateToolJsonSchema,
992
1022
  strict: bool | None = None,
1023
+ requires_approval: bool = False,
993
1024
  ) -> Any:
994
1025
  """Decorator to register a tool function which takes [`RunContext`][pydantic_ai.tools.RunContext] as its first argument.
995
1026
 
@@ -1034,6 +1065,8 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
1034
1065
  schema_generator: The JSON schema generator class to use for this tool. Defaults to `GenerateToolJsonSchema`.
1035
1066
  strict: Whether to enforce JSON schema compliance (only affects OpenAI).
1036
1067
  See [`ToolDefinition`][pydantic_ai.tools.ToolDefinition] for more info.
1068
+ requires_approval: Whether this tool requires human-in-the-loop approval. Defaults to False.
1069
+ See the [tools documentation](../tools.md#human-in-the-loop-tool-approval) for more info.
1037
1070
  """
1038
1071
 
1039
1072
  def tool_decorator(
@@ -1050,6 +1083,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
1050
1083
  require_parameter_descriptions,
1051
1084
  schema_generator,
1052
1085
  strict,
1086
+ requires_approval,
1053
1087
  )
1054
1088
  return func_
1055
1089
 
@@ -1070,6 +1104,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
1070
1104
  require_parameter_descriptions: bool = False,
1071
1105
  schema_generator: type[GenerateJsonSchema] = GenerateToolJsonSchema,
1072
1106
  strict: bool | None = None,
1107
+ requires_approval: bool = False,
1073
1108
  ) -> Callable[[ToolFuncPlain[ToolParams]], ToolFuncPlain[ToolParams]]: ...
1074
1109
 
1075
1110
  def tool_plain(
@@ -1084,6 +1119,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
1084
1119
  require_parameter_descriptions: bool = False,
1085
1120
  schema_generator: type[GenerateJsonSchema] = GenerateToolJsonSchema,
1086
1121
  strict: bool | None = None,
1122
+ requires_approval: bool = False,
1087
1123
  ) -> Any:
1088
1124
  """Decorator to register a tool function which DOES NOT take `RunContext` as an argument.
1089
1125
 
@@ -1128,6 +1164,8 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
1128
1164
  schema_generator: The JSON schema generator class to use for this tool. Defaults to `GenerateToolJsonSchema`.
1129
1165
  strict: Whether to enforce JSON schema compliance (only affects OpenAI).
1130
1166
  See [`ToolDefinition`][pydantic_ai.tools.ToolDefinition] for more info.
1167
+ requires_approval: Whether this tool requires human-in-the-loop approval. Defaults to False.
1168
+ See the [tools documentation](../tools.md#human-in-the-loop-tool-approval) for more info.
1131
1169
  """
1132
1170
 
1133
1171
  def tool_decorator(func_: ToolFuncPlain[ToolParams]) -> ToolFuncPlain[ToolParams]:
@@ -1142,6 +1180,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
1142
1180
  require_parameter_descriptions,
1143
1181
  schema_generator,
1144
1182
  strict,
1183
+ requires_approval,
1145
1184
  )
1146
1185
  return func_
1147
1186
 
@@ -1285,7 +1324,9 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
1285
1324
  toolsets: list[AbstractToolset[AgentDepsT]] = []
1286
1325
 
1287
1326
  if some_tools := self._override_tools.get():
1288
- function_toolset = _AgentFunctionToolset(some_tools.value, max_retries=self._max_tool_retries)
1327
+ function_toolset = _AgentFunctionToolset(
1328
+ some_tools.value, max_retries=self._max_tool_retries, output_schema=self._output_schema
1329
+ )
1289
1330
  else:
1290
1331
  function_toolset = self._function_toolset
1291
1332
  toolsets.append(function_toolset)
@@ -1382,6 +1423,19 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
1382
1423
 
1383
1424
  @dataclasses.dataclass(init=False)
1384
1425
  class _AgentFunctionToolset(FunctionToolset[AgentDepsT]):
1426
+ output_schema: _output.BaseOutputSchema[Any]
1427
+
1428
+ def __init__(
1429
+ self,
1430
+ tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = [],
1431
+ *,
1432
+ max_retries: int = 1,
1433
+ id: str | None = None,
1434
+ output_schema: _output.BaseOutputSchema[Any],
1435
+ ):
1436
+ self.output_schema = output_schema
1437
+ super().__init__(tools, max_retries=max_retries, id=id)
1438
+
1385
1439
  @property
1386
1440
  def id(self) -> str:
1387
1441
  return '<agent>'
@@ -1389,3 +1443,10 @@ class _AgentFunctionToolset(FunctionToolset[AgentDepsT]):
1389
1443
  @property
1390
1444
  def label(self) -> str:
1391
1445
  return 'the agent'
1446
+
1447
+ def add_tool(self, tool: Tool[AgentDepsT]) -> None:
1448
+ if tool.requires_approval and not self.output_schema.allows_deferred_tools:
1449
+ raise exceptions.UserError(
1450
+ 'To use tools that require approval, add `DeferredToolRequests` to the list of output types for this agent.'
1451
+ )
1452
+ super().add_tool(tool)
@@ -2,12 +2,12 @@ from __future__ import annotations as _annotations
2
2
 
3
3
  import inspect
4
4
  from abc import ABC, abstractmethod
5
- from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Iterator, Mapping, Sequence
5
+ from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable, Iterator, Mapping, Sequence
6
6
  from contextlib import AbstractAsyncContextManager, asynccontextmanager, contextmanager
7
7
  from types import FrameType
8
- from typing import TYPE_CHECKING, Any, Callable, Generic, cast, overload
8
+ from typing import TYPE_CHECKING, Any, Generic, TypeAlias, cast, overload
9
9
 
10
- from typing_extensions import Self, TypeAlias, TypeIs, TypeVar
10
+ from typing_extensions import Self, TypeIs, TypeVar
11
11
 
12
12
  from pydantic_graph import End
13
13
  from pydantic_graph._utils import get_event_loop
@@ -27,6 +27,7 @@ from ..run import AgentRun, AgentRunResult
27
27
  from ..settings import ModelSettings
28
28
  from ..tools import (
29
29
  AgentDepsT,
30
+ DeferredToolResults,
30
31
  RunContext,
31
32
  Tool,
32
33
  ToolFuncEither,
@@ -116,6 +117,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
116
117
  *,
117
118
  output_type: None = None,
118
119
  message_history: list[_messages.ModelMessage] | None = None,
120
+ deferred_tool_results: DeferredToolResults | None = None,
119
121
  model: models.Model | models.KnownModelName | str | None = None,
120
122
  deps: AgentDepsT = None,
121
123
  model_settings: ModelSettings | None = None,
@@ -133,6 +135,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
133
135
  *,
134
136
  output_type: OutputSpec[RunOutputDataT],
135
137
  message_history: list[_messages.ModelMessage] | None = None,
138
+ deferred_tool_results: DeferredToolResults | None = None,
136
139
  model: models.Model | models.KnownModelName | str | None = None,
137
140
  deps: AgentDepsT = None,
138
141
  model_settings: ModelSettings | None = None,
@@ -149,6 +152,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
149
152
  *,
150
153
  output_type: OutputSpec[RunOutputDataT] | None = None,
151
154
  message_history: list[_messages.ModelMessage] | None = None,
155
+ deferred_tool_results: DeferredToolResults | None = None,
152
156
  model: models.Model | models.KnownModelName | str | None = None,
153
157
  deps: AgentDepsT = None,
154
158
  model_settings: ModelSettings | None = None,
@@ -180,6 +184,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
180
184
  output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
181
185
  output validators since output validators would expect an argument that matches the agent's output type.
182
186
  message_history: History of the conversation so far.
187
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
183
188
  model: Optional model to use for this run, required if `model` was not set when creating the agent.
184
189
  deps: Optional dependencies to use for this run.
185
190
  model_settings: Optional settings to use for this model's request.
@@ -201,6 +206,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
201
206
  user_prompt=user_prompt,
202
207
  output_type=output_type,
203
208
  message_history=message_history,
209
+ deferred_tool_results=deferred_tool_results,
204
210
  model=model,
205
211
  deps=deps,
206
212
  model_settings=model_settings,
@@ -225,6 +231,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
225
231
  *,
226
232
  output_type: None = None,
227
233
  message_history: list[_messages.ModelMessage] | None = None,
234
+ deferred_tool_results: DeferredToolResults | None = None,
228
235
  model: models.Model | models.KnownModelName | str | None = None,
229
236
  deps: AgentDepsT = None,
230
237
  model_settings: ModelSettings | None = None,
@@ -242,6 +249,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
242
249
  *,
243
250
  output_type: OutputSpec[RunOutputDataT],
244
251
  message_history: list[_messages.ModelMessage] | None = None,
252
+ deferred_tool_results: DeferredToolResults | None = None,
245
253
  model: models.Model | models.KnownModelName | str | None = None,
246
254
  deps: AgentDepsT = None,
247
255
  model_settings: ModelSettings | None = None,
@@ -258,6 +266,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
258
266
  *,
259
267
  output_type: OutputSpec[RunOutputDataT] | None = None,
260
268
  message_history: list[_messages.ModelMessage] | None = None,
269
+ deferred_tool_results: DeferredToolResults | None = None,
261
270
  model: models.Model | models.KnownModelName | str | None = None,
262
271
  deps: AgentDepsT = None,
263
272
  model_settings: ModelSettings | None = None,
@@ -288,6 +297,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
288
297
  output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
289
298
  output validators since output validators would expect an argument that matches the agent's output type.
290
299
  message_history: History of the conversation so far.
300
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
291
301
  model: Optional model to use for this run, required if `model` was not set when creating the agent.
292
302
  deps: Optional dependencies to use for this run.
293
303
  model_settings: Optional settings to use for this model's request.
@@ -308,6 +318,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
308
318
  user_prompt,
309
319
  output_type=output_type,
310
320
  message_history=message_history,
321
+ deferred_tool_results=deferred_tool_results,
311
322
  model=model,
312
323
  deps=deps,
313
324
  model_settings=model_settings,
@@ -326,6 +337,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
326
337
  *,
327
338
  output_type: None = None,
328
339
  message_history: list[_messages.ModelMessage] | None = None,
340
+ deferred_tool_results: DeferredToolResults | None = None,
329
341
  model: models.Model | models.KnownModelName | str | None = None,
330
342
  deps: AgentDepsT = None,
331
343
  model_settings: ModelSettings | None = None,
@@ -343,6 +355,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
343
355
  *,
344
356
  output_type: OutputSpec[RunOutputDataT],
345
357
  message_history: list[_messages.ModelMessage] | None = None,
358
+ deferred_tool_results: DeferredToolResults | None = None,
346
359
  model: models.Model | models.KnownModelName | str | None = None,
347
360
  deps: AgentDepsT = None,
348
361
  model_settings: ModelSettings | None = None,
@@ -360,6 +373,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
360
373
  *,
361
374
  output_type: OutputSpec[RunOutputDataT] | None = None,
362
375
  message_history: list[_messages.ModelMessage] | None = None,
376
+ deferred_tool_results: DeferredToolResults | None = None,
363
377
  model: models.Model | models.KnownModelName | str | None = None,
364
378
  deps: AgentDepsT = None,
365
379
  model_settings: ModelSettings | None = None,
@@ -398,6 +412,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
398
412
  output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
399
413
  output validators since output validators would expect an argument that matches the agent's output type.
400
414
  message_history: History of the conversation so far.
415
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
401
416
  model: Optional model to use for this run, required if `model` was not set when creating the agent.
402
417
  deps: Optional dependencies to use for this run.
403
418
  model_settings: Optional settings to use for this model's request.
@@ -424,6 +439,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
424
439
  user_prompt,
425
440
  output_type=output_type,
426
441
  message_history=message_history,
442
+ deferred_tool_results=deferred_tool_results,
427
443
  model=model,
428
444
  deps=deps,
429
445
  model_settings=model_settings,
@@ -436,8 +452,8 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
436
452
  assert isinstance(first_node, _agent_graph.UserPromptNode) # the first node should be a user prompt node
437
453
  node = first_node
438
454
  while True:
455
+ graph_ctx = agent_run.ctx
439
456
  if self.is_model_request_node(node):
440
- graph_ctx = agent_run.ctx
441
457
  async with node.stream(graph_ctx) as stream:
442
458
  final_result_event = None
443
459
 
@@ -505,6 +521,17 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
505
521
  await event_stream_handler(_agent_graph.build_run_context(agent_run.ctx), stream)
506
522
 
507
523
  next_node = await agent_run.next(node)
524
+ if isinstance(next_node, End) and agent_run.result is not None:
525
+ # A final output could have been produced by the CallToolsNode rather than the ModelRequestNode,
526
+ # if a tool function raised CallDeferred or ApprovalRequired.
527
+ # In this case there's no response to stream, but we still let the user access the output etc as normal.
528
+ yield StreamedRunResult(
529
+ graph_ctx.state.message_history,
530
+ graph_ctx.deps.new_message_index,
531
+ run_result=agent_run.result,
532
+ )
533
+ yielded = True
534
+ break
508
535
  if not isinstance(next_node, _agent_graph.AgentNode):
509
536
  raise exceptions.AgentRunError( # pragma: no cover
510
537
  'Should have produced a StreamedRunResult before getting here'
@@ -521,6 +548,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
521
548
  *,
522
549
  output_type: None = None,
523
550
  message_history: list[_messages.ModelMessage] | None = None,
551
+ deferred_tool_results: DeferredToolResults | None = None,
524
552
  model: models.Model | models.KnownModelName | str | None = None,
525
553
  deps: AgentDepsT = None,
526
554
  model_settings: ModelSettings | None = None,
@@ -537,6 +565,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
537
565
  *,
538
566
  output_type: OutputSpec[RunOutputDataT],
539
567
  message_history: list[_messages.ModelMessage] | None = None,
568
+ deferred_tool_results: DeferredToolResults | None = None,
540
569
  model: models.Model | models.KnownModelName | str | None = None,
541
570
  deps: AgentDepsT = None,
542
571
  model_settings: ModelSettings | None = None,
@@ -554,6 +583,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
554
583
  *,
555
584
  output_type: OutputSpec[RunOutputDataT] | None = None,
556
585
  message_history: list[_messages.ModelMessage] | None = None,
586
+ deferred_tool_results: DeferredToolResults | None = None,
557
587
  model: models.Model | models.KnownModelName | str | None = None,
558
588
  deps: AgentDepsT = None,
559
589
  model_settings: ModelSettings | None = None,
@@ -626,6 +656,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
626
656
  output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
627
657
  output validators since output validators would expect an argument that matches the agent's output type.
628
658
  message_history: History of the conversation so far.
659
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
629
660
  model: Optional model to use for this run, required if `model` was not set when creating the agent.
630
661
  deps: Optional dependencies to use for this run.
631
662
  model_settings: Optional settings to use for this model's request.
@@ -15,6 +15,7 @@ from ..run import AgentRun
15
15
  from ..settings import ModelSettings
16
16
  from ..tools import (
17
17
  AgentDepsT,
18
+ DeferredToolResults,
18
19
  Tool,
19
20
  ToolFuncEither,
20
21
  )
@@ -72,6 +73,7 @@ class WrapperAgent(AbstractAgent[AgentDepsT, OutputDataT]):
72
73
  *,
73
74
  output_type: None = None,
74
75
  message_history: list[_messages.ModelMessage] | None = None,
76
+ deferred_tool_results: DeferredToolResults | None = None,
75
77
  model: models.Model | models.KnownModelName | str | None = None,
76
78
  deps: AgentDepsT = None,
77
79
  model_settings: ModelSettings | None = None,
@@ -88,6 +90,7 @@ class WrapperAgent(AbstractAgent[AgentDepsT, OutputDataT]):
88
90
  *,
89
91
  output_type: OutputSpec[RunOutputDataT],
90
92
  message_history: list[_messages.ModelMessage] | None = None,
93
+ deferred_tool_results: DeferredToolResults | None = None,
91
94
  model: models.Model | models.KnownModelName | str | None = None,
92
95
  deps: AgentDepsT = None,
93
96
  model_settings: ModelSettings | None = None,
@@ -104,6 +107,7 @@ class WrapperAgent(AbstractAgent[AgentDepsT, OutputDataT]):
104
107
  *,
105
108
  output_type: OutputSpec[RunOutputDataT] | None = None,
106
109
  message_history: list[_messages.ModelMessage] | None = None,
110
+ deferred_tool_results: DeferredToolResults | None = None,
107
111
  model: models.Model | models.KnownModelName | str | None = None,
108
112
  deps: AgentDepsT = None,
109
113
  model_settings: ModelSettings | None = None,
@@ -176,6 +180,7 @@ class WrapperAgent(AbstractAgent[AgentDepsT, OutputDataT]):
176
180
  output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
177
181
  output validators since output validators would expect an argument that matches the agent's output type.
178
182
  message_history: History of the conversation so far.
183
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
179
184
  model: Optional model to use for this run, required if `model` was not set when creating the agent.
180
185
  deps: Optional dependencies to use for this run.
181
186
  model_settings: Optional settings to use for this model's request.
@@ -191,6 +196,7 @@ class WrapperAgent(AbstractAgent[AgentDepsT, OutputDataT]):
191
196
  user_prompt=user_prompt,
192
197
  output_type=output_type,
193
198
  message_history=message_history,
199
+ deferred_tool_results=deferred_tool_results,
194
200
  model=model,
195
201
  deps=deps,
196
202
  model_settings=model_settings,
@@ -9,7 +9,7 @@ from typing_extensions import TypedDict
9
9
  __all__ = ('AbstractBuiltinTool', 'WebSearchTool', 'WebSearchUserLocation', 'CodeExecutionTool', 'UrlContextTool')
10
10
 
11
11
 
12
- @dataclass
12
+ @dataclass(kw_only=True)
13
13
  class AbstractBuiltinTool(ABC):
14
14
  """A builtin tool that can be used by an agent.
15
15
 
@@ -19,7 +19,7 @@ class AbstractBuiltinTool(ABC):
19
19
  """
20
20
 
21
21
 
22
- @dataclass
22
+ @dataclass(kw_only=True)
23
23
  class WebSearchTool(AbstractBuiltinTool):
24
24
  """A builtin tool that allows your agent to search the web for information.
25
25
 
@@ -1,5 +1,5 @@
1
1
  import functools
2
- from dataclasses import dataclass
2
+ from dataclasses import KW_ONLY, dataclass
3
3
 
4
4
  import anyio
5
5
  import anyio.to_thread
@@ -43,7 +43,9 @@ class DuckDuckGoSearchTool:
43
43
  client: DDGS
44
44
  """The DuckDuckGo search client."""
45
45
 
46
- max_results: int | None = None
46
+ _: KW_ONLY
47
+
48
+ max_results: int | None
47
49
  """The maximum number of results. If None, returns results only from the first response."""
48
50
 
49
51
  async def __call__(self, query: str) -> list[DuckDuckGoResult]:
@@ -1,10 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import warnings
4
- from collections.abc import AsyncIterator, Sequence
4
+ from collections.abc import AsyncIterator, Callable, Sequence
5
5
  from contextlib import AbstractAsyncContextManager
6
6
  from dataclasses import replace
7
- from typing import Any, Callable
7
+ from typing import Any
8
8
 
9
9
  from pydantic.errors import PydanticUserError
10
10
  from temporalio.client import ClientConfig, Plugin as ClientPlugin, WorkflowHistory
@@ -57,6 +57,8 @@ class PydanticAIPlugin(ClientPlugin, WorkerPlugin):
57
57
  runner,
58
58
  restrictions=runner.restrictions.with_passthrough_modules(
59
59
  'pydantic_ai',
60
+ 'pydantic',
61
+ 'pydantic_core',
60
62
  'logfire',
61
63
  'rich',
62
64
  'httpx',