UNPKG

@tanstack/ai

Version:

Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.

74 lines (66 loc) 2.34 kB
import type { Tool } from '../../../types' /** * Thrown when `chat({ tools })` (or a provider converter) receives two tools * with the same public `name`. * * The common case is a provider-native factory (`webSearchTool()`) next to an * ordinary function that reused the reserved name (`web_search`). Providers * reject that pair, so we fail before the request is built. */ export class DuplicateToolNameError extends Error { readonly toolName: string constructor(toolName: string, message: string) { super(message) this.name = 'DuplicateToolNameError' this.toolName = toolName } } function isProviderNativeTool(tool: Tool): boolean { const kind = tool.metadata?.['__kind'] return typeof kind === 'string' && kind.length > 0 } function nativeAndCustomMessage(toolName: string) { return [ `Cannot pass two tools named "${toolName}" in the same chat() call.`, `One is the provider-native tool from a factory (for example webSearchTool()).`, `The other is your own function with the same public name.`, `Tool names in one tools array must be unique.`, `Keep the factory for hosted search, or keep your function and give it a different name.`, ].join(' ') } function duplicateNameMessage(toolName: string) { return [ `Cannot pass two tools named "${toolName}" in the same chat() call.`, `Tool names in one tools array must be unique.`, ].join(' ') } /** * Throws {@link DuplicateToolNameError} when two tools share a public name. * * The native-vs-custom message fires when one of the colliding tools carries * adapter `metadata.__kind` (set by a provider factory) and another does not. */ export function assertUniqueToolNames(tools: ReadonlyArray<Tool>): void { const byName = new Map<string, Array<Tool>>() for (const tool of tools) { const group = byName.get(tool.name) if (group) { group.push(tool) } else { byName.set(tool.name, [tool]) } } for (const [name, group] of byName) { if (group.length < 2) { continue } const hasNative = group.some(isProviderNativeTool) const hasCustom = group.some((tool) => !isProviderNativeTool(tool)) throw new DuplicateToolNameError( name, hasNative && hasCustom ? nativeAndCustomMessage(name) : duplicateNameMessage(name), ) } }