-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add builtin_tools to Agent
#2102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 74 commits
57e568b
97ab44b
e3dda9d
3ad6d38
0b43f65
fa7fd11
32324fa
f33e568
13d7433
ac85205
c93633f
3a8b640
360de87
cb4e539
4e3769a
ebb536f
c8bb611
6bcc1a8
97ff651
1d47e1e
5f89444
9512987
800a71a
d0f4643
bc298d6
46c06c2
3496567
c193059
427dec2
866ad21
4c2622d
c13736e
a42a75d
e2f1daa
3094a9a
6a3c987
ac0edb6
374e034
7cccdd2
2393c87
21094a7
8ac5294
de8f32d
13bc865
c0fc35c
b4b3752
3a2481a
306514e
c84be82
a77030b
93e26d6
bfb4eef
4376fc2
c029d4b
87f59f1
3fca6d4
37a1845
5b031d6
e1085fa
ae1d4d9
3ee4aa9
9d24f5b
07f7892
7d867ee
ba3f6b2
1ae6155
3f8cc2d
ba962a7
bfd9b21
e2fef42
b1ef2f8
e9cacc3
aa91640
2d451a7
38b5b7d
359b9e1
bd6e5f7
69267ca
1a4a0f3
c9d0d18
a631229
78fe612
a673c90
df2ad47
b4add90
dec1ba3
e134d43
ef4ab6a
bb98ccf
739f142
cc7ab39
5534bf4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
|
||
|
|
@@ -190,6 +191,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, | ||
|
|
@@ -221,6 +223,7 @@ def __init__( | |
| result_tool_description: str | None = None, | ||
| result_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, | ||
|
|
@@ -276,6 +279,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, | ||
|
|
@@ -307,6 +311,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. On models that don't support certain tools, the tool will be ignored. | ||
| 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] | ||
|
|
@@ -413,6 +419,11 @@ def __init__( | |
| self._system_prompt_dynamic_functions = {} | ||
|
|
||
| self._max_result_retries = output_retries if output_retries is not None else retries | ||
| self._builtin_tools: list[AbstractBuiltinTool] = [] | ||
|
|
||
| for tool in builtin_tools: | ||
| self._builtin_tools.append(tool) | ||
|
||
|
|
||
| self._prepare_tools = prepare_tools | ||
| self._prepare_output_tools = prepare_output_tools | ||
|
|
||
|
|
@@ -818,6 +829,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=self._builtin_tools, | ||
| tool_manager=run_toolset, | ||
| tracer=tracer, | ||
| get_instructions=get_instructions, | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,98 @@ | ||||
| from __future__ import annotations as _annotations | ||||
|
|
||||
| from abc import ABC | ||||
| from dataclasses import dataclass | ||||
| from typing import Literal | ||||
|
|
||||
| from typing_extensions import TypedDict | ||||
|
|
||||
| __all__ = ('AbstractBuiltinTool', 'WebSearchTool', 'WebSearchUserLocation') | ||||
|
|
||||
|
|
||||
| @dataclass | ||||
| class AbstractBuiltinTool(ABC): | ||||
| """A builtin tool that can be used by an agent. | ||||
| This class is abstract and cannot be instantiated directly. | ||||
| The builtin tools are passed to the model as part of the `ModelRequestParameters`. | ||||
| """ | ||||
|
|
||||
|
|
||||
| @dataclass | ||||
| class WebSearchTool(AbstractBuiltinTool): | ||||
| """A builtin tool that allows your agent to search the web for information. | ||||
| The parameters that PydanticAI passes depend on the model, as some parameters may not be supported by certain models. | ||||
| Supported by: | ||||
| * OpenAI | ||||
| * Anthropic | ||||
| * Groq | ||||
| """ | ||||
|
|
||||
| search_context_size: Literal['low', 'medium', 'high'] = 'medium' | ||||
| """The `search_context_size` parameter controls how much context is retrieved from the web to help the tool formulate a response. | ||||
| Supported by: | ||||
| * OpenAI | ||||
| """ | ||||
|
|
||||
| user_location: WebSearchUserLocation | None = None | ||||
| """The `user_location` parameter allows you to localize search results based on a user's location. | ||||
| Supported by: | ||||
| * Anthropic | ||||
| * OpenAI | ||||
| """ | ||||
|
|
||||
| blocked_domains: list[str] | None = None | ||||
| """If provided, these domains will never appear in results. | ||||
| With Anthropic, you can only use one of `blocked_domains` or `allowed_domains`, not both. | ||||
| Supported by: | ||||
| * Anthropic (https://docs.anthropic.com/en/docs/build-with-claude/tool-use/web-search-tool#domain-filtering) | ||||
| * Groq (https://console.groq.com/docs/agentic-tooling#search-settings) | ||||
| * MistralAI | ||||
|
||||
| * MistralAI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On OpenAI, these needs to be a 2-letter country code: https://platform.openai.com/docs/guides/tools-web-search?api-mode=responses#user-location
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check doesn't seem necessary. It seems it works fine with 3 characters.
Kludex marked this conversation as resolved.
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -505,8 +505,8 @@ def otel_event(self, settings: InstrumentationSettings) -> Event: | |
|
|
||
|
|
||
| @dataclass(repr=False) | ||
| class ToolReturnPart: | ||
| """A tool return message, this encodes the result of running a tool.""" | ||
| class BaseToolReturnPart: | ||
| """Base class for tool return parts.""" | ||
|
|
||
| tool_name: str | ||
| """The name of the "tool" was called.""" | ||
|
|
@@ -523,9 +523,6 @@ class ToolReturnPart: | |
| timestamp: datetime = field(default_factory=_now_utc) | ||
| """The timestamp, when the tool returned.""" | ||
|
|
||
| part_kind: Literal['tool-return'] = 'tool-return' | ||
| """Part type identifier, this is available on all parts as a discriminator.""" | ||
|
|
||
| def model_response_str(self) -> str: | ||
| """Return a string representation of the content for the model.""" | ||
| if isinstance(self.content, str): | ||
|
|
@@ -552,9 +549,29 @@ def otel_event(self, settings: InstrumentationSettings) -> Event: | |
| }, | ||
| ) | ||
|
|
||
| def has_content(self) -> bool: | ||
| """Return `True` if the tool return has content.""" | ||
| return self.content is not None # pragma: no cover | ||
|
|
||
| __repr__ = _utils.dataclasses_no_defaults_repr | ||
|
|
||
|
|
||
| @dataclass(repr=False) | ||
| class ToolReturnPart(BaseToolReturnPart): | ||
| """A tool return message, this encodes the result of running a tool.""" | ||
|
|
||
| part_kind: Literal['tool-return'] = 'tool-return' | ||
| """Part type identifier, this is available on all parts as a discriminator.""" | ||
|
|
||
|
|
||
| @dataclass(repr=False) | ||
| class BuiltinToolReturnPart(BaseToolReturnPart): | ||
| """A tool return message from a server tool.""" | ||
mattbrandman marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| part_kind: Literal['builtin-tool-return'] = 'builtin-tool-return' | ||
| """Part type identifier, this is available on all parts as a discriminator.""" | ||
|
|
||
|
|
||
| error_details_ta = pydantic.TypeAdapter(list[pydantic_core.ErrorDetails], config=pydantic.ConfigDict(defer_build=True)) | ||
|
|
||
|
|
||
|
|
@@ -696,7 +713,7 @@ def has_content(self) -> bool: | |
|
|
||
|
|
||
| @dataclass(repr=False) | ||
| class ToolCallPart: | ||
| class BaseToolCallPart: | ||
| """A tool call from a model.""" | ||
|
|
||
| tool_name: str | ||
|
|
@@ -714,9 +731,6 @@ class ToolCallPart: | |
| In case the tool call id is not provided by the model, Pydantic AI will generate a random one. | ||
| """ | ||
|
|
||
| part_kind: Literal['tool-call'] = 'tool-call' | ||
| """Part type identifier, this is available on all parts as a discriminator.""" | ||
|
|
||
| def args_as_dict(self) -> dict[str, Any]: | ||
| """Return the arguments as a Python dictionary. | ||
|
|
||
|
|
@@ -753,7 +767,29 @@ def has_content(self) -> bool: | |
| __repr__ = _utils.dataclasses_no_defaults_repr | ||
|
|
||
|
|
||
| ModelResponsePart = Annotated[Union[TextPart, ToolCallPart, ThinkingPart], pydantic.Discriminator('part_kind')] | ||
| @dataclass(repr=False) | ||
| class ToolCallPart(BaseToolCallPart): | ||
| """A tool call from a model.""" | ||
|
|
||
| part_kind: Literal['tool-call'] = 'tool-call' | ||
| """Part type identifier, this is available on all parts as a discriminator.""" | ||
|
|
||
|
|
||
| @dataclass(repr=False) | ||
| class ServerToolCallPart(BaseToolCallPart): | ||
|
||
| """A tool call from a server tool.""" | ||
|
|
||
| model_name: str | None = None | ||
|
||
| """The name of the model that generated the response.""" | ||
|
|
||
| part_kind: Literal['server-tool-call'] = 'server-tool-call' | ||
| """Part type identifier, this is available on all parts as a discriminator.""" | ||
|
|
||
|
|
||
| ModelResponsePart = Annotated[ | ||
| Union[TextPart, ToolCallPart, ServerToolCallPart, BuiltinToolReturnPart, ThinkingPart], | ||
| pydantic.Discriminator('part_kind'), | ||
| ] | ||
| """A message part returned by a model.""" | ||
|
|
||
|
|
||
|
|
@@ -1145,6 +1181,29 @@ def tool_call_id(self) -> str: | |
| __repr__ = _utils.dataclasses_no_defaults_repr | ||
|
|
||
|
|
||
| @dataclass(repr=False) | ||
| class ServerToolCallEvent: | ||
|
||
| """An event indicating the start to a call to a server tool.""" | ||
|
|
||
| part: ServerToolCallPart | ||
| """The server tool call to make.""" | ||
|
|
||
| event_kind: Literal['server_tool_call'] = 'server_tool_call' | ||
DouweM marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """Event type identifier, used as a discriminator.""" | ||
|
|
||
|
|
||
| @dataclass(repr=False) | ||
| class ServerToolResultEvent: | ||
| """An event indicating the result of a server tool call.""" | ||
|
|
||
| result: BuiltinToolReturnPart | ||
| """The result of the call to the server tool.""" | ||
|
|
||
| event_kind: Literal['server_tool_result'] = 'server_tool_result' | ||
| """Event type identifier, used as a discriminator.""" | ||
|
|
||
|
|
||
| HandleResponseEvent = Annotated[ | ||
| Union[FunctionToolCallEvent, FunctionToolResultEvent], pydantic.Discriminator('event_kind') | ||
| Union[FunctionToolCallEvent, FunctionToolResultEvent, ServerToolCallEvent, ServerToolResultEvent], | ||
| pydantic.Discriminator('event_kind'), | ||
| ] | ||
Uh oh!
There was an error while loading. Please reload this page.