@tanstack/ai-gemini 0.14.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -621,13 +621,6 @@ class GeminiTextAdapter extends BaseTextAdapter {
621
621
  contents: this.formatMessages(options.messages),
622
622
  config: {
623
623
  ...modelOpts,
624
- ...options.temperature !== void 0 && {
625
- temperature: options.temperature
626
- },
627
- ...options.topP !== void 0 && { topP: options.topP },
628
- ...options.maxTokens !== void 0 && {
629
- maxOutputTokens: options.maxTokens
630
- },
631
624
  ...mappedThinkingConfig !== void 0 && {
632
625
  thinkingConfig: mappedThinkingConfig
633
626
  },
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { FinishReason } from '@google/genai'\nimport { EventType, normalizeSystemPrompts } from '@tanstack/ai'\nimport { toRunErrorRawEvent } from '@tanstack/ai/adapter-internals'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { convertToolsToProviderFormat } from '../tools/tool-converter'\nimport { buildGeminiUsage } from '../usage'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport { GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS } from '../model-meta'\nimport type {\n GEMINI_MODELS,\n GeminiChatModelProviderOptionsByName,\n GeminiChatModelToolCapabilitiesByName,\n GeminiModelInputModalitiesByName,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type {\n Content,\n GenerateContentParameters,\n GenerateContentResponse,\n GoogleGenAI,\n Part,\n ThinkingLevel,\n} from '@google/genai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type { ExternalTextProviderOptions } from '../text/text-provider-options'\nimport type {\n GeminiMessageMetadataByModality,\n GeminiToolCallMetadata,\n} from '../message-types'\nimport type { GeminiClientConfig } from '../utils'\n\n/**\n * Configuration for Gemini text adapter\n */\nexport interface GeminiTextConfig extends GeminiClientConfig {}\n\n/**\n * Gemini-specific provider options for text/chat\n */\nexport type GeminiTextProviderOptions = ExternalTextProviderOptions\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model.\n * If the model has explicit options in the map, use those; otherwise use base options.\n */\ntype ResolveProviderOptions<TModel extends string> =\n TModel extends keyof GeminiChatModelProviderOptionsByName\n ? GeminiChatModelProviderOptionsByName[TModel]\n : GeminiTextProviderOptions\n\n/**\n * Resolve input modalities for a specific model.\n * If the model has explicit modalities in the map, use those; otherwise use all modalities.\n */\ntype ResolveInputModalities<TModel extends string> =\n TModel extends keyof GeminiModelInputModalitiesByName\n ? GeminiModelInputModalitiesByName[TModel]\n : readonly ['text', 'image', 'audio', 'video', 'document']\n\n/**\n * Resolve tool capabilities for a specific model.\n * If the model has explicit tools in the map, use those; otherwise use empty tuple.\n */\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GeminiChatModelToolCapabilitiesByName\n ? NonNullable<GeminiChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n// ===========================\n// Adapter Implementation\n// ===========================\n\n/**\n * Gemini Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Gemini chat/text completion functionality.\n * Import only what you need for smaller bundle sizes.\n */\nexport class GeminiTextAdapter<\n TModel extends (typeof GEMINI_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GeminiMessageMetadataByModality,\n TToolCapabilities,\n GeminiToolCallMetadata\n> {\n override readonly kind = 'text' as const\n readonly name = 'gemini' as const\n\n private readonly client: GoogleGenAI\n\n constructor(config: GeminiTextConfig, model: TModel) {\n super({}, model)\n this.client = createGeminiClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GeminiTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const mappedOptions = this.mapCommonOptionsToGemini(options)\n const { logger } = options\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: 'gemini', model: this.model },\n )\n const result =\n await this.client.models.generateContentStream(mappedOptions)\n\n yield* this.processStreamChunks(result, options, logger)\n } catch (error) {\n const rawEvent = toRunErrorRawEvent(error)\n logger.errors('gemini.chatStream fatal', {\n error,\n source: 'gemini.chatStream',\n })\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\n // Forward the provider's structured error body when present (see\n // toRunErrorRawEvent); omitted otherwise.\n ...(rawEvent !== undefined && { rawEvent }),\n error: {\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\n },\n }\n }\n }\n\n /**\n * Generate structured output using Gemini's native JSON response format.\n * Uses responseMimeType: 'application/json' and responseSchema for structured output.\n * The outputSchema is already JSON Schema (converted in the ai layer).\n */\n async structuredOutput(\n options: StructuredOutputOptions<GeminiTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const { logger } = chatOptions\n\n const mappedOptions = this.mapCommonOptionsToGemini(chatOptions)\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,\n { provider: 'gemini', model: this.model },\n )\n // Add structured output configuration\n const result = await this.client.models.generateContent({\n ...mappedOptions,\n config: {\n ...mappedOptions.config,\n responseMimeType: 'application/json',\n responseSchema: outputSchema,\n },\n })\n\n // Extract text content from the response\n const rawText = this.extractTextFromResponse(result)\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n return {\n data: parsed,\n rawText,\n usage: result.usageMetadata\n ? buildGeminiUsage(result.usageMetadata)\n : undefined,\n }\n } catch (error) {\n logger.errors('gemini.structuredOutput fatal', {\n error,\n source: 'gemini.structuredOutput',\n })\n throw new Error(\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during structured output generation.',\n )\n }\n }\n\n /**\n * Extract text content from a non-streaming response\n */\n private extractTextFromResponse(response: GenerateContentResponse): string {\n let textContent = ''\n\n if (response.candidates?.[0]?.content?.parts) {\n for (const part of response.candidates[0].content.parts) {\n if (part.text) {\n textContent += part.text\n }\n }\n }\n\n return textContent\n }\n\n private async *processStreamChunks(\n result: AsyncGenerator<GenerateContentResponse, unknown, unknown>,\n options: TextOptions<GeminiTextProviderOptions>,\n logger: InternalLogger,\n ): AsyncIterable<StreamChunk> {\n const model = options.model\n let accumulatedContent = ''\n let accumulatedThinking = ''\n const toolCallMap = new Map<\n string,\n {\n name: string\n args: string\n index: number\n started: boolean\n thoughtSignature?: string\n }\n >()\n let nextToolIndex = 0\n\n // AG-UI lifecycle tracking\n const runId = options.runId ?? generateId(this.name)\n const threadId = options.threadId ?? generateId(this.name)\n const messageId = generateId(this.name)\n let stepId: string | null = null\n let reasoningMessageId: string | null = null\n let hasClosedReasoning = false\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let hasEmittedStepStarted = false\n\n for await (const chunk of result) {\n logger.provider(`provider=gemini`, { chunk })\n // Emit RUN_STARTED on first chunk\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n }\n\n if (chunk.candidates?.[0]?.content?.parts) {\n const parts = chunk.candidates[0].content.parts\n\n for (const part of parts) {\n if (part.text) {\n if (part.thought) {\n // Emit STEP_STARTED and REASONING events on first thinking content\n if (!hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n reasoningMessageId = generateId(this.name)\n\n // Spec REASONING events\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model,\n timestamp: Date.now(),\n }\n\n // Legacy STEP events (kept during transition)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n accumulatedThinking += part.text\n\n // Spec REASONING content event — reasoningMessageId is set in the\n // hasEmittedStepStarted block above (entered on the same `part.thought` path)\n if (!reasoningMessageId) continue\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: part.text,\n model,\n timestamp: Date.now(),\n }\n\n // Legacy STEP event\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId || generateId(this.name),\n stepId: stepId || generateId(this.name),\n model,\n timestamp: Date.now(),\n delta: part.text,\n content: accumulatedThinking,\n }\n } else if (part.text.trim()) {\n // Close reasoning before text starts\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n // Skip whitespace-only text parts (e.g. \"\\n\" during auto-continuation)\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += part.text\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: part.text,\n content: accumulatedContent,\n }\n }\n }\n\n const functionCall = part.functionCall\n if (functionCall) {\n const toolCallId =\n functionCall.id ||\n `${functionCall.name}_${Date.now()}_${nextToolIndex}`\n const functionArgs = functionCall.args || {}\n\n // Gemini emits thoughtSignature as a Part-level sibling of\n // functionCall (per @google/genai Part type), not nested inside\n // functionCall itself.\n const partThoughtSignature = part.thoughtSignature || undefined\n\n let toolCallData = toolCallMap.get(toolCallId)\n if (!toolCallData) {\n toolCallData = {\n name: functionCall.name || '',\n args:\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs),\n index: nextToolIndex++,\n started: false,\n // Only set thoughtSignature when present — under EOPT, the\n // optional field cannot accept an explicit `undefined`.\n ...(partThoughtSignature !== undefined && {\n thoughtSignature: partThoughtSignature,\n }),\n }\n toolCallMap.set(toolCallId, toolCallData)\n } else {\n if (!toolCallData.thoughtSignature && partThoughtSignature) {\n toolCallData.thoughtSignature = partThoughtSignature\n }\n try {\n const existingArgs = JSON.parse(toolCallData.args)\n const newArgs =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n const mergedArgs = { ...existingArgs, ...newArgs }\n toolCallData.args = JSON.stringify(mergedArgs)\n } catch {\n toolCallData.args =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n }\n }\n\n // Emit TOOL_CALL_START if not already started\n if (!toolCallData.started) {\n toolCallData.started = true\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n index: toolCallData.index,\n ...(toolCallData.thoughtSignature && {\n metadata: {\n thoughtSignature: toolCallData.thoughtSignature,\n } satisfies GeminiToolCallMetadata,\n }),\n }\n }\n\n // Emit TOOL_CALL_ARGS\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n model,\n timestamp: Date.now(),\n delta: toolCallData.args,\n args: toolCallData.args,\n }\n }\n }\n } else if (chunk.data && chunk.data.trim()) {\n // Skip whitespace-only data (e.g. \"\\n\" during auto-continuation)\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += chunk.data\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: chunk.data,\n content: accumulatedContent,\n }\n }\n\n if (chunk.candidates?.[0]?.finishReason) {\n const finishReason = chunk.candidates[0].finishReason\n\n if (finishReason === FinishReason.UNEXPECTED_TOOL_CALL) {\n if (chunk.candidates[0].content?.parts) {\n for (const part of chunk.candidates[0].content.parts) {\n const functionCall = part.functionCall\n if (functionCall) {\n const toolCallId =\n functionCall.id ||\n `${functionCall.name}_${Date.now()}_${nextToolIndex}`\n const functionArgs = functionCall.args || {}\n\n const argsString =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n\n toolCallMap.set(toolCallId, {\n name: functionCall.name || '',\n args: argsString,\n index: nextToolIndex++,\n started: true,\n })\n\n // Emit TOOL_CALL_START\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n index: nextToolIndex - 1,\n }\n\n // Emit TOOL_CALL_END with parsed input\n let parsedInput: unknown = {}\n try {\n const parsed =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n }\n }\n }\n\n // Emit TOOL_CALL_END for all tracked tool calls\n for (const [toolCallId, toolCallData] of toolCallMap.entries()) {\n let parsedInput: unknown = {}\n try {\n const parsed = JSON.parse(toolCallData.args)\n parsedInput = parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n\n // Reset so a new TEXT_MESSAGE_START is emitted if text follows tool calls\n if (toolCallMap.size > 0) {\n hasEmittedTextMessageStart = false\n }\n\n if (finishReason === FinishReason.MAX_TOKENS) {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model,\n timestamp: Date.now(),\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n error: {\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n },\n }\n }\n\n // Close reasoning events if still open\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n finishReason: toolCallMap.size > 0 ? 'tool_calls' : 'stop',\n // RunFinishedEvent.usage is `usage?: {...}` (no `| undefined`) under\n // exactOptionalPropertyTypes; only include it when usageMetadata is\n // present rather than assigning an explicit `undefined`.\n ...(chunk.usageMetadata && {\n usage: buildGeminiUsage(chunk.usageMetadata),\n }),\n }\n }\n }\n }\n\n private convertContentPartToGemini(part: ContentPart): Part {\n switch (part.type) {\n case 'text':\n return { text: part.content }\n case 'image':\n case 'audio':\n case 'video':\n case 'document': {\n if (part.source.type === 'data') {\n return {\n inlineData: {\n data: part.source.value,\n mimeType: part.source.mimeType,\n },\n }\n } else {\n // For URL sources, use provided mimeType or fall back to reasonable defaults\n const defaultMimeType = {\n image: 'image/jpeg',\n audio: 'audio/mp3',\n video: 'video/mp4',\n document: 'application/pdf',\n }[part.type]\n\n return {\n fileData: {\n fileUri: part.source.value,\n mimeType: part.source.mimeType ?? defaultMimeType,\n },\n }\n }\n }\n default: {\n const _exhaustiveCheck: never = part\n throw new Error(\n `Unsupported content part type: ${(_exhaustiveCheck as ContentPart).type}`,\n )\n }\n }\n }\n\n private formatMessages(\n messages: Array<ModelMessage>,\n ): GenerateContentParameters['contents'] {\n // Build a lookup from toolCallId → function name so functionResponse uses the\n // correct name instead of the raw call ID.\n const toolCallIdToName = new Map<string, string>()\n for (const msg of messages) {\n if (msg.role === 'assistant' && msg.toolCalls) {\n for (const tc of msg.toolCalls) {\n toolCallIdToName.set(tc.id, tc.function.name)\n }\n }\n }\n\n const formatted = messages.map((msg) => {\n const role: 'user' | 'model' = msg.role === 'assistant' ? 'model' : 'user'\n const parts: Array<Part> = []\n\n if (Array.isArray(msg.content)) {\n for (const contentPart of msg.content) {\n parts.push(this.convertContentPartToGemini(contentPart))\n }\n } else if (msg.content && msg.role !== 'tool') {\n parts.push({ text: msg.content })\n }\n\n if (msg.role === 'assistant' && msg.toolCalls?.length) {\n for (const toolCall of msg.toolCalls) {\n let parsedArgs: Record<string, unknown> = {}\n try {\n parsedArgs = toolCall.function.arguments\n ? (JSON.parse(toolCall.function.arguments) as Record<\n string,\n unknown\n >)\n : {}\n } catch {\n parsedArgs = {}\n }\n\n const thoughtSignature = (\n toolCall.metadata as GeminiToolCallMetadata | undefined\n )?.thoughtSignature\n // Gemini requires thoughtSignature at the Part level (sibling of\n // functionCall), not nested inside functionCall. Nesting it causes\n // the API to reject the next turn with\n // \"Function call is missing a thought_signature\".\n const part: Part = {\n functionCall: {\n id: toolCall.id,\n name: toolCall.function.name,\n args: parsedArgs,\n },\n }\n if (thoughtSignature) {\n part.thoughtSignature = thoughtSignature\n }\n parts.push(part)\n }\n }\n\n if (msg.role === 'tool' && msg.toolCallId) {\n const functionName =\n toolCallIdToName.get(msg.toolCallId) || msg.toolCallId\n const toolContent = msg.content\n if (Array.isArray(toolContent)) {\n const textChunks: Array<string> = []\n const mediaParts: Array<Part> = []\n for (const part of toolContent) {\n if (part.type === 'text') {\n textChunks.push(part.content)\n } else if (part.source.type === 'data') {\n mediaParts.push({\n inlineData: {\n data: part.source.value,\n mimeType: part.source.mimeType,\n },\n })\n } else {\n const defaultMimeType = {\n image: 'image/jpeg',\n audio: 'audio/mp3',\n video: 'video/mp4',\n document: 'application/pdf',\n }[part.type]\n mediaParts.push({\n fileData: {\n fileUri: part.source.value,\n mimeType: part.source.mimeType ?? defaultMimeType,\n },\n })\n }\n }\n parts.push({\n functionResponse: {\n id: msg.toolCallId,\n name: functionName,\n response: { content: textChunks.join('\\n') },\n ...(mediaParts.length > 0 && { parts: mediaParts }),\n },\n })\n } else {\n parts.push({\n functionResponse: {\n id: msg.toolCallId,\n name: functionName,\n response: { content: toolContent || '' },\n },\n })\n }\n }\n\n return {\n role,\n parts: parts.length > 0 ? parts : [{ text: '' }],\n }\n })\n\n // Post-process: Gemini requires strictly alternating user/model roles.\n // Tool results are mapped to role:'user', which can create consecutive\n // user messages when followed by a new user message. Merge them.\n return this.mergeConsecutiveSameRoleMessages(formatted)\n }\n\n /**\n * Merge consecutive messages of the same role into a single message.\n * Gemini's API requires strictly alternating user/model roles.\n * Tool results are mapped to role:'user', which can collide with actual\n * user messages in multi-turn conversations.\n *\n * Also filters out empty model messages (e.g., from a previous failed request)\n * and deduplicates functionResponse parts with the same name (tool call ID).\n */\n private mergeConsecutiveSameRoleMessages(\n messages: Array<Content>,\n ): Array<Content> {\n const merged: Array<Content> = []\n\n for (const msg of messages) {\n const parts = msg.parts || []\n\n // Skip empty model messages (no parts or only empty text)\n if (msg.role === 'model') {\n const hasContent =\n parts.length > 0 &&\n !parts.every(\n (p) => 'text' in p && (p as { text: string }).text === '',\n )\n if (!hasContent) {\n continue\n }\n }\n\n const prev = merged[merged.length - 1]\n if (prev && prev.role === msg.role) {\n // Merge parts arrays\n prev.parts = [...(prev.parts || []), ...parts]\n } else {\n merged.push({ ...msg, parts: [...parts] })\n }\n }\n\n // Deduplicate functionResponse parts with the same name (tool call ID)\n for (const msg of merged) {\n if (!msg.parts) continue\n const seenFunctionResponseNames = new Set<string>()\n msg.parts = msg.parts.filter((part) => {\n if ('functionResponse' in part && part.functionResponse?.name) {\n if (seenFunctionResponseNames.has(part.functionResponse.name)) {\n return false\n }\n seenFunctionResponseNames.add(part.functionResponse.name)\n }\n return true\n })\n }\n\n return merged\n }\n\n private mapCommonOptionsToGemini(\n options: TextOptions<GeminiTextProviderOptions>,\n ) {\n // Separate `thinkingConfig` from the other model options so the loose\n // local `thinkingLevel?: keyof typeof ThinkingLevel` type doesn't leak\n // into the SDK config object via the `...modelOpts` spread — we re-add a\n // properly-typed `ThinkingConfig` below.\n const { thinkingConfig, ...modelOpts } = options.modelOptions ?? {}\n // Build the thinkingConfig payload only when the caller actually supplied\n // one. Our local `thinkingLevel` is typed as `keyof typeof ThinkingLevel`\n // (string union) so users can pass plain strings; the SDK target is the\n // `ThinkingLevel` enum, and every field is `field?: T` under EOPT — so we\n // re-emit fields via conditional spreads.\n const mappedThinkingConfig = thinkingConfig\n ? {\n ...(thinkingConfig.includeThoughts !== undefined && {\n includeThoughts: thinkingConfig.includeThoughts,\n }),\n ...(thinkingConfig.thinkingBudget !== undefined && {\n thinkingBudget: thinkingConfig.thinkingBudget,\n }),\n ...(thinkingConfig.thinkingLevel\n ? {\n thinkingLevel: thinkingConfig.thinkingLevel as ThinkingLevel,\n }\n : {}),\n }\n : undefined\n\n const normalizedPrompts = normalizeSystemPrompts(options.systemPrompts)\n const systemInstruction =\n normalizedPrompts.length > 0\n ? normalizedPrompts.map((p) => p.content).join('\\n')\n : undefined\n\n // Native combined mode (issue #605): when the engine threads\n // `outputSchema` through TextOptions, the adapter declared\n // `supportsCombinedToolsAndSchema` (Gemini 3.x only). The schema is\n // already JSON Schema (pre-converted at the activity boundary). Wire\n // it into `config.responseSchema` + `responseMimeType: 'application/json'`\n // alongside any `tools` — the model emits function calls during the\n // agent loop and the schema-constrained JSON on its natural final\n // turn, so the engine can harvest it without the separate\n // `structuredOutput` finalization round-trip.\n const combinedSchema = options.outputSchema as\n | Record<string, unknown>\n | undefined\n const combinedSchemaConfig = combinedSchema\n ? {\n responseMimeType: 'application/json' as const,\n responseSchema: combinedSchema,\n }\n : undefined\n\n // Vendor `GenerateContentConfig` fields are `field?: T` (no `| undefined`)\n // under EOPT, so spread each common option only when present rather than\n // emitting `field: undefined`s into the wire payload.\n const requestOptions: GenerateContentParameters = {\n model: options.model,\n contents: this.formatMessages(options.messages),\n config: {\n ...modelOpts,\n ...(options.temperature !== undefined && {\n temperature: options.temperature,\n }),\n ...(options.topP !== undefined && { topP: options.topP }),\n ...(options.maxTokens !== undefined && {\n maxOutputTokens: options.maxTokens,\n }),\n ...(mappedThinkingConfig !== undefined && {\n thinkingConfig: mappedThinkingConfig,\n }),\n ...(systemInstruction !== undefined && { systemInstruction }),\n tools: convertToolsToProviderFormat(options.tools),\n ...(combinedSchemaConfig ?? {}),\n },\n }\n\n return requestOptions\n }\n\n /**\n * Gemini 3.x natively combines `tools` + `responseSchema` in a single\n * streaming `generateContentStream` call (issue #605). Gemini 2.x is\n * documented as brittle for the combination and keeps the engine's\n * legacy finalization path.\n */\n supportsCombinedToolsAndSchema(): boolean {\n return GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS.has(this.model)\n }\n}\n\n/**\n * Creates a Gemini text adapter with explicit API key.\n * Type resolution happens here at the call site.\n */\nexport function createGeminiChat<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n return new GeminiTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Gemini text adapter with automatic API key detection.\n * Type resolution happens here at the call site.\n */\nexport function geminiText<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiChat(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { FinishReason } from '@google/genai'\nimport { EventType, normalizeSystemPrompts } from '@tanstack/ai'\nimport { toRunErrorRawEvent } from '@tanstack/ai/adapter-internals'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { convertToolsToProviderFormat } from '../tools/tool-converter'\nimport { buildGeminiUsage } from '../usage'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport { GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS } from '../model-meta'\nimport type {\n GEMINI_MODELS,\n GeminiChatModelProviderOptionsByName,\n GeminiChatModelToolCapabilitiesByName,\n GeminiModelInputModalitiesByName,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type {\n Content,\n GenerateContentParameters,\n GenerateContentResponse,\n GoogleGenAI,\n Part,\n ThinkingLevel,\n} from '@google/genai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type { ExternalTextProviderOptions } from '../text/text-provider-options'\nimport type {\n GeminiMessageMetadataByModality,\n GeminiToolCallMetadata,\n} from '../message-types'\nimport type { GeminiClientConfig } from '../utils'\n\n/**\n * Configuration for Gemini text adapter\n */\nexport interface GeminiTextConfig extends GeminiClientConfig {}\n\n/**\n * Gemini-specific provider options for text/chat\n */\nexport type GeminiTextProviderOptions = ExternalTextProviderOptions\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model.\n * If the model has explicit options in the map, use those; otherwise use base options.\n */\ntype ResolveProviderOptions<TModel extends string> =\n TModel extends keyof GeminiChatModelProviderOptionsByName\n ? GeminiChatModelProviderOptionsByName[TModel]\n : GeminiTextProviderOptions\n\n/**\n * Resolve input modalities for a specific model.\n * If the model has explicit modalities in the map, use those; otherwise use all modalities.\n */\ntype ResolveInputModalities<TModel extends string> =\n TModel extends keyof GeminiModelInputModalitiesByName\n ? GeminiModelInputModalitiesByName[TModel]\n : readonly ['text', 'image', 'audio', 'video', 'document']\n\n/**\n * Resolve tool capabilities for a specific model.\n * If the model has explicit tools in the map, use those; otherwise use empty tuple.\n */\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GeminiChatModelToolCapabilitiesByName\n ? NonNullable<GeminiChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n// ===========================\n// Adapter Implementation\n// ===========================\n\n/**\n * Gemini Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Gemini chat/text completion functionality.\n * Import only what you need for smaller bundle sizes.\n */\nexport class GeminiTextAdapter<\n TModel extends (typeof GEMINI_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GeminiMessageMetadataByModality,\n TToolCapabilities,\n GeminiToolCallMetadata\n> {\n override readonly kind = 'text' as const\n readonly name = 'gemini' as const\n\n private readonly client: GoogleGenAI\n\n constructor(config: GeminiTextConfig, model: TModel) {\n super({}, model)\n this.client = createGeminiClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GeminiTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const mappedOptions = this.mapCommonOptionsToGemini(options)\n const { logger } = options\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: 'gemini', model: this.model },\n )\n const result =\n await this.client.models.generateContentStream(mappedOptions)\n\n yield* this.processStreamChunks(result, options, logger)\n } catch (error) {\n const rawEvent = toRunErrorRawEvent(error)\n logger.errors('gemini.chatStream fatal', {\n error,\n source: 'gemini.chatStream',\n })\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\n // Forward the provider's structured error body when present (see\n // toRunErrorRawEvent); omitted otherwise.\n ...(rawEvent !== undefined && { rawEvent }),\n error: {\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\n },\n }\n }\n }\n\n /**\n * Generate structured output using Gemini's native JSON response format.\n * Uses responseMimeType: 'application/json' and responseSchema for structured output.\n * The outputSchema is already JSON Schema (converted in the ai layer).\n */\n async structuredOutput(\n options: StructuredOutputOptions<GeminiTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const { logger } = chatOptions\n\n const mappedOptions = this.mapCommonOptionsToGemini(chatOptions)\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,\n { provider: 'gemini', model: this.model },\n )\n // Add structured output configuration\n const result = await this.client.models.generateContent({\n ...mappedOptions,\n config: {\n ...mappedOptions.config,\n responseMimeType: 'application/json',\n responseSchema: outputSchema,\n },\n })\n\n // Extract text content from the response\n const rawText = this.extractTextFromResponse(result)\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n return {\n data: parsed,\n rawText,\n usage: result.usageMetadata\n ? buildGeminiUsage(result.usageMetadata)\n : undefined,\n }\n } catch (error) {\n logger.errors('gemini.structuredOutput fatal', {\n error,\n source: 'gemini.structuredOutput',\n })\n throw new Error(\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during structured output generation.',\n )\n }\n }\n\n /**\n * Extract text content from a non-streaming response\n */\n private extractTextFromResponse(response: GenerateContentResponse): string {\n let textContent = ''\n\n if (response.candidates?.[0]?.content?.parts) {\n for (const part of response.candidates[0].content.parts) {\n if (part.text) {\n textContent += part.text\n }\n }\n }\n\n return textContent\n }\n\n private async *processStreamChunks(\n result: AsyncGenerator<GenerateContentResponse, unknown, unknown>,\n options: TextOptions<GeminiTextProviderOptions>,\n logger: InternalLogger,\n ): AsyncIterable<StreamChunk> {\n const model = options.model\n let accumulatedContent = ''\n let accumulatedThinking = ''\n const toolCallMap = new Map<\n string,\n {\n name: string\n args: string\n index: number\n started: boolean\n thoughtSignature?: string\n }\n >()\n let nextToolIndex = 0\n\n // AG-UI lifecycle tracking\n const runId = options.runId ?? generateId(this.name)\n const threadId = options.threadId ?? generateId(this.name)\n const messageId = generateId(this.name)\n let stepId: string | null = null\n let reasoningMessageId: string | null = null\n let hasClosedReasoning = false\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let hasEmittedStepStarted = false\n\n for await (const chunk of result) {\n logger.provider(`provider=gemini`, { chunk })\n // Emit RUN_STARTED on first chunk\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n }\n\n if (chunk.candidates?.[0]?.content?.parts) {\n const parts = chunk.candidates[0].content.parts\n\n for (const part of parts) {\n if (part.text) {\n if (part.thought) {\n // Emit STEP_STARTED and REASONING events on first thinking content\n if (!hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n reasoningMessageId = generateId(this.name)\n\n // Spec REASONING events\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model,\n timestamp: Date.now(),\n }\n\n // Legacy STEP events (kept during transition)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n accumulatedThinking += part.text\n\n // Spec REASONING content event — reasoningMessageId is set in the\n // hasEmittedStepStarted block above (entered on the same `part.thought` path)\n if (!reasoningMessageId) continue\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: part.text,\n model,\n timestamp: Date.now(),\n }\n\n // Legacy STEP event\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId || generateId(this.name),\n stepId: stepId || generateId(this.name),\n model,\n timestamp: Date.now(),\n delta: part.text,\n content: accumulatedThinking,\n }\n } else if (part.text.trim()) {\n // Close reasoning before text starts\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n // Skip whitespace-only text parts (e.g. \"\\n\" during auto-continuation)\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += part.text\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: part.text,\n content: accumulatedContent,\n }\n }\n }\n\n const functionCall = part.functionCall\n if (functionCall) {\n const toolCallId =\n functionCall.id ||\n `${functionCall.name}_${Date.now()}_${nextToolIndex}`\n const functionArgs = functionCall.args || {}\n\n // Gemini emits thoughtSignature as a Part-level sibling of\n // functionCall (per @google/genai Part type), not nested inside\n // functionCall itself.\n const partThoughtSignature = part.thoughtSignature || undefined\n\n let toolCallData = toolCallMap.get(toolCallId)\n if (!toolCallData) {\n toolCallData = {\n name: functionCall.name || '',\n args:\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs),\n index: nextToolIndex++,\n started: false,\n // Only set thoughtSignature when present — under EOPT, the\n // optional field cannot accept an explicit `undefined`.\n ...(partThoughtSignature !== undefined && {\n thoughtSignature: partThoughtSignature,\n }),\n }\n toolCallMap.set(toolCallId, toolCallData)\n } else {\n if (!toolCallData.thoughtSignature && partThoughtSignature) {\n toolCallData.thoughtSignature = partThoughtSignature\n }\n try {\n const existingArgs = JSON.parse(toolCallData.args)\n const newArgs =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n const mergedArgs = { ...existingArgs, ...newArgs }\n toolCallData.args = JSON.stringify(mergedArgs)\n } catch {\n toolCallData.args =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n }\n }\n\n // Emit TOOL_CALL_START if not already started\n if (!toolCallData.started) {\n toolCallData.started = true\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n index: toolCallData.index,\n ...(toolCallData.thoughtSignature && {\n metadata: {\n thoughtSignature: toolCallData.thoughtSignature,\n } satisfies GeminiToolCallMetadata,\n }),\n }\n }\n\n // Emit TOOL_CALL_ARGS\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n model,\n timestamp: Date.now(),\n delta: toolCallData.args,\n args: toolCallData.args,\n }\n }\n }\n } else if (chunk.data && chunk.data.trim()) {\n // Skip whitespace-only data (e.g. \"\\n\" during auto-continuation)\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += chunk.data\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: chunk.data,\n content: accumulatedContent,\n }\n }\n\n if (chunk.candidates?.[0]?.finishReason) {\n const finishReason = chunk.candidates[0].finishReason\n\n if (finishReason === FinishReason.UNEXPECTED_TOOL_CALL) {\n if (chunk.candidates[0].content?.parts) {\n for (const part of chunk.candidates[0].content.parts) {\n const functionCall = part.functionCall\n if (functionCall) {\n const toolCallId =\n functionCall.id ||\n `${functionCall.name}_${Date.now()}_${nextToolIndex}`\n const functionArgs = functionCall.args || {}\n\n const argsString =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n\n toolCallMap.set(toolCallId, {\n name: functionCall.name || '',\n args: argsString,\n index: nextToolIndex++,\n started: true,\n })\n\n // Emit TOOL_CALL_START\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n index: nextToolIndex - 1,\n }\n\n // Emit TOOL_CALL_END with parsed input\n let parsedInput: unknown = {}\n try {\n const parsed =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n }\n }\n }\n\n // Emit TOOL_CALL_END for all tracked tool calls\n for (const [toolCallId, toolCallData] of toolCallMap.entries()) {\n let parsedInput: unknown = {}\n try {\n const parsed = JSON.parse(toolCallData.args)\n parsedInput = parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n\n // Reset so a new TEXT_MESSAGE_START is emitted if text follows tool calls\n if (toolCallMap.size > 0) {\n hasEmittedTextMessageStart = false\n }\n\n if (finishReason === FinishReason.MAX_TOKENS) {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model,\n timestamp: Date.now(),\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n error: {\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n },\n }\n }\n\n // Close reasoning events if still open\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n finishReason: toolCallMap.size > 0 ? 'tool_calls' : 'stop',\n // RunFinishedEvent.usage is `usage?: {...}` (no `| undefined`) under\n // exactOptionalPropertyTypes; only include it when usageMetadata is\n // present rather than assigning an explicit `undefined`.\n ...(chunk.usageMetadata && {\n usage: buildGeminiUsage(chunk.usageMetadata),\n }),\n }\n }\n }\n }\n\n private convertContentPartToGemini(part: ContentPart): Part {\n switch (part.type) {\n case 'text':\n return { text: part.content }\n case 'image':\n case 'audio':\n case 'video':\n case 'document': {\n if (part.source.type === 'data') {\n return {\n inlineData: {\n data: part.source.value,\n mimeType: part.source.mimeType,\n },\n }\n } else {\n // For URL sources, use provided mimeType or fall back to reasonable defaults\n const defaultMimeType = {\n image: 'image/jpeg',\n audio: 'audio/mp3',\n video: 'video/mp4',\n document: 'application/pdf',\n }[part.type]\n\n return {\n fileData: {\n fileUri: part.source.value,\n mimeType: part.source.mimeType ?? defaultMimeType,\n },\n }\n }\n }\n default: {\n const _exhaustiveCheck: never = part\n throw new Error(\n `Unsupported content part type: ${(_exhaustiveCheck as ContentPart).type}`,\n )\n }\n }\n }\n\n private formatMessages(\n messages: Array<ModelMessage>,\n ): GenerateContentParameters['contents'] {\n // Build a lookup from toolCallId → function name so functionResponse uses the\n // correct name instead of the raw call ID.\n const toolCallIdToName = new Map<string, string>()\n for (const msg of messages) {\n if (msg.role === 'assistant' && msg.toolCalls) {\n for (const tc of msg.toolCalls) {\n toolCallIdToName.set(tc.id, tc.function.name)\n }\n }\n }\n\n const formatted = messages.map((msg) => {\n const role: 'user' | 'model' = msg.role === 'assistant' ? 'model' : 'user'\n const parts: Array<Part> = []\n\n if (Array.isArray(msg.content)) {\n for (const contentPart of msg.content) {\n parts.push(this.convertContentPartToGemini(contentPart))\n }\n } else if (msg.content && msg.role !== 'tool') {\n parts.push({ text: msg.content })\n }\n\n if (msg.role === 'assistant' && msg.toolCalls?.length) {\n for (const toolCall of msg.toolCalls) {\n let parsedArgs: Record<string, unknown> = {}\n try {\n parsedArgs = toolCall.function.arguments\n ? (JSON.parse(toolCall.function.arguments) as Record<\n string,\n unknown\n >)\n : {}\n } catch {\n parsedArgs = {}\n }\n\n const thoughtSignature = (\n toolCall.metadata as GeminiToolCallMetadata | undefined\n )?.thoughtSignature\n // Gemini requires thoughtSignature at the Part level (sibling of\n // functionCall), not nested inside functionCall. Nesting it causes\n // the API to reject the next turn with\n // \"Function call is missing a thought_signature\".\n const part: Part = {\n functionCall: {\n id: toolCall.id,\n name: toolCall.function.name,\n args: parsedArgs,\n },\n }\n if (thoughtSignature) {\n part.thoughtSignature = thoughtSignature\n }\n parts.push(part)\n }\n }\n\n if (msg.role === 'tool' && msg.toolCallId) {\n const functionName =\n toolCallIdToName.get(msg.toolCallId) || msg.toolCallId\n const toolContent = msg.content\n if (Array.isArray(toolContent)) {\n const textChunks: Array<string> = []\n const mediaParts: Array<Part> = []\n for (const part of toolContent) {\n if (part.type === 'text') {\n textChunks.push(part.content)\n } else if (part.source.type === 'data') {\n mediaParts.push({\n inlineData: {\n data: part.source.value,\n mimeType: part.source.mimeType,\n },\n })\n } else {\n const defaultMimeType = {\n image: 'image/jpeg',\n audio: 'audio/mp3',\n video: 'video/mp4',\n document: 'application/pdf',\n }[part.type]\n mediaParts.push({\n fileData: {\n fileUri: part.source.value,\n mimeType: part.source.mimeType ?? defaultMimeType,\n },\n })\n }\n }\n parts.push({\n functionResponse: {\n id: msg.toolCallId,\n name: functionName,\n response: { content: textChunks.join('\\n') },\n ...(mediaParts.length > 0 && { parts: mediaParts }),\n },\n })\n } else {\n parts.push({\n functionResponse: {\n id: msg.toolCallId,\n name: functionName,\n response: { content: toolContent || '' },\n },\n })\n }\n }\n\n return {\n role,\n parts: parts.length > 0 ? parts : [{ text: '' }],\n }\n })\n\n // Post-process: Gemini requires strictly alternating user/model roles.\n // Tool results are mapped to role:'user', which can create consecutive\n // user messages when followed by a new user message. Merge them.\n return this.mergeConsecutiveSameRoleMessages(formatted)\n }\n\n /**\n * Merge consecutive messages of the same role into a single message.\n * Gemini's API requires strictly alternating user/model roles.\n * Tool results are mapped to role:'user', which can collide with actual\n * user messages in multi-turn conversations.\n *\n * Also filters out empty model messages (e.g., from a previous failed request)\n * and deduplicates functionResponse parts with the same name (tool call ID).\n */\n private mergeConsecutiveSameRoleMessages(\n messages: Array<Content>,\n ): Array<Content> {\n const merged: Array<Content> = []\n\n for (const msg of messages) {\n const parts = msg.parts || []\n\n // Skip empty model messages (no parts or only empty text)\n if (msg.role === 'model') {\n const hasContent =\n parts.length > 0 &&\n !parts.every(\n (p) => 'text' in p && (p as { text: string }).text === '',\n )\n if (!hasContent) {\n continue\n }\n }\n\n const prev = merged[merged.length - 1]\n if (prev && prev.role === msg.role) {\n // Merge parts arrays\n prev.parts = [...(prev.parts || []), ...parts]\n } else {\n merged.push({ ...msg, parts: [...parts] })\n }\n }\n\n // Deduplicate functionResponse parts with the same name (tool call ID)\n for (const msg of merged) {\n if (!msg.parts) continue\n const seenFunctionResponseNames = new Set<string>()\n msg.parts = msg.parts.filter((part) => {\n if ('functionResponse' in part && part.functionResponse?.name) {\n if (seenFunctionResponseNames.has(part.functionResponse.name)) {\n return false\n }\n seenFunctionResponseNames.add(part.functionResponse.name)\n }\n return true\n })\n }\n\n return merged\n }\n\n private mapCommonOptionsToGemini(\n options: TextOptions<GeminiTextProviderOptions>,\n ) {\n // Separate `thinkingConfig` from the other model options so the loose\n // local `thinkingLevel?: keyof typeof ThinkingLevel` type doesn't leak\n // into the SDK config object via the `...modelOpts` spread — we re-add a\n // properly-typed `ThinkingConfig` below.\n const { thinkingConfig, ...modelOpts } = options.modelOptions ?? {}\n // Build the thinkingConfig payload only when the caller actually supplied\n // one. Our local `thinkingLevel` is typed as `keyof typeof ThinkingLevel`\n // (string union) so users can pass plain strings; the SDK target is the\n // `ThinkingLevel` enum, and every field is `field?: T` under EOPT — so we\n // re-emit fields via conditional spreads.\n const mappedThinkingConfig = thinkingConfig\n ? {\n ...(thinkingConfig.includeThoughts !== undefined && {\n includeThoughts: thinkingConfig.includeThoughts,\n }),\n ...(thinkingConfig.thinkingBudget !== undefined && {\n thinkingBudget: thinkingConfig.thinkingBudget,\n }),\n ...(thinkingConfig.thinkingLevel\n ? {\n thinkingLevel: thinkingConfig.thinkingLevel as ThinkingLevel,\n }\n : {}),\n }\n : undefined\n\n const normalizedPrompts = normalizeSystemPrompts(options.systemPrompts)\n const systemInstruction =\n normalizedPrompts.length > 0\n ? normalizedPrompts.map((p) => p.content).join('\\n')\n : undefined\n\n // Native combined mode (issue #605): when the engine threads\n // `outputSchema` through TextOptions, the adapter declared\n // `supportsCombinedToolsAndSchema` (Gemini 3.x only). The schema is\n // already JSON Schema (pre-converted at the activity boundary). Wire\n // it into `config.responseSchema` + `responseMimeType: 'application/json'`\n // alongside any `tools` — the model emits function calls during the\n // agent loop and the schema-constrained JSON on its natural final\n // turn, so the engine can harvest it without the separate\n // `structuredOutput` finalization round-trip.\n const combinedSchema = options.outputSchema as\n | Record<string, unknown>\n | undefined\n const combinedSchemaConfig = combinedSchema\n ? {\n responseMimeType: 'application/json' as const,\n responseSchema: combinedSchema,\n }\n : undefined\n\n // Vendor `GenerateContentConfig` fields are `field?: T` (no `| undefined`)\n // under EOPT, so spread each common option only when present rather than\n // emitting `field: undefined`s into the wire payload.\n const requestOptions: GenerateContentParameters = {\n model: options.model,\n contents: this.formatMessages(options.messages),\n config: {\n ...modelOpts,\n ...(mappedThinkingConfig !== undefined && {\n thinkingConfig: mappedThinkingConfig,\n }),\n ...(systemInstruction !== undefined && { systemInstruction }),\n tools: convertToolsToProviderFormat(options.tools),\n ...(combinedSchemaConfig ?? {}),\n },\n }\n\n return requestOptions\n }\n\n /**\n * Gemini 3.x natively combines `tools` + `responseSchema` in a single\n * streaming `generateContentStream` call (issue #605). Gemini 2.x is\n * documented as brittle for the combination and keeps the engine's\n * legacy finalization path.\n */\n supportsCombinedToolsAndSchema(): boolean {\n return GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS.has(this.model)\n }\n}\n\n/**\n * Creates a Gemini text adapter with explicit API key.\n * Type resolution happens here at the call site.\n */\nexport function createGeminiChat<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n return new GeminiTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Gemini text adapter with automatic API key detection.\n * Type resolution happens here at the call site.\n */\nexport function geminiText<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiChat(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
@@ -186,15 +186,6 @@ function buildInteractionsRequest(options) {
186
186
  const generationConfig = {
187
187
  ...modelOpts?.generation_config
188
188
  };
189
- if (options.temperature !== void 0) {
190
- generationConfig.temperature = options.temperature;
191
- }
192
- if (options.topP !== void 0) {
193
- generationConfig.top_p = options.topP;
194
- }
195
- if (options.maxTokens !== void 0) {
196
- generationConfig.max_output_tokens = options.maxTokens;
197
- }
198
189
  const hasGenerationConfig = Object.keys(generationConfig).length > 0;
199
190
  const input = convertMessagesToInteractionsInput(
200
191
  options.messages,
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","sources":["../../../../src/experimental/text-interactions/adapter.ts"],"sourcesContent":["import { EventType } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../../utils'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type {\n GeminiChatModelToolCapabilitiesByName,\n GeminiModelInputModalitiesByName,\n GeminiModels,\n} from '../../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { GoogleGenAI, Interactions } from '@google/genai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n Tool,\n} from '@tanstack/ai'\n\nimport type {\n GeminiInteractionsCustomEvent,\n GeminiInteractionsCustomEventValue,\n GeminiInteractionsStream,\n} from './events'\nimport type { ExternalTextInteractionsProviderOptions } from './provider-options'\nimport type { GeminiMessageMetadataByModality } from '../../message-types'\nimport type { GeminiClientConfig } from '../../utils'\n\ntype Interaction = Interactions.Interaction\ntype InteractionSSEEvent = Interactions.InteractionSSEEvent\n\nexport type GeminiTextInteractionsConfig = GeminiClientConfig\n\nexport type GeminiTextInteractionsProviderOptions =\n ExternalTextInteractionsProviderOptions\n\ntype InteractionsTool = NonNullable<\n Interactions.CreateModelInteractionParamsStreaming['tools']\n>[number]\n\ntype ContentBlock = Interactions.Content\n\n// The Interactions API takes `input` as a list of *Steps* (not a list of\n// content blocks, and not a list of `Turn`s — the SDK's type union is\n// misleading on both counts). The live API enforces the Step envelope —\n// raw content arrays produce `invalid_request` / \"value at top-level\n// must be a list\". The wire discriminator is snake_case\n// (`user_input` / `function_result`); see\n// https://ai.google.dev/api/interactions-api for the full Step union.\ntype UserInputStep = {\n type: 'user_input'\n content: Array<ContentBlock>\n}\ntype FunctionResultStep = {\n type: 'function_result'\n call_id: string\n name?: string\n result: string\n}\ntype InteractionsStep = UserInputStep | FunctionResultStep\ntype InteractionsRequestInput = Array<InteractionsStep>\n\n// Concrete wire shape we send to `client.interactions.create`. The SDK's\n// own param union types `input` as `string | Content[] | Turn[] | ...`\n// which is wrong for the live API (see the InteractionsRequestInput\n// comment above), so we type `input` ourselves and cast just once at the\n// SDK boundary instead of casting every field through.\ntype GeminiInteractionsRequestBody = Omit<\n Interactions.CreateModelInteractionParamsStreaming,\n 'input' | 'stream'\n> & {\n input: InteractionsRequestInput\n stream?: boolean\n}\n\ntype ToolCallState = {\n name: string\n // Accumulated args as a parsed object. Kept here in object form so a\n // garbled delta can't corrupt previously-merged fragments (the prior\n // string-then-reparse pipeline replaced the whole accumulator on any\n // parse failure). Stringified only when emitting AG-UI events.\n args: Record<string, unknown>\n index: number\n started: boolean\n ended: boolean\n}\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model. The Interactions API's\n * request shape is the same across all chat-capable Gemini models — the\n * SDK doesn't expose a per-model param union — so this currently falls\n * through to the flat `GeminiTextInteractionsProviderOptions` for every\n * model. The alias exists for parity with `GeminiTextAdapter`, so a\n * per-model map can be slotted in later without changing the adapter\n * signature.\n */\ntype ResolveProviderOptions = GeminiTextInteractionsProviderOptions\n\n/**\n * Resolve input modalities for a specific model. Reuses the chat-model\n * modality map from `model-meta.ts`: passing a `document` content block\n * to a model that doesn't support it is a compile error, matching the\n * sibling `GeminiTextAdapter`.\n */\ntype ResolveInputModalities<TModel extends string> =\n TModel extends keyof GeminiModelInputModalitiesByName\n ? GeminiModelInputModalitiesByName[TModel]\n : readonly ['text', 'image', 'audio', 'video', 'document']\n\n/**\n * Resolve tool capabilities for a specific model. Reuses the chat-model\n * capability map: `google_maps` / `google_search_retrieval` /\n * `mcp_server` are rejected at runtime by `convertToolsToInteractionsFormat`,\n * but per-model gating happens here at compile time.\n */\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GeminiChatModelToolCapabilitiesByName\n ? NonNullable<GeminiChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Tree-shakeable adapter for Gemini's stateful Interactions API. Routes\n * through `client.interactions.create` and surfaces the server-assigned\n * `interactionId` via an AG-UI `CUSTOM` event with\n * `name: 'gemini.interactionId'` emitted just before `RUN_FINISHED`; pass\n * that id back on the next turn via `modelOptions.previous_interaction_id`\n * to continue the conversation without resending history.\n *\n * The Interactions API does NOT support stateless multi-turn replay —\n * passing more than one message in `messages` without a\n * `previous_interaction_id` throws. For a chat UI that maintains local\n * history (e.g. `useChat`), see the \"Wiring with `useChat`\" section of\n * `docs/adapters/gemini.md` for the canonical client/server pattern.\n *\n * Supports user-defined function tools and the built-in tools\n * `google_search`, `code_execution`, `url_context`, `file_search`, and\n * `computer_use`. Built-in tool *activity* for the four search/exec\n * variants is surfaced via `CUSTOM` events\n * (`gemini.googleSearchCall` / `gemini.googleSearchResult` and the\n * corresponding per-tool variants) carrying the raw Interactions delta;\n * see {@link GeminiInteractionsCustomEvent}. `computer_use` is accepted\n * in the request but the Interactions API does not currently stream\n * per-delta CUSTOM events for it. `google_search_retrieval`,\n * `google_maps`, and `mcp_server` are not supported on this adapter.\n *\n * @experimental Interactions API is in Beta per Google; shapes may change.\n * @see https://ai.google.dev/gemini-api/docs/interactions\n */\nexport class GeminiTextInteractionsAdapter<\n TModel extends GeminiModels,\n TProviderOptions extends Record<string, any> = ResolveProviderOptions,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GeminiMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'gemini-text-interactions' as const\n\n private readonly client: GoogleGenAI\n // Tracks the most recent server-assigned interaction id per threadId\n // so the adapter can chain follow-up calls on the same thread without\n // the caller having to thread the id manually. Two callers rely on\n // this:\n // 1. The agent loop's tool-call iterations (each iteration is a new\n // `chatStream` call with accumulated tool messages).\n // 2. The agentic-structured composition: a `chatStream` run followed\n // by `structuredOutput` on the accumulated messages.\n // Cross-request chaining is the caller's job via\n // `modelOptions.previous_interaction_id`. To keep stale ids from\n // chaining a brand-new turn, `chatStream` evicts at the START when\n // the caller signals fresh-turn intent (no caller-provided id AND a\n // single user message — anything else is a follow-up). Errors evict\n // immediately so a failed turn never chains into the next one.\n private readonly interactionIdByThread = new Map<string, string>()\n\n constructor(config: GeminiTextInteractionsConfig, model: TModel) {\n super({}, model)\n this.client = createGeminiClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GeminiTextInteractionsProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const runId = options.runId ?? generateId(this.name)\n const threadId = options.threadId ?? generateId(this.name)\n const timestamp = Date.now()\n const { logger } = options\n\n // Fresh-turn intent: caller didn't thread an id AND only a single\n // user message is queued. Drop any stale captured id so we don't\n // silently chain off a prior turn the caller doesn't know about.\n // Multi-message inputs are follow-ups (agent-loop iteration or\n // structuredOutput composition) and keep the Map entry.\n if (\n !options.modelOptions?.previous_interaction_id &&\n options.messages.length === 1 &&\n options.messages[0]?.role === 'user'\n ) {\n this.interactionIdByThread.delete(threadId)\n }\n\n // Resolve `previous_interaction_id`. Caller-provided wins; otherwise\n // fall back to the id we captured during a prior iteration of this\n // same agent-loop run (matched by threadId).\n const effectivePreviousInteractionId =\n options.modelOptions?.previous_interaction_id ??\n this.interactionIdByThread.get(threadId)\n\n let sawTerminalEvent = false\n // Sentinel for the `.return()` abandonment path. Set to `true` only at\n // the bottom of the `try` block — so a consumer-initiated close (via\n // upstream `break` or abort) leaves it `false`, distinguishing\n // abandonment from normal completion. This is the only signal that\n // catches abandonment AFTER a `RUN_FINISHED(tool_calls)`, where\n // `sawTerminalEvent` is `true` but the in-loop deliberately kept the\n // map entry for an agent-loop iteration that will now never run.\n let completedTryBlock = false\n try {\n const request = buildInteractionsRequest({\n ...options,\n modelOptions: {\n ...options.modelOptions,\n previous_interaction_id: effectivePreviousInteractionId,\n },\n })\n logger.request(\n `activity=chat provider=gemini-text-interactions model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n {\n provider: 'gemini-text-interactions',\n model: this.model,\n request,\n },\n )\n const stream = (await this.client.interactions.create(\n { ...request, stream: true } as GeminiInteractionsRequestBody &\n Parameters<typeof this.client.interactions.create>[0],\n { signal: options.abortController?.signal },\n )) as AsyncIterable<InteractionSSEEvent>\n\n for await (const chunk of translateInteractionEvents(\n stream,\n options.model,\n runId,\n threadId,\n options.parentRunId,\n timestamp,\n this.name,\n logger,\n )) {\n // Capture the server-assigned id so the next agent-loop\n // iteration on this thread can chain off it. The CUSTOM event\n // is also yielded downstream as usual — callers consume it via\n // `onCustomEvent` for cross-request chaining.\n //\n // The yield type can't be narrowed to GeminiInteractionsStream\n // here without fighting zod-passthrough variance (StreamChunk's\n // CustomEvent variant carries `[k: string]: unknown`), so we\n // narrow via the literal `name` and trust the typed\n // construction inside `translateInteractionEvents`.\n if (\n chunk.type === EventType.CUSTOM &&\n chunk.name === 'gemini.interactionId'\n ) {\n const value =\n chunk.value as GeminiInteractionsCustomEventValue<'gemini.interactionId'>\n this.interactionIdByThread.set(threadId, value.interactionId)\n }\n if (chunk.type === EventType.RUN_FINISHED) {\n sawTerminalEvent = true\n // Keep the captured id for follow-ups (next agent-loop\n // iteration OR a structuredOutput call composing this turn).\n // The next *fresh* chatStream call evicts at the top via the\n // fresh-turn guard.\n } else if (chunk.type === EventType.RUN_ERROR) {\n sawTerminalEvent = true\n this.interactionIdByThread.delete(threadId)\n }\n yield chunk\n }\n\n if (!sawTerminalEvent) {\n // SDK stream ended without either `interaction.complete` or\n // `error` — surface the truncation rather than silently leaving\n // downstream consumers waiting on a `RUN_FINISHED` that will\n // never come.\n this.interactionIdByThread.delete(threadId)\n const message =\n 'Gemini Interactions stream ended without a terminal event (no interaction.complete or error)'\n logger.errors('gemini-text-interactions.chatStream truncated', {\n source: 'gemini-text-interactions.chatStream',\n runId,\n threadId,\n })\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: options.model,\n timestamp,\n message,\n error: { message },\n }\n }\n completedTryBlock = true\n } catch (error) {\n this.interactionIdByThread.delete(threadId)\n const message =\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the interactions stream.'\n logger.errors('gemini-text-interactions.chatStream fatal', {\n error,\n source: 'gemini-text-interactions.chatStream',\n })\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: options.model,\n timestamp,\n message,\n error: { message },\n }\n } finally {\n // Abandonment cleanup — consumer `.return()` (upstream `break` /\n // abort) bypasses both the truncation guard and the catch handler.\n // `completedTryBlock` is the sentinel that distinguishes natural\n // completion from abandonment; on abandonment we evict so a stale\n // id from a half-finished turn can't chain into a follow-up. The\n // catch handler also lands here with the flag false; its explicit\n // delete is harmless to repeat.\n if (!completedTryBlock) {\n this.interactionIdByThread.delete(threadId)\n }\n }\n }\n\n async structuredOutput(\n options: StructuredOutputOptions<GeminiTextInteractionsProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const { logger } = chatOptions\n const threadId = chatOptions.threadId\n\n // Mirror the chatStream fallback: the agentic-structured flow runs\n // the chat loop first and then calls structuredOutput with the\n // accumulated `messages`. If any tool ran during the loop, the\n // messages include assistant/tool turns; without a chained\n // previous_interaction_id those would throw \"cannot send prior\n // conversation history on a fresh interaction\".\n const effectivePreviousInteractionId =\n chatOptions.modelOptions?.previous_interaction_id ??\n (threadId ? this.interactionIdByThread.get(threadId) : undefined)\n\n const baseRequest = buildInteractionsRequest({\n ...chatOptions,\n modelOptions: {\n ...chatOptions.modelOptions,\n previous_interaction_id: effectivePreviousInteractionId,\n },\n })\n\n const request: GeminiInteractionsRequestBody = {\n ...baseRequest,\n response_mime_type: 'application/json',\n response_format: outputSchema,\n }\n\n try {\n logger.request(\n `activity=chat provider=gemini-text-interactions model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,\n {\n provider: 'gemini-text-interactions',\n model: this.model,\n request,\n },\n )\n const result = (await this.client.interactions.create(\n request as Parameters<typeof this.client.interactions.create>[0],\n { signal: chatOptions.abortController?.signal },\n )) as Interaction\n\n const rawText = extractTextFromInteraction(result)\n\n if (!rawText) {\n throw new Error(\n `Gemini Interactions returned no text output for structured-output request (status: ${result.status}). The model may have produced only tool calls or non-text content.`,\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n return { data: parsed, rawText }\n } catch (error) {\n logger.errors('gemini-text-interactions.structuredOutput fatal', {\n error,\n source: 'gemini-text-interactions.structuredOutput',\n })\n // Preserve the original error as `cause` so the stack trace and any\n // SDK-attached status/code/headers survive for Sentry dedup.\n throw new Error(\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during structured output generation.',\n { cause: error },\n )\n }\n }\n}\n\n/** @experimental Interactions API is in Beta. */\nexport function createGeminiTextInteractions<TModel extends GeminiModels>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiTextInteractionsConfig, 'apiKey'>,\n): GeminiTextInteractionsAdapter<\n TModel,\n ResolveProviderOptions,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n return new GeminiTextInteractionsAdapter({ apiKey, ...config }, model)\n}\n\n/** @experimental Interactions API is in Beta. */\nexport function geminiTextInteractions<TModel extends GeminiModels>(\n model: TModel,\n config?: Omit<GeminiTextInteractionsConfig, 'apiKey'>,\n): GeminiTextInteractionsAdapter<\n TModel,\n ResolveProviderOptions,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiTextInteractions(model, apiKey, config)\n}\n\nfunction buildInteractionsRequest(\n options: TextOptions<GeminiTextInteractionsProviderOptions>,\n): GeminiInteractionsRequestBody {\n const modelOpts = options.modelOptions\n\n const systemInstruction =\n modelOpts?.system_instruction ?? options.systemPrompts?.join('\\n')\n\n const generationConfig: Interactions.GenerationConfig = {\n ...modelOpts?.generation_config,\n }\n if (options.temperature !== undefined) {\n generationConfig.temperature = options.temperature\n }\n if (options.topP !== undefined) {\n generationConfig.top_p = options.topP\n }\n if (options.maxTokens !== undefined) {\n generationConfig.max_output_tokens = options.maxTokens\n }\n\n const hasGenerationConfig = Object.keys(generationConfig).length > 0\n\n const input = convertMessagesToInteractionsInput(\n options.messages,\n modelOpts?.previous_interaction_id !== undefined,\n )\n\n return {\n model: options.model,\n input,\n previous_interaction_id: modelOpts?.previous_interaction_id,\n system_instruction: systemInstruction,\n tools: convertToolsToInteractionsFormat(options.tools),\n generation_config: hasGenerationConfig ? generationConfig : undefined,\n store: modelOpts?.store,\n background: modelOpts?.background,\n response_modalities: modelOpts?.response_modalities,\n response_format: modelOpts?.response_format,\n response_mime_type: modelOpts?.response_mime_type,\n }\n}\n\n// Google's Interactions API takes `input` as `Array<Step>`. Each Step\n// is `{type: 'user_input' | 'function_result' | ..., ...}` — content\n// blocks (text/image/etc.) live nested inside a Step's `content` array,\n// they are NOT valid at the top level. Sending raw `Array<Content>`\n// produces `invalid_request` / \"value at top-level must be a list\",\n// because the API is looking for a Step list at the top level and\n// gets content objects instead. The SDK's type union\n// (`string | Array<Content> | Array<Turn> | ...`) is misleading; see\n// https://ai.google.dev/api/interactions-api for the real Step union.\n//\n// When `hasPreviousInteraction` is true the server holds the transcript\n// up through the last assistant turn, so we only send the steps that\n// come after it (a new `user_input`, one or more `function_result`s\n// continuing a tool call, etc.). Otherwise the conversation is fresh\n// and only the latest user turn is supported — multi-turn replay\n// without `previous_interaction_id` is not part of the API contract.\nfunction convertMessagesToInteractionsInput(\n messages: Array<ModelMessage>,\n hasPreviousInteraction: boolean,\n): InteractionsRequestInput {\n const toolCallIdToName = new Map<string, string>()\n for (const msg of messages) {\n if (msg.role === 'assistant' && msg.toolCalls) {\n for (const tc of msg.toolCalls) {\n toolCallIdToName.set(tc.id, tc.function.name)\n }\n }\n }\n\n const source = hasPreviousInteraction\n ? messagesAfterLastAssistant(messages)\n : messages\n\n if (hasPreviousInteraction && source.length === 0) {\n throw new Error(\n 'Gemini Interactions adapter: modelOptions.previous_interaction_id was provided but no new messages were found after the last assistant turn. Append at least one user or tool message before chaining.',\n )\n }\n\n if (!hasPreviousInteraction) {\n const [only, ...rest] = source\n if (!only) {\n throw new Error('Gemini Interactions adapter: no messages to send.')\n }\n if (rest.length > 0) {\n throw new Error(\n 'Gemini Interactions adapter: cannot send prior conversation history on a fresh interaction. Either set modelOptions.previous_interaction_id to chain prior turns server-side, or trim the message list to a single new user turn. See docs/adapters/gemini.md (\"Wiring with useChat\") for the canonical client/server pattern.',\n )\n }\n if (only.role !== 'user') {\n throw new Error(\n `Gemini Interactions adapter: the first message of a fresh interaction must be a user turn (got role=\"${only.role}\"). Set modelOptions.previous_interaction_id to continue an existing interaction.`,\n )\n }\n const content = messageToContentBlocks(only)\n if (content.length === 0) {\n throw new Error(\n 'Gemini Interactions adapter: the user message produced no content blocks to send.',\n )\n }\n return [{ type: 'user_input', content }]\n }\n\n // Chained path: each post-assistant message becomes one Step. A user\n // reply maps to `user_input`; a tool reply maps to `function_result`.\n // Assistant turns shouldn't appear here (sliced off above) — if one\n // somehow does we skip it rather than letting it shape the wire.\n const steps: Array<InteractionsStep> = []\n for (const msg of source) {\n if (msg.role === 'tool' && msg.toolCallId) {\n const result = serializeToolResultContent(msg.content)\n steps.push({\n type: 'function_result',\n call_id: msg.toolCallId,\n name: toolCallIdToName.get(msg.toolCallId),\n result,\n })\n } else if (msg.role === 'user') {\n const content = messageToContentBlocks(msg)\n if (content.length > 0) {\n steps.push({ type: 'user_input', content })\n }\n }\n }\n if (steps.length === 0) {\n throw new Error(\n 'Gemini Interactions adapter: messages after the last assistant turn produced no steps to send.',\n )\n }\n return steps\n}\n\n// The Interactions API's `function_result.result` field is a string. We\n// fail loudly on non-string tool content rather than silently coercing\n// to `''` — silent coercion meant the model lost the entire tool\n// output for callers that returned content as an array (e.g. image +\n// text) or `null`. If you need to send structured tool output, encode\n// it yourself before passing.\nfunction serializeToolResultContent(\n content: ModelMessage['content'] | undefined,\n): string {\n if (typeof content === 'string') return content\n if (content === null || content === undefined) {\n throw new Error(\n 'Gemini Interactions adapter: tool message has no content. The Interactions API requires a string `result` on function_result steps — return a string from your tool implementation (encode JSON/multimodal output yourself).',\n )\n }\n throw new Error(\n 'Gemini Interactions adapter: tool message content must be a string (got an array of content parts). The Interactions API requires a string `result` on function_result steps — stringify multimodal tool output before returning it from your tool.',\n )\n}\n\n// Extracts the content blocks (text / image / audio / video / document)\n// from a single message. Tool calls and tool results live one level up\n// as Steps, not as content, so they are NOT emitted here.\nfunction messageToContentBlocks(msg: ModelMessage): Array<ContentBlock> {\n const blocks: Array<ContentBlock> = []\n\n if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n blocks.push(contentPartToBlock(part))\n }\n } else if (\n typeof msg.content === 'string' &&\n msg.content &&\n msg.role !== 'tool'\n ) {\n blocks.push({ type: 'text', text: msg.content })\n }\n\n return blocks\n}\n\nfunction messagesAfterLastAssistant(\n messages: Array<ModelMessage>,\n): Array<ModelMessage> {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]?.role === 'assistant') {\n return messages.slice(i + 1)\n }\n }\n return messages\n}\n\nfunction safeParseToolArguments(\n raw: string | undefined,\n logger: InternalLogger,\n): Record<string, unknown> {\n if (!raw) return {}\n try {\n const parsed = JSON.parse(raw)\n return parsed && typeof parsed === 'object' ? parsed : {}\n } catch (error) {\n logger.errors(\n 'gemini-text-interactions.safeParseToolArguments parse failed',\n {\n error,\n raw,\n source: 'gemini-text-interactions.chatStream',\n },\n )\n return {}\n }\n}\n\n// `satisfies` pins these arrays to the SDK's narrow mime-type unions: if\n// Google removes a format the build breaks, and if they add one ours keeps\n// working (we just won't accept the new one until added here).\nconst IMAGE_MIME_TYPES = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'image/heic',\n 'image/heif',\n] as const satisfies ReadonlyArray<\n NonNullable<Interactions.ImageContent['mime_type']>\n>\n\nconst AUDIO_MIME_TYPES = [\n 'audio/wav',\n 'audio/mp3',\n 'audio/aiff',\n 'audio/aac',\n 'audio/ogg',\n 'audio/flac',\n] as const satisfies ReadonlyArray<\n NonNullable<Interactions.AudioContent['mime_type']>\n>\n\nconst VIDEO_MIME_TYPES = [\n 'video/mp4',\n 'video/mpeg',\n 'video/mpg',\n 'video/mov',\n 'video/avi',\n 'video/x-flv',\n 'video/webm',\n 'video/wmv',\n 'video/3gpp',\n] as const satisfies ReadonlyArray<\n NonNullable<Interactions.VideoContent['mime_type']>\n>\n\nconst DOCUMENT_MIME_TYPES = [\n 'application/pdf',\n] as const satisfies ReadonlyArray<\n NonNullable<Interactions.DocumentContent['mime_type']>\n>\n\nfunction validateMime<T extends string>(\n allowed: ReadonlyArray<T>,\n value: string | undefined,\n kind: string,\n): T | undefined {\n if (value === undefined) return undefined\n if ((allowed as ReadonlyArray<string>).includes(value)) {\n return value as T\n }\n throw new Error(\n `Unsupported ${kind} mime type \"${value}\" for the Gemini Interactions API. Allowed: ${allowed.join(', ')}.`,\n )\n}\n\nfunction contentPartToBlock(part: ContentPart): ContentBlock {\n if (part.type === 'text') {\n return { type: 'text', text: part.content }\n }\n const isData = part.source.type === 'data'\n switch (part.type) {\n case 'image': {\n const mime_type = validateMime(\n IMAGE_MIME_TYPES,\n part.source.mimeType,\n 'image',\n )\n return isData\n ? { type: 'image', data: part.source.value, mime_type }\n : { type: 'image', uri: part.source.value, mime_type }\n }\n case 'audio': {\n const mime_type = validateMime(\n AUDIO_MIME_TYPES,\n part.source.mimeType,\n 'audio',\n )\n return isData\n ? { type: 'audio', data: part.source.value, mime_type }\n : { type: 'audio', uri: part.source.value, mime_type }\n }\n case 'video': {\n const mime_type = validateMime(\n VIDEO_MIME_TYPES,\n part.source.mimeType,\n 'video',\n )\n return isData\n ? { type: 'video', data: part.source.value, mime_type }\n : { type: 'video', uri: part.source.value, mime_type }\n }\n case 'document': {\n const mime_type = validateMime(\n DOCUMENT_MIME_TYPES,\n part.source.mimeType,\n 'document',\n )\n return isData\n ? { type: 'document', data: part.source.value, mime_type }\n : { type: 'document', uri: part.source.value, mime_type }\n }\n }\n}\n\n// Built-in Gemini tools use snake_case field names in the Interactions API\n// that differ from the camelCase fields used on `client.models.generateContent`\n// (e.g. `fileSearchStoreNames` vs `file_search_store_names`). Translate\n// explicitly so callers keep using the same tool factories across adapters.\nfunction convertToolsToInteractionsFormat<TTool extends Tool>(\n tools: Array<TTool> | undefined,\n): Array<InteractionsTool> | undefined {\n if (!tools || tools.length === 0) return undefined\n\n const result: Array<InteractionsTool> = []\n\n for (const tool of tools) {\n switch (tool.name) {\n case 'google_search': {\n const metadata = (tool.metadata ?? {}) as {\n search_types?: Array<'web_search' | 'image_search'>\n }\n result.push({\n type: 'google_search',\n ...(metadata.search_types\n ? { search_types: metadata.search_types }\n : {}),\n })\n break\n }\n case 'code_execution': {\n result.push({ type: 'code_execution' })\n break\n }\n case 'url_context': {\n result.push({ type: 'url_context' })\n break\n }\n case 'file_search': {\n const metadata = (tool.metadata ?? {}) as {\n fileSearchStoreNames?: Array<string>\n topK?: number\n metadataFilter?: string\n }\n result.push({\n type: 'file_search',\n ...(metadata.fileSearchStoreNames\n ? { file_search_store_names: metadata.fileSearchStoreNames }\n : {}),\n ...(metadata.topK !== undefined ? { top_k: metadata.topK } : {}),\n ...(metadata.metadataFilter !== undefined\n ? { metadata_filter: metadata.metadataFilter }\n : {}),\n })\n break\n }\n case 'computer_use': {\n const metadata = (tool.metadata ?? {}) as {\n environment?: string\n excludedPredefinedFunctions?: Array<string>\n }\n if (metadata.environment && metadata.environment !== 'browser') {\n throw new Error(\n `computer_use environment \"${metadata.environment}\" is not supported on the Gemini Interactions API. Only \"browser\" is accepted.`,\n )\n }\n result.push({\n type: 'computer_use',\n ...(metadata.environment\n ? { environment: metadata.environment as 'browser' }\n : {}),\n ...(metadata.excludedPredefinedFunctions\n ? {\n excludedPredefinedFunctions:\n metadata.excludedPredefinedFunctions,\n }\n : {}),\n })\n break\n }\n case 'google_search_retrieval':\n throw new Error(\n '`google_search_retrieval` is not supported on the Gemini Interactions API. Use `googleSearchTool()` (`google_search`) with `geminiTextInteractions()`, or call `geminiText()` for the legacy retrieval tool.',\n )\n case 'google_maps':\n throw new Error(\n '`google_maps` is not yet supported on the Gemini Interactions API. Use `geminiText()` for Google Maps grounding.',\n )\n case 'mcp_server':\n throw new Error(\n '`mcp_server` is not yet supported on the `geminiTextInteractions()` adapter.',\n )\n default: {\n if (!tool.description) {\n throw new Error(\n `Tool ${tool.name} requires a description for the Gemini Interactions adapter`,\n )\n }\n result.push({\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: sanitizeToolParameters(\n tool.inputSchema ?? { type: 'object', properties: {} },\n ),\n })\n }\n }\n }\n\n return result\n}\n\n// Map of API-level status values onto the AG-UI `finishReason` field.\n// `requires_action` is the Interactions API's signal that the model\n// produced one or more function calls and is waiting for results — we\n// always map that to 'tool_calls' regardless of whether deltas were\n// observed (a function_call may have arrived in a single delta with no\n// other content). `incomplete` is the truncation signal (max_tokens\n// exceeded etc.) — map to 'length'. `completed` is normal stop.\nfunction statusToFinishReason(\n status: Interaction['status'] | undefined,\n sawFunctionCall: boolean,\n): 'stop' | 'length' | 'tool_calls' | null {\n if (status === 'requires_action') return 'tool_calls'\n if (status === 'incomplete') return 'length'\n if (sawFunctionCall) return 'tool_calls'\n return 'stop'\n}\n\n// Statuses that mean the interaction did not produce a usable result.\n// `failed`/`cancelled` map to RUN_ERROR. `incomplete` is the model\n// hitting a stop condition (max_tokens etc.) and is reported via\n// `finishReason: 'length'` on RUN_FINISHED so callers can decide how to\n// react without it looking like a hard error.\nfunction statusIsError(\n status: Interaction['status'] | undefined,\n): status is 'failed' | 'cancelled' {\n return status === 'failed' || status === 'cancelled'\n}\n\nasync function* translateInteractionEvents(\n stream: AsyncIterable<InteractionSSEEvent>,\n model: string,\n runId: string,\n threadId: string,\n parentRunId: string | undefined,\n timestamp: number,\n adapterName: string,\n logger: InternalLogger,\n): AsyncIterable<StreamChunk> {\n const messageId = generateId(adapterName)\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let textAccumulated = ''\n let interactionId: string | undefined\n let sawFunctionCall = false\n const toolCalls = new Map<string, ToolCallState>()\n let nextToolIndex = 0\n let thinkingStepId: string | null = null\n let thinkingAccumulated = ''\n let reasoningMessageId: string | null = null\n let hasClosedReasoning = false\n\n const closeReasoningIfNeeded = function* (): Generator<StreamChunk> {\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n // Reset so that a later `thought_summary` delta (the API\n // interleaves text → thought → text on some models) opens a\n // fresh reasoning block instead of re-using an already-ended\n // messageId, which would violate AG-UI ordering.\n thinkingStepId = null\n reasoningMessageId = null\n hasClosedReasoning = false\n }\n }\n\n // Seals any in-flight messages and tool calls. Called both on the\n // normal terminal path (`interaction.complete`) and on the error path\n // (`error` SSE event + premature EOF) so the StreamProcessor never\n // sees orphan TEXT_MESSAGE_START / TOOL_CALL_START / REASONING_*\n // events on RUN_ERROR.\n const closeOpenState = function* (): Generator<StreamChunk> {\n yield* closeReasoningIfNeeded()\n for (const [toolCallId, state] of toolCalls) {\n if (state.ended) continue\n state.ended = true\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolName: state.name,\n model,\n timestamp,\n input: state.args,\n }\n }\n if (hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = false\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp,\n }\n }\n }\n\n const emitRunStartedIfNeeded = function* (): Generator<StreamChunk> {\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp,\n parentRunId,\n }\n }\n }\n\n for await (const event of stream) {\n logger.provider(`provider=gemini-text-interactions`, { event })\n switch (event.event_type) {\n case 'interaction.start': {\n interactionId = event.interaction.id\n yield* emitRunStartedIfNeeded()\n break\n }\n\n case 'content.start': {\n yield* emitRunStartedIfNeeded()\n break\n }\n\n case 'content.delta': {\n yield* emitRunStartedIfNeeded()\n const delta = event.delta\n switch (delta.type) {\n case 'text': {\n yield* closeReasoningIfNeeded()\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp,\n role: 'assistant',\n }\n }\n textAccumulated += delta.text\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp,\n delta: delta.text,\n content: textAccumulated,\n }\n break\n }\n case 'function_call': {\n yield* closeReasoningIfNeeded()\n sawFunctionCall = true\n const toolCallId = delta.id\n const deltaArgs: Record<string, unknown> =\n typeof delta.arguments === 'string'\n ? safeParseToolArguments(delta.arguments, logger)\n : delta.arguments\n let state = toolCalls.get(toolCallId)\n if (!state) {\n state = {\n name: delta.name,\n args: { ...deltaArgs },\n index: nextToolIndex++,\n started: false,\n ended: false,\n }\n toolCalls.set(toolCallId, state)\n } else {\n state.args = { ...state.args, ...deltaArgs }\n if (delta.name) state.name = delta.name\n }\n if (!state.started) {\n state.started = true\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: state.name,\n toolName: state.name,\n model,\n timestamp,\n index: state.index,\n }\n }\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n model,\n timestamp,\n delta: JSON.stringify(deltaArgs),\n args: JSON.stringify(state.args),\n }\n break\n }\n case 'google_search_call': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.googleSearchCall',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'google_search_result': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.googleSearchResult',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'code_execution_call': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.codeExecutionCall',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'code_execution_result': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.codeExecutionResult',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'url_context_call': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.urlContextCall',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'url_context_result': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.urlContextResult',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'file_search_call': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.fileSearchCall',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'file_search_result': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.fileSearchResult',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'thought_summary': {\n const thoughtText =\n delta.content && 'text' in delta.content ? delta.content.text : ''\n if (!thoughtText) break\n if (thinkingStepId === null || reasoningMessageId === null) {\n thinkingStepId = generateId(adapterName)\n reasoningMessageId = generateId(adapterName)\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning',\n model,\n timestamp,\n }\n yield {\n type: EventType.STEP_STARTED,\n stepName: thinkingStepId,\n stepId: thinkingStepId,\n model,\n timestamp,\n stepType: 'thinking',\n }\n }\n thinkingAccumulated += thoughtText\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: thoughtText,\n model,\n timestamp,\n }\n yield {\n type: EventType.STEP_FINISHED,\n stepName: thinkingStepId,\n stepId: thinkingStepId,\n model,\n timestamp,\n delta: thoughtText,\n content: thinkingAccumulated,\n }\n break\n }\n // The following delta types are valid per the SDK type union\n // but aren't yet translated by this adapter (output modalities\n // text-only adapter shouldn't see, response-side function_result\n // / mcp_server_*, thought_signature). Falling through to the\n // observability default so SDK drift is visible.\n case 'image':\n case 'audio':\n case 'video':\n case 'document':\n case 'function_result':\n case 'mcp_server_tool_call':\n case 'mcp_server_tool_result':\n case 'thought_signature':\n default:\n logger.provider(\n `gemini-text-interactions unhandled content.delta type`,\n { delta },\n )\n break\n }\n break\n }\n\n case 'content.stop':\n case 'interaction.status_update': {\n break\n }\n\n case 'interaction.complete': {\n if (event.interaction.id) {\n interactionId = event.interaction.id\n }\n\n yield* closeOpenState()\n\n const status = event.interaction.status\n if (statusIsError(status)) {\n const message = `Gemini Interactions ${status}: the interaction ended without a usable response.`\n logger.errors(\n 'gemini-text-interactions.translateInteractionEvents non-success status',\n {\n source: 'gemini-text-interactions.chatStream',\n status,\n interactionId,\n },\n )\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model,\n timestamp,\n message,\n code: status,\n error: { message, code: status },\n }\n return\n }\n\n const usage = event.interaction.usage\n const finishReason = statusToFinishReason(status, sawFunctionCall)\n\n if (interactionId) {\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.interactionId',\n value: { interactionId },\n model,\n timestamp,\n }\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp,\n finishReason,\n usage: usage\n ? {\n promptTokens: usage.total_input_tokens ?? 0,\n completionTokens: usage.total_output_tokens ?? 0,\n totalTokens: usage.total_tokens ?? 0,\n }\n : undefined,\n }\n return\n }\n\n case 'error': {\n // Close any in-flight TEXT_MESSAGE_START / TOOL_CALL_START /\n // REASONING_* so downstream consumers don't see orphan open\n // state after RUN_ERROR.\n yield* closeOpenState()\n const rawMessage = event.error?.message\n const message =\n typeof rawMessage === 'string' && rawMessage.length > 0\n ? rawMessage\n : `Gemini Interactions error (no message): ${JSON.stringify(event.error ?? {})}`\n const rawCode = event.error?.code\n const code =\n typeof rawCode === 'string' || typeof rawCode === 'number'\n ? String(rawCode)\n : undefined\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model,\n timestamp,\n message,\n code,\n error: { message, code },\n }\n return\n }\n\n default:\n logger.provider(`gemini-text-interactions unhandled event_type`, {\n event,\n })\n break\n }\n }\n\n // Stream ended without `interaction.complete` or `error` (both `return`\n // out of the loop). Seal any in-flight TEXT/TOOL/REASONING blocks here\n // so the truncation-fallback RUN_ERROR yielded by `chatStream` doesn't\n // leave orphan `*_START` events open downstream.\n yield* closeOpenState()\n}\n\nfunction extractTextFromInteraction(interaction: Interaction): string {\n let text = ''\n for (const output of interaction.outputs ?? []) {\n if (output.type === 'text') {\n text += output.text\n }\n }\n return text\n}\n\n// The live Interactions API rejects tool parameter schemas that contain\n// an empty `required: []` array with the misleading top-level error\n// `\"value at top-level must be a list\"`. Empty `properties: {}` and\n// `parameters: {}` are both fine — only the empty `required` array is\n// poison. The Zod -> JSON Schema converter (and many hand-written\n// schemas) emit `required: []` whenever a tool has zero required\n// parameters, so we strip those instances recursively before sending.\n// Non-empty `required` arrays are passed through unchanged.\nfunction sanitizeToolParameters(schema: unknown): unknown {\n if (!schema || typeof schema !== 'object') return schema\n if (Array.isArray(schema)) return schema.map(sanitizeToolParameters)\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(schema)) {\n if (key === 'required' && Array.isArray(value) && value.length === 0) {\n continue\n }\n out[key] = sanitizeToolParameters(value)\n }\n return out\n}\n\n// Re-export the stream type so consumers can import it alongside the\n// adapter from a single path: `import type { GeminiInteractionsStream }\n// from '@tanstack/ai-gemini/experimental'`.\nexport type { GeminiInteractionsStream }\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.js","sources":["../../../../src/experimental/text-interactions/adapter.ts"],"sourcesContent":["import { EventType } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../../utils'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type {\n GeminiChatModelToolCapabilitiesByName,\n GeminiModelInputModalitiesByName,\n GeminiModels,\n} from '../../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { GoogleGenAI, Interactions } from '@google/genai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n Tool,\n} from '@tanstack/ai'\n\nimport type {\n GeminiInteractionsCustomEvent,\n GeminiInteractionsCustomEventValue,\n GeminiInteractionsStream,\n} from './events'\nimport type { ExternalTextInteractionsProviderOptions } from './provider-options'\nimport type { GeminiMessageMetadataByModality } from '../../message-types'\nimport type { GeminiClientConfig } from '../../utils'\n\ntype Interaction = Interactions.Interaction\ntype InteractionSSEEvent = Interactions.InteractionSSEEvent\n\nexport type GeminiTextInteractionsConfig = GeminiClientConfig\n\nexport type GeminiTextInteractionsProviderOptions =\n ExternalTextInteractionsProviderOptions\n\ntype InteractionsTool = NonNullable<\n Interactions.CreateModelInteractionParamsStreaming['tools']\n>[number]\n\ntype ContentBlock = Interactions.Content\n\n// The Interactions API takes `input` as a list of *Steps* (not a list of\n// content blocks, and not a list of `Turn`s — the SDK's type union is\n// misleading on both counts). The live API enforces the Step envelope —\n// raw content arrays produce `invalid_request` / \"value at top-level\n// must be a list\". The wire discriminator is snake_case\n// (`user_input` / `function_result`); see\n// https://ai.google.dev/api/interactions-api for the full Step union.\ntype UserInputStep = {\n type: 'user_input'\n content: Array<ContentBlock>\n}\ntype FunctionResultStep = {\n type: 'function_result'\n call_id: string\n name?: string\n result: string\n}\ntype InteractionsStep = UserInputStep | FunctionResultStep\ntype InteractionsRequestInput = Array<InteractionsStep>\n\n// Concrete wire shape we send to `client.interactions.create`. The SDK's\n// own param union types `input` as `string | Content[] | Turn[] | ...`\n// which is wrong for the live API (see the InteractionsRequestInput\n// comment above), so we type `input` ourselves and cast just once at the\n// SDK boundary instead of casting every field through.\ntype GeminiInteractionsRequestBody = Omit<\n Interactions.CreateModelInteractionParamsStreaming,\n 'input' | 'stream'\n> & {\n input: InteractionsRequestInput\n stream?: boolean\n}\n\ntype ToolCallState = {\n name: string\n // Accumulated args as a parsed object. Kept here in object form so a\n // garbled delta can't corrupt previously-merged fragments (the prior\n // string-then-reparse pipeline replaced the whole accumulator on any\n // parse failure). Stringified only when emitting AG-UI events.\n args: Record<string, unknown>\n index: number\n started: boolean\n ended: boolean\n}\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model. The Interactions API's\n * request shape is the same across all chat-capable Gemini models — the\n * SDK doesn't expose a per-model param union — so this currently falls\n * through to the flat `GeminiTextInteractionsProviderOptions` for every\n * model. The alias exists for parity with `GeminiTextAdapter`, so a\n * per-model map can be slotted in later without changing the adapter\n * signature.\n */\ntype ResolveProviderOptions = GeminiTextInteractionsProviderOptions\n\n/**\n * Resolve input modalities for a specific model. Reuses the chat-model\n * modality map from `model-meta.ts`: passing a `document` content block\n * to a model that doesn't support it is a compile error, matching the\n * sibling `GeminiTextAdapter`.\n */\ntype ResolveInputModalities<TModel extends string> =\n TModel extends keyof GeminiModelInputModalitiesByName\n ? GeminiModelInputModalitiesByName[TModel]\n : readonly ['text', 'image', 'audio', 'video', 'document']\n\n/**\n * Resolve tool capabilities for a specific model. Reuses the chat-model\n * capability map: `google_maps` / `google_search_retrieval` /\n * `mcp_server` are rejected at runtime by `convertToolsToInteractionsFormat`,\n * but per-model gating happens here at compile time.\n */\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GeminiChatModelToolCapabilitiesByName\n ? NonNullable<GeminiChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Tree-shakeable adapter for Gemini's stateful Interactions API. Routes\n * through `client.interactions.create` and surfaces the server-assigned\n * `interactionId` via an AG-UI `CUSTOM` event with\n * `name: 'gemini.interactionId'` emitted just before `RUN_FINISHED`; pass\n * that id back on the next turn via `modelOptions.previous_interaction_id`\n * to continue the conversation without resending history.\n *\n * The Interactions API does NOT support stateless multi-turn replay —\n * passing more than one message in `messages` without a\n * `previous_interaction_id` throws. For a chat UI that maintains local\n * history (e.g. `useChat`), see the \"Wiring with `useChat`\" section of\n * `docs/adapters/gemini.md` for the canonical client/server pattern.\n *\n * Supports user-defined function tools and the built-in tools\n * `google_search`, `code_execution`, `url_context`, `file_search`, and\n * `computer_use`. Built-in tool *activity* for the four search/exec\n * variants is surfaced via `CUSTOM` events\n * (`gemini.googleSearchCall` / `gemini.googleSearchResult` and the\n * corresponding per-tool variants) carrying the raw Interactions delta;\n * see {@link GeminiInteractionsCustomEvent}. `computer_use` is accepted\n * in the request but the Interactions API does not currently stream\n * per-delta CUSTOM events for it. `google_search_retrieval`,\n * `google_maps`, and `mcp_server` are not supported on this adapter.\n *\n * @experimental Interactions API is in Beta per Google; shapes may change.\n * @see https://ai.google.dev/gemini-api/docs/interactions\n */\nexport class GeminiTextInteractionsAdapter<\n TModel extends GeminiModels,\n TProviderOptions extends Record<string, any> = ResolveProviderOptions,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GeminiMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'gemini-text-interactions' as const\n\n private readonly client: GoogleGenAI\n // Tracks the most recent server-assigned interaction id per threadId\n // so the adapter can chain follow-up calls on the same thread without\n // the caller having to thread the id manually. Two callers rely on\n // this:\n // 1. The agent loop's tool-call iterations (each iteration is a new\n // `chatStream` call with accumulated tool messages).\n // 2. The agentic-structured composition: a `chatStream` run followed\n // by `structuredOutput` on the accumulated messages.\n // Cross-request chaining is the caller's job via\n // `modelOptions.previous_interaction_id`. To keep stale ids from\n // chaining a brand-new turn, `chatStream` evicts at the START when\n // the caller signals fresh-turn intent (no caller-provided id AND a\n // single user message — anything else is a follow-up). Errors evict\n // immediately so a failed turn never chains into the next one.\n private readonly interactionIdByThread = new Map<string, string>()\n\n constructor(config: GeminiTextInteractionsConfig, model: TModel) {\n super({}, model)\n this.client = createGeminiClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GeminiTextInteractionsProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const runId = options.runId ?? generateId(this.name)\n const threadId = options.threadId ?? generateId(this.name)\n const timestamp = Date.now()\n const { logger } = options\n\n // Fresh-turn intent: caller didn't thread an id AND only a single\n // user message is queued. Drop any stale captured id so we don't\n // silently chain off a prior turn the caller doesn't know about.\n // Multi-message inputs are follow-ups (agent-loop iteration or\n // structuredOutput composition) and keep the Map entry.\n if (\n !options.modelOptions?.previous_interaction_id &&\n options.messages.length === 1 &&\n options.messages[0]?.role === 'user'\n ) {\n this.interactionIdByThread.delete(threadId)\n }\n\n // Resolve `previous_interaction_id`. Caller-provided wins; otherwise\n // fall back to the id we captured during a prior iteration of this\n // same agent-loop run (matched by threadId).\n const effectivePreviousInteractionId =\n options.modelOptions?.previous_interaction_id ??\n this.interactionIdByThread.get(threadId)\n\n let sawTerminalEvent = false\n // Sentinel for the `.return()` abandonment path. Set to `true` only at\n // the bottom of the `try` block — so a consumer-initiated close (via\n // upstream `break` or abort) leaves it `false`, distinguishing\n // abandonment from normal completion. This is the only signal that\n // catches abandonment AFTER a `RUN_FINISHED(tool_calls)`, where\n // `sawTerminalEvent` is `true` but the in-loop deliberately kept the\n // map entry for an agent-loop iteration that will now never run.\n let completedTryBlock = false\n try {\n const request = buildInteractionsRequest({\n ...options,\n modelOptions: {\n ...options.modelOptions,\n previous_interaction_id: effectivePreviousInteractionId,\n },\n })\n logger.request(\n `activity=chat provider=gemini-text-interactions model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n {\n provider: 'gemini-text-interactions',\n model: this.model,\n request,\n },\n )\n const stream = (await this.client.interactions.create(\n { ...request, stream: true } as GeminiInteractionsRequestBody &\n Parameters<typeof this.client.interactions.create>[0],\n { signal: options.abortController?.signal },\n )) as AsyncIterable<InteractionSSEEvent>\n\n for await (const chunk of translateInteractionEvents(\n stream,\n options.model,\n runId,\n threadId,\n options.parentRunId,\n timestamp,\n this.name,\n logger,\n )) {\n // Capture the server-assigned id so the next agent-loop\n // iteration on this thread can chain off it. The CUSTOM event\n // is also yielded downstream as usual — callers consume it via\n // `onCustomEvent` for cross-request chaining.\n //\n // The yield type can't be narrowed to GeminiInteractionsStream\n // here without fighting zod-passthrough variance (StreamChunk's\n // CustomEvent variant carries `[k: string]: unknown`), so we\n // narrow via the literal `name` and trust the typed\n // construction inside `translateInteractionEvents`.\n if (\n chunk.type === EventType.CUSTOM &&\n chunk.name === 'gemini.interactionId'\n ) {\n const value =\n chunk.value as GeminiInteractionsCustomEventValue<'gemini.interactionId'>\n this.interactionIdByThread.set(threadId, value.interactionId)\n }\n if (chunk.type === EventType.RUN_FINISHED) {\n sawTerminalEvent = true\n // Keep the captured id for follow-ups (next agent-loop\n // iteration OR a structuredOutput call composing this turn).\n // The next *fresh* chatStream call evicts at the top via the\n // fresh-turn guard.\n } else if (chunk.type === EventType.RUN_ERROR) {\n sawTerminalEvent = true\n this.interactionIdByThread.delete(threadId)\n }\n yield chunk\n }\n\n if (!sawTerminalEvent) {\n // SDK stream ended without either `interaction.complete` or\n // `error` — surface the truncation rather than silently leaving\n // downstream consumers waiting on a `RUN_FINISHED` that will\n // never come.\n this.interactionIdByThread.delete(threadId)\n const message =\n 'Gemini Interactions stream ended without a terminal event (no interaction.complete or error)'\n logger.errors('gemini-text-interactions.chatStream truncated', {\n source: 'gemini-text-interactions.chatStream',\n runId,\n threadId,\n })\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: options.model,\n timestamp,\n message,\n error: { message },\n }\n }\n completedTryBlock = true\n } catch (error) {\n this.interactionIdByThread.delete(threadId)\n const message =\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the interactions stream.'\n logger.errors('gemini-text-interactions.chatStream fatal', {\n error,\n source: 'gemini-text-interactions.chatStream',\n })\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model: options.model,\n timestamp,\n message,\n error: { message },\n }\n } finally {\n // Abandonment cleanup — consumer `.return()` (upstream `break` /\n // abort) bypasses both the truncation guard and the catch handler.\n // `completedTryBlock` is the sentinel that distinguishes natural\n // completion from abandonment; on abandonment we evict so a stale\n // id from a half-finished turn can't chain into a follow-up. The\n // catch handler also lands here with the flag false; its explicit\n // delete is harmless to repeat.\n if (!completedTryBlock) {\n this.interactionIdByThread.delete(threadId)\n }\n }\n }\n\n async structuredOutput(\n options: StructuredOutputOptions<GeminiTextInteractionsProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const { logger } = chatOptions\n const threadId = chatOptions.threadId\n\n // Mirror the chatStream fallback: the agentic-structured flow runs\n // the chat loop first and then calls structuredOutput with the\n // accumulated `messages`. If any tool ran during the loop, the\n // messages include assistant/tool turns; without a chained\n // previous_interaction_id those would throw \"cannot send prior\n // conversation history on a fresh interaction\".\n const effectivePreviousInteractionId =\n chatOptions.modelOptions?.previous_interaction_id ??\n (threadId ? this.interactionIdByThread.get(threadId) : undefined)\n\n const baseRequest = buildInteractionsRequest({\n ...chatOptions,\n modelOptions: {\n ...chatOptions.modelOptions,\n previous_interaction_id: effectivePreviousInteractionId,\n },\n })\n\n const request: GeminiInteractionsRequestBody = {\n ...baseRequest,\n response_mime_type: 'application/json',\n response_format: outputSchema,\n }\n\n try {\n logger.request(\n `activity=chat provider=gemini-text-interactions model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,\n {\n provider: 'gemini-text-interactions',\n model: this.model,\n request,\n },\n )\n const result = (await this.client.interactions.create(\n request as Parameters<typeof this.client.interactions.create>[0],\n { signal: chatOptions.abortController?.signal },\n )) as Interaction\n\n const rawText = extractTextFromInteraction(result)\n\n if (!rawText) {\n throw new Error(\n `Gemini Interactions returned no text output for structured-output request (status: ${result.status}). The model may have produced only tool calls or non-text content.`,\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n return { data: parsed, rawText }\n } catch (error) {\n logger.errors('gemini-text-interactions.structuredOutput fatal', {\n error,\n source: 'gemini-text-interactions.structuredOutput',\n })\n // Preserve the original error as `cause` so the stack trace and any\n // SDK-attached status/code/headers survive for Sentry dedup.\n throw new Error(\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during structured output generation.',\n { cause: error },\n )\n }\n }\n}\n\n/** @experimental Interactions API is in Beta. */\nexport function createGeminiTextInteractions<TModel extends GeminiModels>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiTextInteractionsConfig, 'apiKey'>,\n): GeminiTextInteractionsAdapter<\n TModel,\n ResolveProviderOptions,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n return new GeminiTextInteractionsAdapter({ apiKey, ...config }, model)\n}\n\n/** @experimental Interactions API is in Beta. */\nexport function geminiTextInteractions<TModel extends GeminiModels>(\n model: TModel,\n config?: Omit<GeminiTextInteractionsConfig, 'apiKey'>,\n): GeminiTextInteractionsAdapter<\n TModel,\n ResolveProviderOptions,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiTextInteractions(model, apiKey, config)\n}\n\nfunction buildInteractionsRequest(\n options: TextOptions<GeminiTextInteractionsProviderOptions>,\n): GeminiInteractionsRequestBody {\n const modelOpts = options.modelOptions\n\n const systemInstruction =\n modelOpts?.system_instruction ?? options.systemPrompts?.join('\\n')\n\n const generationConfig: Interactions.GenerationConfig = {\n ...modelOpts?.generation_config,\n }\n\n const hasGenerationConfig = Object.keys(generationConfig).length > 0\n\n const input = convertMessagesToInteractionsInput(\n options.messages,\n modelOpts?.previous_interaction_id !== undefined,\n )\n\n return {\n model: options.model,\n input,\n previous_interaction_id: modelOpts?.previous_interaction_id,\n system_instruction: systemInstruction,\n tools: convertToolsToInteractionsFormat(options.tools),\n generation_config: hasGenerationConfig ? generationConfig : undefined,\n store: modelOpts?.store,\n background: modelOpts?.background,\n response_modalities: modelOpts?.response_modalities,\n response_format: modelOpts?.response_format,\n response_mime_type: modelOpts?.response_mime_type,\n }\n}\n\n// Google's Interactions API takes `input` as `Array<Step>`. Each Step\n// is `{type: 'user_input' | 'function_result' | ..., ...}` — content\n// blocks (text/image/etc.) live nested inside a Step's `content` array,\n// they are NOT valid at the top level. Sending raw `Array<Content>`\n// produces `invalid_request` / \"value at top-level must be a list\",\n// because the API is looking for a Step list at the top level and\n// gets content objects instead. The SDK's type union\n// (`string | Array<Content> | Array<Turn> | ...`) is misleading; see\n// https://ai.google.dev/api/interactions-api for the real Step union.\n//\n// When `hasPreviousInteraction` is true the server holds the transcript\n// up through the last assistant turn, so we only send the steps that\n// come after it (a new `user_input`, one or more `function_result`s\n// continuing a tool call, etc.). Otherwise the conversation is fresh\n// and only the latest user turn is supported — multi-turn replay\n// without `previous_interaction_id` is not part of the API contract.\nfunction convertMessagesToInteractionsInput(\n messages: Array<ModelMessage>,\n hasPreviousInteraction: boolean,\n): InteractionsRequestInput {\n const toolCallIdToName = new Map<string, string>()\n for (const msg of messages) {\n if (msg.role === 'assistant' && msg.toolCalls) {\n for (const tc of msg.toolCalls) {\n toolCallIdToName.set(tc.id, tc.function.name)\n }\n }\n }\n\n const source = hasPreviousInteraction\n ? messagesAfterLastAssistant(messages)\n : messages\n\n if (hasPreviousInteraction && source.length === 0) {\n throw new Error(\n 'Gemini Interactions adapter: modelOptions.previous_interaction_id was provided but no new messages were found after the last assistant turn. Append at least one user or tool message before chaining.',\n )\n }\n\n if (!hasPreviousInteraction) {\n const [only, ...rest] = source\n if (!only) {\n throw new Error('Gemini Interactions adapter: no messages to send.')\n }\n if (rest.length > 0) {\n throw new Error(\n 'Gemini Interactions adapter: cannot send prior conversation history on a fresh interaction. Either set modelOptions.previous_interaction_id to chain prior turns server-side, or trim the message list to a single new user turn. See docs/adapters/gemini.md (\"Wiring with useChat\") for the canonical client/server pattern.',\n )\n }\n if (only.role !== 'user') {\n throw new Error(\n `Gemini Interactions adapter: the first message of a fresh interaction must be a user turn (got role=\"${only.role}\"). Set modelOptions.previous_interaction_id to continue an existing interaction.`,\n )\n }\n const content = messageToContentBlocks(only)\n if (content.length === 0) {\n throw new Error(\n 'Gemini Interactions adapter: the user message produced no content blocks to send.',\n )\n }\n return [{ type: 'user_input', content }]\n }\n\n // Chained path: each post-assistant message becomes one Step. A user\n // reply maps to `user_input`; a tool reply maps to `function_result`.\n // Assistant turns shouldn't appear here (sliced off above) — if one\n // somehow does we skip it rather than letting it shape the wire.\n const steps: Array<InteractionsStep> = []\n for (const msg of source) {\n if (msg.role === 'tool' && msg.toolCallId) {\n const result = serializeToolResultContent(msg.content)\n steps.push({\n type: 'function_result',\n call_id: msg.toolCallId,\n name: toolCallIdToName.get(msg.toolCallId),\n result,\n })\n } else if (msg.role === 'user') {\n const content = messageToContentBlocks(msg)\n if (content.length > 0) {\n steps.push({ type: 'user_input', content })\n }\n }\n }\n if (steps.length === 0) {\n throw new Error(\n 'Gemini Interactions adapter: messages after the last assistant turn produced no steps to send.',\n )\n }\n return steps\n}\n\n// The Interactions API's `function_result.result` field is a string. We\n// fail loudly on non-string tool content rather than silently coercing\n// to `''` — silent coercion meant the model lost the entire tool\n// output for callers that returned content as an array (e.g. image +\n// text) or `null`. If you need to send structured tool output, encode\n// it yourself before passing.\nfunction serializeToolResultContent(\n content: ModelMessage['content'] | undefined,\n): string {\n if (typeof content === 'string') return content\n if (content === null || content === undefined) {\n throw new Error(\n 'Gemini Interactions adapter: tool message has no content. The Interactions API requires a string `result` on function_result steps — return a string from your tool implementation (encode JSON/multimodal output yourself).',\n )\n }\n throw new Error(\n 'Gemini Interactions adapter: tool message content must be a string (got an array of content parts). The Interactions API requires a string `result` on function_result steps — stringify multimodal tool output before returning it from your tool.',\n )\n}\n\n// Extracts the content blocks (text / image / audio / video / document)\n// from a single message. Tool calls and tool results live one level up\n// as Steps, not as content, so they are NOT emitted here.\nfunction messageToContentBlocks(msg: ModelMessage): Array<ContentBlock> {\n const blocks: Array<ContentBlock> = []\n\n if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n blocks.push(contentPartToBlock(part))\n }\n } else if (\n typeof msg.content === 'string' &&\n msg.content &&\n msg.role !== 'tool'\n ) {\n blocks.push({ type: 'text', text: msg.content })\n }\n\n return blocks\n}\n\nfunction messagesAfterLastAssistant(\n messages: Array<ModelMessage>,\n): Array<ModelMessage> {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]?.role === 'assistant') {\n return messages.slice(i + 1)\n }\n }\n return messages\n}\n\nfunction safeParseToolArguments(\n raw: string | undefined,\n logger: InternalLogger,\n): Record<string, unknown> {\n if (!raw) return {}\n try {\n const parsed = JSON.parse(raw)\n return parsed && typeof parsed === 'object' ? parsed : {}\n } catch (error) {\n logger.errors(\n 'gemini-text-interactions.safeParseToolArguments parse failed',\n {\n error,\n raw,\n source: 'gemini-text-interactions.chatStream',\n },\n )\n return {}\n }\n}\n\n// `satisfies` pins these arrays to the SDK's narrow mime-type unions: if\n// Google removes a format the build breaks, and if they add one ours keeps\n// working (we just won't accept the new one until added here).\nconst IMAGE_MIME_TYPES = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'image/heic',\n 'image/heif',\n] as const satisfies ReadonlyArray<\n NonNullable<Interactions.ImageContent['mime_type']>\n>\n\nconst AUDIO_MIME_TYPES = [\n 'audio/wav',\n 'audio/mp3',\n 'audio/aiff',\n 'audio/aac',\n 'audio/ogg',\n 'audio/flac',\n] as const satisfies ReadonlyArray<\n NonNullable<Interactions.AudioContent['mime_type']>\n>\n\nconst VIDEO_MIME_TYPES = [\n 'video/mp4',\n 'video/mpeg',\n 'video/mpg',\n 'video/mov',\n 'video/avi',\n 'video/x-flv',\n 'video/webm',\n 'video/wmv',\n 'video/3gpp',\n] as const satisfies ReadonlyArray<\n NonNullable<Interactions.VideoContent['mime_type']>\n>\n\nconst DOCUMENT_MIME_TYPES = [\n 'application/pdf',\n] as const satisfies ReadonlyArray<\n NonNullable<Interactions.DocumentContent['mime_type']>\n>\n\nfunction validateMime<T extends string>(\n allowed: ReadonlyArray<T>,\n value: string | undefined,\n kind: string,\n): T | undefined {\n if (value === undefined) return undefined\n if ((allowed as ReadonlyArray<string>).includes(value)) {\n return value as T\n }\n throw new Error(\n `Unsupported ${kind} mime type \"${value}\" for the Gemini Interactions API. Allowed: ${allowed.join(', ')}.`,\n )\n}\n\nfunction contentPartToBlock(part: ContentPart): ContentBlock {\n if (part.type === 'text') {\n return { type: 'text', text: part.content }\n }\n const isData = part.source.type === 'data'\n switch (part.type) {\n case 'image': {\n const mime_type = validateMime(\n IMAGE_MIME_TYPES,\n part.source.mimeType,\n 'image',\n )\n return isData\n ? { type: 'image', data: part.source.value, mime_type }\n : { type: 'image', uri: part.source.value, mime_type }\n }\n case 'audio': {\n const mime_type = validateMime(\n AUDIO_MIME_TYPES,\n part.source.mimeType,\n 'audio',\n )\n return isData\n ? { type: 'audio', data: part.source.value, mime_type }\n : { type: 'audio', uri: part.source.value, mime_type }\n }\n case 'video': {\n const mime_type = validateMime(\n VIDEO_MIME_TYPES,\n part.source.mimeType,\n 'video',\n )\n return isData\n ? { type: 'video', data: part.source.value, mime_type }\n : { type: 'video', uri: part.source.value, mime_type }\n }\n case 'document': {\n const mime_type = validateMime(\n DOCUMENT_MIME_TYPES,\n part.source.mimeType,\n 'document',\n )\n return isData\n ? { type: 'document', data: part.source.value, mime_type }\n : { type: 'document', uri: part.source.value, mime_type }\n }\n }\n}\n\n// Built-in Gemini tools use snake_case field names in the Interactions API\n// that differ from the camelCase fields used on `client.models.generateContent`\n// (e.g. `fileSearchStoreNames` vs `file_search_store_names`). Translate\n// explicitly so callers keep using the same tool factories across adapters.\nfunction convertToolsToInteractionsFormat<TTool extends Tool>(\n tools: Array<TTool> | undefined,\n): Array<InteractionsTool> | undefined {\n if (!tools || tools.length === 0) return undefined\n\n const result: Array<InteractionsTool> = []\n\n for (const tool of tools) {\n switch (tool.name) {\n case 'google_search': {\n const metadata = (tool.metadata ?? {}) as {\n search_types?: Array<'web_search' | 'image_search'>\n }\n result.push({\n type: 'google_search',\n ...(metadata.search_types\n ? { search_types: metadata.search_types }\n : {}),\n })\n break\n }\n case 'code_execution': {\n result.push({ type: 'code_execution' })\n break\n }\n case 'url_context': {\n result.push({ type: 'url_context' })\n break\n }\n case 'file_search': {\n const metadata = (tool.metadata ?? {}) as {\n fileSearchStoreNames?: Array<string>\n topK?: number\n metadataFilter?: string\n }\n result.push({\n type: 'file_search',\n ...(metadata.fileSearchStoreNames\n ? { file_search_store_names: metadata.fileSearchStoreNames }\n : {}),\n ...(metadata.topK !== undefined ? { top_k: metadata.topK } : {}),\n ...(metadata.metadataFilter !== undefined\n ? { metadata_filter: metadata.metadataFilter }\n : {}),\n })\n break\n }\n case 'computer_use': {\n const metadata = (tool.metadata ?? {}) as {\n environment?: string\n excludedPredefinedFunctions?: Array<string>\n }\n if (metadata.environment && metadata.environment !== 'browser') {\n throw new Error(\n `computer_use environment \"${metadata.environment}\" is not supported on the Gemini Interactions API. Only \"browser\" is accepted.`,\n )\n }\n result.push({\n type: 'computer_use',\n ...(metadata.environment\n ? { environment: metadata.environment as 'browser' }\n : {}),\n ...(metadata.excludedPredefinedFunctions\n ? {\n excludedPredefinedFunctions:\n metadata.excludedPredefinedFunctions,\n }\n : {}),\n })\n break\n }\n case 'google_search_retrieval':\n throw new Error(\n '`google_search_retrieval` is not supported on the Gemini Interactions API. Use `googleSearchTool()` (`google_search`) with `geminiTextInteractions()`, or call `geminiText()` for the legacy retrieval tool.',\n )\n case 'google_maps':\n throw new Error(\n '`google_maps` is not yet supported on the Gemini Interactions API. Use `geminiText()` for Google Maps grounding.',\n )\n case 'mcp_server':\n throw new Error(\n '`mcp_server` is not yet supported on the `geminiTextInteractions()` adapter.',\n )\n default: {\n if (!tool.description) {\n throw new Error(\n `Tool ${tool.name} requires a description for the Gemini Interactions adapter`,\n )\n }\n result.push({\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: sanitizeToolParameters(\n tool.inputSchema ?? { type: 'object', properties: {} },\n ),\n })\n }\n }\n }\n\n return result\n}\n\n// Map of API-level status values onto the AG-UI `finishReason` field.\n// `requires_action` is the Interactions API's signal that the model\n// produced one or more function calls and is waiting for results — we\n// always map that to 'tool_calls' regardless of whether deltas were\n// observed (a function_call may have arrived in a single delta with no\n// other content). `incomplete` is the truncation signal (max_tokens\n// exceeded etc.) — map to 'length'. `completed` is normal stop.\nfunction statusToFinishReason(\n status: Interaction['status'] | undefined,\n sawFunctionCall: boolean,\n): 'stop' | 'length' | 'tool_calls' | null {\n if (status === 'requires_action') return 'tool_calls'\n if (status === 'incomplete') return 'length'\n if (sawFunctionCall) return 'tool_calls'\n return 'stop'\n}\n\n// Statuses that mean the interaction did not produce a usable result.\n// `failed`/`cancelled` map to RUN_ERROR. `incomplete` is the model\n// hitting a stop condition (max_tokens etc.) and is reported via\n// `finishReason: 'length'` on RUN_FINISHED so callers can decide how to\n// react without it looking like a hard error.\nfunction statusIsError(\n status: Interaction['status'] | undefined,\n): status is 'failed' | 'cancelled' {\n return status === 'failed' || status === 'cancelled'\n}\n\nasync function* translateInteractionEvents(\n stream: AsyncIterable<InteractionSSEEvent>,\n model: string,\n runId: string,\n threadId: string,\n parentRunId: string | undefined,\n timestamp: number,\n adapterName: string,\n logger: InternalLogger,\n): AsyncIterable<StreamChunk> {\n const messageId = generateId(adapterName)\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let textAccumulated = ''\n let interactionId: string | undefined\n let sawFunctionCall = false\n const toolCalls = new Map<string, ToolCallState>()\n let nextToolIndex = 0\n let thinkingStepId: string | null = null\n let thinkingAccumulated = ''\n let reasoningMessageId: string | null = null\n let hasClosedReasoning = false\n\n const closeReasoningIfNeeded = function* (): Generator<StreamChunk> {\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n // Reset so that a later `thought_summary` delta (the API\n // interleaves text → thought → text on some models) opens a\n // fresh reasoning block instead of re-using an already-ended\n // messageId, which would violate AG-UI ordering.\n thinkingStepId = null\n reasoningMessageId = null\n hasClosedReasoning = false\n }\n }\n\n // Seals any in-flight messages and tool calls. Called both on the\n // normal terminal path (`interaction.complete`) and on the error path\n // (`error` SSE event + premature EOF) so the StreamProcessor never\n // sees orphan TEXT_MESSAGE_START / TOOL_CALL_START / REASONING_*\n // events on RUN_ERROR.\n const closeOpenState = function* (): Generator<StreamChunk> {\n yield* closeReasoningIfNeeded()\n for (const [toolCallId, state] of toolCalls) {\n if (state.ended) continue\n state.ended = true\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolName: state.name,\n model,\n timestamp,\n input: state.args,\n }\n }\n if (hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = false\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp,\n }\n }\n }\n\n const emitRunStartedIfNeeded = function* (): Generator<StreamChunk> {\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp,\n parentRunId,\n }\n }\n }\n\n for await (const event of stream) {\n logger.provider(`provider=gemini-text-interactions`, { event })\n switch (event.event_type) {\n case 'interaction.start': {\n interactionId = event.interaction.id\n yield* emitRunStartedIfNeeded()\n break\n }\n\n case 'content.start': {\n yield* emitRunStartedIfNeeded()\n break\n }\n\n case 'content.delta': {\n yield* emitRunStartedIfNeeded()\n const delta = event.delta\n switch (delta.type) {\n case 'text': {\n yield* closeReasoningIfNeeded()\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp,\n role: 'assistant',\n }\n }\n textAccumulated += delta.text\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp,\n delta: delta.text,\n content: textAccumulated,\n }\n break\n }\n case 'function_call': {\n yield* closeReasoningIfNeeded()\n sawFunctionCall = true\n const toolCallId = delta.id\n const deltaArgs: Record<string, unknown> =\n typeof delta.arguments === 'string'\n ? safeParseToolArguments(delta.arguments, logger)\n : delta.arguments\n let state = toolCalls.get(toolCallId)\n if (!state) {\n state = {\n name: delta.name,\n args: { ...deltaArgs },\n index: nextToolIndex++,\n started: false,\n ended: false,\n }\n toolCalls.set(toolCallId, state)\n } else {\n state.args = { ...state.args, ...deltaArgs }\n if (delta.name) state.name = delta.name\n }\n if (!state.started) {\n state.started = true\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: state.name,\n toolName: state.name,\n model,\n timestamp,\n index: state.index,\n }\n }\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n model,\n timestamp,\n delta: JSON.stringify(deltaArgs),\n args: JSON.stringify(state.args),\n }\n break\n }\n case 'google_search_call': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.googleSearchCall',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'google_search_result': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.googleSearchResult',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'code_execution_call': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.codeExecutionCall',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'code_execution_result': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.codeExecutionResult',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'url_context_call': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.urlContextCall',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'url_context_result': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.urlContextResult',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'file_search_call': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.fileSearchCall',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'file_search_result': {\n yield* closeReasoningIfNeeded()\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.fileSearchResult',\n value: delta,\n model,\n timestamp,\n }\n break\n }\n case 'thought_summary': {\n const thoughtText =\n delta.content && 'text' in delta.content ? delta.content.text : ''\n if (!thoughtText) break\n if (thinkingStepId === null || reasoningMessageId === null) {\n thinkingStepId = generateId(adapterName)\n reasoningMessageId = generateId(adapterName)\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning',\n model,\n timestamp,\n }\n yield {\n type: EventType.STEP_STARTED,\n stepName: thinkingStepId,\n stepId: thinkingStepId,\n model,\n timestamp,\n stepType: 'thinking',\n }\n }\n thinkingAccumulated += thoughtText\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: thoughtText,\n model,\n timestamp,\n }\n yield {\n type: EventType.STEP_FINISHED,\n stepName: thinkingStepId,\n stepId: thinkingStepId,\n model,\n timestamp,\n delta: thoughtText,\n content: thinkingAccumulated,\n }\n break\n }\n // The following delta types are valid per the SDK type union\n // but aren't yet translated by this adapter (output modalities\n // text-only adapter shouldn't see, response-side function_result\n // / mcp_server_*, thought_signature). Falling through to the\n // observability default so SDK drift is visible.\n case 'image':\n case 'audio':\n case 'video':\n case 'document':\n case 'function_result':\n case 'mcp_server_tool_call':\n case 'mcp_server_tool_result':\n case 'thought_signature':\n default:\n logger.provider(\n `gemini-text-interactions unhandled content.delta type`,\n { delta },\n )\n break\n }\n break\n }\n\n case 'content.stop':\n case 'interaction.status_update': {\n break\n }\n\n case 'interaction.complete': {\n if (event.interaction.id) {\n interactionId = event.interaction.id\n }\n\n yield* closeOpenState()\n\n const status = event.interaction.status\n if (statusIsError(status)) {\n const message = `Gemini Interactions ${status}: the interaction ended without a usable response.`\n logger.errors(\n 'gemini-text-interactions.translateInteractionEvents non-success status',\n {\n source: 'gemini-text-interactions.chatStream',\n status,\n interactionId,\n },\n )\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model,\n timestamp,\n message,\n code: status,\n error: { message, code: status },\n }\n return\n }\n\n const usage = event.interaction.usage\n const finishReason = statusToFinishReason(status, sawFunctionCall)\n\n if (interactionId) {\n yield {\n type: EventType.CUSTOM,\n name: 'gemini.interactionId',\n value: { interactionId },\n model,\n timestamp,\n }\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp,\n finishReason,\n usage: usage\n ? {\n promptTokens: usage.total_input_tokens ?? 0,\n completionTokens: usage.total_output_tokens ?? 0,\n totalTokens: usage.total_tokens ?? 0,\n }\n : undefined,\n }\n return\n }\n\n case 'error': {\n // Close any in-flight TEXT_MESSAGE_START / TOOL_CALL_START /\n // REASONING_* so downstream consumers don't see orphan open\n // state after RUN_ERROR.\n yield* closeOpenState()\n const rawMessage = event.error?.message\n const message =\n typeof rawMessage === 'string' && rawMessage.length > 0\n ? rawMessage\n : `Gemini Interactions error (no message): ${JSON.stringify(event.error ?? {})}`\n const rawCode = event.error?.code\n const code =\n typeof rawCode === 'string' || typeof rawCode === 'number'\n ? String(rawCode)\n : undefined\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model,\n timestamp,\n message,\n code,\n error: { message, code },\n }\n return\n }\n\n default:\n logger.provider(`gemini-text-interactions unhandled event_type`, {\n event,\n })\n break\n }\n }\n\n // Stream ended without `interaction.complete` or `error` (both `return`\n // out of the loop). Seal any in-flight TEXT/TOOL/REASONING blocks here\n // so the truncation-fallback RUN_ERROR yielded by `chatStream` doesn't\n // leave orphan `*_START` events open downstream.\n yield* closeOpenState()\n}\n\nfunction extractTextFromInteraction(interaction: Interaction): string {\n let text = ''\n for (const output of interaction.outputs ?? []) {\n if (output.type === 'text') {\n text += output.text\n }\n }\n return text\n}\n\n// The live Interactions API rejects tool parameter schemas that contain\n// an empty `required: []` array with the misleading top-level error\n// `\"value at top-level must be a list\"`. Empty `properties: {}` and\n// `parameters: {}` are both fine — only the empty `required` array is\n// poison. The Zod -> JSON Schema converter (and many hand-written\n// schemas) emit `required: []` whenever a tool has zero required\n// parameters, so we strip those instances recursively before sending.\n// Non-empty `required` arrays are passed through unchanged.\nfunction sanitizeToolParameters(schema: unknown): unknown {\n if (!schema || typeof schema !== 'object') return schema\n if (Array.isArray(schema)) return schema.map(sanitizeToolParameters)\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(schema)) {\n if (key === 'required' && Array.isArray(value) && value.length === 0) {\n continue\n }\n out[key] = sanitizeToolParameters(value)\n }\n return out\n}\n\n// Re-export the stream type so consumers can import it alongside the\n// adapter from a single path: `import type { GeminiInteractionsStream }\n// from '@tanstack/ai-gemini/experimental'`.\nexport type { GeminiInteractionsStream }\n"],"names":[],"mappings":"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"}
@@ -39,6 +39,12 @@ export interface GeminiCommonConfigOptions {
39
39
  Note: The default value varies by Model and is specified by theModel.top_p attribute returned from the getModel function. An empty topK attribute indicates that the model doesn't apply top-k sampling and doesn't allow setting topK on requests.
40
40
  */
41
41
  topK?: number;
42
+ /** Controls randomness, range [0.0, 2.0]. Higher = more random. Use this or topP, not both. */
43
+ temperature?: number;
44
+ /** Nucleus sampling probability mass, range (0.0, 1.0]. */
45
+ topP?: number;
46
+ /** Maximum number of tokens to generate in the response. */
47
+ maxOutputTokens?: number;
42
48
  /**
43
49
  * Seed used in decoding. If not set, the request uses a randomly generated seed.
44
50
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-gemini",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Google Gemini adapter for TanStack AI chat, images, speech, audio generation, and structured outputs.",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -50,13 +50,13 @@
50
50
  "@tanstack/ai-utils": "0.2.1"
51
51
  },
52
52
  "peerDependencies": {
53
- "@tanstack/ai": "^0.26.0"
53
+ "@tanstack/ai": "^0.27.0"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@vitest/coverage-v8": "4.0.14",
57
57
  "vite": "^7.3.3",
58
58
  "zod": "^4.2.0",
59
- "@tanstack/ai": "0.26.0"
59
+ "@tanstack/ai": "0.27.0"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "vite build",
@@ -927,13 +927,6 @@ export class GeminiTextAdapter<
927
927
  contents: this.formatMessages(options.messages),
928
928
  config: {
929
929
  ...modelOpts,
930
- ...(options.temperature !== undefined && {
931
- temperature: options.temperature,
932
- }),
933
- ...(options.topP !== undefined && { topP: options.topP }),
934
- ...(options.maxTokens !== undefined && {
935
- maxOutputTokens: options.maxTokens,
936
- }),
937
930
  ...(mappedThinkingConfig !== undefined && {
938
931
  thinkingConfig: mappedThinkingConfig,
939
932
  }),
@@ -471,15 +471,6 @@ function buildInteractionsRequest(
471
471
  const generationConfig: Interactions.GenerationConfig = {
472
472
  ...modelOpts?.generation_config,
473
473
  }
474
- if (options.temperature !== undefined) {
475
- generationConfig.temperature = options.temperature
476
- }
477
- if (options.topP !== undefined) {
478
- generationConfig.top_p = options.topP
479
- }
480
- if (options.maxTokens !== undefined) {
481
- generationConfig.max_output_tokens = options.maxTokens
482
- }
483
474
 
484
475
  const hasGenerationConfig = Object.keys(generationConfig).length > 0
485
476
 
@@ -50,6 +50,12 @@ Gemini models use Top-p (nucleus) sampling or a combination of Top-k and nucleus
50
50
  Note: The default value varies by Model and is specified by theModel.top_p attribute returned from the getModel function. An empty topK attribute indicates that the model doesn't apply top-k sampling and doesn't allow setting topK on requests.
51
51
  */
52
52
  topK?: number
53
+ /** Controls randomness, range [0.0, 2.0]. Higher = more random. Use this or topP, not both. */
54
+ temperature?: number
55
+ /** Nucleus sampling probability mass, range (0.0, 1.0]. */
56
+ topP?: number
57
+ /** Maximum number of tokens to generate in the response. */
58
+ maxOutputTokens?: number
53
59
  /**
54
60
  * Seed used in decoding. If not set, the request uses a randomly generated seed.
55
61
  */