pydantic-ai-slim 0.6.1__tar.gz → 0.6.2__tar.gz

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.
Files changed (102) hide show
  1. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/.gitignore +1 -0
  2. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/PKG-INFO +4 -4
  3. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/__init__.py +5 -0
  4. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_agent_graph.py +7 -0
  5. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_utils.py +7 -1
  6. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/agent.py +9 -0
  7. pydantic_ai_slim-0.6.2/pydantic_ai/builtin_tools.py +105 -0
  8. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/messages.py +73 -11
  9. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/__init__.py +2 -0
  10. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/anthropic.py +120 -12
  11. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/bedrock.py +8 -0
  12. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/cohere.py +10 -0
  13. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/function.py +9 -0
  14. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/gemini.py +5 -0
  15. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/google.py +36 -2
  16. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/groq.py +36 -3
  17. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/huggingface.py +9 -0
  18. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/mistral.py +16 -0
  19. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/openai.py +73 -12
  20. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/test.py +9 -0
  21. pydantic_ai_slim-0.6.2/pydantic_ai/profiles/groq.py +23 -0
  22. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/groq.py +2 -0
  23. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pyproject.toml +1 -1
  24. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/LICENSE +0 -0
  25. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/README.md +0 -0
  26. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/__main__.py +0 -0
  27. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_a2a.py +0 -0
  28. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_cli.py +0 -0
  29. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_function_schema.py +0 -0
  30. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_griffe.py +0 -0
  31. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_mcp.py +0 -0
  32. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_output.py +0 -0
  33. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_parts_manager.py +0 -0
  34. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_run_context.py +0 -0
  35. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_system_prompt.py +0 -0
  36. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_thinking_part.py +0 -0
  37. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/_tool_manager.py +0 -0
  38. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/ag_ui.py +0 -0
  39. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/common_tools/__init__.py +0 -0
  40. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/common_tools/duckduckgo.py +0 -0
  41. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/common_tools/tavily.py +0 -0
  42. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/direct.py +0 -0
  43. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/exceptions.py +0 -0
  44. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/ext/__init__.py +0 -0
  45. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/ext/aci.py +0 -0
  46. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/ext/langchain.py +0 -0
  47. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/format_prompt.py +0 -0
  48. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/mcp.py +0 -0
  49. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/fallback.py +0 -0
  50. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/instrumented.py +0 -0
  51. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/mcp_sampling.py +0 -0
  52. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/models/wrapper.py +0 -0
  53. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/output.py +0 -0
  54. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/__init__.py +0 -0
  55. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/_json_schema.py +0 -0
  56. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/amazon.py +0 -0
  57. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/anthropic.py +0 -0
  58. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/cohere.py +0 -0
  59. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/deepseek.py +0 -0
  60. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/google.py +0 -0
  61. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/grok.py +0 -0
  62. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/meta.py +0 -0
  63. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/mistral.py +0 -0
  64. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/moonshotai.py +0 -0
  65. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/openai.py +0 -0
  66. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/profiles/qwen.py +0 -0
  67. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/__init__.py +0 -0
  68. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/anthropic.py +0 -0
  69. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/azure.py +0 -0
  70. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/bedrock.py +0 -0
  71. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/cohere.py +0 -0
  72. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/deepseek.py +0 -0
  73. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/fireworks.py +0 -0
  74. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/github.py +0 -0
  75. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/google.py +0 -0
  76. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/google_gla.py +0 -0
  77. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/google_vertex.py +0 -0
  78. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/grok.py +0 -0
  79. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/heroku.py +0 -0
  80. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/huggingface.py +0 -0
  81. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/mistral.py +0 -0
  82. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/moonshotai.py +0 -0
  83. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/openai.py +0 -0
  84. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/openrouter.py +0 -0
  85. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/together.py +0 -0
  86. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/providers/vercel.py +0 -0
  87. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/py.typed +0 -0
  88. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/result.py +0 -0
  89. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/retries.py +0 -0
  90. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/settings.py +0 -0
  91. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/tools.py +0 -0
  92. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/toolsets/__init__.py +0 -0
  93. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/toolsets/abstract.py +0 -0
  94. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/toolsets/combined.py +0 -0
  95. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/toolsets/deferred.py +0 -0
  96. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/toolsets/filtered.py +0 -0
  97. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/toolsets/function.py +0 -0
  98. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/toolsets/prefixed.py +0 -0
  99. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/toolsets/prepared.py +0 -0
  100. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/toolsets/renamed.py +0 -0
  101. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/toolsets/wrapper.py +0 -0
  102. {pydantic_ai_slim-0.6.1 → pydantic_ai_slim-0.6.2}/pydantic_ai/usage.py +0 -0
@@ -19,3 +19,4 @@ node_modules/
19
19
  **.idea/
20
20
  .coverage*
21
21
  /test_tmp/
22
+ .mcp.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-ai-slim
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: Agent Framework / shim to use Pydantic with LLMs, slim package
5
5
  Author-email: Samuel Colvin <samuel@pydantic.dev>, Marcelo Trylesinski <marcelotryle@gmail.com>, David Montague <david@pydantic.dev>, Alex Hall <alex@pydantic.dev>, Douwe Maan <douwe@pydantic.dev>
6
6
  License-Expression: MIT
@@ -30,7 +30,7 @@ Requires-Dist: exceptiongroup; python_version < '3.11'
30
30
  Requires-Dist: griffe>=1.3.2
31
31
  Requires-Dist: httpx>=0.27
32
32
  Requires-Dist: opentelemetry-api>=1.28.0
33
- Requires-Dist: pydantic-graph==0.6.1
33
+ Requires-Dist: pydantic-graph==0.6.2
34
34
  Requires-Dist: pydantic>=2.10
35
35
  Requires-Dist: typing-inspection>=0.4.0
36
36
  Provides-Extra: a2a
@@ -51,11 +51,11 @@ Requires-Dist: cohere>=5.16.0; (platform_system != 'Emscripten') and extra == 'c
51
51
  Provides-Extra: duckduckgo
52
52
  Requires-Dist: ddgs>=9.0.0; extra == 'duckduckgo'
53
53
  Provides-Extra: evals
54
- Requires-Dist: pydantic-evals==0.6.1; extra == 'evals'
54
+ Requires-Dist: pydantic-evals==0.6.2; extra == 'evals'
55
55
  Provides-Extra: google
56
56
  Requires-Dist: google-genai>=1.28.0; extra == 'google'
57
57
  Provides-Extra: groq
58
- Requires-Dist: groq>=0.19.0; extra == 'groq'
58
+ Requires-Dist: groq>=0.25.0; extra == 'groq'
59
59
  Provides-Extra: huggingface
60
60
  Requires-Dist: huggingface-hub[inference]>=0.33.5; extra == 'huggingface'
61
61
  Provides-Extra: logfire
@@ -1,6 +1,7 @@
1
1
  from importlib.metadata import version as _metadata_version
2
2
 
3
3
  from .agent import Agent, CallToolsNode, EndStrategy, ModelRequestNode, UserPromptNode, capture_run_messages
4
+ from .builtin_tools import CodeExecutionTool, WebSearchTool, WebSearchUserLocation
4
5
  from .exceptions import (
5
6
  AgentRunError,
6
7
  FallbackExceptionGroup,
@@ -41,6 +42,10 @@ __all__ = (
41
42
  # tools
42
43
  'Tool',
43
44
  'RunContext',
45
+ # builtin_tools
46
+ 'WebSearchTool',
47
+ 'WebSearchUserLocation',
48
+ 'CodeExecutionTool',
44
49
  # output
45
50
  'ToolOutput',
46
51
  'NativeOutput',
@@ -16,6 +16,7 @@ from typing_extensions import TypeGuard, TypeVar, assert_never
16
16
  from pydantic_ai._function_schema import _takes_ctx as is_takes_ctx # type: ignore
17
17
  from pydantic_ai._tool_manager import ToolManager
18
18
  from pydantic_ai._utils import is_async_callable, run_in_executor
19
+ from pydantic_ai.builtin_tools import AbstractBuiltinTool
19
20
  from pydantic_graph import BaseNode, Graph, GraphRunContext
20
21
  from pydantic_graph.nodes import End, NodeRunEndT
21
22
 
@@ -112,6 +113,7 @@ class GraphAgentDeps(Generic[DepsT, OutputDataT]):
112
113
 
113
114
  history_processors: Sequence[HistoryProcessor[DepsT]]
114
115
 
116
+ builtin_tools: list[AbstractBuiltinTool] = dataclasses.field(repr=False)
115
117
  tool_manager: ToolManager[DepsT]
116
118
 
117
119
  tracer: Tracer
@@ -269,6 +271,7 @@ async def _prepare_request_parameters(
269
271
 
270
272
  return models.ModelRequestParameters(
271
273
  function_tools=function_tools,
274
+ builtin_tools=ctx.deps.builtin_tools,
272
275
  output_mode=output_schema.mode,
273
276
  output_tools=output_tools,
274
277
  output_object=output_object,
@@ -443,6 +446,10 @@ class CallToolsNode(AgentNode[DepsT, NodeRunEndT]):
443
446
  texts.append(part.content)
444
447
  elif isinstance(part, _messages.ToolCallPart):
445
448
  tool_calls.append(part)
449
+ elif isinstance(part, _messages.BuiltinToolCallPart):
450
+ yield _messages.BuiltinToolCallEvent(part)
451
+ elif isinstance(part, _messages.BuiltinToolReturnPart):
452
+ yield _messages.BuiltinToolResultEvent(part)
446
453
  elif isinstance(part, _messages.ThinkingPart):
447
454
  # We don't need to do anything with thinking parts in this tool-calling node.
448
455
  # We need to handle text parts in case there are no tool calls and/or the desired output comes
@@ -227,7 +227,13 @@ def now_utc() -> datetime:
227
227
  return datetime.now(tz=timezone.utc)
228
228
 
229
229
 
230
- def guard_tool_call_id(t: _messages.ToolCallPart | _messages.ToolReturnPart | _messages.RetryPromptPart) -> str:
230
+ def guard_tool_call_id(
231
+ t: _messages.ToolCallPart
232
+ | _messages.ToolReturnPart
233
+ | _messages.RetryPromptPart
234
+ | _messages.BuiltinToolCallPart
235
+ | _messages.BuiltinToolReturnPart,
236
+ ) -> str:
231
237
  """Type guard that either returns the tool call id or generates a new one if it's None."""
232
238
  return t.tool_call_id or generate_tool_call_id()
233
239
 
@@ -16,6 +16,7 @@ from opentelemetry.trace import NoOpTracer, use_span
16
16
  from pydantic.json_schema import GenerateJsonSchema
17
17
  from typing_extensions import Literal, Never, Self, TypeIs, TypeVar, deprecated
18
18
 
19
+ from pydantic_ai.builtin_tools import AbstractBuiltinTool
19
20
  from pydantic_graph import End, Graph, GraphRun, GraphRunContext
20
21
  from pydantic_graph._utils import get_event_loop
21
22
 
@@ -188,6 +189,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
188
189
  retries: int = 1,
189
190
  output_retries: int | None = None,
190
191
  tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (),
192
+ builtin_tools: Sequence[AbstractBuiltinTool] = (),
191
193
  prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
192
194
  prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
193
195
  toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
@@ -215,6 +217,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
215
217
  retries: int = 1,
216
218
  output_retries: int | None = None,
217
219
  tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (),
220
+ builtin_tools: Sequence[AbstractBuiltinTool] = (),
218
221
  prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
219
222
  prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
220
223
  mcp_servers: Sequence[MCPServer] = (),
@@ -240,6 +243,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
240
243
  retries: int = 1,
241
244
  output_retries: int | None = None,
242
245
  tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (),
246
+ builtin_tools: Sequence[AbstractBuiltinTool] = (),
243
247
  prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
244
248
  prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None,
245
249
  toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
@@ -271,6 +275,8 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
271
275
  output_retries: The maximum number of retries to allow for output validation, defaults to `retries`.
272
276
  tools: Tools to register with the agent, you can also register tools via the decorators
273
277
  [`@agent.tool`][pydantic_ai.Agent.tool] and [`@agent.tool_plain`][pydantic_ai.Agent.tool_plain].
278
+ builtin_tools: The builtin tools that the agent will use. This depends on the model, as some models may not
279
+ support certain tools. If the model doesn't support the builtin tools, an error will be raised.
274
280
  prepare_tools: Custom function to prepare the tool definition of all tools for each step, except output tools.
275
281
  This is useful if you want to customize the definition of multiple tools or you want to register
276
282
  a subset of tools for a given step. See [`ToolsPrepareFunc`][pydantic_ai.tools.ToolsPrepareFunc]
@@ -340,6 +346,8 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
340
346
  self._system_prompt_dynamic_functions = {}
341
347
 
342
348
  self._max_result_retries = output_retries if output_retries is not None else retries
349
+ self._builtin_tools = builtin_tools
350
+
343
351
  self._prepare_tools = prepare_tools
344
352
  self._prepare_output_tools = prepare_output_tools
345
353
 
@@ -700,6 +708,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
700
708
  output_schema=output_schema,
701
709
  output_validators=output_validators,
702
710
  history_processors=self.history_processors,
711
+ builtin_tools=list(self._builtin_tools),
703
712
  tool_manager=run_toolset,
704
713
  tracer=tracer,
705
714
  get_instructions=get_instructions,
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations as _annotations
2
+
3
+ from abc import ABC
4
+ from dataclasses import dataclass
5
+ from typing import Literal
6
+
7
+ from typing_extensions import TypedDict
8
+
9
+ __all__ = ('AbstractBuiltinTool', 'WebSearchTool', 'WebSearchUserLocation', 'CodeExecutionTool')
10
+
11
+
12
+ @dataclass
13
+ class AbstractBuiltinTool(ABC):
14
+ """A builtin tool that can be used by an agent.
15
+
16
+ This class is abstract and cannot be instantiated directly.
17
+
18
+ The builtin tools are passed to the model as part of the `ModelRequestParameters`.
19
+ """
20
+
21
+
22
+ @dataclass
23
+ class WebSearchTool(AbstractBuiltinTool):
24
+ """A builtin tool that allows your agent to search the web for information.
25
+
26
+ The parameters that PydanticAI passes depend on the model, as some parameters may not be supported by certain models.
27
+
28
+ Supported by:
29
+ * Anthropic
30
+ * OpenAI
31
+ * Groq
32
+ """
33
+
34
+ search_context_size: Literal['low', 'medium', 'high'] = 'medium'
35
+ """The `search_context_size` parameter controls how much context is retrieved from the web to help the tool formulate a response.
36
+
37
+ Supported by:
38
+ * OpenAI
39
+ """
40
+
41
+ user_location: WebSearchUserLocation | None = None
42
+ """The `user_location` parameter allows you to localize search results based on a user's location.
43
+
44
+ Supported by:
45
+ * Anthropic
46
+ * OpenAI
47
+ """
48
+
49
+ blocked_domains: list[str] | None = None
50
+ """If provided, these domains will never appear in results.
51
+
52
+ With Anthropic, you can only use one of `blocked_domains` or `allowed_domains`, not both.
53
+
54
+ Supported by:
55
+ * Anthropic (https://docs.anthropic.com/en/docs/build-with-claude/tool-use/web-search-tool#domain-filtering)
56
+ * Groq (https://console.groq.com/docs/agentic-tooling#search-settings)
57
+ """
58
+
59
+ allowed_domains: list[str] | None = None
60
+ """If provided, only these domains will be included in results.
61
+
62
+ With Anthropic, you can only use one of `blocked_domains` or `allowed_domains`, not both.
63
+
64
+ Supported by:
65
+ * Anthropic (https://docs.anthropic.com/en/docs/build-with-claude/tool-use/web-search-tool#domain-filtering)
66
+ * Groq (https://console.groq.com/docs/agentic-tooling#search-settings)
67
+ """
68
+
69
+ max_uses: int | None = None
70
+ """If provided, the tool will stop searching the web after the given number of uses.
71
+
72
+ Supported by:
73
+ * Anthropic
74
+ """
75
+
76
+
77
+ class WebSearchUserLocation(TypedDict, total=False):
78
+ """Allows you to localize search results based on a user's location.
79
+
80
+ Supported by:
81
+ * Anthropic
82
+ * OpenAI
83
+ """
84
+
85
+ city: str
86
+ """The city where the user is located."""
87
+
88
+ country: str
89
+ """The country where the user is located. For OpenAI, this must be a 2-letter country code (e.g., 'US', 'GB')."""
90
+
91
+ region: str
92
+ """The region or state where the user is located."""
93
+
94
+ timezone: str
95
+ """The timezone of the user's location."""
96
+
97
+
98
+ class CodeExecutionTool(AbstractBuiltinTool):
99
+ """A builtin tool that allows your agent to execute code.
100
+
101
+ Supported by:
102
+ * Anthropic
103
+ * OpenAI
104
+ * Google
105
+ """
@@ -532,8 +532,8 @@ tool_return_ta: pydantic.TypeAdapter[Any] = pydantic.TypeAdapter(
532
532
 
533
533
 
534
534
  @dataclass(repr=False)
535
- class ToolReturnPart:
536
- """A tool return message, this encodes the result of running a tool."""
535
+ class BaseToolReturnPart:
536
+ """Base class for tool return parts."""
537
537
 
538
538
  tool_name: str
539
539
  """The name of the "tool" was called."""
@@ -550,9 +550,6 @@ class ToolReturnPart:
550
550
  timestamp: datetime = field(default_factory=_now_utc)
551
551
  """The timestamp, when the tool returned."""
552
552
 
553
- part_kind: Literal['tool-return'] = 'tool-return'
554
- """Part type identifier, this is available on all parts as a discriminator."""
555
-
556
553
  def model_response_str(self) -> str:
557
554
  """Return a string representation of the content for the model."""
558
555
  if isinstance(self.content, str):
@@ -579,9 +576,32 @@ class ToolReturnPart:
579
576
  },
580
577
  )
581
578
 
579
+ def has_content(self) -> bool:
580
+ """Return `True` if the tool return has content."""
581
+ return self.content is not None # pragma: no cover
582
+
582
583
  __repr__ = _utils.dataclasses_no_defaults_repr
583
584
 
584
585
 
586
+ @dataclass(repr=False)
587
+ class ToolReturnPart(BaseToolReturnPart):
588
+ """A tool return message, this encodes the result of running a tool."""
589
+
590
+ part_kind: Literal['tool-return'] = 'tool-return'
591
+ """Part type identifier, this is available on all parts as a discriminator."""
592
+
593
+
594
+ @dataclass(repr=False)
595
+ class BuiltinToolReturnPart(BaseToolReturnPart):
596
+ """A tool return message from a built-in tool."""
597
+
598
+ provider_name: str | None = None
599
+ """The name of the provider that generated the response."""
600
+
601
+ part_kind: Literal['builtin-tool-return'] = 'builtin-tool-return'
602
+ """Part type identifier, this is available on all parts as a discriminator."""
603
+
604
+
585
605
  error_details_ta = pydantic.TypeAdapter(list[pydantic_core.ErrorDetails], config=pydantic.ConfigDict(defer_build=True))
586
606
 
587
607
 
@@ -723,7 +743,7 @@ class ThinkingPart:
723
743
 
724
744
 
725
745
  @dataclass(repr=False)
726
- class ToolCallPart:
746
+ class BaseToolCallPart:
727
747
  """A tool call from a model."""
728
748
 
729
749
  tool_name: str
@@ -741,9 +761,6 @@ class ToolCallPart:
741
761
  In case the tool call id is not provided by the model, Pydantic AI will generate a random one.
742
762
  """
743
763
 
744
- part_kind: Literal['tool-call'] = 'tool-call'
745
- """Part type identifier, this is available on all parts as a discriminator."""
746
-
747
764
  def args_as_dict(self) -> dict[str, Any]:
748
765
  """Return the arguments as a Python dictionary.
749
766
 
@@ -780,7 +797,29 @@ class ToolCallPart:
780
797
  __repr__ = _utils.dataclasses_no_defaults_repr
781
798
 
782
799
 
783
- ModelResponsePart = Annotated[Union[TextPart, ToolCallPart, ThinkingPart], pydantic.Discriminator('part_kind')]
800
+ @dataclass(repr=False)
801
+ class ToolCallPart(BaseToolCallPart):
802
+ """A tool call from a model."""
803
+
804
+ part_kind: Literal['tool-call'] = 'tool-call'
805
+ """Part type identifier, this is available on all parts as a discriminator."""
806
+
807
+
808
+ @dataclass(repr=False)
809
+ class BuiltinToolCallPart(BaseToolCallPart):
810
+ """A tool call to a built-in tool."""
811
+
812
+ provider_name: str | None = None
813
+ """The name of the provider that generated the response."""
814
+
815
+ part_kind: Literal['builtin-tool-call'] = 'builtin-tool-call'
816
+ """Part type identifier, this is available on all parts as a discriminator."""
817
+
818
+
819
+ ModelResponsePart = Annotated[
820
+ Union[TextPart, ToolCallPart, BuiltinToolCallPart, BuiltinToolReturnPart, ThinkingPart],
821
+ pydantic.Discriminator('part_kind'),
822
+ ]
784
823
  """A message part returned by a model."""
785
824
 
786
825
 
@@ -1172,6 +1211,29 @@ class FunctionToolResultEvent:
1172
1211
  __repr__ = _utils.dataclasses_no_defaults_repr
1173
1212
 
1174
1213
 
1214
+ @dataclass(repr=False)
1215
+ class BuiltinToolCallEvent:
1216
+ """An event indicating the start to a call to a built-in tool."""
1217
+
1218
+ part: BuiltinToolCallPart
1219
+ """The built-in tool call to make."""
1220
+
1221
+ event_kind: Literal['builtin_tool_call'] = 'builtin_tool_call'
1222
+ """Event type identifier, used as a discriminator."""
1223
+
1224
+
1225
+ @dataclass(repr=False)
1226
+ class BuiltinToolResultEvent:
1227
+ """An event indicating the result of a built-in tool call."""
1228
+
1229
+ result: BuiltinToolReturnPart
1230
+ """The result of the call to the built-in tool."""
1231
+
1232
+ event_kind: Literal['builtin_tool_result'] = 'builtin_tool_result'
1233
+ """Event type identifier, used as a discriminator."""
1234
+
1235
+
1175
1236
  HandleResponseEvent = Annotated[
1176
- Union[FunctionToolCallEvent, FunctionToolResultEvent], pydantic.Discriminator('event_kind')
1237
+ Union[FunctionToolCallEvent, FunctionToolResultEvent, BuiltinToolCallEvent, BuiltinToolResultEvent],
1238
+ pydantic.Discriminator('event_kind'),
1177
1239
  ]
@@ -18,6 +18,7 @@ from typing import Generic, TypeVar, overload
18
18
  import httpx
19
19
  from typing_extensions import Literal, TypeAliasType, TypedDict
20
20
 
21
+ from pydantic_ai.builtin_tools import AbstractBuiltinTool
21
22
  from pydantic_ai.profiles import DEFAULT_PROFILE, ModelProfile, ModelProfileSpec
22
23
 
23
24
  from .. import _utils
@@ -336,6 +337,7 @@ class ModelRequestParameters:
336
337
  """Configuration for an agent's request to a model, specifically related to tools and output handling."""
337
338
 
338
339
  function_tools: list[ToolDefinition] = field(default_factory=list)
340
+ builtin_tools: list[AbstractBuiltinTool] = field(default_factory=list)
339
341
 
340
342
  output_mode: OutputMode = 'text'
341
343
  output_object: OutputObjectDefinition | None = None
@@ -8,12 +8,24 @@ from dataclasses import dataclass, field
8
8
  from datetime import datetime, timezone
9
9
  from typing import Any, Literal, Union, cast, overload
10
10
 
11
+ from anthropic.types.beta import (
12
+ BetaCitationsDelta,
13
+ BetaCodeExecutionToolResultBlock,
14
+ BetaCodeExecutionToolResultBlockParam,
15
+ BetaInputJSONDelta,
16
+ BetaServerToolUseBlockParam,
17
+ BetaWebSearchToolResultBlockParam,
18
+ )
11
19
  from typing_extensions import assert_never
12
20
 
21
+ from pydantic_ai.builtin_tools import CodeExecutionTool, WebSearchTool
22
+
13
23
  from .. import ModelHTTPError, UnexpectedModelBehavior, _utils, usage
14
24
  from .._utils import guard_tool_call_id as _guard_tool_call_id
15
25
  from ..messages import (
16
26
  BinaryContent,
27
+ BuiltinToolCallPart,
28
+ BuiltinToolReturnPart,
17
29
  DocumentUrl,
18
30
  ImageUrl,
19
31
  ModelMessage,
@@ -33,13 +45,21 @@ from ..profiles import ModelProfileSpec
33
45
  from ..providers import Provider, infer_provider
34
46
  from ..settings import ModelSettings
35
47
  from ..tools import ToolDefinition
36
- from . import Model, ModelRequestParameters, StreamedResponse, check_allow_model_requests, download_item, get_user_agent
48
+ from . import (
49
+ Model,
50
+ ModelRequestParameters,
51
+ StreamedResponse,
52
+ check_allow_model_requests,
53
+ download_item,
54
+ get_user_agent,
55
+ )
37
56
 
38
57
  try:
39
58
  from anthropic import NOT_GIVEN, APIStatusError, AsyncAnthropic, AsyncStream
40
59
  from anthropic.types.beta import (
41
60
  BetaBase64PDFBlockParam,
42
61
  BetaBase64PDFSourceParam,
62
+ BetaCodeExecutionTool20250522Param,
43
63
  BetaContentBlock,
44
64
  BetaContentBlockParam,
45
65
  BetaImageBlockParam,
@@ -55,6 +75,7 @@ try:
55
75
  BetaRawMessageStopEvent,
56
76
  BetaRawMessageStreamEvent,
57
77
  BetaRedactedThinkingBlock,
78
+ BetaServerToolUseBlock,
58
79
  BetaSignatureDelta,
59
80
  BetaTextBlock,
60
81
  BetaTextBlockParam,
@@ -66,9 +87,13 @@ try:
66
87
  BetaToolChoiceParam,
67
88
  BetaToolParam,
68
89
  BetaToolResultBlockParam,
90
+ BetaToolUnionParam,
69
91
  BetaToolUseBlock,
70
92
  BetaToolUseBlockParam,
93
+ BetaWebSearchTool20250305Param,
94
+ BetaWebSearchToolResultBlock,
71
95
  )
96
+ from anthropic.types.beta.beta_web_search_tool_20250305_param import UserLocation
72
97
  from anthropic.types.model_param import ModelParam
73
98
 
74
99
  except ImportError as _import_error:
@@ -218,6 +243,7 @@ class AnthropicModel(Model):
218
243
  ) -> BetaMessage | AsyncStream[BetaRawMessageStreamEvent]:
219
244
  # standalone function to make it easier to override
220
245
  tools = self._get_tools(model_request_parameters)
246
+ tools += self._get_builtin_tools(model_request_parameters)
221
247
  tool_choice: BetaToolChoiceParam | None
222
248
 
223
249
  if not tools:
@@ -236,6 +262,7 @@ class AnthropicModel(Model):
236
262
  try:
237
263
  extra_headers = model_settings.get('extra_headers', {})
238
264
  extra_headers.setdefault('User-Agent', get_user_agent())
265
+ extra_headers.setdefault('anthropic-beta', 'code-execution-2025-05-22')
239
266
  return await self.client.beta.messages.create(
240
267
  max_tokens=model_settings.get('max_tokens', 4096),
241
268
  system=system_prompt or NOT_GIVEN,
@@ -264,6 +291,24 @@ class AnthropicModel(Model):
264
291
  for item in response.content:
265
292
  if isinstance(item, BetaTextBlock):
266
293
  items.append(TextPart(content=item.text))
294
+ elif isinstance(item, (BetaWebSearchToolResultBlock, BetaCodeExecutionToolResultBlock)):
295
+ items.append(
296
+ BuiltinToolReturnPart(
297
+ provider_name='anthropic',
298
+ tool_name=item.type,
299
+ content=item.content,
300
+ tool_call_id=item.tool_use_id,
301
+ )
302
+ )
303
+ elif isinstance(item, BetaServerToolUseBlock):
304
+ items.append(
305
+ BuiltinToolCallPart(
306
+ provider_name='anthropic',
307
+ tool_name=item.name,
308
+ args=cast(dict[str, Any], item.input),
309
+ tool_call_id=item.id,
310
+ )
311
+ )
267
312
  elif isinstance(item, BetaRedactedThinkingBlock): # pragma: no cover
268
313
  warnings.warn(
269
314
  'Pydantic AI currently does not handle redacted thinking blocks. '
@@ -302,6 +347,24 @@ class AnthropicModel(Model):
302
347
  tools += [self._map_tool_definition(r) for r in model_request_parameters.output_tools]
303
348
  return tools
304
349
 
350
+ def _get_builtin_tools(self, model_request_parameters: ModelRequestParameters) -> list[BetaToolUnionParam]:
351
+ tools: list[BetaToolUnionParam] = []
352
+ for tool in model_request_parameters.builtin_tools:
353
+ if isinstance(tool, WebSearchTool):
354
+ user_location = UserLocation(type='approximate', **tool.user_location) if tool.user_location else None
355
+ tools.append(
356
+ BetaWebSearchTool20250305Param(
357
+ name='web_search',
358
+ type='web_search_20250305',
359
+ allowed_domains=tool.allowed_domains,
360
+ blocked_domains=tool.blocked_domains,
361
+ user_location=user_location,
362
+ )
363
+ )
364
+ elif isinstance(tool, CodeExecutionTool): # pragma: no branch
365
+ tools.append(BetaCodeExecutionTool20250522Param(name='code_execution', type='code_execution_20250522'))
366
+ return tools
367
+
305
368
  async def _map_message(self, messages: list[ModelMessage]) -> tuple[str, list[BetaMessageParam]]: # noqa: C901
306
369
  """Just maps a `pydantic_ai.Message` to a `anthropic.types.MessageParam`."""
307
370
  system_prompt_parts: list[str] = []
@@ -338,11 +401,26 @@ class AnthropicModel(Model):
338
401
  if len(user_content_params) > 0:
339
402
  anthropic_messages.append(BetaMessageParam(role='user', content=user_content_params))
340
403
  elif isinstance(m, ModelResponse):
341
- assistant_content_params: list[BetaTextBlockParam | BetaToolUseBlockParam | BetaThinkingBlockParam] = []
404
+ assistant_content_params: list[
405
+ BetaTextBlockParam
406
+ | BetaToolUseBlockParam
407
+ | BetaServerToolUseBlockParam
408
+ | BetaWebSearchToolResultBlockParam
409
+ | BetaCodeExecutionToolResultBlockParam
410
+ | BetaThinkingBlockParam
411
+ ] = []
342
412
  for response_part in m.parts:
343
413
  if isinstance(response_part, TextPart):
344
- if response_part.content: # Only add non-empty text
414
+ if response_part.content:
345
415
  assistant_content_params.append(BetaTextBlockParam(text=response_part.content, type='text'))
416
+ elif isinstance(response_part, ToolCallPart):
417
+ tool_use_block_param = BetaToolUseBlockParam(
418
+ id=_guard_tool_call_id(t=response_part),
419
+ type='tool_use',
420
+ name=response_part.tool_name,
421
+ input=response_part.args_as_dict(),
422
+ )
423
+ assistant_content_params.append(tool_use_block_param)
346
424
  elif isinstance(response_part, ThinkingPart):
347
425
  # NOTE: We only send thinking part back for Anthropic, otherwise they raise an error.
348
426
  if response_part.signature is not None: # pragma: no branch
@@ -351,14 +429,31 @@ class AnthropicModel(Model):
351
429
  thinking=response_part.content, signature=response_part.signature, type='thinking'
352
430
  )
353
431
  )
432
+ elif isinstance(response_part, BuiltinToolCallPart):
433
+ if response_part.provider_name == 'anthropic':
434
+ server_tool_use_block_param = BetaServerToolUseBlockParam(
435
+ id=_guard_tool_call_id(t=response_part),
436
+ type='server_tool_use',
437
+ name=cast(Literal['web_search', 'code_execution'], response_part.tool_name),
438
+ input=response_part.args_as_dict(),
439
+ )
440
+ assistant_content_params.append(server_tool_use_block_param)
441
+ elif isinstance(response_part, BuiltinToolReturnPart):
442
+ if response_part.provider_name == 'anthropic':
443
+ tool_use_id = _guard_tool_call_id(t=response_part)
444
+ if response_part.tool_name == 'web_search_tool_result':
445
+ server_tool_result_block_param = BetaWebSearchToolResultBlockParam(
446
+ tool_use_id=tool_use_id, type=response_part.tool_name, content=response_part.content
447
+ )
448
+ elif response_part.tool_name == 'code_execution_tool_result':
449
+ server_tool_result_block_param = BetaCodeExecutionToolResultBlockParam(
450
+ tool_use_id=tool_use_id, type=response_part.tool_name, content=response_part.content
451
+ )
452
+ else:
453
+ raise ValueError(f'Unsupported tool name: {response_part.tool_name}')
454
+ assistant_content_params.append(server_tool_result_block_param)
354
455
  else:
355
- tool_use_block_param = BetaToolUseBlockParam(
356
- id=_guard_tool_call_id(t=response_part),
357
- type='tool_use',
358
- name=response_part.tool_name,
359
- input=response_part.args_as_dict(),
360
- )
361
- assistant_content_params.append(tool_use_block_param)
456
+ assert_never(response_part)
362
457
  if len(assistant_content_params) > 0:
363
458
  anthropic_messages.append(BetaMessageParam(role='assistant', content=assistant_content_params))
364
459
  else:
@@ -476,7 +571,10 @@ class AnthropicStreamedResponse(StreamedResponse):
476
571
  async for event in self._response:
477
572
  self._usage += _map_usage(event)
478
573
 
479
- if isinstance(event, BetaRawContentBlockStartEvent):
574
+ if isinstance(event, BetaRawMessageStartEvent):
575
+ pass
576
+
577
+ elif isinstance(event, BetaRawContentBlockStartEvent):
480
578
  current_block = event.content_block
481
579
  if isinstance(current_block, BetaTextBlock) and current_block.text:
482
580
  maybe_event = self._parts_manager.handle_text_delta(
@@ -499,6 +597,8 @@ class AnthropicStreamedResponse(StreamedResponse):
499
597
  )
500
598
  if maybe_event is not None: # pragma: no branch
501
599
  yield maybe_event
600
+ elif isinstance(current_block, BetaServerToolUseBlock):
601
+ pass
502
602
 
503
603
  elif isinstance(event, BetaRawContentBlockDeltaEvent):
504
604
  if isinstance(event.delta, BetaTextDelta):
@@ -528,8 +628,16 @@ class AnthropicStreamedResponse(StreamedResponse):
528
628
  )
529
629
  if maybe_event is not None: # pragma: no branch
530
630
  yield maybe_event
631
+ elif isinstance(event.delta, BetaInputJSONDelta):
632
+ pass
633
+ # TODO(Marcelo): We need to handle citations.
634
+ elif isinstance(event.delta, BetaCitationsDelta):
635
+ pass
636
+
637
+ elif isinstance(event, BetaRawMessageDeltaEvent):
638
+ pass
531
639
 
532
- elif isinstance(event, (BetaRawContentBlockStopEvent, BetaRawMessageStopEvent)):
640
+ elif isinstance(event, (BetaRawContentBlockStopEvent, BetaRawMessageStopEvent)): # pragma: no branch
533
641
  current_block = None
534
642
 
535
643
  @property