feature request: V6 needsApproval mid-execute instead of before execute
Description
I'm working on an AI SDK powered app right now, I'm very excited to see the new needsApproval flag in the V6 beta. However, I just implemented something similar for my own app and this flag wouldn't be enough for my use case. I need something like the following:
export const myTool = tool({
description: 'Does a thing',
inputSchema: z.object({
input: z.string(),
}),
execute: async ({ input }, { waitForApproval }) => {
const data = await fetchPrerequisteData(input);
sendToClient(data);
await waitForApproval();
const result = await executeTool(input, data);
return result;
},
});
In my tool I want to fetch some content from my database and update a component on the client with that data to be shown when the user is asked to Accept/Deny a tool call. I think it's a pretty common pattern if your agent's tool call components render more than just the data provided by the tool call.
AI SDK Version
No response
Code of Conduct
- [x] I agree to follow this project's Code of Conduct
Thanks for sharing, that's a great use case. I'll see if we can have first-class support for it as part of v6
want to fetch some content from my database and update a component on the client with that data to be shown when the user is asked to Accept/Deny a tool call
can you split it up into two tool calls, one to fetch the data one to execute, and add the approval in-between?
It would be useful to be able to attach additional information to the approval request after fetching some data in the tool. For example in a tool like this, it would fetch the product information to display a better approval message.
buy_product: tool({
title: 'Buy Product',
description: 'Purchase a product by its ID.',
inputSchema: z.object({
product_id: z.string().describe('The ID of the product to purchase'),
}),
needsApproval: true,
execute: async ({ product_id }) => {
...
Would love to have this feature - have very similar use case for this on my application, would much prefer to avoid a 2nd step just to trigger the execution.
I also have a fairly similar use case but with the possibility to modify the data by the user within a UI form.
export const myTool = tool({
description: 'Does a thing',
inputSchema: z.object({
input: z.string(),
}),
execute: async ({ input }, { waitForApproval }) => {
const data = await fetchPrerequisteData(input);
sendToClient(data);
modifiedData = await waitForApproval();
const result = await executeTool(input, modifiedData);
return result;
},
});
want to fetch some content from my database and update a component on the client with that data to be shown when the user is asked to Accept/Deny a tool call
can you split it up into two tool calls, one to fetch the data one to execute, and add the approval in-between?
I don't think we want to do that. It's more expensive and error prone. There's a part of the function that is deterministic and doesn't need the LLM at all.
This feature request breaks the approval loop assumptions. I do not think it is feasible for AI SDK 6, and I am also unsure it should be implemented as a tool approval or something else. Tool approvals means approval for running the tool. Here the tool is already running and then gets some input.
The tool call input often does not contain enough information to build an actionable approval dialog in the UI and I don't believe it would need to break the approval loop.
If the needsApproval property would accept an async function in addition to a simple boolean you could easily fetch the required data for the approval without needing to implement additional API endpoints.
buy_product: tool({
title: 'Buy Product',
description: 'Purchase a product by its ID.',
inputSchema: z.object({
product_id: z.string().describe('The ID of the product to purchase'),
}),
needsApproval: async ({ product_id }) => {
const productInfo = await lookupProduct(product_id);
return productInfo;
},
execute: async ({ product_id }) => {
...
This could then be exposed as part.approval.data to the UI components.
Yup, that would work. A little duplicative if the needs approval function uses an async method whose result is also needed in the execute function but I can cache it in the outer scope.
Ideally this functions in a similar way to how Mastra workflows work with a resumeSchema and a resumeData.
Would make this functionality a lot more usable than purely approve/deny.
https://mastra.ai/docs/workflows/human-in-the-loop
Oh yeah that's exactly what I've been looking for!