-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add tool approval integration for Vercel AI adapter #3772
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
Open
bendrucker
wants to merge
19
commits into
pydantic:main
Choose a base branch
from
bendrucker:vercel-ai-tool-approval
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+538
−17
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ab6ca8a to
099f07a
Compare
099f07a to
1160591
Compare
DouweM
requested changes
Dec 19, 2025
Contributor
Author
|
Thanks for the quick reviews! Will get the test issue fixed and reply to your comments shortly. |
- Add test for from_request() with tool_approval parameter - Add test verifying approval chunks not emitted when tool_approval=False - Add test for deferred_tool_results fallback from instance field - Add test for denied_tool_ids with ToolUIPart (builtin tools) - Fix docs link to point to VercelAIAdapter.from_request - Reword Tool Approval section to clarify AI SDK UI vs AI Elements
- Make _extract_deferred_tool_results private and inline into from_request - Make _denied_tool_ids a private cached_property - Simplify approval values to True/False (per reviewer suggestion) - Add AI SDK v6 requirement note to documentation and docstrings - Update tests for new API and simplified return values
- Inline extract_deferred_tool_results logic into from_request() - Remove unit tests for private _extract_deferred_tool_results method - Remove unit tests for private _denied_tool_ids property - Update test_tool_output_denied_chunk_emission to use public interface
- Update test_tool_output_denied_chunk_emission to use from_request() with explicit type binding to test the full public interface - Remove test_from_request_with_tool_approval_enabled (now redundant) - Remove test_deferred_tool_results_fallback_from_instance (tested internal plumbing rather than observable behavior)
52dea3e to
676530b
Compare
2deeb25 to
9eebd80
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Adds tool approval integration for the Vercel AI adapter, enabling human-in-the-loop workflows with AI SDK UI.
Summary
tool_approvalflag for simplified tool approval workflowstool-approval-requestchunks for deferred tool approvalstool-output-deniedchunks when user denies tool executionapprovalfield to tool parts withToolApprovalRequested/ToolApprovalRespondedstatesUsage
When
tool_approval=True, the adapter will:tool-approval-requestchunks when tools withrequires_approval=Trueare calledtool-output-deniedchunks for rejected toolsAI SDK Tool Approval Protocol
Tool approval is an AI SDK v6 feature (not available in v5, the current stable). Here's how the protocol works:
Protocol Flow
sequenceDiagram participant Model participant Server participant Client Model->>Server: tool call Server->>Client: tool-input-start Server->>Client: tool-input-available Server->>Client: tool-approval-request Note over Client: User approves/denies Client->>Server: approval response (next request) alt Approved Server->>Server: Execute tool Server->>Client: tool-output-available else Denied Server->>Client: tool-output-denied Server->>Model: denial info endChunk Types
tool-approval-requestServer → client:
{ "type": "tool-approval-request", "approvalId": "<uuid>", "toolCallId": "<tool-call-id>" }tool-output-deniedServer → client, when denied:
{ "type": "tool-output-denied", "toolCallId": "<tool-call-id>" }Why the Server Emits This Chunk
This is correct—the denial decision originates from the user via the
ToolApprovalRespondedfield in tool parts. However, the AI SDK protocol expects the server to emittool-output-deniedfor two reasons:The client needs confirmation that the tool lifecycle is complete. When the server receives a denial and emits
tool-output-denied, the client can transition its UI from "awaiting result" to "denied".Just as the server emits
tool-output-availablewhen a tool executes successfully, it emitstool-output-deniedwhen execution is skipped due to denial. This gives the client a consistent signal for each tool call's final state.The flow is:
approval: { id, approved: false, reason }in the tool partdeferred_tool_resultsand passes it to the agenttool-output-deniedto the clientTool Part Approval States
Tool parts include an
approvalfield tracking the approval lifecycle:Awaiting Response
{ "id": "<approval-id>" }User Responded
{ "id": "<approval-id>", "approved": true/false, "reason": "optional" }Two-Step Flow
Unlike regular tool calls, approved tools require two model interactions:
tool-approval-request→ awaits userChanges
Types
ToolApprovalRequestedandToolApprovalRespondedtypesToolApprovalRequestChunkandToolOutputDeniedChunkresponse chunksapprovalfield to allToolUIPartandDynamicToolUIPartvariantsEvent Stream
ToolApprovalRequestChunkwhen agent returnsDeferredToolRequests(only whentool_approval=True)ToolOutputDeniedChunkwhen processing denied tool results (only whentool_approval=True)denied_tool_idsproperty to track which tools were deniedAdapter
tool_approvalparameter tofrom_request()for opt-in behaviordeferred_tool_resultsinstance field on baseUIAdapterclasstool_approval=Truedeferred_tool_resultsto agent via instance field fallbackDocumentation
Testing
ToolApprovalRequestChunkemission whenrequires_approval=Truetool is calledToolOutputDeniedChunkemission when user denies tool executiontool_approval=Falsefrom_request()correctly handlestool_approvalparameterdeferred_tool_resultsfallback from instance fielddenied_tool_idsextraction for both dynamic and builtin toolsextract_deferred_tool_results()for approved, denied, and no-approval casesapprovalfield on tool partsReferences