vercel / ai

Build AI-powered applications with React, Svelte, Vue, and Solid
https://sdk.vercel.ai/docs
Other
10.29k stars 1.55k forks source link

Support sending any variable to a tool's `execute` function. #3938

Open ShervK opened 21 hours ago

ShervK commented 21 hours ago

Feature Description

Similar to #3468

When working on agents or tools that require retrieving, saving, or working with a session, you might need to use dynamic IDs or data that were sent with the original API call but not included as an option or argument on the tool call. It would be great if you can extend ToolExecutionOptions and send in your own variables to be used when the tool.execute function is running.

To keep it type safe, you can define a type for the variables you're going to send and add the type as a generic type parameter. So an example might look like:

// set up type
export type ExtendedOptions = {
  chatId: string;
  userId: string;
}

// define your tools
export const fileSearch = tool<ExtendedOptions>({
  description: 'Semantically search for relevant files the user owns',
  parameters: z.object({
    query: z.string().describe('The search query'),
  }),
  execute: async ({ query }, { userId }) => {
    const result = await searchFiles({
      query,
      userId,
    });
    return {
      files: result.files,
    };
  },
});

export const codeExecution = tool<ExtendedOptions>({
  description:
    "Execute code in a sandboxed Python Notebook environment dedicated to the user's project",
  parameters: z.object({
    code: z.string().describe('The code to execute'),
  }),
  execute: async ({ code }, { chatId, userId, messages }) => {
    const codeCells = getAllCodeToolCalls(messages);
    codeCells.push(code);
    const result = await executeCodeInSandbox({
      codeCells,
      userId,
      chatId,
    });
    return {
      result: result.stdout,
    };
  },
});

Then in the API route, you could send in the dynamic data with the tools

    const { chatId } = await request.json();
    const session = await auth();

    // set up the dynamic option object
    const options: ExtendedOptions = {
      chatId: chatId,
      userId: session.user.id,
    };

    // set up the tools with the dynamic options
    const tools = {
      'file-search': {
        ...fileSearch,
        options,
      },
      'code-execution': {
        ...codeExecution,
        options,
      },
    };

    const { result } = streamText(
      // rest of the parameters
      ...
      tools,
    )

This is a pretty basic example, a feature like this would be more useful for agentic workloads where your agents need access to dynamic data, like chatId or userId for example.

Use Cases

No response

Additional context

There is a workaround where you have a function that takes the dynamic data as arguments which then creates the tool. It's doable but becomes harder to manage as your toolset grows.

lgrammel commented 19 hours ago

You can solve this with closures and factory methods. Why does this need to be part of the framework?