Skip to content

Conversation

@AlanPonnachan
Copy link

Allow dynamic configuration of built-in tools via RunContext

This PR closes #3555

Adding support for dynamic configuration of built-in tools (e.g., WebSearchTool) based on the agent's RunContext.

Previously, builtin_tools only accepted static instances. Now, it accepts AbstractBuiltinTool instances or a BuiltinToolFunc — a callable that takes RunContext and returns an AbstractBuiltinTool or None. This allows users to inject context-aware parameters (like a user's location) into tools at runtime.

Key Changes:

  • New Type: Added BuiltinToolFunc alias for Callable[[RunContext], Awaitable[AbstractBuiltinTool | None] | AbstractBuiltinTool | None].
  • Agent Logic: Updated _agent_graph.py to resolve these dynamic functions before generating model parameters.
  • Signatures: Updated type hints for Agent, AbstractAgent, WrapperAgent, and durable execution integrations (DBOS, Prefect, Temporal).
  • Tests: Added tests/test_dynamic_builtin_tools.py covering sync/async definitions, conditional omission (returning None), and runtime injection.

Example Usage:

async def dynamic_search(ctx: RunContext[UserDeps]) -> WebSearchTool:
    return WebSearchTool(
        user_location={'city': ctx.deps.city}
    )

agent = Agent(builtin_tools=[dynamic_search], deps_type=UserDeps)

@DouweM
Copy link
Collaborator

DouweM commented Dec 1, 2025

@AlanPonnachan Nice work! Can you please mention this in the Builtin tools docs as well?

@AlanPonnachan AlanPonnachan force-pushed the feat/dynamic-builtin-tools branch from 8caac69 to 289f8d0 Compare December 2, 2025 14:49
@AlanPonnachan
Copy link
Author

@DouweM I added docs as well. Can you please review now


## Dynamic Configuration

Sometimes you need to configure a built-in tool dynamically based on the [run context](api/tools.md#pydantic_ai.tools.RunContext) (e.g., user dependencies). You can achieve this by passing a function to `builtin_tools` that takes [`RunContext`][pydantic_ai.tools.RunContext] as an argument and returns an [`AbstractBuiltinTool`][pydantic_ai.builtin_tools.AbstractBuiltinTool] or `None`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Sometimes you need to configure a built-in tool dynamically based on the [run context](api/tools.md#pydantic_ai.tools.RunContext) (e.g., user dependencies). You can achieve this by passing a function to `builtin_tools` that takes [`RunContext`][pydantic_ai.tools.RunContext] as an argument and returns an [`AbstractBuiltinTool`][pydantic_ai.builtin_tools.AbstractBuiltinTool] or `None`.
Sometimes you need to configure a built-in tool dynamically based on the [run context][pydantic_ai.tools.RunContext] (e.g., user dependencies). You can achieve this by passing a function to `builtin_tools` that takes [`RunContext`][pydantic_ai.tools.RunContext] as an argument and returns an [`AbstractBuiltinTool`][pydantic_ai.builtin_tools.AbstractBuiltinTool] or `None`.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's also explicitly mention this can be used for omitting, as is mentioned in the docstring

model_settings: ModelSettings | None,
model_request_parameters: ModelRequestParameters,
) -> ModelResponse:
self.captured_tools = model_request_parameters.builtin_tools
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's use TestModel instead, which already has last_model_request_parameters that we can read this info off of

Copy link
Collaborator

Choose a reason for hiding this comment

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

Please move this to test_agent.py.

This is useful if you want to customize the builtin tool based on the run context (e.g. user dependencies),
or omit it completely from a step.
Usage `BuiltinToolFunc[AgentDepsT]`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't add much :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Built-in tool should allow for dynamic configuration

2 participants