Add tool approval integration for Vercel AI adapter#3772
Add tool approval integration for Vercel AI adapter#3772DouweM merged 49 commits intopydantic:mainfrom
Conversation
099f07a to
1160591
Compare
|
Thanks for the quick reviews! Will get the test issue fixed and reply to your comments shortly. |
2deeb25 to
9eebd80
Compare
|
This PR is stale, and will be closed in 3 days if no reply is received. |
|
Closing this PR as it has been inactive for 10 days. |
|
Have been on a break for a bit, will get the comments addressed early this week if not today. |
|
@bendrucker Thanks Ben, I should probably have disabled the stale bot before the holidays :) |
|
Thanks for your patience! Made the requested changes and provided a reference to relevant AI SDK test snapshots for the behavioral question. |
|
Squashing another upstream cause of a test flake: pytest-dev/pytest-xdist#1299 |
|
@bendrucker I merged some other changes for the Vercel AI event stream that had been in the works for a few weeks -- can you resolve the conflicts please? |
|
Resolved! |
|
@bendrucker Please have a look at the conflicts |
The override was silently dropping **kwargs instead of forwarding them to the constructor, which could surprise subclasses.
Only document the new sdk_version parameter and refer to the base class for all other parameters, since this override exists purely for IDE autocomplete.
Imports only needed for dispatch_request/from_request method signatures are now lazy, keeping the runtime import footprint light.
addToolResult is deprecated in AI SDK v6 in favor of addToolApprovalResponse for the tool approval flow.
|
@bendrucker Did you mean to push your changes? |
|
Whoops, done! |
|
@bendrucker We've got merge conflicts 😅 |
# Conflicts: # docs/ui/vercel-ai.md # pydantic_ai_slim/pydantic_ai/ui/vercel_ai/_event_stream.py # tests/test_vercel_ai.py
|
No worries, saw em, fixing them now! |
|
@bendrucker Thanks Ben! It took a minute but I'll get this out in a release later today :) |
|
Thank you! Appreciate your patience, Python is still somewhat new to me. Learned a bunch and will be eager to integrate this soon! |
Closes #4279
Adds tool approval integration for the Vercel AI adapter, enabling human-in-the-loop workflows with AI SDK UI.
Summary
sdk_version=6enables tool approvaltool-approval-requestchunks for deferred tool approvalstool-output-deniedchunks when user denies tool executionapprovalfield to tool parts withToolApprovalRequested/ToolApprovalRespondedstatesUsage
When
sdk_version=6, the adapter will:tool-approval-requestchunks when tools withrequires_approval=Trueare calledtool-output-deniedchunks for rejected tools, passing the denialreasonthrough asToolDeniedAI SDK Tool Approval Protocol
Tool approval is an AI SDK v6 feature. 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
ToolApprovalRequested/ToolApprovalRespondedtypes andapprovalfield to tool UI partsToolApprovalRequestChunkandToolOutputDeniedChunkresponse chunks to event streamsdk_version=6(added in Fix compatibility with Vercel AI SDK v5 by adding SDK version param #4166), enabling auto-extraction of approval responses viadeferred_tool_resultscached propertyreasonthrough asToolDeniedwhen providedTesting
approvalfield on tool partsReferences