UNPKG

@ai-sdk/google

Version:

The **[Google Generative AI provider](https://ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai)** for the [AI SDK](https://ai-sdk.dev/docs) contains language model support for the [Google Generative AI](https://ai.google/discover/generativeai/)

246 lines (234 loc) 8.83 kB
import type { LanguageModelV3CallOptions, SharedV3Warning, } from '@ai-sdk/provider'; import type { GoogleInteractionsTool, GoogleInteractionsToolChoice, } from './google-interactions-prompt'; export type PrepareGoogleInteractionsToolsResult = { tools: Array<GoogleInteractionsTool> | undefined; toolChoice: GoogleInteractionsToolChoice | undefined; toolWarnings: Array<SharedV3Warning>; }; /** * Maps AI SDK tool definitions and `toolChoice` onto the Gemini Interactions * `tools[]` and `tool_choice` request fields. * * AI SDK function tools (`{ type: 'function', name, description, inputSchema }`) * map to Interactions `{ type: 'function', name, description, parameters }`, * with `parameters` passed through as plain JSON Schema (per * `googleapis/js-genai` `samples/interactions_tool_call_with_functions.ts` and * `src/interactions/resources/interactions.ts` `Function.parameters: unknown`). * * Provider-defined tools (`{ type: 'provider', id: 'google.<name>', args }`) * map to the discriminated `Tool` union. The full set of * provider-defined tool ids supported here: * * - `google.google_search` -> `{ type: 'google_search', search_types? }` * - `google.code_execution` -> `{ type: 'code_execution' }` * - `google.url_context` -> `{ type: 'url_context' }` * - `google.file_search` -> `{ type: 'file_search', file_search_store_names?, top_k?, metadata_filter? }` * - `google.google_maps` -> `{ type: 'google_maps', latitude?, longitude?, enable_widget? }` * - `google.computer_use` -> `{ type: 'computer_use', environment?, excludedPredefinedFunctions? }` * - `google.mcp_server` -> `{ type: 'mcp_server', name?, url?, headers?, allowed_tools? }` * - `google.retrieval` -> `{ type: 'retrieval', retrieval_types?, vertex_ai_search_config? }` * * `toolChoice` shapes: * - `'auto'` -> `'auto'` * - `'required'` -> `'any'` * - `'none'` -> `'none'` * - `{ type: 'tool', toolName }` -> `{ allowed_tools: { mode: 'validated', tools: [name] } }` * (Interactions `AllowedTools.tools` is an `Array<string>` of function * names, not tool descriptors -- see `src/interactions/resources/interactions.ts` * line ~151). */ export function prepareGoogleInteractionsTools({ tools, toolChoice, }: { tools: LanguageModelV3CallOptions['tools']; toolChoice?: LanguageModelV3CallOptions['toolChoice']; }): PrepareGoogleInteractionsToolsResult { const toolWarnings: Array<SharedV3Warning> = []; const normalized = tools?.length ? tools : undefined; if (normalized == null) { return { tools: undefined, toolChoice: undefined, toolWarnings }; } const interactionsTools: Array<GoogleInteractionsTool> = []; for (const tool of normalized) { if (tool.type === 'function') { interactionsTools.push({ type: 'function', name: tool.name, description: tool.description ?? '', parameters: tool.inputSchema, }); continue; } if (tool.type === 'provider') { const args = (tool.args ?? {}) as Record<string, unknown>; switch (tool.id) { case 'google.google_search': { const searchTypesArg = args.searchTypes as | { webSearch?: unknown; imageSearch?: unknown } | undefined; let search_types: | Array<'web_search' | 'image_search' | 'enterprise_web_search'> | undefined; if (searchTypesArg != null && typeof searchTypesArg === 'object') { const list: Array< 'web_search' | 'image_search' | 'enterprise_web_search' > = []; if (searchTypesArg.webSearch != null) list.push('web_search'); if (searchTypesArg.imageSearch != null) list.push('image_search'); if (list.length > 0) { search_types = list; } } interactionsTools.push({ type: 'google_search', ...(search_types != null ? { search_types } : {}), }); break; } case 'google.code_execution': { interactionsTools.push({ type: 'code_execution' }); break; } case 'google.url_context': { interactionsTools.push({ type: 'url_context' }); break; } case 'google.file_search': { interactionsTools.push({ type: 'file_search', ...(args.fileSearchStoreNames != null ? { file_search_store_names: args.fileSearchStoreNames as Array<string>, } : {}), ...(args.topK != null ? { top_k: args.topK as number } : {}), ...(args.metadataFilter != null ? { metadata_filter: args.metadataFilter as string } : {}), }); break; } case 'google.google_maps': { interactionsTools.push({ type: 'google_maps', ...(args.latitude != null ? { latitude: args.latitude as number } : {}), ...(args.longitude != null ? { longitude: args.longitude as number } : {}), ...(args.enableWidget != null ? { enable_widget: args.enableWidget as boolean } : {}), }); break; } case 'google.computer_use': { interactionsTools.push({ type: 'computer_use', environment: (args.environment as 'browser' | undefined) ?? 'browser', ...(args.excludedPredefinedFunctions != null ? { excludedPredefinedFunctions: args.excludedPredefinedFunctions as Array<string>, } : {}), }); break; } case 'google.mcp_server': { interactionsTools.push({ type: 'mcp_server', ...(args.name != null ? { name: args.name as string } : {}), ...(args.url != null ? { url: args.url as string } : {}), ...(args.headers != null ? { headers: args.headers as Record<string, string> } : {}), ...(args.allowedTools != null ? { allowed_tools: args.allowedTools as Array<unknown> } : {}), }); break; } case 'google.retrieval': { const vertexAiSearchConfig = (args.vertexAiSearchConfig as | { datastores?: Array<string>; engine?: string } | undefined) ?? undefined; interactionsTools.push({ type: 'retrieval', ...(args.retrievalTypes != null ? { retrieval_types: args.retrievalTypes as Array<'vertex_ai_search'>, } : { retrieval_types: ['vertex_ai_search'] }), ...(vertexAiSearchConfig != null ? { vertex_ai_search_config: vertexAiSearchConfig } : {}), }); break; } default: { toolWarnings.push({ type: 'unsupported', feature: `provider-defined tool ${tool.id}`, details: `provider-defined tool ${tool.id} is not supported by google.interactions; tool dropped.`, }); break; } } continue; } toolWarnings.push({ type: 'unsupported', feature: `tool of type ${(tool as { type: string }).type}`, details: 'Only function tools and google.* provider-defined tools are supported by google.interactions; tool dropped.', }); } /* * `tool_choice` on the Interactions API governs function calling only -- the * API rejects requests with `tool_choice` set when no `function` tools are * present (`{"error":{"message":"Function calling config is set without * function_declarations."}}`). Drop `tool_choice` when the resolved tool * list is empty or contains no function tools. */ const hasFunctionTool = interactionsTools.some(t => t.type === 'function'); let mappedToolChoice: GoogleInteractionsToolChoice | undefined; if (toolChoice != null && hasFunctionTool) { switch (toolChoice.type) { case 'auto': mappedToolChoice = 'auto'; break; case 'required': mappedToolChoice = 'any'; break; case 'none': mappedToolChoice = 'none'; break; case 'tool': mappedToolChoice = { allowed_tools: { mode: 'validated', tools: [toolChoice.toolName], }, }; break; } } return { tools: interactionsTools.length > 0 ? interactionsTools : undefined, toolChoice: mappedToolChoice, toolWarnings, }; }