Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/agent-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ capabilities:

## Loading specs

[`Agent.from_file`][pydantic_ai.Agent.from_file] loads a spec from a YAML or JSON file and constructs an agent:
[`Agent.from_file`][pydantic_ai.agent.Agent.from_file] loads a spec from a YAML or JSON file and constructs an agent:

```python {title="from_file_example.py" test="skip"}
from pydantic_ai import Agent

agent = Agent.from_file('agent.yaml')
```

[`Agent.from_spec`][pydantic_ai.Agent.from_spec] accepts a dict or [`AgentSpec`][pydantic_ai.agent.spec.AgentSpec] instance and supports additional keyword arguments that supplement or override the spec:
[`Agent.from_spec`][pydantic_ai.agent.Agent.from_spec] accepts a dict or [`AgentSpec`](#agentspec-reference) instance and supports additional keyword arguments that supplement or override the spec:

```python {title="from_spec_example.py"}
from dataclasses import dataclass
Expand Down Expand Up @@ -71,15 +71,15 @@ For more control over spec loading, use [`AgentSpec.from_file`][pydantic_ai.agen

## Template strings

[`TemplateStr`][pydantic_ai.TemplateStr] provides Handlebars-style templates (`{{variable}}`) that are rendered against the agent's [dependencies](dependencies.md) at runtime. In spec files, strings containing `{{` are automatically converted to template strings:
[`TemplateStr`][pydantic_ai._template.TemplateStr] provides Handlebars-style templates (`{{variable}}`) that are rendered against the agent's [dependencies](dependencies.md) at runtime. In spec files, strings containing `{{` are automatically converted to template strings:

```yaml {test="skip"}
instructions: "You are assisting {{name}}, who is a {{role}}."
```

Template variables are resolved from the fields of the `deps` object. When a `deps_type` (or [`deps_schema`](#deps_schema)) is provided, template variable names are validated at construction time.

In Python code, [`TemplateStr`][pydantic_ai.TemplateStr] can be used explicitly, but a callable with [`RunContext`][pydantic_ai.tools.RunContext] is generally preferred for IDE autocomplete and type checking:
In Python code, [`TemplateStr`][pydantic_ai._template.TemplateStr] can be used explicitly, but a callable with [`RunContext`][pydantic_ai.tools.RunContext] is generally preferred for IDE autocomplete and type checking:

```python {title="template_instructions.py"}
from dataclasses import dataclass
Expand Down Expand Up @@ -117,7 +117,7 @@ See [Publishing capabilities](capabilities.md#publishing-capabilities) for how t

## `AgentSpec` reference

The [`AgentSpec`][pydantic_ai.agent.spec.AgentSpec] model represents the full spec structure:
The [`AgentSpec`](#agentspec-reference) model represents the full spec structure:

| Field | Type | Description |
|---|---|---|
Expand Down
5 changes: 5 additions & 0 deletions docs/api/agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@
- capture_run_messages
- InstrumentationSettings
- EventStreamHandler
- AgentInstructions
- AgentModelSettings
- CallToolsNode
- ModelRequestNode
- UserPromptNode
11 changes: 11 additions & 0 deletions docs/api/agent_spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# `pydantic_ai.agent.spec`

::: pydantic_ai.agent.spec
options:
members:
- AgentSpec

::: pydantic_ai._template
options:
members:
- TemplateStr
1 change: 1 addition & 0 deletions docs/api/models/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
members:
- KnownModelName
- ModelRequestParameters
- ModelRequestContext
- Model
- AbstractToolDefinition
- StreamedResponse
Expand Down
1 change: 1 addition & 0 deletions docs/api/toolsets.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
::: pydantic_ai.toolsets
options:
members:
- AgentToolset
- AbstractToolset
- CombinedToolset
- ExternalToolset
Expand Down
56 changes: 28 additions & 28 deletions docs/capabilities.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/extensibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ To make a capability installable and usable in [agent specs](agent-spec.md):

3. **Package naming** — use the `pydantic-ai-` prefix (e.g. `pydantic-ai-guardrails`) so users can find your package.

4. **Registration** — users pass custom capability types via `custom_capability_types` on [`Agent.from_spec`][pydantic_ai.Agent.from_spec] or [`Agent.from_file`][pydantic_ai.Agent.from_file].
4. **Registration** — users pass custom capability types via `custom_capability_types` on [`Agent.from_spec`][pydantic_ai.agent.Agent.from_spec] or [`Agent.from_file`][pydantic_ai.agent.Agent.from_file].

```python {test="skip" lint="skip"}
from pydantic_ai import Agent
Expand Down
111 changes: 110 additions & 1 deletion docs/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,115 @@ print(result.output)

Both sync and async hook functions are accepted. Sync functions are automatically wrapped for async execution.

## Lifecycle overview

The following sequence diagram shows all hooks firing during a complete run with one tool call (happy path). Error hooks (`on_*_error`) are mutually exclusive with `after_*` — see [error hooks](capabilities.md#error-hooks) for that flow.

??? note "Expand sequence diagram"

```mermaid
sequenceDiagram
participant App as Application
participant R as Run Hooks
participant N as Node Hooks
participant M as Model Hooks
participant P as prepare_tools
participant TV as Tool Validate Hooks
participant TEx as Tool Execute Hooks
participant LLM as LLM Provider
participant Fn as Tool Function

App->>R: before_run(ctx)
activate R
Note right of R: wrap_run enters

Note over R,Fn: ── UserPromptNode ──
R->>N: before_node_run(ctx, node)
activate N
Note right of N: wrap_node_run enters
Note over N: Build user prompt message
Note right of N: wrap_node_run exits
N-->>R: after_node_run → next: ModelRequestNode
deactivate N

Note over R,Fn: ── ModelRequestNode ──
R->>N: before_node_run(ctx, node)
activate N
Note right of N: wrap_node_run enters
N->>P: prepare_tools(ctx, tool_defs)
P-->>N: filtered tool_defs
N->>M: before_model_request(ctx, request_context)
activate M
Note right of M: wrap_model_request enters
M->>LLM: HTTP request
LLM-->>M: Response (with tool_calls)
Note right of M: wrap_model_request exits
M-->>N: after_model_request(ctx, request_context, response)
deactivate M
Note right of N: wrap_node_run exits
N-->>R: after_node_run → next: CallToolsNode
deactivate N

Note over R,Fn: ── CallToolsNode ──
R->>N: before_node_run(ctx, node)
activate N
Note right of N: wrap_node_run enters

Note over TV,TEx: For each tool call
N->>TV: before_tool_validate(ctx, call, tool_def, raw_args)
activate TV
Note right of TV: wrap_tool_validate enters
Note over TV: Parse & validate args against schema
Note right of TV: wrap_tool_validate exits
TV-->>N: after_tool_validate(ctx, call, tool_def, validated_args)
deactivate TV

N->>TEx: before_tool_execute(ctx, call, tool_def, args)
activate TEx
Note right of TEx: wrap_tool_execute enters
TEx->>Fn: call tool function
Fn-->>TEx: result
Note right of TEx: wrap_tool_execute exits
TEx-->>N: after_tool_execute(ctx, call, tool_def, args, result)
deactivate TEx

Note right of N: wrap_node_run exits
N-->>R: after_node_run → next: ModelRequestNode
deactivate N

Note over R,Fn: ── ModelRequestNode (with tool results) ──
R->>N: before_node_run(ctx, node)
activate N
Note right of N: wrap_node_run enters
N->>P: prepare_tools(ctx, tool_defs)
P-->>N: filtered tool_defs
N->>M: before_model_request(ctx, request_context)
activate M
Note right of M: wrap_model_request enters
M->>LLM: HTTP request
LLM-->>M: Response (text only, no tool calls)
Note right of M: wrap_model_request exits
M-->>N: after_model_request(ctx, request_context, response)
deactivate M
Note right of N: wrap_node_run exits
N-->>R: after_node_run → next: CallToolsNode
deactivate N

Note over R,Fn: ── CallToolsNode (no tool calls) ──
R->>N: before_node_run(ctx, node)
activate N
Note right of N: wrap_node_run enters
Note over N: No tool calls → End(FinalResult)
Note right of N: wrap_node_run exits
N-->>R: after_node_run → End
deactivate N

Note right of R: wrap_run exits
R-->>App: after_run(ctx, result)
deactivate R
Note over App: AgentRunResult
```

## Hook types

### Run hooks
Expand All @@ -96,7 +205,7 @@ Run hooks fire once per agent run. `wrap_run` (registered via `hooks.on.run`) wr
| `node_run` | `node_run=` | `wrap_node_run` |
| `node_run_error` | `node_run_error=` | `on_node_run_error` |

Node hooks fire for each graph step ([`UserPromptNode`][pydantic_ai.UserPromptNode], [`ModelRequestNode`][pydantic_ai.ModelRequestNode], [`CallToolsNode`][pydantic_ai.CallToolsNode]).
Node hooks fire for each graph step ([`UserPromptNode`][pydantic_ai.agent.UserPromptNode], [`ModelRequestNode`][pydantic_ai.agent.ModelRequestNode], [`CallToolsNode`][pydantic_ai.agent.CallToolsNode]).

!!! note
`wrap_node_run` hooks are called automatically by [`agent.run()`][pydantic_ai.agent.AbstractAgent.run], [`agent.run_stream()`][pydantic_ai.agent.AbstractAgent.run_stream], and [`agent_run.next()`][pydantic_ai.run.AgentRun.next], but **not** when iterating with bare `async for node in agent_run:`.
Expand Down
2 changes: 1 addition & 1 deletion docs/thinking.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ agent = Agent(model, model_settings=settings)
To enable thinking, use the [`AnthropicModelSettings.anthropic_thinking`][pydantic_ai.models.anthropic.AnthropicModelSettings.anthropic_thinking] [model setting](agent.md#model-run-settings).

!!! note
Extended thinking (`type: 'enabled'` with `budget_tokens`) is deprecated on `claude-opus-4-6`+. For those models, use [adaptive thinking](#adaptive-thinking--effort) instead.
Extended thinking (`type: 'enabled'` with `budget_tokens`) is deprecated on `claude-opus-4-6`+. For those models, use [adaptive thinking](#adaptive-thinking-effort) instead.

```python {title="anthropic_thinking_part.py"}
from pydantic_ai import Agent
Expand Down
3 changes: 2 additions & 1 deletion pydantic_ai_slim/pydantic_ai/agent/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
'WrapperAgent',
'AbstractAgent',
'EventStreamHandler',
'AgentInstructions',
'AgentModelSettings',
'BuiltinToolFunc',
)
Expand Down Expand Up @@ -799,7 +800,7 @@ def from_file(
The file format is inferred from the extension (`.yaml`/`.yml` or `.json`)
unless overridden with the `fmt` argument.

All other arguments are forwarded to [`from_spec`][pydantic_ai.Agent.from_spec].
All other arguments are forwarded to [`from_spec`][pydantic_ai.agent.Agent.from_spec].
"""
spec = AgentSpec.from_file(path, fmt=fmt)
return cls.from_spec(
Expand Down
14 changes: 7 additions & 7 deletions pydantic_ai_slim/pydantic_ai/capabilities/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class AbstractCapability(ABC, Generic[AgentDepsT]):

[`get_serialization_name`][pydantic_ai.capabilities.AbstractCapability.get_serialization_name]
and [`from_spec`][pydantic_ai.capabilities.AbstractCapability.from_spec] support
YAML/JSON specs (via [`Agent.from_spec`][pydantic_ai.Agent.from_spec]); they have
YAML/JSON specs (via [`Agent.from_spec`][pydantic_ai.agent.Agent.from_spec]); they have
sensible defaults and typically don't need to be overridden.
"""

Expand Down Expand Up @@ -113,7 +113,7 @@ def get_instructions(self) -> AgentInstructions[AgentDepsT] | None:
This method is called once at agent construction time. To get dynamic
per-request behavior, return a callable that receives
[`RunContext`][pydantic_ai.tools.RunContext] or a
[`TemplateStr`][pydantic_ai.TemplateStr] — not a dynamic string.
[`TemplateStr`][pydantic_ai._template.TemplateStr] — not a dynamic string.
"""
return None

Expand Down Expand Up @@ -272,10 +272,10 @@ async def wrap_node_run(
the returned next node, call `handler` multiple times (retry), or
return a different node to redirect graph progression.

Note: this hook fires when using [`agent.run()`][pydantic_ai.Agent.run],
[`agent.run_stream()`][pydantic_ai.Agent.run_stream], and when manually driving
an [`agent.iter()`][pydantic_ai.Agent.iter] run with
[`next()`][pydantic_ai.result.AgentRun.next], but it does **not** fire when
Note: this hook fires when using [`agent.run()`][pydantic_ai.agent.AbstractAgent.run],
[`agent.run_stream()`][pydantic_ai.agent.AbstractAgent.run_stream], and when manually driving
an [`agent.iter()`][pydantic_ai.agent.Agent.iter] run with
[`next()`][pydantic_ai.run.AgentRun.next], but it does **not** fire when
iterating over the run with bare `async for` (which yields stream events, not
node results).

Expand Down Expand Up @@ -419,7 +419,7 @@ async def on_tool_validate_error(

This is the error counterpart to
[`after_tool_validate`][pydantic_ai.capabilities.AbstractCapability.after_tool_validate].
Fires for [`ValidationError`][pydantic.ValidationError] (schema mismatch) and
Fires for `ValidationError` (schema mismatch) and
[`ModelRetry`][pydantic_ai.exceptions.ModelRetry] (custom validator rejection).

**Raise** the original `error` (or a different exception) to propagate it.
Expand Down
2 changes: 1 addition & 1 deletion pydantic_ai_slim/pydantic_ai/capabilities/builtin_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class BuiltinTool(AbstractCapability[AgentDepsT]):
[`AbstractBuiltinTool`][pydantic_ai.builtin_tools.AbstractBuiltinTool] instance or a callable
that dynamically produces one.
When `builtin_tools` is passed to [`Agent.__init__`][pydantic_ai.Agent.__init__], each item is
When `builtin_tools` is passed to [`Agent.__init__`][pydantic_ai.agent.Agent.__init__], each item is
automatically wrapped in a `BuiltinTool` capability.
"""

Expand Down
4 changes: 2 additions & 2 deletions pydantic_ai_slim/pydantic_ai/mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ class MCPServer(AbstractToolset[Any], ABC):

When enabled (default), tools are fetched once and cached until either:
- The server sends a `notifications/tools/list_changed` notification
- [`MCPServer.__aexit__`][pydantic_ai.mcp.MCPServer.__aexit__] is called (when the last context exits)
- `MCPServer.__aexit__` is called (when the last context exits)

Set to `False` for servers that change tools dynamically without sending notifications.

Expand All @@ -354,7 +354,7 @@ class MCPServer(AbstractToolset[Any], ABC):

When enabled (default), resources are fetched once and cached until either:
- The server sends a `notifications/resources/list_changed` notification
- [`MCPServer.__aexit__`][pydantic_ai.mcp.MCPServer.__aexit__] is called (when the last context exits)
- `MCPServer.__aexit__` is called (when the last context exits)

Set to `False` for servers that change resources dynamically without sending notifications.
"""
Expand Down
2 changes: 1 addition & 1 deletion pydantic_ai_slim/pydantic_ai/profiles/grok.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class GrokModelProfile(ModelProfile):
"""Whether the model supports builtin tools (web_search, code_execution, mcp)."""

grok_supports_tool_choice_required: bool = True
"""Whether the provider accepts the value ``tool_choice='required'`` in the request payload."""
"""Whether the provider accepts the value `tool_choice='required'` in the request payload."""


def grok_model_profile(model_name: str) -> ModelProfile | None:
Expand Down
Loading