Allow conditionally skipping tool call

I am building an agent using AI SDK + OpenAI SDK. I’m calling a second generic “Agent Service” which is OpenAI compatible. Several tools are executed on the generic Service side, while some are executed on the Agent itself. When a tool is called on the agent side, the generic service will wait until the agent sent the tool call result to the generic service.

This works as expected, but we would like to stream ALL tool calls from the generic service to the agent, but only execute the “custom” ones that cannot be executed on the generic service. Currently, when we stream all tool calls, some will be executed twice.

We are passing a flag whether it is executed already or not via the raw tool-call OpenAI event. How can I use this to prevent AI SDK from making the tool call a second time?

Thanks in advance! :folded_hands:

You can handle this by using the experimental_repairToolCall option in the AI SDK to conditionally skip tool calls that have already run.

Example:

const result = await streamText({
  model: openai('gpt-4'),
  messages,
  tools: {
    // your tools
  },
  experimental_repairToolCall: async ({ toolCall }) => {
    // Check your custom flag on the toolCall
    if (toolCall.alreadyExecuted) {
      // Skip this tool call
      return null;
    }

    // Otherwise, proceed normally
    return toolCall;
  }
});

Another option is to use experimental_activeTools to dynamically control which tools can run:

const result = await streamText({
  model: openai('gpt-4'),
  messages,
  tools: {
    customTool1: tool({ ... }),
    customTool2: tool({ ... }),
    genericTool1: tool({ ... }),
  },
  experimental_activeTools: ['customTool1', 'customTool2'], // Only allow these
});

You can also handle this directly inside your tool definition by checking a flag and returning early:

const customTool = tool({
  description: 'Custom tool',
  parameters: z.object({ ... }),
  execute: async (params, context) => {
    if (context.alreadyExecuted) {
      return { skipped: true };
    }

    return await executeCustomLogic(params);
  },
});