UNPKG

ai

Version:

AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.

159 lines (133 loc) 4.33 kB
import { experimental_getToolCaller, type Experimental_ToolCallerTool, type Tool, type ToolSet, } from '@ai-sdk/provider-utils'; import { InvalidArgumentError } from '../error/invalid-argument-error'; const DIRECT_TOOL_CALL = 'AI_SDK_DIRECT_TOOL_CALL'; type ToolCallerName<TOOLS extends ToolSet> = { [NAME in keyof TOOLS]: TOOLS[NAME] extends Experimental_ToolCallerTool ? NAME : never; }[keyof TOOLS] & string; export type Experimental_ToolCallers<TOOLS extends ToolSet> = { [NAME in keyof TOOLS]?: ReadonlyArray< 'AI_SDK_DIRECT_TOOL_CALL' | ToolCallerName<TOOLS> >; }; export type ResolvedToolCallers = Record<string, ReadonlyArray<string>>; export function resolveToolCallerConfiguration<TOOLS extends ToolSet>({ tools, toolCallers, }: { tools: TOOLS | undefined; toolCallers: Experimental_ToolCallers<TOOLS> | undefined; }): ResolvedToolCallers | undefined { if (tools == null || toolCallers == null) { return undefined; } const resolved: ResolvedToolCallers = {}; for (const [toolName, callers] of Object.entries(toolCallers)) { if (!Object.prototype.hasOwnProperty.call(tools, toolName)) { throw new InvalidArgumentError({ parameter: 'experimental_toolCallers', value: toolCallers, message: `unknown tool "${toolName}".`, }); } if (!Array.isArray(callers)) { throw new InvalidArgumentError({ parameter: 'experimental_toolCallers', value: toolCallers, message: `callers for tool "${toolName}" must be an array.`, }); } resolved[toolName] = callers.map(caller => { if (caller === DIRECT_TOOL_CALL) { return caller; } if ( typeof caller !== 'string' || !Object.prototype.hasOwnProperty.call(tools, caller) || experimental_getToolCaller(tools[caller]) == null ) { throw new InvalidArgumentError({ parameter: 'experimental_toolCallers', value: toolCallers, message: `tool "${toolName}" contains an invalid caller.`, }); } return caller; }); } return resolved; } export function prepareToolsForToolCallers({ tools, toolCallers, }: { tools: ToolSet | undefined; toolCallers: ResolvedToolCallers | undefined; }): { executionTools: ToolSet | undefined; modelTools: ToolSet | undefined; } { if (tools == null || toolCallers == null) { return { executionTools: tools, modelTools: tools }; } const executionTools: ToolSet = { ...tools }; const modelTools: ToolSet = { ...tools }; const localToolsByCaller = new Map<string, ToolSet>(); for (const [toolName, callerNames] of Object.entries(toolCallers)) { const tool = executionTools[toolName]; if (tool == null) { continue; } let availableDirectly = false; let availableToProvider = false; let preparedTool: Tool = tool; for (const callerName of callerNames) { if (callerName === DIRECT_TOOL_CALL) { availableDirectly = true; continue; } const caller = experimental_getToolCaller(executionTools[callerName]); if (caller == null) { continue; } if (caller.type === 'provider') { availableToProvider = true; preparedTool = { ...preparedTool, providerOptions: caller.prepareProviderOptions( preparedTool.providerOptions, ), } as Tool; } else { const localTools = localToolsByCaller.get(callerName) ?? {}; localTools[toolName] = preparedTool; localToolsByCaller.set(callerName, localTools); } } executionTools[toolName] = preparedTool; if (availableDirectly || availableToProvider) { modelTools[toolName] = preparedTool; } else { delete modelTools[toolName]; } } for (const [callerName, callerTool] of Object.entries(executionTools)) { const caller = experimental_getToolCaller(callerTool); if (caller?.type !== 'local') { continue; } const boundCaller = caller.bind(localToolsByCaller.get(callerName) ?? {}); executionTools[callerName] = boundCaller; if (Object.prototype.hasOwnProperty.call(modelTools, callerName)) { modelTools[callerName] = boundCaller; } } return { executionTools, modelTools }; }