Skip to content

Conversation

@DouweM
Copy link
Collaborator

@DouweM DouweM commented Aug 15, 2025


  • Adds requires_approval: bool to Tool and @agent.tool{_plain}
  • Lets tool functions raise ApprovalRequired so they can dynamically determine if approval is required, based on e.g. tool args or explicit approval in message history
  • Lets tool functions raise CallDeferred so they can dynamically determine if the tool call can be completed inside the agent loop or not, like in the case of long-running queries or background jobs.
  • Adds RunContext.tool_call_approved: bool to check from inside a tool function whether approval was granted (so ApprovalRequired wouldn't be raised again)
  • Adds deferred_tool_results: DeferredToolResults to run and iter methods to pass in tool call IDs mapped to approvals (True/False/ToolApproved(override_args=...)/ToolDenied(message=...)) or external call results (Any/ToolReturn/ModelRetry)
  • Adds ApprovalRequiredToolset that can wrap e.g. an MCP server and require approval for either all tools, or selectively based on a function that is given the run context, tool def, and tool args.
  • Renames DeferredToolset to ExternalToolset since there are now 2 types of deferred tool calls: external and approval-required
  • Renames DeferredToolCalls to DeferredToolRequests

With the changes in this PR, it would also be very easy to support return EndRun(output: OutputT) from inside a tool function (return rather than raise so we can enforce the generic output type when using the agent tool decorators), if we decide we want that. I think it'd be useful!


  • Tests
  • Docs

@DouweM DouweM self-assigned this Aug 15, 2025
@github-actions
Copy link

github-actions bot commented Aug 15, 2025

Docs Preview

commit: 8cb93e9
Preview URL: https://55dbd5ff-pydantic-ai-previews.pydantic.workers.dev

@DouweM DouweM changed the title Let tool_call_results be passed to run when message history ended on deferred tool calls Let deferred_tool_results be passed to run when message history ended on deferred tool calls Aug 16, 2025
@yf-yang
Copy link
Contributor

yf-yang commented Aug 16, 2025

@DouweM Thanks, after reading this commit I notice there are two different workflows:

  1. Tool call -> HITL approval -> Tool execution at server side -> Tool result (For example, delete a file at server side)
  2. Tool call -> Tool execution at client side -> Tool result (For example, delete a file from user's desktop)

In the second procedure, approval/denial is not an adequate abstraction, as typically they should be organized as ToolSuccessful/ToolFailed, where

Successful -> tool approved, then executed, then finished
Failed -> tool denial, OR tool fails to execute, OR user simply closes the client and reopen, so all pending tools become invalid and should be treated as failed

Can we organize those two scenarios in one set of API? Or are they the difference between deferred and unapproved?

@DouweM
Copy link
Collaborator Author

DouweM commented Aug 16, 2025

@yf-yang Yep, that's exactly the difference between approvals/calls on the DefferedTool{Requests,Results} classes, which maps to unapproved/deferred on the ToolDefinition (naming could still change).

I intend to add more convenient ways to make requires_approval conditional on deps and args (e.g. letting you raise a RequiresApproval exception from inside the tool). An ApprovalRequiredToolset that could wrap an MCP server would also be useful.

All of that will come after my vacation next week though :)

@DouweM DouweM changed the title Let deferred_tool_results be passed to run when message history ended on deferred tool calls Add support for human-in-the-loop tool call approval Aug 27, 2025
@DouweM DouweM enabled auto-merge (squash) August 29, 2025 21:05
@DouweM DouweM merged commit e854f98 into main Aug 29, 2025
30 checks passed
@DouweM DouweM deleted the tool-call-results branch August 29, 2025 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parallel tool call containing both ToolReturn and deferred tool does not work Human-in-the-loop approval of tool calls

3 participants