Skip to content
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

feat(js/ai): added a simple way to interrupt tool execution #1583

Open
wants to merge 3 commits into
base: pj/toolChoice
Choose a base branch
from

Conversation

pavelgj
Copy link
Collaborator

@pavelgj pavelgj commented Jan 4, 2025

ai.defineTool(
  { name: 'testTool', description: 'description' },
  async () => {
    if (anyReason) {
      interruptTool()
    }
    return doToolThings();
  }
);

Comment on lines +200 to +202
export function interruptTool() {
throw new ToolInterruptError();
}
Copy link
Collaborator

@mbleigh mbleigh Jan 23, 2025

Choose a reason for hiding this comment

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

I'd like this to be able to take an argument of Record<string, any> and whatever gets passed to that gets annotated onto the toolRequest it's associated with.

const transferMoney = ai.defineTool({
  inputSchema: z.object({amount: z.number(), fromAccount: z.string(), toAccount: z.string()}),
  outputSchema: z.object({balance: z.number()}),
}, ({amount, fromAccount, toAccount}, {interrupt, context}) {
  if (amount > 1000) interrupt({confirm: "The requested amount is large, please confirm all the details carefully."});
  if (!getUserAccounts(context.auth.uid).includes(toAccount)) interrupt({confirm: `The account '${toAccount}' does not belong to you. Please confirm you wish to transfer money to an account owned by someone else.`;
  return doMoneyTransfer(fromAccount,toAccount,amount);
});

Results in:

{role: "model", content: [..., {toolRequest: {name: "transferMoney", input: {amount: 5000, ...}, metadata: {interrupt: {confirm: "..."}}}}]}

This will allow the developer to customize and control how interrupts happen and also lay foundation for higher-level interrupt primitives down the road.

Copy link
Collaborator

Choose a reason for hiding this comment

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

And maybe metadata: {interrupt: true} if no argument provided

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this implies this interrupt is resumable/rerunnable where the interrupt won't be triggered? Like we we had with durable flows? How do you imagine the user facing api for this?

* Interrupts current tool execution causing tool request to be returned in the generation response.
* Should only be called within a tool.
*/
export function interruptTool() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What do you think about adding interrupt as an item in the second argument of the tool instead?

ai.defineTool(..., (input, {context, interrupt}) => {
  interrupt();
});

@mbleigh
Copy link
Collaborator

mbleigh commented Jan 23, 2025 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

2 participants