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

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

Open
ShervK opened this issue Nov 29, 2024 · 2 comments
Open

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

ShervK opened this issue Nov 29, 2024 · 2 comments
Labels
enhancement New feature or request

Comments

@ShervK
Copy link

ShervK commented Nov 29, 2024

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.

@ShervK ShervK added the enhancement New feature or request label Nov 29, 2024
@lgrammel
Copy link
Collaborator

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

@matthiasbayer
Copy link

A closure is definitely the most straight forward way to solve this and it works well. I'm also not sure if this should get baked into the framework itself, but it might be a common enough problem worth a mention in the docs at least. Wdyt?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants