You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/agents.md
+4-3Lines changed: 4 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -125,10 +125,11 @@ It also takes an optional `event_stream_handler` argument that you can use to ga
125
125
The example below shows how to stream events and text output. You can also [stream structured output](output.md#streaming-structured-output).
126
126
127
127
!!! note
128
-
As the `run_stream()` method will consider the first output matching the [output type](output.md#structured-output) to be the final output,
129
-
it will stop running the agent graph and will not execute any tool calls made by the model after this "final" output.
128
+
The `run_stream()` method will consider the first output that matches the [output type](output.md#structured-output) to be the final output of the agent run, even when the model generates tool calls after this "final" output.
130
129
131
-
If you want to always run the agent graph to completion and stream all events from the model's streaming response and the agent's execution of tools,
130
+
These "dangling" tool calls will not be executed unless the agent's [`end_strategy`][pydantic_ai.agent.Agent.end_strategy] is set to `'exhaustive'`, and even then their results will not be sent back to the model as the agent run will already be considered completed.
131
+
132
+
If you want to always keep running the agent when it performs tool calls, and stream all events from the model's streaming response and the agent's execution of tools,
132
133
use [`agent.run_stream_events()`][pydantic_ai.agent.AbstractAgent.run_stream_events] or [`agent.iter()`][pydantic_ai.agent.AbstractAgent.iter] instead, as described in the following sections.
1. The original `Agent` cannot be used inside a deterministic Temporal workflow, but the `TemporalAgent` can.
134
-
2. As explained above, the workflow represents a deterministic piece of code that can use non-deterministic activities for operations that require I/O.
135
-
3.[`TemporalAgent.run()`][pydantic_ai.durable_exec.temporal.TemporalAgent.run] works just like [`Agent.run()`][pydantic_ai.Agent.run], but it will automatically offload model requests, tool calls, and MCP server communication to Temporal activities.
136
-
4.We connect to the Temporal server which keeps track of workflow and activity execution.
137
-
5.This assumes the Temporal server is [running locally](https://github.com/temporalio/temporal#download-and-start-temporal-server-locally).
138
-
6.The [`PydanticAIPlugin`][pydantic_ai.durable_exec.temporal.PydanticAIPlugin] tells Temporal to use Pydantic for serialization and deserialization, and to treat [`UserError`][pydantic_ai.exceptions.UserError] exceptions as non-retryable.
139
-
7.We start the worker that will listen on the specified task queue and run workflows and activities. In a real world application, this might be run in a separate service.
140
-
8.The [`AgentPlugin`][pydantic_ai.durable_exec.temporal.AgentPlugin] registers the `TemporalAgent`'s activities with the worker.
141
-
9.We call on the server to execute the workflow on a worker that's listening on the specified task queue.
142
-
10.The agent's `name` is used to uniquely identify its activities.
135
+
2. As explained above, the workflow represents a deterministic piece of code that can use non-deterministic activities for operations that require I/O. Subclassing [`PydanticAIWorkflow`][pydantic_ai.durable_exec.temporal.PydanticAIWorkflow] is optional but provides proper typing for the `__pydantic_ai_agents__` class variable.
136
+
3.List the `TemporalAgent`s used by this workflow. The [`PydanticAIPlugin`][pydantic_ai.durable_exec.temporal.PydanticAIPlugin] will automatically register their activities with the worker. Alternatively, if modifying the worker initialization is easier than the workflow class, you can use [`AgentPlugin`][pydantic_ai.durable_exec.temporal.AgentPlugin] to register agents directly on the worker.
137
+
4.[`TemporalAgent.run()`][pydantic_ai.durable_exec.temporal.TemporalAgent.run] works just like [`Agent.run()`][pydantic_ai.Agent.run], but it will automatically offload model requests, tool calls, and MCP server communication to Temporal activities.
138
+
5.We connect to the Temporal server which keeps track of workflow and activity execution.
139
+
6.This assumes the Temporal server is [running locally](https://github.com/temporalio/temporal#download-and-start-temporal-server-locally).
140
+
7.The [`PydanticAIPlugin`][pydantic_ai.durable_exec.temporal.PydanticAIPlugin] tells Temporal to use Pydantic for serialization and deserialization, treats [`UserError`][pydantic_ai.exceptions.UserError] exceptions as non-retryable, and automatically registers activities for agents listed in `__pydantic_ai_agents__`.
141
+
8.We start the worker that will listen on the specified task queue and run workflows and activities. In a real world application, this might be run in a separate service.
142
+
9.The agent's `name` is used to uniquely identify its activities.
143
+
10.We call on the server to execute the workflow on a worker that's listening on the specified task queue.
143
144
144
145
_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_
Copy file name to clipboardExpand all lines: docs/output.md
+9Lines changed: 9 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -306,6 +306,15 @@ print(repr(result.output))
306
306
307
307
_(This example is complete, it can be run "as is")_
308
308
309
+
##### Parallel Output Tool Calls
310
+
311
+
When the model calls other tools in parallel with an output tool, you can control how tool calls are executed by setting the agent's [`end_strategy`][pydantic_ai.agent.Agent.end_strategy]:
312
+
313
+
-`'early'` (default): Output tools are executed first. Once a valid final result is found, remaining function and output tool calls are skipped
314
+
-`'exhaustive'`: Output tools are executed first, then all function tools are executed. The first valid output tool result becomes the final output
315
+
316
+
The `'exhaustive'` strategy is useful when tools have important side effects (like logging, sending notifications, or updating metrics) that should always execute.
317
+
309
318
#### Native Output
310
319
311
320
Native Output mode uses a model's native "Structured Outputs" feature (aka "JSON Schema response format"), where the model is forced to only output text matching the provided JSON schema. Note that this is not supported by all models, and sometimes comes with restrictions. For example, Gemini cannot use tools at the same time as structured output, and attempting to do so will result in an error.
Raising `ModelRetry` also generates a `RetryPromptPart` containing the exception message, which is sent back to the LLM to guide its next attempt. Both `ValidationError` and `ModelRetry` respect the `retries` setting configured on the `Tool` or `Agent`.
373
373
374
+
### Tool Timeout
375
+
376
+
You can set a timeout for tool execution to prevent tools from running indefinitely. If a tool exceeds its timeout, it is treated as a failure and a retry prompt is sent to the model (counting towards the retry limit).
377
+
378
+
```python
379
+
import asyncio
380
+
381
+
from pydantic_ai import Agent
382
+
383
+
# Set a default timeout for all tools on the agent
384
+
agent = Agent('test', tool_timeout=30)
385
+
386
+
387
+
@agent.tool_plain
388
+
asyncdefslow_tool() -> str:
389
+
"""This tool will use the agent's default timeout (30 seconds)."""
390
+
await asyncio.sleep(10)
391
+
return'Done'
392
+
393
+
394
+
@agent.tool_plain(timeout=5)
395
+
asyncdeffast_tool() -> str:
396
+
"""This tool has its own timeout (5 seconds) that overrides the agent default."""
397
+
await asyncio.sleep(1)
398
+
return'Done'
399
+
```
400
+
401
+
-**Agent-level timeout**: Set `tool_timeout` on the [`Agent`][pydantic_ai.Agent] to apply a default timeout to all tools.
402
+
-**Per-tool timeout**: Set `timeout` on individual tools via [`@agent.tool`][pydantic_ai.Agent.tool], [`@agent.tool_plain`][pydantic_ai.Agent.tool_plain], or the [`Tool`][pydantic_ai.tools.Tool] dataclass. This overrides the agent-level default.
403
+
404
+
When a timeout occurs, the tool is considered to have failed and the model receives a retry prompt with the message `"Timed out after {timeout} seconds."`. This counts towards the tool's retry limit just like validation errors or explicit [`ModelRetry`][pydantic_ai.exceptions.ModelRetry] exceptions.
405
+
374
406
### Parallel tool calls & concurrency
375
407
376
408
When a model returns multiple tool calls in one response, Pydantic AI schedules them concurrently using `asyncio.create_task`.
@@ -381,6 +413,13 @@ Async functions are run on the event loop, while sync functions are offloaded to
381
413
!!! note "Limiting tool executions"
382
414
You can cap tool executions within a run using [`UsageLimits(tool_calls_limit=...)`](agents.md#usage-limits). The counter increments only after a successful tool invocation. Output tools (used for [structured output](output.md)) are not counted in the `tool_calls` metric.
383
415
416
+
#### Output Tool Calls
417
+
418
+
When a model calls an [output tool](output.md#tool-output) in parallel with other tools, the agent's [`end_strategy`][pydantic_ai.agent.Agent.end_strategy] parameter controls how these tool calls are executed.
419
+
The `'exhaustive'` strategy ensures all tools are executed even after a final result is found, which is useful when tools have side effects (like logging, sending notifications, or updating metrics) that should always execute.
420
+
421
+
For more information of how `end_strategy` works with both function tools and output tools, see the [Output Tool](output.md#parallel-output-tool-calls) docs.
422
+
384
423
## See Also
385
424
386
425
-[Function Tools](tools.md) - Basic tool concepts and registration
0 commit comments