Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
57e568b
Add `builtin_tools` to `Agent`
Kludex May 14, 2025
97ab44b
make AbstractBuiltinTool serializable
Kludex May 14, 2025
e3dda9d
Add more work on it
Kludex May 14, 2025
3ad6d38
Merge remote-tracking branch 'origin/main' into add-builtin-tools
Kludex May 23, 2025
0b43f65
Add builtin tools
Kludex May 23, 2025
fa7fd11
merge
Kludex May 26, 2025
32324fa
add more built-in-tools
Kludex May 27, 2025
f33e568
Fix test
Kludex May 27, 2025
13d7433
Add support on Groq
Kludex May 27, 2025
ac85205
Add support for Google
Kludex May 28, 2025
c93633f
Add support for MCP's Streamable HTTP transport (#1716)
BrandonShar May 26, 2025
3a8b640
Timeout for initializing MCP client (#1833)
alexmojaki May 27, 2025
360de87
Require mcp 1.9.0+ (#1840)
DouweM May 27, 2025
cb4e539
Don't send empty messages to Anthropic (#1027)
oscar-broman May 27, 2025
4e3769a
Add `vendor_id` and `finish_reason` to Gemini/Google model responses …
davide-andreoli May 27, 2025
ebb536f
Fix units of `sse_read_timeout` `timedelta` (#1843)
alexmojaki May 27, 2025
c8bb611
Support functions as output_type, as well as lists of functions and o…
DouweM May 27, 2025
6bcc1a8
Enhance Gemini usage tracking to collect comprehensive token data (#1…
kiqaps May 28, 2025
97ff651
more
Kludex May 30, 2025
1d47e1e
merge
Kludex May 30, 2025
5f89444
merge
Kludex Jun 1, 2025
9512987
merge
Kludex Jun 20, 2025
800a71a
Pass tests
Kludex Jun 20, 2025
d0f4643
Merge main into builtin-tool branch and resolve conflicts
mattbrandman Jul 1, 2025
bc298d6
Fix remaining merge conflict markers in openai.py, anthropic.py, and …
mattbrandman Jul 1, 2025
46c06c2
add extra google
mattbrandman Jul 1, 2025
3496567
fix formatting
mattbrandman Jul 1, 2025
c193059
fix codespell
mattbrandman Jul 1, 2025
427dec2
fixing types
mattbrandman Jul 1, 2025
866ad21
fixing types in gemini
mattbrandman Jul 1, 2025
4c2622d
misspells are on purpose oops
mattbrandman Jul 1, 2025
c13736e
ignore misspellings
mattbrandman Jul 1, 2025
a42a75d
ignore misspellings
mattbrandman Jul 1, 2025
e2f1daa
fixing comment
mattbrandman Jul 1, 2025
3094a9a
fixing tests and coverage
mattbrandman Jul 1, 2025
6a3c987
fixing types
mattbrandman Jul 1, 2025
ac0edb6
Revert "ignore misspellings"
mattbrandman Jul 2, 2025
374e034
revert to known good
mattbrandman Jul 2, 2025
7cccdd2
merging main
mattbrandman Jul 2, 2025
2393c87
adding anthropic test coverage
mattbrandman Jul 2, 2025
21094a7
adding pragma no cover to has_content
mattbrandman Jul 2, 2025
8ac5294
Merge branch 'main' into builtin-tool
mattbrandman Jul 5, 2025
de8f32d
Merge branch 'main' into builtin-tool
mattbrandman Jul 7, 2025
13bc865
Merge branch 'main' into builtin-tool
Kludex Jul 16, 2025
c0fc35c
Support WebSearch streaming for OpenAI
Kludex Jul 16, 2025
b4b3752
Handle Anthropic streaming
Kludex Jul 16, 2025
3a2481a
Drop extra from dependencies on test
Kludex Jul 16, 2025
306514e
move import up
Kludex Jul 16, 2025
c84be82
add coverage-n
Kludex Jul 16, 2025
a77030b
add coverage-n
Kludex Jul 16, 2025
93e26d6
drop pragma
Kludex Jul 16, 2025
bfb4eef
Update pydantic_ai_slim/pydantic_ai/builtin_tools.py
Kludex Jul 16, 2025
4376fc2
Apply suggestions from code review
mattbrandman Jul 18, 2025
c029d4b
Merge branch 'main' into builtin-tool
mattbrandman Jul 21, 2025
87f59f1
updates types
mattbrandman Jul 21, 2025
3fca6d4
updating types
mattbrandman Jul 21, 2025
37a1845
fixing event
mattbrandman Jul 21, 2025
5b031d6
Merge branch 'main' into builtin-tool
mattbrandman Jul 21, 2025
e1085fa
updates
mattbrandman Jul 21, 2025
ae1d4d9
Merge branch 'main' into builtin-tool
mattbrandman Jul 21, 2025
3ee4aa9
Merge branch 'main' into builtin-tool
mattbrandman Jul 21, 2025
9d24f5b
Merge branch 'main' into builtin-tool
mattbrandman Jul 21, 2025
07f7892
Merge branch 'main' into builtin-tool
mattbrandman Jul 22, 2025
7d867ee
Merge branch 'main' into builtin-tool
mattbrandman Jul 23, 2025
ba3f6b2
Update pydantic_ai_slim/pydantic_ai/agent.py
Kludex Jul 23, 2025
1ae6155
addressing comments
mattbrandman Jul 26, 2025
3f8cc2d
Merge branch 'main' into builtin-tool
mattbrandman Jul 26, 2025
ba962a7
updates
mattbrandman Jul 26, 2025
bfd9b21
fixing lint
mattbrandman Jul 26, 2025
e2fef42
fixing lint
mattbrandman Jul 26, 2025
b1ef2f8
fix misspellings
mattbrandman Jul 26, 2025
e9cacc3
rename servertool to builtin tool everywhere
mattbrandman Jul 26, 2025
aa91640
fixing ordering
mattbrandman Jul 28, 2025
2d451a7
Merge branch 'main' into builtin-tool
mattbrandman Jul 28, 2025
38b5b7d
addressing comments
mattbrandman Aug 6, 2025
359b9e1
Merge upstream/main into builtin-tool branch - resolved conflicts
mattbrandman Aug 6, 2025
bd6e5f7
Fix failing tests after merge - update snapshots for builtin tools
mattbrandman Aug 6, 2025
69267ca
fixing types
mattbrandman Aug 6, 2025
1a4a0f3
Merge remote-tracking branch 'origin/main' into builtin-tool
Kludex Aug 7, 2025
c9d0d18
Remove deprecated stuff from yesterday
Kludex Aug 7, 2025
a631229
Merge branch 'main' into builtin-tool
Kludex Aug 7, 2025
78fe612
Simplify cohere
Kludex Aug 7, 2025
a673c90
refactor a bit
Kludex Aug 7, 2025
df2ad47
Handle Google builtin tools
Kludex Aug 7, 2025
b4add90
Add test for unsupported builtin tools
Kludex Aug 7, 2025
dec1ba3
Add 100% coverage
Kludex Aug 7, 2025
e134d43
fix linting issue
Kludex Aug 7, 2025
ef4ab6a
Add API documentation for builtin tools
Kludex Aug 7, 2025
bb98ccf
Add documentation
Kludex Aug 7, 2025
739f142
pass docs tests
Kludex Aug 7, 2025
cc7ab39
Add Groq model profile for compound models that always have web searc…
DouweM Aug 7, 2025
5534bf4
add pragma
Kludex Aug 7, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ node_modules/
**.idea/
.coverage*
/test_tmp/
.mcp.json
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For curiosity, why MCP servers did you use here?

3 changes: 3 additions & 0 deletions docs/api/builtin_tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `pydantic_ai.builtin_tools`

::: pydantic_ai.builtin_tools
123 changes: 123 additions & 0 deletions docs/builtin-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Builtin Tools

Builtin tools are native tools provided by LLM providers that can be used to enhance your agent's capabilities. Unlike [common tools](common-tools.md), which are custom implementations that PydanticAI executes, builtin tools are executed directly by the model provider.

## Overview

PydanticAI supports two types of builtin tools:

- **[`WebSearchTool`][pydantic_ai.builtin_tools.WebSearchTool]**: Allows agents to search the web
- **[`CodeExecutionTool`][pydantic_ai.builtin_tools.CodeExecutionTool]**: Enables agents to execute code in a secure environment

These tools are passed to the agent via the `builtin_tools` parameter and are executed by the model provider's infrastructure.

!!! warning "Provider Support"
Not all model providers support builtin tools. If you use a builtin tool with an unsupported provider, PydanticAI will raise a [`UserError`][pydantic_ai.exceptions.UserError] when you try to run the agent.

## Web Search Tool

The [`WebSearchTool`][pydantic_ai.builtin_tools.WebSearchTool] allows your agent to search the web,
making it ideal for queries that require up-to-date data.

### Provider Support

| Provider | Supported | Notes |
|----------|-----------|-------|
| OpenAI | ✅ | Full feature support |
| Anthropic | ✅ | Full feature support |
| Groq | ✅ | Limited parameter support |
| Google | ❌ | Not supported |
| Bedrock | ❌ | Not supported |
| Mistral | ❌ | Not supported |
| Cohere | ❌ | Not supported |
| HuggingFace | ❌ | Not supported |

!!! note "Groq Support"
To use web search capabilities with Groq, you need to use the [compound models](https://console.groq.com/docs/compound).

### Usage

```py title="web_search_basic.py"
from pydantic_ai import Agent, WebSearchTool

agent = Agent('anthropic:claude-sonnet-4-0', builtin_tools=[WebSearchTool()])

result = agent.run_sync('Give me a sentence with the biggest news in AI this week.')
# > Scientists have developed a universal AI detector that can identify deepfake videos.

```

### Configuration Options

The `WebSearchTool` supports several configuration parameters:

```py title="web_search_configured.py"
from pydantic_ai import Agent, WebSearchTool, WebSearchUserLocation

agent = Agent(
'anthropic:claude-sonnet-4-0',
builtin_tools=[
WebSearchTool(
search_context_size='high',
user_location=WebSearchUserLocation(
city='San Francisco',
country='US',
region='CA',
timezone='America/Los_Angeles',
),
blocked_domains=['example.com', 'spam-site.net'],
allowed_domains=None, # Cannot use both blocked_domains and allowed_domains with Anthropic
max_uses=5, # Anthropic only: limit tool usage
)
],
)

result = agent.run_sync('Use the web to get the current time.')
# > In San Francisco, it's 8:21:41 pm PDT on Wednesday, August 6, 2025.
```

### Parameter Support by Provider

| Parameter | OpenAI | Anthropic | Groq |
|-----------|--------|-----------|------|
| `search_context_size` | ✅ | ❌ | ❌ |
| `user_location` | ✅ | ✅ | ❌ |
| `blocked_domains` | ❌ | ✅ | ✅ |
| `allowed_domains` | ❌ | ✅ | ✅ |
| `max_uses` | ❌ | ✅ | ❌ |

!!! note "Anthropic Domain Filtering"
With Anthropic, you can only use either `blocked_domains` or `allowed_domains`, not both.

## Code Execution Tool

The [`CodeExecutionTool`][pydantic_ai.builtin_tools.CodeExecutionTool] enables your agent to execute code
in a secure environment, making it perfect for computational tasks, data analysis, and mathematical operations.

### Provider Support

| Provider | Supported |
|----------|-----------|
| OpenAI | ✅ |
| Anthropic | ✅ |
| Google | ✅ |
| Groq | ❌ |
| Bedrock | ❌ |
| Mistral | ❌ |
| Cohere | ❌ |
| HuggingFace | ❌ |

### Usage

```py title="code_execution_basic.py"
from pydantic_ai import Agent, CodeExecutionTool

agent = Agent('anthropic:claude-sonnet-4-0', builtin_tools=[CodeExecutionTool()])

result = agent.run_sync('Calculate the factorial of 15 and show your work')
# > The factorial of 15 is **1,307,674,368,000**.
```

## API Reference

For complete API documentation, see the [API Reference](api/builtin_tools.md).
34 changes: 18 additions & 16 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ nav:
- input.md
- thinking.md
- direct.md
- builtin-tools.md
- common-tools.md
- retries.md
- MCP:
Expand Down Expand Up @@ -72,6 +73,7 @@ nav:
- api/agent.md
- api/tools.md
- api/toolsets.md
- api/builtin_tools.md
- api/common_tools.md
- api/output.md
- api/result.md
Expand Down Expand Up @@ -118,29 +120,29 @@ extra:
generator: false

theme:
name: 'material'
name: "material"
custom_dir: docs/.overrides
palette:
- media: '(prefers-color-scheme)'
- media: "(prefers-color-scheme)"
primary: pink
accent: pink
toggle:
icon: material/brightness-auto
name: 'Switch to light mode'
- media: '(prefers-color-scheme: light)'
name: "Switch to light mode"
- media: "(prefers-color-scheme: light)"
scheme: default
primary: pink
accent: pink
toggle:
icon: material/brightness-7
name: 'Switch to dark mode'
- media: '(prefers-color-scheme: dark)'
name: "Switch to dark mode"
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: pink
accent: pink
toggle:
icon: material/brightness-4
name: 'Switch to system preference'
name: "Switch to system preference"
features:
- search.suggest
- search.highlight
Expand All @@ -153,8 +155,8 @@ theme:
- navigation.sections
- navigation.tracking
- toc.follow
logo: 'img/logo-white.svg'
favicon: 'favicon.ico'
logo: "img/logo-white.svg"
favicon: "favicon.ico"

# https://www.mkdocs.org/user-guide/configuration/#validation
validation:
Expand All @@ -164,13 +166,13 @@ validation:
anchors: warn

extra_css:
- 'extra/tweaks.css'
- "extra/tweaks.css"
# used for analytics
extra_javascript:
- '/flarelytics/client.js'
- 'https://cdn.jsdelivr.net/npm/[email protected]/dist/lite/builds/browser.umd.js'
- 'https://cdn.jsdelivr.net/npm/[email protected]/dist/instantsearch.production.min.js'
- '/javascripts/algolia-search.js'
- "/flarelytics/client.js"
- "https://cdn.jsdelivr.net/npm/[email protected]/dist/lite/builds/browser.umd.js"
- "https://cdn.jsdelivr.net/npm/[email protected]/dist/instantsearch.production.min.js"
- "/javascripts/algolia-search.js"

markdown_extensions:
- tables
Expand Down Expand Up @@ -272,5 +274,5 @@ plugins:
- examples/*.md

hooks:
- 'docs/.hooks/main.py'
- 'docs/.hooks/algolia.py'
- "docs/.hooks/main.py"
- "docs/.hooks/algolia.py"
5 changes: 5 additions & 0 deletions pydantic_ai_slim/pydantic_ai/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from importlib.metadata import version as _metadata_version

from .agent import Agent, CallToolsNode, EndStrategy, ModelRequestNode, UserPromptNode, capture_run_messages
from .builtin_tools import CodeExecutionTool, WebSearchTool, WebSearchUserLocation
from .exceptions import (
AgentRunError,
FallbackExceptionGroup,
Expand Down Expand Up @@ -41,6 +42,10 @@
# tools
'Tool',
'RunContext',
# builtin_tools
'WebSearchTool',
'WebSearchUserLocation',
'CodeExecutionTool',
# output
'ToolOutput',
'NativeOutput',
Expand Down
7 changes: 7 additions & 0 deletions pydantic_ai_slim/pydantic_ai/_agent_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from pydantic_ai._function_schema import _takes_ctx as is_takes_ctx # type: ignore
from pydantic_ai._tool_manager import ToolManager
from pydantic_ai._utils import is_async_callable, run_in_executor
from pydantic_ai.builtin_tools import AbstractBuiltinTool
from pydantic_graph import BaseNode, Graph, GraphRunContext
from pydantic_graph.nodes import End, NodeRunEndT

Expand Down Expand Up @@ -112,6 +113,7 @@ class GraphAgentDeps(Generic[DepsT, OutputDataT]):

history_processors: Sequence[HistoryProcessor[DepsT]]

builtin_tools: list[AbstractBuiltinTool] = dataclasses.field(repr=False)
tool_manager: ToolManager[DepsT]

tracer: Tracer
Expand Down Expand Up @@ -269,6 +271,7 @@ async def _prepare_request_parameters(

return models.ModelRequestParameters(
function_tools=function_tools,
builtin_tools=ctx.deps.builtin_tools,
output_mode=output_schema.mode,
output_tools=output_tools,
output_object=output_object,
Expand Down Expand Up @@ -443,6 +446,10 @@ async def _run_stream() -> AsyncIterator[_messages.HandleResponseEvent]:
texts.append(part.content)
elif isinstance(part, _messages.ToolCallPart):
tool_calls.append(part)
elif isinstance(part, _messages.BuiltinToolCallPart):
yield _messages.BuiltinToolCallEvent(part)
elif isinstance(part, _messages.BuiltinToolReturnPart):
yield _messages.BuiltinToolResultEvent(part)
elif isinstance(part, _messages.ThinkingPart):
# We don't need to do anything with thinking parts in this tool-calling node.
# We need to handle text parts in case there are no tool calls and/or the desired output comes
Expand Down
8 changes: 7 additions & 1 deletion pydantic_ai_slim/pydantic_ai/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,13 @@ def now_utc() -> datetime:
return datetime.now(tz=timezone.utc)


def guard_tool_call_id(t: _messages.ToolCallPart | _messages.ToolReturnPart | _messages.RetryPromptPart) -> str:
def guard_tool_call_id(
t: _messages.ToolCallPart
| _messages.ToolReturnPart
| _messages.RetryPromptPart
| _messages.BuiltinToolCallPart
| _messages.BuiltinToolReturnPart,
) -> str:
"""Type guard that either returns the tool call id or generates a new one if it's None."""
return t.tool_call_id or generate_tool_call_id()

Expand Down
9 changes: 9 additions & 0 deletions pydantic_ai_slim/pydantic_ai/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from pydantic.json_schema import GenerateJsonSchema
from typing_extensions import Literal, Never, Self, TypeIs, TypeVar, deprecated

from pydantic_ai.builtin_tools import AbstractBuiltinTool
from pydantic_graph import End, Graph, GraphRun, GraphRunContext
from pydantic_graph._utils import get_event_loop

Expand Down Expand Up @@ -188,6 +189,7 @@ def __init__(
retries: int = 1,
output_retries: int | None = None,
tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (),
builtin_tools: Sequence[AbstractBuiltinTool] = (),
prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
Expand Down Expand Up @@ -215,6 +217,7 @@ def __init__(
retries: int = 1,
output_retries: int | None = None,
tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (),
builtin_tools: Sequence[AbstractBuiltinTool] = (),
prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
mcp_servers: Sequence[MCPServer] = (),
Expand All @@ -240,6 +243,7 @@ def __init__(
retries: int = 1,
output_retries: int | None = None,
tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (),
builtin_tools: Sequence[AbstractBuiltinTool] = (),
prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
Expand Down Expand Up @@ -271,6 +275,8 @@ def __init__(
output_retries: The maximum number of retries to allow for output validation, defaults to `retries`.
tools: Tools to register with the agent, you can also register tools via the decorators
[`@agent.tool`][pydantic_ai.Agent.tool] and [`@agent.tool_plain`][pydantic_ai.Agent.tool_plain].
builtin_tools: The builtin tools that the agent will use. This depends on the model, as some models may not
support certain tools. If the model doesn't support the builtin tools, an error will be raised.
prepare_tools: Custom function to prepare the tool definition of all tools for each step, except output tools.
This is useful if you want to customize the definition of multiple tools or you want to register
a subset of tools for a given step. See [`ToolsPrepareFunc`][pydantic_ai.tools.ToolsPrepareFunc]
Expand Down Expand Up @@ -340,6 +346,8 @@ def __init__(
self._system_prompt_dynamic_functions = {}

self._max_result_retries = output_retries if output_retries is not None else retries
self._builtin_tools = builtin_tools

self._prepare_tools = prepare_tools
self._prepare_output_tools = prepare_output_tools

Expand Down Expand Up @@ -700,6 +708,7 @@ async def get_instructions(run_context: RunContext[AgentDepsT]) -> str | None:
output_schema=output_schema,
output_validators=output_validators,
history_processors=self.history_processors,
builtin_tools=list(self._builtin_tools),
tool_manager=run_toolset,
tracer=tracer,
get_instructions=get_instructions,
Expand Down
Loading