@tanstack/ai-gemini 0.32.1 → 0.34.1
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.
- package/README.md +1 -0
- package/dist/esm/adapters/files.d.ts +27 -0
- package/dist/esm/adapters/files.js +62 -0
- package/dist/esm/adapters/files.js.map +1 -0
- package/dist/esm/adapters/image.d.ts +1 -0
- package/dist/esm/adapters/image.js +3 -2
- package/dist/esm/adapters/image.js.map +1 -1
- package/dist/esm/adapters/text.d.ts +1 -0
- package/dist/esm/adapters/text.js +68 -5
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/adapters/video.d.ts +1 -0
- package/dist/esm/adapters/video.js +8 -5
- package/dist/esm/adapters/video.js.map +1 -1
- package/dist/esm/experimental/text-interactions/adapter.d.ts +1 -0
- package/dist/esm/experimental/text-interactions/adapter.js +11 -9
- package/dist/esm/experimental/text-interactions/adapter.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/message-types.d.ts +6 -1
- package/package.json +4 -3
- package/src/adapters/files.ts +84 -0
- package/src/adapters/image.ts +13 -3
- package/src/adapters/text.ts +105 -8
- package/src/adapters/video.ts +27 -5
- package/src/experimental/text-interactions/adapter.ts +18 -9
- package/src/index.ts +8 -0
- package/src/message-types.ts +7 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","names":[],"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 VideoMetadata,\n} from '@google/genai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n AdapterYieldChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type { ExternalTextProviderOptions } from '../text/text-provider-options'\nimport type {\n GeminiMessageMetadataByModality,\n GeminiToolCallMetadata,\n GeminiVideoMetadata,\n GeminiVideoProcessing,\n} from '../message-types'\nimport type { GeminiClientConfig } from '../utils/client'\n\n/**\n * Fallback MIME types for URL-sourced media parts that don't specify one.\n */\nconst DEFAULT_MEDIA_MIME_TYPES = {\n image: 'image/jpeg',\n audio: 'audio/mp3',\n video: 'video/mp4',\n document: 'application/pdf',\n} as const\n\n/**\n * Content block shape for an Interactions API `input` step. The installed\n * @google/genai types predate the video `processing` field, so we model the\n * subset we emit and cast at the call site.\n */\ntype InteractionContent =\n | { type: 'text'; text: string }\n | {\n type: 'video'\n uri?: string\n data?: string\n mime_type?: string\n processing?: GeminiVideoProcessing\n }\n | {\n type: 'image' | 'audio' | 'document'\n uri?: string\n data?: string\n mime_type?: string\n }\n\ninterface InteractionStep {\n type: 'user_input' | 'model_output'\n content: Array<InteractionContent>\n}\n\n/** True when any message carries a video part requesting agentic processing. */\nfunction hasAgenticVideo(messages: Array<ModelMessage>): boolean {\n return messages.some(\n (msg) =>\n Array.isArray(msg.content) &&\n msg.content.some(\n (part) =>\n part.type === 'video' &&\n (part.metadata as GeminiVideoMetadata | undefined)?.processing ===\n 'agentic',\n ),\n )\n}\n\n/** Convert a single content part to an Interactions API content block. */\nfunction contentPartToInteraction(part: ContentPart): InteractionContent {\n if (part.type === 'text') {\n return { type: 'text', text: part.content }\n }\n\n const source = part.source\n const mimeType =\n source.type === 'data'\n ? source.mimeType\n : (source.mimeType ?? DEFAULT_MEDIA_MIME_TYPES[part.type])\n const base =\n source.type === 'data'\n ? { data: source.value, mime_type: mimeType }\n : { uri: source.value, mime_type: mimeType }\n\n if (part.type === 'video') {\n const processing = (part.metadata as GeminiVideoMetadata | undefined)\n ?.processing\n return { type: 'video', ...base, ...(processing && { processing }) }\n }\n return { type: part.type, ...base }\n}\n\n/**\n * Build the Interactions API `input` from chat messages. Each user/assistant\n * message becomes a `user_input` / `model_output` step wrapping its content\n * blocks — the wrapping the Python SDK performs implicitly but the JS SDK\n * does not. Tool messages are skipped (unsupported on this path).\n */\nfunction buildInteractionsInput(\n messages: Array<ModelMessage>,\n): Array<InteractionStep> {\n const steps: Array<InteractionStep> = []\n for (const msg of messages) {\n if (msg.role === 'tool') continue\n const stepType = msg.role === 'assistant' ? 'model_output' : 'user_input'\n const content: Array<InteractionContent> = []\n if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n content.push(contentPartToInteraction(part))\n }\n } else if (msg.content) {\n content.push({ type: 'text', text: msg.content })\n }\n if (content.length > 0) {\n steps.push({ type: stepType, content })\n }\n }\n return steps\n}\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<AdapterYieldChunk> {\n // Agentic video understanding is only exposed through the Interactions API,\n // not generateContent. Detect it and take that path instead.\n if (hasAgenticVideo(options.messages)) {\n yield* this.interactionsStream(options)\n return\n }\n\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 * Agentic video-understanding path via the Interactions API.\n *\n * The Interactions API (unlike `generateContent`) requires message parts to\n * be wrapped in `user_input` / `model_output` steps, and it accepts the\n * `processing: 'agentic'` video flag. This is a non-streaming call whose\n * single text result is re-emitted as AG-UI stream chunks.\n */\n private async *interactionsStream(\n options: TextOptions<GeminiTextProviderOptions>,\n ): AsyncIterable<AdapterYieldChunk> {\n const model = options.model\n const { logger } = options\n const runId = options.runId ?? generateId(this.name)\n const threadId = options.threadId ?? generateId(this.name)\n const messageId = generateId(this.name)\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${model} messages=${options.messages.length} mode=interactions-agentic-video`,\n { provider: 'gemini', model },\n )\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 const input = buildInteractionsInput(options.messages)\n\n // The installed @google/genai (2.10.0) Interactions `VideoContent` type\n // predates the `processing` field, so the structurally-built input is\n // cast at the call boundary. The SDK forwards it to the wire unchanged.\n const interaction = await this.client.interactions.create({\n model,\n ...(systemInstruction !== undefined && {\n system_instruction: systemInstruction,\n }),\n input: input as never,\n })\n\n const text = interaction.output_text ?? ''\n\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n if (text) {\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: text,\n content: text,\n }\n }\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n finishReason: 'stop',\n }\n } catch (error) {\n const rawEvent = toRunErrorRawEvent(error)\n logger.errors('gemini.interactionsStream fatal', {\n error,\n source: 'gemini.interactionsStream',\n })\n yield {\n type: EventType.RUN_ERROR,\n model,\n timestamp: Date.now(),\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\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(jsonContentParseError(rawText, 'structured output'))\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 * Stream schema-constrained JSON from Gemini natively.\n *\n * `chat({ outputSchema, stream: true })` calls this when the adapter\n * implements it. Without it, the engine buffers `structuredOutput()` and\n * emits one synthetic delta.\n */\n async *structuredOutputStream(\n options: StructuredOutputOptions<GeminiTextProviderOptions>,\n ): AsyncIterable<AdapterYieldChunk> {\n const { chatOptions: requestedChatOptions, outputSchema } = options\n const chatOptions = {\n ...requestedChatOptions,\n runId: requestedChatOptions.runId ?? generateId(this.name),\n }\n const mappedOptions = this.mapCommonOptionsToGemini(chatOptions)\n\n try {\n chatOptions.logger.request(\n `activity=structuredOutputStream provider=gemini model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: 'gemini', model: this.model },\n )\n const result = await this.client.models.generateContentStream({\n ...mappedOptions,\n config: {\n ...mappedOptions.config,\n responseMimeType: 'application/json',\n responseSchema: outputSchema,\n },\n })\n\n let rawText = ''\n let finished:\n | Extract<AdapterYieldChunk, { type: typeof EventType.RUN_FINISHED }>\n | undefined\n let failed = false\n for await (const chunk of this.processStreamChunks(\n result,\n chatOptions,\n chatOptions.logger,\n )) {\n if (chunk.type === EventType.TEXT_MESSAGE_CONTENT) {\n rawText += chunk.delta\n }\n if (chunk.type === EventType.RUN_ERROR) failed = true\n if (chunk.type === EventType.RUN_FINISHED) {\n finished = chunk\n } else {\n yield chunk\n }\n }\n\n if (failed) return\n if (!finished) {\n yield structuredStreamError(\n chatOptions,\n 'Gemini structured-output stream ended without a terminal event',\n 'truncated-stream',\n )\n return\n }\n if (!rawText) {\n yield structuredStreamError(\n chatOptions,\n 'Gemini structured-output stream contained no content',\n 'empty-response',\n )\n return\n }\n\n let object: unknown\n try {\n object = JSON.parse(rawText)\n } catch {\n yield structuredStreamError(\n chatOptions,\n jsonContentParseError(rawText, 'Gemini structured-output stream'),\n 'parse-error',\n )\n return\n }\n\n yield {\n type: EventType.CUSTOM,\n name: 'structured-output.complete',\n value: { object, raw: rawText },\n model: chatOptions.model,\n timestamp: Date.now(),\n }\n yield { ...finished, timestamp: Date.now() }\n } catch (error) {\n const rawEvent = toRunErrorRawEvent(error)\n const message =\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during structured output streaming.'\n chatOptions.logger.errors('gemini.structuredOutputStream fatal', {\n error,\n source: 'gemini.structuredOutputStream',\n })\n yield {\n ...structuredStreamError(chatOptions, message, 'provider-error'),\n ...(rawEvent !== undefined && { rawEvent }),\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<AdapterYieldChunk> {\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 parentMessageId: messageId,\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 // Emit TOOL_CALL_END for all tracked tool calls. functionCall parts on\n // this chunk (including UNEXPECTED_TOOL_CALL finishes) were already\n // registered and started by the per-part loop above.\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 const geminiPart: Part =\n part.source.type === 'data'\n ? {\n inlineData: {\n data: part.source.value,\n mimeType: part.source.mimeType,\n },\n }\n : {\n fileData: {\n fileUri: part.source.value,\n // For URL sources, use provided mimeType or fall back to\n // reasonable defaults.\n mimeType:\n part.source.mimeType ?? DEFAULT_MEDIA_MIME_TYPES[part.type],\n },\n }\n\n // Apply single-pass video sampling controls (fps / clip offsets) from\n // the part metadata. `processing: 'agentic'` is handled separately via\n // the Interactions API and never reaches this generateContent path.\n if (part.type === 'video') {\n const meta = part.metadata as GeminiVideoMetadata | undefined\n const videoMetadata: VideoMetadata = {\n ...(meta?.fps !== undefined && { fps: meta.fps }),\n ...(meta?.startOffset !== undefined && {\n startOffset: meta.startOffset,\n }),\n ...(meta?.endOffset !== undefined && { endOffset: meta.endOffset }),\n }\n if (Object.keys(videoMetadata).length > 0) {\n geminiPart.videoMetadata = videoMetadata\n }\n }\n\n return geminiPart\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 id (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 id (tool call ID).\n // Two parallel calls to the *same* tool share a `name` but have distinct\n // `id`s — keying on `name` dropped every response but the first for\n // same-tool parallel calls, leaving Gemini with fewer response parts\n // than call parts and a 400 on the next request.\n for (const msg of merged) {\n if (!msg.parts) continue\n const seenFunctionResponseIds = new Set<string>()\n msg.parts = msg.parts.filter((part) => {\n if ('functionResponse' in part && part.functionResponse?.id) {\n if (seenFunctionResponseIds.has(part.functionResponse.id)) {\n return false\n }\n seenFunctionResponseIds.add(part.functionResponse.id)\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 // Forward the caller's abort signal so cancellation reaches the SDK\n // request, matching the OpenAI-compatible adapters (issue #1374).\n ...(options.request?.signal != null && {\n abortSignal: options.request.signal,\n }),\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\nfunction jsonContentParseError(rawText: string, label: string) {\n const snippet = rawText.slice(0, 200)\n const ellipsis = rawText.length > 200 ? '...' : ''\n return `Failed to parse ${label} as JSON. Content: ${snippet}${ellipsis}`\n}\n\nfunction structuredStreamError(\n options: TextOptions<GeminiTextProviderOptions>,\n message: string,\n code: string,\n): Extract<AdapterYieldChunk, { type: typeof EventType.RUN_ERROR }> {\n return {\n type: EventType.RUN_ERROR,\n runId: options.runId,\n model: options.model,\n timestamp: Date.now(),\n message,\n code,\n error: { message, code },\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"],"mappings":";;;;;;;;;;;;;AAmDA,IAAM,2BAA2B;CAC/B,OAAO;CACP,OAAO;CACP,OAAO;CACP,UAAU;AACZ;;AA6BA,SAAS,gBAAgB,UAAwC;CAC/D,OAAO,SAAS,MACb,QACC,MAAM,QAAQ,IAAI,OAAO,KACzB,IAAI,QAAQ,MACT,SACC,KAAK,SAAS,WACb,KAAK,UAA8C,eAClD,SACN,CACJ;AACF;;AAGA,SAAS,yBAAyB,MAAuC;CACvE,IAAI,KAAK,SAAS,QAChB,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK;CAAQ;CAG5C,MAAM,SAAS,KAAK;CACpB,MAAM,WACJ,OAAO,SAAS,SACZ,OAAO,WACN,OAAO,YAAY,yBAAyB,KAAK;CACxD,MAAM,OACJ,OAAO,SAAS,SACZ;EAAE,MAAM,OAAO;EAAO,WAAW;CAAS,IAC1C;EAAE,KAAK,OAAO;EAAO,WAAW;CAAS;CAE/C,IAAI,KAAK,SAAS,SAAS;EACzB,MAAM,aAAc,KAAK,UACrB;EACJ,OAAO;GAAE,MAAM;GAAS,GAAG;GAAM,GAAI,cAAc,EAAE,WAAW;EAAG;CACrE;CACA,OAAO;EAAE,MAAM,KAAK;EAAM,GAAG;CAAK;AACpC;;;;;;;AAQA,SAAS,uBACP,UACwB;CACxB,MAAM,QAAgC,CAAC;CACvC,KAAK,MAAM,OAAO,UAAU;EAC1B,IAAI,IAAI,SAAS,QAAQ;EACzB,MAAM,WAAW,IAAI,SAAS,cAAc,iBAAiB;EAC7D,MAAM,UAAqC,CAAC;EAC5C,IAAI,MAAM,QAAQ,IAAI,OAAO,GAC3B,KAAK,MAAM,QAAQ,IAAI,SACrB,QAAQ,KAAK,yBAAyB,IAAI,CAAC;OAExC,IAAI,IAAI,SACb,QAAQ,KAAK;GAAE,MAAM;GAAQ,MAAM,IAAI;EAAQ,CAAC;EAElD,IAAI,QAAQ,SAAS,GACnB,MAAM,KAAK;GAAE,MAAM;GAAU;EAAQ,CAAC;CAE1C;CACA,OAAO;AACT;;;;;;;AAqDA,IAAa,oBAAb,cAOU,gBAOR;CACA,OAAyB;CACzB,OAAgB;CAEhB;CAEA,YAAY,QAA0B,OAAe;EACnD,MAAM,CAAC,GAAG,KAAK;EACf,KAAK,SAAS,mBAAmB,MAAM;CACzC;CAEA,OAAO,WACL,SACkC;EAGlC,IAAI,gBAAgB,QAAQ,QAAQ,GAAG;GACrC,OAAO,KAAK,mBAAmB,OAAO;GACtC;EACF;EAEA,MAAM,gBAAgB,KAAK,yBAAyB,OAAO;EAC3D,MAAM,EAAE,WAAW;EAEnB,IAAI;GACF,OAAO,QACL,uCAAuC,KAAK,MAAM,YAAY,QAAQ,SAAS,OAAO,SAAS,QAAQ,OAAO,UAAU,EAAE,eAC1H;IAAE,UAAU;IAAU,OAAO,KAAK;GAAM,CAC1C;GACA,MAAM,SACJ,MAAM,KAAK,OAAO,OAAO,sBAAsB,aAAa;GAE9D,OAAO,KAAK,oBAAoB,QAAQ,SAAS,MAAM;EACzD,SAAS,OAAO;GACd,MAAM,WAAW,mBAAmB,KAAK;GACzC,OAAO,OAAO,2BAA2B;IACvC;IACA,QAAQ;GACV,CAAC;GACD,MAAM;IACJ,MAAM,UAAU;IAChB,OAAO,QAAQ;IACf,WAAW,KAAK,IAAI;IACpB,SACE,iBAAiB,QACb,MAAM,UACN;IAGN,GAAI,aAAa,KAAA,KAAa,EAAE,SAAS;IACzC,OAAO,EACL,SACE,iBAAiB,QACb,MAAM,UACN,oDACR;GACF;EACF;CACF;;;;;;;;;CAUA,OAAe,mBACb,SACkC;EAClC,MAAM,QAAQ,QAAQ;EACtB,MAAM,EAAE,WAAW;EACnB,MAAM,QAAQ,QAAQ,SAAS,WAAW,KAAK,IAAI;EACnD,MAAM,WAAW,QAAQ,YAAY,WAAW,KAAK,IAAI;EACzD,MAAM,YAAY,WAAW,KAAK,IAAI;EAEtC,IAAI;GACF,OAAO,QACL,uCAAuC,MAAM,YAAY,QAAQ,SAAS,OAAO,mCACjF;IAAE,UAAU;IAAU;GAAM,CAC9B;GAEA,MAAM,oBAAoB,uBAAuB,QAAQ,aAAa;GACtE,MAAM,oBACJ,kBAAkB,SAAS,IACvB,kBAAkB,KAAK,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,IAAI,IACjD,KAAA;GAEN,MAAM,QAAQ,uBAAuB,QAAQ,QAAQ;GAarD,MAAM,QAAO,MARa,KAAK,OAAO,aAAa,OAAO;IACxD;IACA,GAAI,sBAAsB,KAAA,KAAa,EACrC,oBAAoB,kBACtB;IACO;GACT,CAAC,EAAA,CAEwB,eAAe;GAExC,MAAM;IACJ,MAAM,UAAU;IAChB;IACA;IACA;IACA,WAAW,KAAK,IAAI;IACpB,aAAa,QAAQ;GACvB;GACA,MAAM;IACJ,MAAM,UAAU;IAChB;IACA;IACA,WAAW,KAAK,IAAI;IACpB,MAAM;GACR;GACA,IAAI,MACF,MAAM;IACJ,MAAM,UAAU;IAChB;IACA;IACA,WAAW,KAAK,IAAI;IACpB,OAAO;IACP,SAAS;GACX;GAEF,MAAM;IACJ,MAAM,UAAU;IAChB;IACA;IACA,WAAW,KAAK,IAAI;GACtB;GACA,MAAM;IACJ,MAAM,UAAU;IAChB;IACA;IACA;IACA,WAAW,KAAK,IAAI;IACpB,cAAc;GAChB;EACF,SAAS,OAAO;GACd,MAAM,WAAW,mBAAmB,KAAK;GACzC,OAAO,OAAO,mCAAmC;IAC/C;IACA,QAAQ;GACV,CAAC;GACD,MAAM;IACJ,MAAM,UAAU;IAChB;IACA,WAAW,KAAK,IAAI;IACpB,SACE,iBAAiB,QACb,MAAM,UACN;IACN,GAAI,aAAa,KAAA,KAAa,EAAE,SAAS;IACzC,OAAO,EACL,SACE,iBAAiB,QACb,MAAM,UACN,oDACR;GACF;EACF;CACF;;;;;;CAOA,MAAM,iBACJ,SAC0C;EAC1C,MAAM,EAAE,aAAa,iBAAiB;EACtC,MAAM,EAAE,WAAW;EAEnB,MAAM,gBAAgB,KAAK,yBAAyB,WAAW;EAE/D,IAAI;GACF,OAAO,QACL,uCAAuC,KAAK,MAAM,YAAY,YAAY,SAAS,OAAO,SAAS,YAAY,OAAO,UAAU,EAAE,gBAClI;IAAE,UAAU;IAAU,OAAO,KAAK;GAAM,CAC1C;GAEA,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,gBAAgB;IACtD,GAAG;IACH,QAAQ;KACN,GAAG,cAAc;KACjB,kBAAkB;KAClB,gBAAgB;IAClB;GACF,CAAC;GAGD,MAAM,UAAU,KAAK,wBAAwB,MAAM;GAGnD,IAAI;GACJ,IAAI;IACF,SAAS,KAAK,MAAM,OAAO;GAC7B,QAAQ;IACN,MAAM,IAAI,MAAM,sBAAsB,SAAS,mBAAmB,CAAC;GACrE;GAEA,OAAO;IACL,MAAM;IACN;IACA,OAAO,OAAO,gBACV,iBAAiB,OAAO,aAAa,IACrC,KAAA;GACN;EACF,SAAS,OAAO;GACd,OAAO,OAAO,iCAAiC;IAC7C;IACA,QAAQ;GACV,CAAC;GACD,MAAM,IAAI,MACR,iBAAiB,QACb,MAAM,UACN,gEACN;EACF;CACF;;;;;;;;CASA,OAAO,uBACL,SACkC;EAClC,MAAM,EAAE,aAAa,sBAAsB,iBAAiB;EAC5D,MAAM,cAAc;GAClB,GAAG;GACH,OAAO,qBAAqB,SAAS,WAAW,KAAK,IAAI;EAC3D;EACA,MAAM,gBAAgB,KAAK,yBAAyB,WAAW;EAE/D,IAAI;GACF,YAAY,OAAO,QACjB,yDAAyD,KAAK,MAAM,YAAY,YAAY,SAAS,UACrG;IAAE,UAAU;IAAU,OAAO,KAAK;GAAM,CAC1C;GACA,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,sBAAsB;IAC5D,GAAG;IACH,QAAQ;KACN,GAAG,cAAc;KACjB,kBAAkB;KAClB,gBAAgB;IAClB;GACF,CAAC;GAED,IAAI,UAAU;GACd,IAAI;GAGJ,IAAI,SAAS;GACb,WAAW,MAAM,SAAS,KAAK,oBAC7B,QACA,aACA,YAAY,MACd,GAAG;IACD,IAAI,MAAM,SAAS,UAAU,sBAC3B,WAAW,MAAM;IAEnB,IAAI,MAAM,SAAS,UAAU,WAAW,SAAS;IACjD,IAAI,MAAM,SAAS,UAAU,cAC3B,WAAW;SAEX,MAAM;GAEV;GAEA,IAAI,QAAQ;GACZ,IAAI,CAAC,UAAU;IACb,MAAM,sBACJ,aACA,kEACA,kBACF;IACA;GACF;GACA,IAAI,CAAC,SAAS;IACZ,MAAM,sBACJ,aACA,wDACA,gBACF;IACA;GACF;GAEA,IAAI;GACJ,IAAI;IACF,SAAS,KAAK,MAAM,OAAO;GAC7B,QAAQ;IACN,MAAM,sBACJ,aACA,sBAAsB,SAAS,iCAAiC,GAChE,aACF;IACA;GACF;GAEA,MAAM;IACJ,MAAM,UAAU;IAChB,MAAM;IACN,OAAO;KAAE;KAAQ,KAAK;IAAQ;IAC9B,OAAO,YAAY;IACnB,WAAW,KAAK,IAAI;GACtB;GACA,MAAM;IAAE,GAAG;IAAU,WAAW,KAAK,IAAI;GAAE;EAC7C,SAAS,OAAO;GACd,MAAM,WAAW,mBAAmB,KAAK;GACzC,MAAM,UACJ,iBAAiB,QACb,MAAM,UACN;GACN,YAAY,OAAO,OAAO,uCAAuC;IAC/D;IACA,QAAQ;GACV,CAAC;GACD,MAAM;IACJ,GAAG,sBAAsB,aAAa,SAAS,gBAAgB;IAC/D,GAAI,aAAa,KAAA,KAAa,EAAE,SAAS;GAC3C;EACF;CACF;;;;CAKA,wBAAgC,UAA2C;EACzE,IAAI,cAAc;EAElB,IAAI,SAAS,aAAa,EAAE,EAAE,SAAS,OAChC;QAAA,MAAM,QAAQ,SAAS,WAAW,EAAE,CAAC,QAAQ,OAChD,IAAI,KAAK,MACP,eAAe,KAAK;EAAA;EAK1B,OAAO;CACT;CAEA,OAAe,oBACb,QACA,SACA,QACkC;EAClC,MAAM,QAAQ,QAAQ;EACtB,IAAI,qBAAqB;EACzB,IAAI,sBAAsB;EAC1B,MAAM,8BAAc,IAAI,IAStB;EACF,IAAI,gBAAgB;EAGpB,MAAM,QAAQ,QAAQ,SAAS,WAAW,KAAK,IAAI;EACnD,MAAM,WAAW,QAAQ,YAAY,WAAW,KAAK,IAAI;EACzD,MAAM,YAAY,WAAW,KAAK,IAAI;EACtC,IAAI,SAAwB;EAC5B,IAAI,qBAAoC;EACxC,IAAI,qBAAqB;EACzB,IAAI,uBAAuB;EAC3B,IAAI,6BAA6B;EACjC,IAAI,wBAAwB;EAE5B,WAAW,MAAM,SAAS,QAAQ;GAChC,OAAO,SAAS,mBAAmB,EAAE,MAAM,CAAC;GAE5C,IAAI,CAAC,sBAAsB;IACzB,uBAAuB;IACvB,MAAM;KACJ,MAAM,UAAU;KAChB;KACA;KACA;KACA,WAAW,KAAK,IAAI;KACpB,aAAa,QAAQ;IACvB;GACF;GAEA,IAAI,MAAM,aAAa,EAAE,EAAE,SAAS,OAAO;IACzC,MAAM,QAAQ,MAAM,WAAW,EAAE,CAAC,QAAQ;IAE1C,KAAK,MAAM,QAAQ,OAAO;KACxB,IAAI,KAAK,MAAM;MACb,IAAI,KAAK,SAAS;OAEhB,IAAI,CAAC,uBAAuB;QAC1B,wBAAwB;QACxB,SAAS,WAAW,KAAK,IAAI;QAC7B,qBAAqB,WAAW,KAAK,IAAI;QAGzC,MAAM;SACJ,MAAM,UAAU;SAChB,WAAW;SACX;SACA,WAAW,KAAK,IAAI;QACtB;QACA,MAAM;SACJ,MAAM,UAAU;SAChB,WAAW;SACX,MAAM;SACN;SACA,WAAW,KAAK,IAAI;QACtB;QAGA,MAAM;SACJ,MAAM,UAAU;SAChB,UAAU;SACV;SACA;SACA,WAAW,KAAK,IAAI;SACpB,UAAU;QACZ;OACF;OAEA,uBAAuB,KAAK;OAI5B,IAAI,CAAC,oBAAoB;OACzB,MAAM;QACJ,MAAM,UAAU;QAChB,WAAW;QACX,OAAO,KAAK;QACZ;QACA,WAAW,KAAK,IAAI;OACtB;OAGA,MAAM;QACJ,MAAM,UAAU;QAChB,UAAU,UAAU,WAAW,KAAK,IAAI;QACxC,QAAQ,UAAU,WAAW,KAAK,IAAI;QACtC;QACA,WAAW,KAAK,IAAI;QACpB,OAAO,KAAK;QACZ,SAAS;OACX;MACF,OAAO,IAAI,KAAK,KAAK,KAAK,GAAG;OAE3B,IAAI,sBAAsB,CAAC,oBAAoB;QAC7C,qBAAqB;QACrB,MAAM;SACJ,MAAM,UAAU;SAChB,WAAW;SACX;SACA,WAAW,KAAK,IAAI;QACtB;QACA,MAAM;SACJ,MAAM,UAAU;SAChB,WAAW;SACX;SACA,WAAW,KAAK,IAAI;QACtB;OACF;OAIA,IAAI,CAAC,4BAA4B;QAC/B,6BAA6B;QAC7B,MAAM;SACJ,MAAM,UAAU;SAChB;SACA;SACA,WAAW,KAAK,IAAI;SACpB,MAAM;QACR;OACF;OAEA,sBAAsB,KAAK;OAC3B,MAAM;QACJ,MAAM,UAAU;QAChB;QACA;QACA,WAAW,KAAK,IAAI;QACpB,OAAO,KAAK;QACZ,SAAS;OACX;MACF;KACF;KAEA,MAAM,eAAe,KAAK;KAC1B,IAAI,cAAc;MAChB,MAAM,aACJ,aAAa,MACb,GAAG,aAAa,KAAK,GAAG,KAAK,IAAI,EAAE,GAAG;MACxC,MAAM,eAAe,aAAa,QAAQ,CAAC;MAK3C,MAAM,uBAAuB,KAAK,oBAAoB,KAAA;MAEtD,IAAI,eAAe,YAAY,IAAI,UAAU;MAC7C,IAAI,CAAC,cAAc;OACjB,eAAe;QACb,MAAM,aAAa,QAAQ;QAC3B,MACE,OAAO,iBAAiB,WACpB,eACA,KAAK,UAAU,YAAY;QACjC,OAAO;QACP,SAAS;QAGT,GAAI,yBAAyB,KAAA,KAAa,EACxC,kBAAkB,qBACpB;OACF;OACA,YAAY,IAAI,YAAY,YAAY;MAC1C,OAAO;OACL,IAAI,CAAC,aAAa,oBAAoB,sBACpC,aAAa,mBAAmB;OAElC,IAAI;QACF,MAAM,eAAe,KAAK,MAAM,aAAa,IAAI;QACjD,MAAM,UACJ,OAAO,iBAAiB,WACpB,KAAK,MAAM,YAAY,IACvB;QACN,MAAM,aAAa;SAAE,GAAG;SAAc,GAAG;QAAQ;QACjD,aAAa,OAAO,KAAK,UAAU,UAAU;OAC/C,QAAQ;QACN,aAAa,OACX,OAAO,iBAAiB,WACpB,eACA,KAAK,UAAU,YAAY;OACnC;MACF;MAGA,IAAI,CAAC,aAAa,SAAS;OACzB,aAAa,UAAU;OACvB,MAAM;QACJ,MAAM,UAAU;QAChB;QACA,cAAc,aAAa;QAC3B,UAAU,aAAa;QACvB,iBAAiB;QACjB;QACA,WAAW,KAAK,IAAI;QACpB,OAAO,aAAa;QACpB,GAAI,aAAa,oBAAoB,EACnC,UAAU,EACR,kBAAkB,aAAa,iBACjC,EACF;OACF;MACF;MAGA,MAAM;OACJ,MAAM,UAAU;OAChB;OACA;OACA,WAAW,KAAK,IAAI;OACpB,OAAO,aAAa;OACpB,MAAM,aAAa;MACrB;KACF;IACF;GACF,OAAO,IAAI,MAAM,QAAQ,MAAM,KAAK,KAAK,GAAG;IAG1C,IAAI,CAAC,4BAA4B;KAC/B,6BAA6B;KAC7B,MAAM;MACJ,MAAM,UAAU;MAChB;MACA;MACA,WAAW,KAAK,IAAI;MACpB,MAAM;KACR;IACF;IAEA,sBAAsB,MAAM;IAC5B,MAAM;KACJ,MAAM,UAAU;KAChB;KACA;KACA,WAAW,KAAK,IAAI;KACpB,OAAO,MAAM;KACb,SAAS;IACX;GACF;GAEA,IAAI,MAAM,aAAa,EAAE,EAAE,cAAc;IACvC,MAAM,eAAe,MAAM,WAAW,EAAE,CAAC;IAKzC,KAAK,MAAM,CAAC,YAAY,iBAAiB,YAAY,QAAQ,GAAG;KAC9D,IAAI,cAAuB,CAAC;KAC5B,IAAI;MACF,MAAM,SAAS,KAAK,MAAM,aAAa,IAAI;MAC3C,cAAc,UAAU,OAAO,WAAW,WAAW,SAAS,CAAC;KACjE,QAAQ;MACN,cAAc,CAAC;KACjB;KAEA,MAAM;MACJ,MAAM,UAAU;MAChB;MACA,cAAc,aAAa;MAC3B,UAAU,aAAa;MACvB;MACA,WAAW,KAAK,IAAI;MACpB,OAAO;KACT;IACF;IAGA,IAAI,YAAY,OAAO,GACrB,6BAA6B;IAG/B,IAAI,iBAAiB,aAAa,YAChC,MAAM;KACJ,MAAM,UAAU;KAChB;KACA;KACA,WAAW,KAAK,IAAI;KACpB,SACE;KACF,MAAM;KACN,OAAO;MACL,SACE;MACF,MAAM;KACR;IACF;IAIF,IAAI,sBAAsB,CAAC,oBAAoB;KAC7C,qBAAqB;KACrB,MAAM;MACJ,MAAM,UAAU;MAChB,WAAW;MACX;MACA,WAAW,KAAK,IAAI;KACtB;KACA,MAAM;MACJ,MAAM,UAAU;MAChB,WAAW;MACX;MACA,WAAW,KAAK,IAAI;KACtB;IACF;IAGA,IAAI,4BACF,MAAM;KACJ,MAAM,UAAU;KAChB;KACA;KACA,WAAW,KAAK,IAAI;IACtB;IAGF,MAAM;KACJ,MAAM,UAAU;KAChB;KACA;KACA;KACA,WAAW,KAAK,IAAI;KACpB,cAAc,YAAY,OAAO,IAAI,eAAe;KAIpD,GAAI,MAAM,iBAAiB,EACzB,OAAO,iBAAiB,MAAM,aAAa,EAC7C;IACF;GACF;EACF;CACF;CAEA,2BAAmC,MAAyB;EAC1D,QAAQ,KAAK,MAAb;GACE,KAAK,QACH,OAAO,EAAE,MAAM,KAAK,QAAQ;GAC9B,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,YAAY;IACf,MAAM,aACJ,KAAK,OAAO,SAAS,SACjB,EACE,YAAY;KACV,MAAM,KAAK,OAAO;KAClB,UAAU,KAAK,OAAO;IACxB,EACF,IACA,EACE,UAAU;KACR,SAAS,KAAK,OAAO;KAGrB,UACE,KAAK,OAAO,YAAY,yBAAyB,KAAK;IAC1D,EACF;IAKN,IAAI,KAAK,SAAS,SAAS;KACzB,MAAM,OAAO,KAAK;KAClB,MAAM,gBAA+B;MACnC,GAAI,MAAM,QAAQ,KAAA,KAAa,EAAE,KAAK,KAAK,IAAI;MAC/C,GAAI,MAAM,gBAAgB,KAAA,KAAa,EACrC,aAAa,KAAK,YACpB;MACA,GAAI,MAAM,cAAc,KAAA,KAAa,EAAE,WAAW,KAAK,UAAU;KACnE;KACA,IAAI,OAAO,KAAK,aAAa,CAAC,CAAC,SAAS,GACtC,WAAW,gBAAgB;IAE/B;IAEA,OAAO;GACT;GACA,SAEE,MAAM,IAAI,MACR,kCAAmC,KAAiC,MACtE;EAEJ;CACF;CAEA,eACE,UACuC;EAGvC,MAAM,mCAAmB,IAAI,IAAoB;EACjD,KAAK,MAAM,OAAO,UAChB,IAAI,IAAI,SAAS,eAAe,IAAI,WAClC,KAAK,MAAM,MAAM,IAAI,WACnB,iBAAiB,IAAI,GAAG,IAAI,GAAG,SAAS,IAAI;EAKlD,MAAM,YAAY,SAAS,KAAK,QAAQ;GACtC,MAAM,OAAyB,IAAI,SAAS,cAAc,UAAU;GACpE,MAAM,QAAqB,CAAC;GAE5B,IAAI,MAAM,QAAQ,IAAI,OAAO,GAC3B,KAAK,MAAM,eAAe,IAAI,SAC5B,MAAM,KAAK,KAAK,2BAA2B,WAAW,CAAC;QAEpD,IAAI,IAAI,WAAW,IAAI,SAAS,QACrC,MAAM,KAAK,EAAE,MAAM,IAAI,QAAQ,CAAC;GAGlC,IAAI,IAAI,SAAS,eAAe,IAAI,WAAW,QAC7C,KAAK,MAAM,YAAY,IAAI,WAAW;IACpC,IAAI,aAAsC,CAAC;IAC3C,IAAI;KACF,aAAa,SAAS,SAAS,YAC1B,KAAK,MAAM,SAAS,SAAS,SAAS,IAIvC,CAAC;IACP,QAAQ;KACN,aAAa,CAAC;IAChB;IAEA,MAAM,mBACJ,SAAS,UACR;IAKH,MAAM,OAAa,EACjB,cAAc;KACZ,IAAI,SAAS;KACb,MAAM,SAAS,SAAS;KACxB,MAAM;IACR,EACF;IACA,IAAI,kBACF,KAAK,mBAAmB;IAE1B,MAAM,KAAK,IAAI;GACjB;GAGF,IAAI,IAAI,SAAS,UAAU,IAAI,YAAY;IACzC,MAAM,eACJ,iBAAiB,IAAI,IAAI,UAAU,KAAK,IAAI;IAC9C,MAAM,cAAc,IAAI;IACxB,IAAI,MAAM,QAAQ,WAAW,GAAG;KAC9B,MAAM,aAA4B,CAAC;KACnC,MAAM,aAA0B,CAAC;KACjC,KAAK,MAAM,QAAQ,aACjB,IAAI,KAAK,SAAS,QAChB,WAAW,KAAK,KAAK,OAAO;UACvB,IAAI,KAAK,OAAO,SAAS,QAC9B,WAAW,KAAK,EACd,YAAY;MACV,MAAM,KAAK,OAAO;MAClB,UAAU,KAAK,OAAO;KACxB,EACF,CAAC;UACI;MACL,MAAM,kBAAkB;OACtB,OAAO;OACP,OAAO;OACP,OAAO;OACP,UAAU;MACZ,EAAE,KAAK;MACP,WAAW,KAAK,EACd,UAAU;OACR,SAAS,KAAK,OAAO;OACrB,UAAU,KAAK,OAAO,YAAY;MACpC,EACF,CAAC;KACH;KAEF,MAAM,KAAK,EACT,kBAAkB;MAChB,IAAI,IAAI;MACR,MAAM;MACN,UAAU,EAAE,SAAS,WAAW,KAAK,IAAI,EAAE;MAC3C,GAAI,WAAW,SAAS,KAAK,EAAE,OAAO,WAAW;KACnD,EACF,CAAC;IACH,OACE,MAAM,KAAK,EACT,kBAAkB;KAChB,IAAI,IAAI;KACR,MAAM;KACN,UAAU,EAAE,SAAS,eAAe,GAAG;IACzC,EACF,CAAC;GAEL;GAEA,OAAO;IACL;IACA,OAAO,MAAM,SAAS,IAAI,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC;GACjD;EACF,CAAC;EAKD,OAAO,KAAK,iCAAiC,SAAS;CACxD;;;;;;;;;;CAWA,iCACE,UACgB;EAChB,MAAM,SAAyB,CAAC;EAEhC,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,QAAQ,IAAI,SAAS,CAAC;GAG5B,IAAI,IAAI,SAAS,SAMX;QAAA,EAJF,MAAM,SAAS,KACf,CAAC,MAAM,OACJ,MAAM,UAAU,KAAM,EAAuB,SAAS,EACzD,IAEA;GAAA;GAIJ,MAAM,OAAO,OAAO,OAAO,SAAS;GACpC,IAAI,QAAQ,KAAK,SAAS,IAAI,MAE5B,KAAK,QAAQ,CAAC,GAAI,KAAK,SAAS,CAAC,GAAI,GAAG,KAAK;QAE7C,OAAO,KAAK;IAAE,GAAG;IAAK,OAAO,CAAC,GAAG,KAAK;GAAE,CAAC;EAE7C;EAOA,KAAK,MAAM,OAAO,QAAQ;GACxB,IAAI,CAAC,IAAI,OAAO;GAChB,MAAM,0CAA0B,IAAI,IAAY;GAChD,IAAI,QAAQ,IAAI,MAAM,QAAQ,SAAS;IACrC,IAAI,sBAAsB,QAAQ,KAAK,kBAAkB,IAAI;KAC3D,IAAI,wBAAwB,IAAI,KAAK,iBAAiB,EAAE,GACtD,OAAO;KAET,wBAAwB,IAAI,KAAK,iBAAiB,EAAE;IACtD;IACA,OAAO;GACT,CAAC;EACH;EAEA,OAAO;CACT;CAEA,yBACE,SACA;EAKA,MAAM,EAAE,gBAAgB,GAAG,cAAc,QAAQ,gBAAgB,CAAC;EAMlE,MAAM,uBAAuB,iBACzB;GACE,GAAI,eAAe,oBAAoB,KAAA,KAAa,EAClD,iBAAiB,eAAe,gBAClC;GACA,GAAI,eAAe,mBAAmB,KAAA,KAAa,EACjD,gBAAgB,eAAe,eACjC;GACA,GAAI,eAAe,gBACf,EACE,eAAe,eAAe,cAChC,IACA,CAAC;EACP,IACA,KAAA;EAEJ,MAAM,oBAAoB,uBAAuB,QAAQ,aAAa;EACtE,MAAM,oBACJ,kBAAkB,SAAS,IACvB,kBAAkB,KAAK,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,IAAI,IACjD,KAAA;EAWN,MAAM,iBAAiB,QAAQ;EAG/B,MAAM,uBAAuB,iBACzB;GACE,kBAAkB;GAClB,gBAAgB;EAClB,IACA,KAAA;EAwBJ,OAAO;GAlBL,OAAO,QAAQ;GACf,UAAU,KAAK,eAAe,QAAQ,QAAQ;GAC9C,QAAQ;IACN,GAAG;IACH,GAAI,yBAAyB,KAAA,KAAa,EACxC,gBAAgB,qBAClB;IACA,GAAI,sBAAsB,KAAA,KAAa,EAAE,kBAAkB;IAC3D,OAAO,6BAA6B,QAAQ,KAAK;IACjD,GAAI,wBAAwB,CAAC;IAG7B,GAAI,QAAQ,SAAS,UAAU,QAAQ,EACrC,aAAa,QAAQ,QAAQ,OAC/B;GACF;EAGK;CACT;;;;;;;CAQA,iCAA0C;EACxC,OAAO,wCAAwC,IAAI,KAAK,KAAK;CAC/D;AACF;AAEA,SAAS,sBAAsB,SAAiB,OAAe;CAG7D,OAAO,mBAAmB,MAAM,qBAFhB,QAAQ,MAAM,GAAG,GAEoB,IADpC,QAAQ,SAAS,MAAM,QAAQ;AAElD;AAEA,SAAS,sBACP,SACA,SACA,MACkE;CAClE,OAAO;EACL,MAAM,UAAU;EAChB,OAAO,QAAQ;EACf,OAAO,QAAQ;EACf,WAAW,KAAK,IAAI;EACpB;EACA;EACA,OAAO;GAAE;GAAS;EAAK;CACzB;AACF;;;;;AAMA,SAAgB,iBACd,OACA,QACA,QAMA;CACA,OAAO,IAAI,kBAAkB;EAAE;EAAQ,GAAG;CAAO,GAAG,KAAK;AAC3D;;;;;AAMA,SAAgB,WACd,OACA,QAMA;CAEA,OAAO,iBAAiB,OADT,uBACgB,GAAQ,MAAM;AAC/C"}
|
|
1
|
+
{"version":3,"file":"text.js","names":[],"sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { FinishReason } from '@google/genai'\nimport {\n EventType,\n fileReferenceFor,\n isFileSource,\n normalizeSystemPrompts,\n} 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 GroundingMetadata,\n Part,\n ThinkingLevel,\n VideoMetadata,\n} from '@google/genai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n AdapterYieldChunk,\n ProviderExecutedToolSource,\n TextOptions,\n} from '@tanstack/ai'\nimport type { ExternalTextProviderOptions } from '../text/text-provider-options'\nimport type {\n GeminiMessageMetadataByModality,\n GeminiToolCallMetadata,\n GeminiVideoMetadata,\n GeminiVideoProcessing,\n} from '../message-types'\nimport type { GeminiClientConfig } from '../utils/client'\n\n/**\n * Fallback MIME types for URL-sourced media parts that don't specify one.\n */\nconst DEFAULT_MEDIA_MIME_TYPES = {\n image: 'image/jpeg',\n audio: 'audio/mp3',\n video: 'video/mp4',\n document: 'application/pdf',\n} as const\n\nfunction getGroundingSources(\n metadata: GroundingMetadata,\n): Array<ProviderExecutedToolSource> {\n const sources = new Map<string, ProviderExecutedToolSource>()\n for (const chunk of metadata.groundingChunks ?? []) {\n const url = chunk.web?.uri\n if (!url) continue\n const existing = sources.get(url)\n if (existing) {\n if (!existing.title && chunk.web?.title) existing.title = chunk.web.title\n continue\n }\n sources.set(url, {\n url,\n ...(chunk.web?.title ? { title: chunk.web.title } : {}),\n })\n }\n return [...sources.values()]\n}\n\n/**\n * Content block shape for an Interactions API `input` step. The installed\n * @google/genai types predate the video `processing` field, so we model the\n * subset we emit and cast at the call site.\n */\ntype InteractionContent =\n | { type: 'text'; text: string }\n | {\n type: 'video'\n uri?: string\n data?: string\n mime_type?: string\n processing?: GeminiVideoProcessing\n }\n | {\n type: 'image' | 'audio' | 'document'\n uri?: string\n data?: string\n mime_type?: string\n }\n\ninterface InteractionStep {\n type: 'user_input' | 'model_output'\n content: Array<InteractionContent>\n}\n\n/** True when any message carries a video part requesting agentic processing. */\nfunction hasAgenticVideo(messages: Array<ModelMessage>): boolean {\n return messages.some(\n (msg) =>\n Array.isArray(msg.content) &&\n msg.content.some(\n (part) =>\n part.type === 'video' &&\n (part.metadata as GeminiVideoMetadata | undefined)?.processing ===\n 'agentic',\n ),\n )\n}\n\n/** Convert a single content part to an Interactions API content block. */\nfunction contentPartToInteraction(part: ContentPart): InteractionContent {\n if (part.type === 'text') {\n return { type: 'text', text: part.content }\n }\n\n const source = part.source\n const mimeType =\n source.type === 'data'\n ? source.mimeType\n : (source.mimeType ?? DEFAULT_MEDIA_MIME_TYPES[part.type])\n // A Gemini Files API handle maps to the `uri` field, same as a public URL;\n // `fileReferenceFor` throws when another provider issued it.\n const base = isFileSource(source)\n ? { uri: fileReferenceFor(source, 'gemini'), mime_type: mimeType }\n : source.type === 'data'\n ? { data: source.value, mime_type: mimeType }\n : { uri: source.value, mime_type: mimeType }\n\n if (part.type === 'video') {\n const processing = (part.metadata as GeminiVideoMetadata | undefined)\n ?.processing\n return { type: 'video', ...base, ...(processing && { processing }) }\n }\n return { type: part.type, ...base }\n}\n\n/**\n * Build the Interactions API `input` from chat messages. Each user/assistant\n * message becomes a `user_input` / `model_output` step wrapping its content\n * blocks — the wrapping the Python SDK performs implicitly but the JS SDK\n * does not. Tool messages are skipped (unsupported on this path).\n */\nfunction buildInteractionsInput(\n messages: Array<ModelMessage>,\n): Array<InteractionStep> {\n const steps: Array<InteractionStep> = []\n for (const msg of messages) {\n if (msg.role === 'tool') continue\n const stepType = msg.role === 'assistant' ? 'model_output' : 'user_input'\n const content: Array<InteractionContent> = []\n if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n content.push(contentPartToInteraction(part))\n }\n } else if (msg.content) {\n content.push({ type: 'text', text: msg.content })\n }\n if (content.length > 0) {\n steps.push({ type: stepType, content })\n }\n }\n return steps\n}\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 // Consumes Gemini Files API references (geminiFiles()) as fileData.fileUri.\n override readonly supportsFileSources = true\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<AdapterYieldChunk> {\n // Agentic video understanding is only exposed through the Interactions API,\n // not generateContent. Detect it and take that path instead.\n if (hasAgenticVideo(options.messages)) {\n yield* this.interactionsStream(options)\n return\n }\n\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 * Agentic video-understanding path via the Interactions API.\n *\n * The Interactions API (unlike `generateContent`) requires message parts to\n * be wrapped in `user_input` / `model_output` steps, and it accepts the\n * `processing: 'agentic'` video flag. This is a non-streaming call whose\n * single text result is re-emitted as AG-UI stream chunks.\n */\n private async *interactionsStream(\n options: TextOptions<GeminiTextProviderOptions>,\n ): AsyncIterable<AdapterYieldChunk> {\n const model = options.model\n const { logger } = options\n const runId = options.runId ?? generateId(this.name)\n const threadId = options.threadId ?? generateId(this.name)\n const messageId = generateId(this.name)\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${model} messages=${options.messages.length} mode=interactions-agentic-video`,\n { provider: 'gemini', model },\n )\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 const input = buildInteractionsInput(options.messages)\n\n // The installed @google/genai (2.10.0) Interactions `VideoContent` type\n // predates the `processing` field, so the structurally-built input is\n // cast at the call boundary. The SDK forwards it to the wire unchanged.\n const interaction = await this.client.interactions.create({\n model,\n ...(systemInstruction !== undefined && {\n system_instruction: systemInstruction,\n }),\n input: input as never,\n })\n\n const text = interaction.output_text ?? ''\n\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n if (text) {\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: text,\n content: text,\n }\n }\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n finishReason: 'stop',\n }\n } catch (error) {\n const rawEvent = toRunErrorRawEvent(error)\n logger.errors('gemini.interactionsStream fatal', {\n error,\n source: 'gemini.interactionsStream',\n })\n yield {\n type: EventType.RUN_ERROR,\n model,\n timestamp: Date.now(),\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\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(jsonContentParseError(rawText, 'structured output'))\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 * Stream schema-constrained JSON from Gemini natively.\n *\n * `chat({ outputSchema, stream: true })` calls this when the adapter\n * implements it. Without it, the engine buffers `structuredOutput()` and\n * emits one synthetic delta.\n */\n async *structuredOutputStream(\n options: StructuredOutputOptions<GeminiTextProviderOptions>,\n ): AsyncIterable<AdapterYieldChunk> {\n const { chatOptions: requestedChatOptions, outputSchema } = options\n const chatOptions = {\n ...requestedChatOptions,\n runId: requestedChatOptions.runId ?? generateId(this.name),\n }\n const mappedOptions = this.mapCommonOptionsToGemini(chatOptions)\n\n try {\n chatOptions.logger.request(\n `activity=structuredOutputStream provider=gemini model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: 'gemini', model: this.model },\n )\n const result = await this.client.models.generateContentStream({\n ...mappedOptions,\n config: {\n ...mappedOptions.config,\n responseMimeType: 'application/json',\n responseSchema: outputSchema,\n },\n })\n\n let rawText = ''\n let finished:\n | Extract<AdapterYieldChunk, { type: typeof EventType.RUN_FINISHED }>\n | undefined\n let failed = false\n for await (const chunk of this.processStreamChunks(\n result,\n chatOptions,\n chatOptions.logger,\n )) {\n if (chunk.type === EventType.TEXT_MESSAGE_CONTENT) {\n rawText += chunk.delta\n }\n if (chunk.type === EventType.RUN_ERROR) failed = true\n if (chunk.type === EventType.RUN_FINISHED) {\n finished = chunk\n } else {\n yield chunk\n }\n }\n\n if (failed) return\n if (!finished) {\n yield structuredStreamError(\n chatOptions,\n 'Gemini structured-output stream ended without a terminal event',\n 'truncated-stream',\n )\n return\n }\n if (!rawText) {\n yield structuredStreamError(\n chatOptions,\n 'Gemini structured-output stream contained no content',\n 'empty-response',\n )\n return\n }\n\n let object: unknown\n try {\n object = JSON.parse(rawText)\n } catch {\n yield structuredStreamError(\n chatOptions,\n jsonContentParseError(rawText, 'Gemini structured-output stream'),\n 'parse-error',\n )\n return\n }\n\n yield {\n type: EventType.CUSTOM,\n name: 'structured-output.complete',\n value: { object, raw: rawText },\n model: chatOptions.model,\n timestamp: Date.now(),\n }\n yield { ...finished, timestamp: Date.now() }\n } catch (error) {\n const rawEvent = toRunErrorRawEvent(error)\n const message =\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during structured output streaming.'\n chatOptions.logger.errors('gemini.structuredOutputStream fatal', {\n error,\n source: 'gemini.structuredOutputStream',\n })\n yield {\n ...structuredStreamError(chatOptions, message, 'provider-error'),\n ...(rawEvent !== undefined && { rawEvent }),\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<AdapterYieldChunk> {\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 let groundingMetadata: GroundingMetadata | undefined\n let groundingCallEmitted = false\n const adapterName = this.name\n\n const emitGroundingToolCall = function* (): Generator<AdapterYieldChunk> {\n if (!groundingMetadata || groundingCallEmitted) return\n const sources = getGroundingSources(groundingMetadata)\n const hasGoogleSearchEvidence =\n sources.length > 0 ||\n (groundingMetadata.webSearchQueries?.length ?? 0) > 0 ||\n groundingMetadata.searchEntryPoint !== undefined\n if (!hasGoogleSearchEvidence) return\n\n groundingCallEmitted = true\n const toolCallId = generateId(adapterName)\n const metadata: GeminiToolCallMetadata = {\n providerExecuted: true,\n sources,\n gemini: { groundingMetadata },\n }\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: 'google_search',\n toolName: 'google_search',\n parentMessageId: messageId,\n model,\n timestamp: Date.now(),\n index: toolCallMap.size,\n metadata,\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: 'google_search',\n toolName: 'google_search',\n model,\n timestamp: Date.now(),\n input: {\n ...(groundingMetadata.webSearchQueries && {\n queries: groundingMetadata.webSearchQueries,\n }),\n },\n }\n }\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]?.groundingMetadata) {\n groundingMetadata = chunk.candidates[0].groundingMetadata\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 parentMessageId: messageId,\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 yield* emitGroundingToolCall()\n const finishReason = chunk.candidates[0].finishReason\n\n // Emit TOOL_CALL_END for all tracked tool calls. functionCall parts on\n // this chunk (including UNEXPECTED_TOOL_CALL finishes) were already\n // registered and started by the per-part loop above.\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 yield* emitGroundingToolCall()\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 // File references (Gemini Files API) and public URLs both pass\n // through as `fileData`; Gemini fetches the URI server-side. A\n // file source's handle is the file URI (throws when another provider\n // issued it).\n const fileUri = isFileSource(part.source)\n ? fileReferenceFor(part.source, this.name)\n : part.source.value\n const geminiPart: Part =\n part.source.type === 'data'\n ? {\n inlineData: {\n data: part.source.value,\n mimeType: part.source.mimeType,\n },\n }\n : {\n fileData: {\n fileUri,\n // For URL sources, use provided mimeType or fall back to\n // reasonable defaults.\n mimeType:\n part.source.mimeType ?? DEFAULT_MEDIA_MIME_TYPES[part.type],\n },\n }\n\n // Apply single-pass video sampling controls (fps / clip offsets) from\n // the part metadata. `processing: 'agentic'` is handled separately via\n // the Interactions API and never reaches this generateContent path.\n if (part.type === 'video') {\n const meta = part.metadata as GeminiVideoMetadata | undefined\n const videoMetadata: VideoMetadata = {\n ...(meta?.fps !== undefined && { fps: meta.fps }),\n ...(meta?.startOffset !== undefined && {\n startOffset: meta.startOffset,\n }),\n ...(meta?.endOffset !== undefined && { endOffset: meta.endOffset }),\n }\n if (Object.keys(videoMetadata).length > 0) {\n geminiPart.videoMetadata = videoMetadata\n }\n }\n\n return geminiPart\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 const metadata = toolCall.metadata as\n | GeminiToolCallMetadata\n | undefined\n if (metadata?.providerExecuted) continue\n\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 = metadata?.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 fileUri = isFileSource(part.source)\n ? fileReferenceFor(part.source, this.name)\n : part.source.value\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,\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 id (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 id (tool call ID).\n // Two parallel calls to the *same* tool share a `name` but have distinct\n // `id`s — keying on `name` dropped every response but the first for\n // same-tool parallel calls, leaving Gemini with fewer response parts\n // than call parts and a 400 on the next request.\n for (const msg of merged) {\n if (!msg.parts) continue\n const seenFunctionResponseIds = new Set<string>()\n msg.parts = msg.parts.filter((part) => {\n if ('functionResponse' in part && part.functionResponse?.id) {\n if (seenFunctionResponseIds.has(part.functionResponse.id)) {\n return false\n }\n seenFunctionResponseIds.add(part.functionResponse.id)\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 // Forward the caller's abort signal so cancellation reaches the SDK\n // request, matching the OpenAI-compatible adapters (issue #1374).\n ...(options.request?.signal != null && {\n abortSignal: options.request.signal,\n }),\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\nfunction jsonContentParseError(rawText: string, label: string) {\n const snippet = rawText.slice(0, 200)\n const ellipsis = rawText.length > 200 ? '...' : ''\n return `Failed to parse ${label} as JSON. Content: ${snippet}${ellipsis}`\n}\n\nfunction structuredStreamError(\n options: TextOptions<GeminiTextProviderOptions>,\n message: string,\n code: string,\n): Extract<AdapterYieldChunk, { type: typeof EventType.RUN_ERROR }> {\n return {\n type: EventType.RUN_ERROR,\n runId: options.runId,\n model: options.model,\n timestamp: Date.now(),\n message,\n code,\n error: { message, code },\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"],"mappings":"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"}
|
|
@@ -56,6 +56,7 @@ export interface GeminiVideoConfig extends GeminiClientConfig {
|
|
|
56
56
|
*/
|
|
57
57
|
export declare class GeminiVideoAdapter<TModel extends GeminiVideoModel> extends BaseVideoAdapter<TModel, GeminiVideoModelProviderOptionsByName[TModel], GeminiVideoModelProviderOptionsByName, GeminiVideoModelSizeByName, GeminiVideoModelInputModalitiesByName, GeminiVideoModelDurationByName> {
|
|
58
58
|
readonly name: "gemini";
|
|
59
|
+
readonly supportsFileSources = true;
|
|
59
60
|
protected client: GoogleGenAI;
|
|
60
61
|
private readonly allowUrlFetch;
|
|
61
62
|
constructor(config: GeminiVideoConfig, model: TModel);
|
|
@@ -2,7 +2,7 @@ import { createGeminiClient, getGeminiApiKeyFromEnv } from "../utils/client.js";
|
|
|
2
2
|
import "../utils/index.js";
|
|
3
3
|
import { getGeminiVideoDurationOptions, isInteractionsVideoModel, parseGeminiOmniVideoSize } from "../video/video-provider-options.js";
|
|
4
4
|
import { GenerateVideosOperation, VideoGenerationReferenceType } from "@google/genai";
|
|
5
|
-
import { resolveMediaPrompt } from "@tanstack/ai";
|
|
5
|
+
import { fileReferenceFor, isFileSource, resolveMediaPrompt, unsupportedFileSourceError } from "@tanstack/ai";
|
|
6
6
|
import { BaseVideoAdapter, snapToDurationOption } from "@tanstack/ai/adapters";
|
|
7
7
|
import { arrayBufferToBase64 } from "@tanstack/ai-utils";
|
|
8
8
|
//#region src/adapters/video.ts
|
|
@@ -31,6 +31,7 @@ async function imagePartToVeoImage(part, allowUrlFetch) {
|
|
|
31
31
|
imageBytes: part.source.value,
|
|
32
32
|
mimeType: part.source.mimeType || "image/png"
|
|
33
33
|
};
|
|
34
|
+
if (isFileSource(part.source)) throw unsupportedFileSourceError("gemini", "for Veo video generation, which needs inline image bytes or a gs:// reference — pass a data: URI or gs:// URL");
|
|
34
35
|
const url = part.source.value;
|
|
35
36
|
if (url.startsWith("gs://")) return {
|
|
36
37
|
gcsUri: url,
|
|
@@ -60,23 +61,24 @@ async function imagePartToVeoImage(part, allowUrlFetch) {
|
|
|
60
61
|
* as `uri` (Files API URIs — mirrors the Interactions text adapter).
|
|
61
62
|
*/
|
|
62
63
|
function mediaPartToInteractionsContent(part) {
|
|
64
|
+
const sourceValue = isFileSource(part.source) ? fileReferenceFor(part.source, "gemini") : part.source.value;
|
|
63
65
|
const mimeType = part.source.mimeType;
|
|
64
66
|
if (part.type === "image") return part.source.type === "data" ? {
|
|
65
67
|
type: "image",
|
|
66
|
-
data:
|
|
68
|
+
data: sourceValue,
|
|
67
69
|
mime_type: mimeType
|
|
68
70
|
} : {
|
|
69
71
|
type: "image",
|
|
70
|
-
uri:
|
|
72
|
+
uri: sourceValue,
|
|
71
73
|
mime_type: mimeType
|
|
72
74
|
};
|
|
73
75
|
return part.source.type === "data" ? {
|
|
74
76
|
type: "video",
|
|
75
|
-
data:
|
|
77
|
+
data: sourceValue,
|
|
76
78
|
mime_type: mimeType
|
|
77
79
|
} : {
|
|
78
80
|
type: "video",
|
|
79
|
-
uri:
|
|
81
|
+
uri: sourceValue,
|
|
80
82
|
mime_type: mimeType
|
|
81
83
|
};
|
|
82
84
|
}
|
|
@@ -157,6 +159,7 @@ function interactionUsageToTokenUsage(usage) {
|
|
|
157
159
|
*/
|
|
158
160
|
var GeminiVideoAdapter = class extends BaseVideoAdapter {
|
|
159
161
|
name = "gemini";
|
|
162
|
+
supportsFileSources = true;
|
|
160
163
|
client;
|
|
161
164
|
allowUrlFetch;
|
|
162
165
|
constructor(config, model) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video.js","names":[],"sources":["../../../src/adapters/video.ts"],"sourcesContent":["import {\n GenerateVideosOperation,\n VideoGenerationReferenceType,\n} from '@google/genai'\nimport { resolveMediaPrompt } from '@tanstack/ai'\nimport { BaseVideoAdapter, snapToDurationOption } from '@tanstack/ai/adapters'\nimport { arrayBufferToBase64 } from '@tanstack/ai-utils'\nimport { createGeminiClient, getGeminiApiKeyFromEnv } from '../utils'\nimport {\n getGeminiVideoDurationOptions,\n isInteractionsVideoModel,\n parseGeminiOmniVideoSize,\n} from '../video/video-provider-options'\nimport type { DurationOptions } from '@tanstack/ai/adapters'\nimport type {\n ImagePart,\n MediaInputMetadata,\n TokenUsage,\n VideoGenerationOptions,\n VideoJobResult,\n VideoPart,\n VideoStatusResult,\n VideoUrlResult,\n} from '@tanstack/ai'\nimport type {\n GenerateVideosConfig,\n GoogleGenAI,\n Image,\n Interactions,\n VideoGenerationReferenceImage,\n} from '@google/genai'\nimport type {\n GeminiOmniVideoProviderOptions,\n GeminiVideoModel,\n GeminiVideoModelDurationByName,\n GeminiVideoModelInputModalitiesByName,\n GeminiVideoModelProviderOptionsByName,\n GeminiVideoModelSizeByName,\n GeminiVideoProviderOptions,\n} from '../video/video-provider-options'\nimport type { GeminiClientConfig } from '../utils/client'\n\ntype Interaction = Interactions.Interaction\ntype InteractionContent = Interactions.Content\n\n/**\n * Configuration for Gemini video adapter.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport interface GeminiVideoConfig extends GeminiClientConfig {\n /**\n * Opt into fetching HTTP(S) image URL inputs. Veo's predict API accepts\n * only inline `imageBytes` or a `gcsUri`, so an HTTP(S) URL has to be\n * downloaded and base64-encoded locally — which buffers the whole image in\n * memory and can OOM constrained runtimes (e.g. Cloudflare Workers). When\n * `false` (the default), HTTP(S) URL image inputs throw; pass a `data:` URI\n * or a `gs://` reference, or set this to `true` to opt into buffering.\n */\n allowUrlFetch?: boolean\n}\n\n/**\n * Extract a human-readable message from a long-running operation's error,\n * which the SDK types as `Record<string, unknown>` (a google.rpc.Status).\n */\nfunction operationErrorMessage(error: Record<string, unknown>): string {\n if (typeof error.message === 'string' && error.message.length > 0) {\n return error.message\n }\n return JSON.stringify(error)\n}\n\n/**\n * Convert a TanStack image prompt part into the genai `Image` shape Veo\n * accepts: base64 `imageBytes` (data sources, data: URIs, fetched HTTP\n * URLs) or a `gcsUri` passthrough for Cloud Storage references.\n *\n * Unlike `generateContent` (chat / native image generation), Veo's predict\n * API has no `fileData.fileUri` equivalent — `Image` only accepts\n * `imageBytes` or `gcsUri`. An HTTP(S) URL therefore has to be fetched and\n * inlined locally, which buffers the whole image in memory; that only happens\n * when the caller opts in via `allowUrlFetch`, otherwise it throws. Prefer a\n * `gs://` reference on memory-constrained runtimes.\n */\nasync function imagePartToVeoImage(\n part: ImagePart<MediaInputMetadata>,\n allowUrlFetch: boolean,\n): Promise<Image> {\n if (part.source.type === 'data') {\n return {\n imageBytes: part.source.value,\n mimeType: part.source.mimeType || 'image/png',\n }\n }\n const url = part.source.value\n if (url.startsWith('gs://')) {\n return {\n gcsUri: url,\n ...(part.source.mimeType && { mimeType: part.source.mimeType }),\n }\n }\n if (url.startsWith('data:')) {\n const match = url.match(/^data:([^;,]+)?(;base64)?,(.*)$/)\n if (!match || !match[2]) {\n throw new Error(\n 'gemini: only base64 data: URIs are supported for video image inputs.',\n )\n }\n return {\n imageBytes: match[3] ?? '',\n mimeType: match[1] || part.source.mimeType || 'image/png',\n }\n }\n if (!allowUrlFetch) {\n throw new Error(\n `gemini Veo: HTTP(S) URL image inputs are not fetched by default because ` +\n `Veo accepts only inline bytes, so the image would be downloaded and ` +\n `buffered in memory (risking OOM on constrained runtimes). Pass a ` +\n `data: URI or a gs:// reference, or set \\`allowUrlFetch: true\\` on the ` +\n `adapter config to opt into fetching. URL: ${url}`,\n )\n }\n const response = await fetch(url)\n if (!response.ok) {\n throw new Error(\n `Failed to fetch image input (${response.status} ${response.statusText}): ${url}`,\n )\n }\n const blob = await response.blob()\n const buffer = await blob.arrayBuffer()\n return {\n imageBytes: arrayBufferToBase64(buffer),\n mimeType: part.source.mimeType || blob.type || 'image/png',\n }\n}\n\n/**\n * Convert an image or video prompt part into an Interactions API content\n * block. Data sources become inline base64 `data`; URL sources pass through\n * as `uri` (Files API URIs — mirrors the Interactions text adapter).\n */\nfunction mediaPartToInteractionsContent(\n part: ImagePart<MediaInputMetadata> | VideoPart<MediaInputMetadata>,\n): InteractionContent {\n const mimeType = part.source.mimeType\n if (part.type === 'image') {\n return part.source.type === 'data'\n ? { type: 'image', data: part.source.value, mime_type: mimeType }\n : { type: 'image', uri: part.source.value, mime_type: mimeType }\n }\n return part.source.type === 'data'\n ? { type: 'video', data: part.source.value, mime_type: mimeType }\n : { type: 'video', uri: part.source.value, mime_type: mimeType }\n}\n\n/**\n * Pull the generated video out of a completed interaction. Prefers the\n * SDK's `output_video` sugar, then walks `steps` back-to-front for the last\n * `model_output` step carrying a video content block (the wire shape the\n * raw REST response uses).\n */\nfunction extractInteractionVideo(\n interaction: Interaction,\n): { data?: string; uri?: string; mimeType: string } | undefined {\n const direct = interaction.output_video\n if (direct && (direct.data || direct.uri)) {\n return {\n data: direct.data,\n uri: direct.uri,\n mimeType: direct.mime_type || 'video/mp4',\n }\n }\n const steps = interaction.steps ?? []\n for (let i = steps.length - 1; i >= 0; i--) {\n const step = steps[i]\n if (step?.type !== 'model_output') continue\n for (const block of step.content ?? []) {\n if (block.type === 'video' && (block.data || block.uri)) {\n return {\n data: block.data,\n uri: block.uri,\n mimeType: block.mime_type || 'video/mp4',\n }\n }\n }\n }\n return undefined\n}\n\n/**\n * Map Interactions usage onto the canonical TokenUsage shape. Omni reports\n * video output via `output_tokens_by_modality`; fall back to the video\n * modality entry when the total is absent.\n */\nfunction interactionUsageToTokenUsage(\n usage: Interaction['usage'],\n): TokenUsage | undefined {\n if (!usage) return undefined\n const videoTokens = usage.output_tokens_by_modality?.find(\n (entry) => entry.modality === 'video',\n )?.tokens\n const promptTokens = usage.total_input_tokens ?? 0\n const completionTokens = usage.total_output_tokens ?? videoTokens ?? 0\n return {\n promptTokens,\n completionTokens,\n totalTokens: usage.total_tokens ?? promptTokens + completionTokens,\n }\n}\n\n/**\n * Gemini Video Generation Adapter (Veo + Gemini Omni Flash)\n *\n * Tree-shakeable adapter for Google video generation, routing by model:\n *\n * **Veo models** run as a long-running operation: `createVideoJob` starts\n * the operation via the `:predictLongRunning` endpoint, `getVideoStatus`\n * polls it, and `getVideoUrl` extracts the generated video's URI once it\n * completes. Image prompt parts are routed by `metadata.role`:\n * - `'start_frame'` (or the first un-roled image) → the input image the\n * video starts from\n * - `'end_frame'` → `lastFrame` (the frame the video ends on)\n * - `'reference'` / `'character'` → `referenceImages` (asset references,\n * Veo 3.1)\n *\n * Note: the returned Veo video URI is served by the Gemini Files API and\n * requires the API key (`x-goog-api-key` header or `?key=` query\n * parameter) to download.\n *\n * **Gemini Omni Flash** (`gemini-omni-1.1-flash`, plus the deprecated\n * `gemini-omni-flash-preview` alias) only serves the Interactions API:\n * `createVideoJob` creates a background interaction with\n * `response_modalities: ['video']`, `getVideoStatus` polls it by id, and\n * `getVideoUrl` returns the inline base64 MP4 as a `data:` URL (or the\n * Files API URI when the server delivers by reference). Image and video\n * prompt parts are sent as interaction content blocks, grouped as images,\n * then videos, then the text prompt (interleaving is not preserved); pass\n * `modelOptions.previous_interaction_id` to conversationally edit a prior\n * Omni generation. `size` is an `aspectRatio_resolution` template\n * (`'16:9'` or `'16:9_1080p'`); the optional suffix maps onto\n * `response_format.resolution` (`'360p' | '720p' | '1080p' | '4k'`,\n * default 720p).\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport class GeminiVideoAdapter<\n TModel extends GeminiVideoModel,\n> extends BaseVideoAdapter<\n TModel,\n GeminiVideoModelProviderOptionsByName[TModel],\n GeminiVideoModelProviderOptionsByName,\n GeminiVideoModelSizeByName,\n GeminiVideoModelInputModalitiesByName,\n GeminiVideoModelDurationByName\n> {\n readonly name = 'gemini' as const\n\n protected client: GoogleGenAI\n private readonly allowUrlFetch: boolean\n\n constructor(config: GeminiVideoConfig, model: TModel) {\n super({}, model)\n this.client = createGeminiClient(config)\n this.allowUrlFetch = config.allowUrlFetch ?? false\n }\n\n async createVideoJob(\n options: VideoGenerationOptions<\n GeminiVideoModelProviderOptionsByName[TModel],\n GeminiVideoModelSizeByName[TModel],\n GeminiVideoModelDurationByName[TModel]\n >,\n ): Promise<VideoJobResult> {\n const { prompt, size, duration, logger } = options\n\n logger.request(\n `activity=video.create provider=${this.name} model=${this.model} size=${size ?? 'default'} duration=${duration ?? 'default'}`,\n { provider: this.name, model: this.model },\n )\n\n if (isInteractionsVideoModel(this.model)) {\n return await this.createInteractionsVideoJob(options)\n }\n const modelOptions = options.modelOptions as\n | GeminiVideoProviderOptions\n | undefined\n\n try {\n const resolved = resolveMediaPrompt(prompt)\n\n if (resolved.videos.length > 0) {\n throw new Error(\n `${this.name}.createVideoJob does not support video prompt parts (model: ${this.model}).`,\n )\n }\n if (resolved.audios.length > 0) {\n throw new Error(\n `${this.name}.createVideoJob does not support audio prompt parts (model: ${this.model}).`,\n )\n }\n\n const { image, lastFrame, referenceImages } = await this.routeImageParts(\n resolved.images,\n )\n\n const config: GenerateVideosConfig = {\n ...modelOptions,\n ...(size !== undefined && { aspectRatio: size }),\n ...(duration !== undefined && { durationSeconds: duration }),\n ...(lastFrame && { lastFrame }),\n ...(referenceImages.length > 0 && { referenceImages }),\n }\n\n const operation = await this.client.models.generateVideos({\n model: this.model,\n prompt: resolved.text,\n ...(image && { image }),\n config,\n })\n\n if (!operation.name) {\n throw new Error(\n 'Veo did not return an operation name for the video generation job.',\n )\n }\n\n return { jobId: operation.name, model: this.model }\n } catch (error) {\n logger.errors(`${this.name}.createVideoJob fatal`, {\n error,\n source: `${this.name}.createVideoJob`,\n })\n throw error\n }\n }\n\n /**\n * Gemini Omni Flash job creation via the Interactions API. Creates a\n * background interaction requesting video output; the interaction id is\n * the job id polled by `getVideoStatus` / `getVideoUrl`.\n */\n private async createInteractionsVideoJob(\n options: VideoGenerationOptions<\n GeminiVideoModelProviderOptionsByName[TModel],\n GeminiVideoModelSizeByName[TModel],\n GeminiVideoModelDurationByName[TModel]\n >,\n ): Promise<VideoJobResult> {\n const { prompt, size, duration, logger } = options\n const modelOptions = options.modelOptions as\n | GeminiOmniVideoProviderOptions\n | undefined\n\n try {\n const resolved = resolveMediaPrompt(prompt)\n\n if (resolved.audios.length > 0) {\n throw new Error(\n `${this.name}.createVideoJob does not support audio prompt parts (model: ${this.model}).`,\n )\n }\n\n const content: Array<InteractionContent> = [\n ...resolved.images.map(mediaPartToInteractionsContent),\n ...resolved.videos.map(mediaPartToInteractionsContent),\n ]\n if (resolved.text) {\n content.push({ type: 'text', text: resolved.text })\n }\n if (content.length === 0) {\n throw new Error(\n `${this.name}.createVideoJob: the prompt produced no content to send (model: ${this.model}).`,\n )\n }\n\n // Reject out-of-range durations locally rather than snapping (which\n // would silently change the clip length the caller asked for) or\n // letting the live API reject them after the round trip.\n const durations = this.availableDurations()\n if (\n duration !== undefined &&\n durations.kind === 'range' &&\n (duration < durations.min || duration > durations.max)\n ) {\n throw new Error(\n `${this.name}.createVideoJob: duration ${duration}s is outside the ${durations.min}–${durations.max}s range supported by ${this.model}. Use snapDuration() to snap arbitrary values into range.`,\n )\n }\n\n // Aspect ratio, clip length, and resolution ride on `response_format`.\n // Duration is a `\"<seconds>s\"` string. Resolution comes from the\n // optional `size` suffix (`'16:9_1080p'`), defaulting to 720p when\n // omitted — https://ai.google.dev/gemini-api/docs/omni\n const parsedSize =\n size !== undefined ? parseGeminiOmniVideoSize(size) : undefined\n const responseFormat =\n parsedSize !== undefined || duration !== undefined\n ? {\n response_format: {\n type: 'video' as const,\n ...(parsedSize !== undefined && {\n aspect_ratio: parsedSize.aspectRatio,\n ...(parsedSize.resolution !== undefined && {\n resolution: parsedSize.resolution,\n }),\n }),\n ...(duration !== undefined && { duration: `${duration}s` }),\n },\n }\n : {}\n\n const interaction = await this.client.interactions.create({\n ...modelOptions,\n model: this.model,\n input: [{ type: 'user_input', content }],\n response_modalities: ['video'],\n background: true,\n ...responseFormat,\n })\n\n if (!interaction.id) {\n throw new Error(\n 'Gemini Omni did not return an interaction id for the video generation job.',\n )\n }\n\n return { jobId: interaction.id, model: this.model }\n } catch (error) {\n logger.errors(`${this.name}.createVideoJob fatal`, {\n error,\n source: `${this.name}.createVideoJob`,\n })\n throw error\n }\n }\n\n /**\n * Route image prompt parts onto Veo's request fields by `metadata.role`.\n */\n private async routeImageParts(\n parts: Array<ImagePart<MediaInputMetadata>>,\n ): Promise<{\n image: Image | undefined\n lastFrame: Image | undefined\n referenceImages: Array<VideoGenerationReferenceImage>\n }> {\n let image: Image | undefined\n let lastFrame: Image | undefined\n const referenceImages: Array<VideoGenerationReferenceImage> = []\n\n for (const part of parts) {\n const role = part.metadata?.role\n switch (role) {\n case 'end_frame': {\n if (lastFrame) {\n throw new Error(\n `${this.name}: Veo accepts at most one 'end_frame' image.`,\n )\n }\n lastFrame = await imagePartToVeoImage(part, this.allowUrlFetch)\n break\n }\n case 'reference':\n case 'character': {\n referenceImages.push({\n image: await imagePartToVeoImage(part, this.allowUrlFetch),\n referenceType: VideoGenerationReferenceType.ASSET,\n })\n break\n }\n case 'start_frame':\n case undefined: {\n if (image) {\n throw new Error(\n `${this.name}: Veo accepts at most one starting image; received multiple 'start_frame'/un-roled images. Use metadata.role ('end_frame', 'reference') to disambiguate the others.`,\n )\n }\n image = await imagePartToVeoImage(part, this.allowUrlFetch)\n break\n }\n case 'mask':\n case 'control':\n throw new Error(\n `${this.name}: unsupported image role \"${role}\" for Veo video generation.`,\n )\n }\n }\n\n return { image, lastFrame, referenceImages }\n }\n\n async getVideoStatus(jobId: string): Promise<VideoStatusResult> {\n if (isInteractionsVideoModel(this.model)) {\n return await this.getInteractionsVideoStatus(jobId)\n }\n const operation = await this.getOperation(jobId)\n\n if (!operation.done) {\n return { jobId, status: 'processing' }\n }\n\n if (operation.error) {\n return {\n jobId,\n status: 'failed',\n error: operationErrorMessage(operation.error),\n }\n }\n\n // The operation can finish \"successfully\" with every sample dropped by\n // Responsible-AI filters — surface that as a failure instead of letting\n // getVideoUrl() throw on an empty response.\n const videos = operation.response?.generatedVideos ?? []\n if (videos.length === 0) {\n const reasons = operation.response?.raiMediaFilteredReasons\n return {\n jobId,\n status: 'failed',\n error: reasons?.length\n ? `Video was filtered by Responsible-AI: ${reasons.join('; ')}`\n : 'Veo returned no generated videos.',\n }\n }\n\n return { jobId, status: 'completed' }\n }\n\n /**\n * Poll an Omni background interaction. `in_progress` maps to\n * 'processing'; a `completed` interaction with no video content (e.g.\n * filtered output) is surfaced as a failure so `getVideoUrl` doesn't\n * throw on an empty response. `requires_action` also fails: the adapter\n * never sends tools, so it can only arise via\n * `previous_interaction_id` chaining onto a tool-bearing interaction —\n * and such an interaction never progresses without a client response,\n * so polling it would spin until timeout.\n */\n private async getInteractionsVideoStatus(\n jobId: string,\n ): Promise<VideoStatusResult> {\n const interaction = await this.getInteraction(jobId)\n const status = interaction.status\n\n if (status === 'in_progress') {\n return { jobId, status: 'processing' }\n }\n if (status === 'requires_action') {\n return {\n jobId,\n status: 'failed',\n error:\n 'Gemini Omni interaction is waiting on a client action (tool response), which the video jobs flow does not support.',\n }\n }\n if (status === 'completed') {\n if (!extractInteractionVideo(interaction)) {\n return {\n jobId,\n status: 'failed',\n error:\n 'Gemini Omni completed the interaction without returning a video (the output may have been filtered).',\n }\n }\n return { jobId, status: 'completed' }\n }\n return {\n jobId,\n status: 'failed',\n error: `Gemini Omni video generation ended with status \"${status}\".`,\n }\n }\n\n async getVideoUrl(jobId: string): Promise<VideoUrlResult> {\n if (isInteractionsVideoModel(this.model)) {\n return await this.getInteractionsVideoUrl(jobId)\n }\n const operation = await this.getOperation(jobId)\n\n if (!operation.done) {\n throw new Error(\n `Video is not ready yet. Check status first. Job ID: ${jobId}`,\n )\n }\n\n if (operation.error) {\n throw new Error(\n `Video generation failed: ${operationErrorMessage(operation.error)}`,\n )\n }\n\n const uri = operation.response?.generatedVideos?.[0]?.video?.uri\n if (!uri) {\n const reasons = operation.response?.raiMediaFilteredReasons\n throw new Error(\n reasons?.length\n ? `Video was filtered by Responsible-AI: ${reasons.join('; ')}`\n : `Video URL not found in operation response. Job ID: ${jobId}`,\n )\n }\n\n return { jobId, url: uri }\n }\n\n /**\n * Extract the finished Omni video. Inline base64 output (the API default)\n * becomes a `data:` URL — matching the OpenAI Sora adapter's inline\n * delivery — and URI delivery passes through (Files API URIs need the API\n * key to download, like Veo). Usage carries the video-modality output\n * tokens (Omni bills per second of video, reported as tokens).\n */\n private async getInteractionsVideoUrl(\n jobId: string,\n ): Promise<VideoUrlResult> {\n const interaction = await this.getInteraction(jobId)\n const status = interaction.status\n\n if (status === 'in_progress') {\n throw new Error(\n `Video is not ready yet. Check status first. Job ID: ${jobId}`,\n )\n }\n if (status !== 'completed') {\n throw new Error(\n `Video generation failed: Gemini Omni interaction ended with status \"${status}\". Job ID: ${jobId}`,\n )\n }\n\n const video = extractInteractionVideo(interaction)\n if (!video) {\n throw new Error(\n `Video not found in interaction response (the output may have been filtered). Job ID: ${jobId}`,\n )\n }\n\n const usage = interactionUsageToTokenUsage(interaction.usage)\n const url = video.uri ?? `data:${video.mimeType};base64,${video.data}`\n return { jobId, url, ...(usage && { usage }) }\n }\n\n override availableDurations(): DurationOptions<\n GeminiVideoModelDurationByName[TModel]\n > {\n return getGeminiVideoDurationOptions(this.model)\n }\n\n override snapDuration(\n seconds: number,\n ): GeminiVideoModelDurationByName[TModel] | undefined {\n return snapToDurationOption(seconds, this.availableDurations())\n }\n\n /**\n * Fetch the long-running operation by name. The SDK's\n * `operations.getVideosOperation` needs a real `GenerateVideosOperation`\n * instance (it calls `_fromAPIResponse` on it), so reconstruct one from\n * the job ID rather than passing an object literal.\n */\n private async getOperation(jobId: string): Promise<GenerateVideosOperation> {\n const operation = new GenerateVideosOperation()\n operation.name = jobId\n return await this.client.operations.getVideosOperation({ operation })\n }\n\n /**\n * Fetch an Omni background interaction by id.\n */\n private async getInteraction(jobId: string): Promise<Interaction> {\n return await this.client.interactions.get(jobId)\n }\n}\n\n/** @deprecated Shuts down 2026-09-30. Use `gemini-omni-1.1-flash`. */\nexport function createGeminiVideo(\n model: 'gemini-omni-flash-preview',\n apiKey: string,\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<'gemini-omni-flash-preview'>\n/**\n * Creates a Gemini video adapter with an explicit API key.\n * Type resolution happens here at the call site.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @param model - The model name (e.g., 'veo-3.1-generate-preview')\n * @param apiKey - Your Google API key\n * @param config - Optional additional configuration\n * @returns Configured Gemini video adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGeminiVideo('veo-3.1-generate-preview', 'your-api-key');\n *\n * const { jobId } = await generateVideo({\n * adapter,\n * prompt: 'A beautiful sunset over the ocean',\n * duration: adapter.snapDuration(7), // → 6\n * });\n * ```\n */\nexport function createGeminiVideo<TModel extends GeminiVideoModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<TModel>\nexport function createGeminiVideo<TModel extends GeminiVideoModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<TModel> {\n return new GeminiVideoAdapter({ apiKey, ...config }, model)\n}\n\n/** @deprecated Shuts down 2026-09-30. Use `gemini-omni-1.1-flash`. */\nexport function geminiVideo(\n model: 'gemini-omni-flash-preview',\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<'gemini-omni-flash-preview'>\n/**\n * Creates a Gemini video adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `GOOGLE_API_KEY` or `GEMINI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @param model - The model name (e.g., 'veo-3.1-generate-preview')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Gemini video adapter instance with resolved types\n * @throws Error if GOOGLE_API_KEY or GEMINI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses GOOGLE_API_KEY from environment\n * const adapter = geminiVideo('veo-3.1-generate-preview');\n *\n * // Create a video generation job\n * const { jobId } = await generateVideo({\n * adapter,\n * prompt: 'A cat playing piano'\n * });\n *\n * // Poll for status\n * const status = await getVideoJobStatus({ adapter, jobId });\n * ```\n */\nexport function geminiVideo<TModel extends GeminiVideoModel>(\n model: TModel,\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<TModel>\nexport function geminiVideo<TModel extends GeminiVideoModel>(\n model: TModel,\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<TModel> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiVideo(model, apiKey, config)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"video.js","names":[],"sources":["../../../src/adapters/video.ts"],"sourcesContent":["import {\n GenerateVideosOperation,\n VideoGenerationReferenceType,\n} from '@google/genai'\nimport {\n fileReferenceFor,\n isFileSource,\n resolveMediaPrompt,\n unsupportedFileSourceError,\n} from '@tanstack/ai'\nimport { BaseVideoAdapter, snapToDurationOption } from '@tanstack/ai/adapters'\nimport { arrayBufferToBase64 } from '@tanstack/ai-utils'\nimport { createGeminiClient, getGeminiApiKeyFromEnv } from '../utils'\nimport {\n getGeminiVideoDurationOptions,\n isInteractionsVideoModel,\n parseGeminiOmniVideoSize,\n} from '../video/video-provider-options'\nimport type { DurationOptions } from '@tanstack/ai/adapters'\nimport type {\n ImagePart,\n MediaInputMetadata,\n TokenUsage,\n VideoGenerationOptions,\n VideoJobResult,\n VideoPart,\n VideoStatusResult,\n VideoUrlResult,\n} from '@tanstack/ai'\nimport type {\n GenerateVideosConfig,\n GoogleGenAI,\n Image,\n Interactions,\n VideoGenerationReferenceImage,\n} from '@google/genai'\nimport type {\n GeminiOmniVideoProviderOptions,\n GeminiVideoModel,\n GeminiVideoModelDurationByName,\n GeminiVideoModelInputModalitiesByName,\n GeminiVideoModelProviderOptionsByName,\n GeminiVideoModelSizeByName,\n GeminiVideoProviderOptions,\n} from '../video/video-provider-options'\nimport type { GeminiClientConfig } from '../utils/client'\n\ntype Interaction = Interactions.Interaction\ntype InteractionContent = Interactions.Content\n\n/**\n * Configuration for Gemini video adapter.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport interface GeminiVideoConfig extends GeminiClientConfig {\n /**\n * Opt into fetching HTTP(S) image URL inputs. Veo's predict API accepts\n * only inline `imageBytes` or a `gcsUri`, so an HTTP(S) URL has to be\n * downloaded and base64-encoded locally — which buffers the whole image in\n * memory and can OOM constrained runtimes (e.g. Cloudflare Workers). When\n * `false` (the default), HTTP(S) URL image inputs throw; pass a `data:` URI\n * or a `gs://` reference, or set this to `true` to opt into buffering.\n */\n allowUrlFetch?: boolean\n}\n\n/**\n * Extract a human-readable message from a long-running operation's error,\n * which the SDK types as `Record<string, unknown>` (a google.rpc.Status).\n */\nfunction operationErrorMessage(error: Record<string, unknown>): string {\n if (typeof error.message === 'string' && error.message.length > 0) {\n return error.message\n }\n return JSON.stringify(error)\n}\n\n/**\n * Convert a TanStack image prompt part into the genai `Image` shape Veo\n * accepts: base64 `imageBytes` (data sources, data: URIs, fetched HTTP\n * URLs) or a `gcsUri` passthrough for Cloud Storage references.\n *\n * Unlike `generateContent` (chat / native image generation), Veo's predict\n * API has no `fileData.fileUri` equivalent — `Image` only accepts\n * `imageBytes` or `gcsUri`. An HTTP(S) URL therefore has to be fetched and\n * inlined locally, which buffers the whole image in memory; that only happens\n * when the caller opts in via `allowUrlFetch`, otherwise it throws. Prefer a\n * `gs://` reference on memory-constrained runtimes.\n */\nasync function imagePartToVeoImage(\n part: ImagePart<MediaInputMetadata>,\n allowUrlFetch: boolean,\n): Promise<Image> {\n if (part.source.type === 'data') {\n return {\n imageBytes: part.source.value,\n mimeType: part.source.mimeType || 'image/png',\n }\n }\n if (isFileSource(part.source)) {\n // Veo's predict API accepts only inline bytes or a gs:// reference — there's\n // no way to reference a Files API handle here.\n throw unsupportedFileSourceError(\n 'gemini',\n 'for Veo video generation, which needs inline image bytes or a gs:// reference — pass a data: URI or gs:// URL',\n )\n }\n const url = part.source.value\n if (url.startsWith('gs://')) {\n return {\n gcsUri: url,\n ...(part.source.mimeType && { mimeType: part.source.mimeType }),\n }\n }\n if (url.startsWith('data:')) {\n const match = url.match(/^data:([^;,]+)?(;base64)?,(.*)$/)\n if (!match || !match[2]) {\n throw new Error(\n 'gemini: only base64 data: URIs are supported for video image inputs.',\n )\n }\n return {\n imageBytes: match[3] ?? '',\n mimeType: match[1] || part.source.mimeType || 'image/png',\n }\n }\n if (!allowUrlFetch) {\n throw new Error(\n `gemini Veo: HTTP(S) URL image inputs are not fetched by default because ` +\n `Veo accepts only inline bytes, so the image would be downloaded and ` +\n `buffered in memory (risking OOM on constrained runtimes). Pass a ` +\n `data: URI or a gs:// reference, or set \\`allowUrlFetch: true\\` on the ` +\n `adapter config to opt into fetching. URL: ${url}`,\n )\n }\n const response = await fetch(url)\n if (!response.ok) {\n throw new Error(\n `Failed to fetch image input (${response.status} ${response.statusText}): ${url}`,\n )\n }\n const blob = await response.blob()\n const buffer = await blob.arrayBuffer()\n return {\n imageBytes: arrayBufferToBase64(buffer),\n mimeType: part.source.mimeType || blob.type || 'image/png',\n }\n}\n\n/**\n * Convert an image or video prompt part into an Interactions API content\n * block. Data sources become inline base64 `data`; URL sources pass through\n * as `uri` (Files API URIs — mirrors the Interactions text adapter).\n */\nfunction mediaPartToInteractionsContent(\n part: ImagePart<MediaInputMetadata> | VideoPart<MediaInputMetadata>,\n): InteractionContent {\n // A Gemini Files API reference maps to the `uri` field, same as a public\n // URL (mirrors the Interactions text adapter). `fileReferenceFor` throws\n // when another provider issued the handle.\n const sourceValue = isFileSource(part.source)\n ? fileReferenceFor(part.source, 'gemini')\n : part.source.value\n const mimeType = part.source.mimeType\n if (part.type === 'image') {\n return part.source.type === 'data'\n ? { type: 'image', data: sourceValue, mime_type: mimeType }\n : { type: 'image', uri: sourceValue, mime_type: mimeType }\n }\n return part.source.type === 'data'\n ? { type: 'video', data: sourceValue, mime_type: mimeType }\n : { type: 'video', uri: sourceValue, mime_type: mimeType }\n}\n\n/**\n * Pull the generated video out of a completed interaction. Prefers the\n * SDK's `output_video` sugar, then walks `steps` back-to-front for the last\n * `model_output` step carrying a video content block (the wire shape the\n * raw REST response uses).\n */\nfunction extractInteractionVideo(\n interaction: Interaction,\n): { data?: string; uri?: string; mimeType: string } | undefined {\n const direct = interaction.output_video\n if (direct && (direct.data || direct.uri)) {\n return {\n data: direct.data,\n uri: direct.uri,\n mimeType: direct.mime_type || 'video/mp4',\n }\n }\n const steps = interaction.steps ?? []\n for (let i = steps.length - 1; i >= 0; i--) {\n const step = steps[i]\n if (step?.type !== 'model_output') continue\n for (const block of step.content ?? []) {\n if (block.type === 'video' && (block.data || block.uri)) {\n return {\n data: block.data,\n uri: block.uri,\n mimeType: block.mime_type || 'video/mp4',\n }\n }\n }\n }\n return undefined\n}\n\n/**\n * Map Interactions usage onto the canonical TokenUsage shape. Omni reports\n * video output via `output_tokens_by_modality`; fall back to the video\n * modality entry when the total is absent.\n */\nfunction interactionUsageToTokenUsage(\n usage: Interaction['usage'],\n): TokenUsage | undefined {\n if (!usage) return undefined\n const videoTokens = usage.output_tokens_by_modality?.find(\n (entry) => entry.modality === 'video',\n )?.tokens\n const promptTokens = usage.total_input_tokens ?? 0\n const completionTokens = usage.total_output_tokens ?? videoTokens ?? 0\n return {\n promptTokens,\n completionTokens,\n totalTokens: usage.total_tokens ?? promptTokens + completionTokens,\n }\n}\n\n/**\n * Gemini Video Generation Adapter (Veo + Gemini Omni Flash)\n *\n * Tree-shakeable adapter for Google video generation, routing by model:\n *\n * **Veo models** run as a long-running operation: `createVideoJob` starts\n * the operation via the `:predictLongRunning` endpoint, `getVideoStatus`\n * polls it, and `getVideoUrl` extracts the generated video's URI once it\n * completes. Image prompt parts are routed by `metadata.role`:\n * - `'start_frame'` (or the first un-roled image) → the input image the\n * video starts from\n * - `'end_frame'` → `lastFrame` (the frame the video ends on)\n * - `'reference'` / `'character'` → `referenceImages` (asset references,\n * Veo 3.1)\n *\n * Note: the returned Veo video URI is served by the Gemini Files API and\n * requires the API key (`x-goog-api-key` header or `?key=` query\n * parameter) to download.\n *\n * **Gemini Omni Flash** (`gemini-omni-1.1-flash`, plus the deprecated\n * `gemini-omni-flash-preview` alias) only serves the Interactions API:\n * `createVideoJob` creates a background interaction with\n * `response_modalities: ['video']`, `getVideoStatus` polls it by id, and\n * `getVideoUrl` returns the inline base64 MP4 as a `data:` URL (or the\n * Files API URI when the server delivers by reference). Image and video\n * prompt parts are sent as interaction content blocks, grouped as images,\n * then videos, then the text prompt (interleaving is not preserved); pass\n * `modelOptions.previous_interaction_id` to conversationally edit a prior\n * Omni generation. `size` is an `aspectRatio_resolution` template\n * (`'16:9'` or `'16:9_1080p'`); the optional suffix maps onto\n * `response_format.resolution` (`'360p' | '720p' | '1080p' | '4k'`,\n * default 720p).\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport class GeminiVideoAdapter<\n TModel extends GeminiVideoModel,\n> extends BaseVideoAdapter<\n TModel,\n GeminiVideoModelProviderOptionsByName[TModel],\n GeminiVideoModelProviderOptionsByName,\n GeminiVideoModelSizeByName,\n GeminiVideoModelInputModalitiesByName,\n GeminiVideoModelDurationByName\n> {\n readonly name = 'gemini' as const\n // The Interactions path consumes Gemini Files API references as content\n // `uri`s; the Veo path still rejects them (raw bytes / gs:// only).\n override readonly supportsFileSources = true\n\n protected client: GoogleGenAI\n private readonly allowUrlFetch: boolean\n\n constructor(config: GeminiVideoConfig, model: TModel) {\n super({}, model)\n this.client = createGeminiClient(config)\n this.allowUrlFetch = config.allowUrlFetch ?? false\n }\n\n async createVideoJob(\n options: VideoGenerationOptions<\n GeminiVideoModelProviderOptionsByName[TModel],\n GeminiVideoModelSizeByName[TModel],\n GeminiVideoModelDurationByName[TModel]\n >,\n ): Promise<VideoJobResult> {\n const { prompt, size, duration, logger } = options\n\n logger.request(\n `activity=video.create provider=${this.name} model=${this.model} size=${size ?? 'default'} duration=${duration ?? 'default'}`,\n { provider: this.name, model: this.model },\n )\n\n if (isInteractionsVideoModel(this.model)) {\n return await this.createInteractionsVideoJob(options)\n }\n const modelOptions = options.modelOptions as\n | GeminiVideoProviderOptions\n | undefined\n\n try {\n const resolved = resolveMediaPrompt(prompt)\n\n if (resolved.videos.length > 0) {\n throw new Error(\n `${this.name}.createVideoJob does not support video prompt parts (model: ${this.model}).`,\n )\n }\n if (resolved.audios.length > 0) {\n throw new Error(\n `${this.name}.createVideoJob does not support audio prompt parts (model: ${this.model}).`,\n )\n }\n\n const { image, lastFrame, referenceImages } = await this.routeImageParts(\n resolved.images,\n )\n\n const config: GenerateVideosConfig = {\n ...modelOptions,\n ...(size !== undefined && { aspectRatio: size }),\n ...(duration !== undefined && { durationSeconds: duration }),\n ...(lastFrame && { lastFrame }),\n ...(referenceImages.length > 0 && { referenceImages }),\n }\n\n const operation = await this.client.models.generateVideos({\n model: this.model,\n prompt: resolved.text,\n ...(image && { image }),\n config,\n })\n\n if (!operation.name) {\n throw new Error(\n 'Veo did not return an operation name for the video generation job.',\n )\n }\n\n return { jobId: operation.name, model: this.model }\n } catch (error) {\n logger.errors(`${this.name}.createVideoJob fatal`, {\n error,\n source: `${this.name}.createVideoJob`,\n })\n throw error\n }\n }\n\n /**\n * Gemini Omni Flash job creation via the Interactions API. Creates a\n * background interaction requesting video output; the interaction id is\n * the job id polled by `getVideoStatus` / `getVideoUrl`.\n */\n private async createInteractionsVideoJob(\n options: VideoGenerationOptions<\n GeminiVideoModelProviderOptionsByName[TModel],\n GeminiVideoModelSizeByName[TModel],\n GeminiVideoModelDurationByName[TModel]\n >,\n ): Promise<VideoJobResult> {\n const { prompt, size, duration, logger } = options\n const modelOptions = options.modelOptions as\n | GeminiOmniVideoProviderOptions\n | undefined\n\n try {\n const resolved = resolveMediaPrompt(prompt)\n\n if (resolved.audios.length > 0) {\n throw new Error(\n `${this.name}.createVideoJob does not support audio prompt parts (model: ${this.model}).`,\n )\n }\n\n const content: Array<InteractionContent> = [\n ...resolved.images.map(mediaPartToInteractionsContent),\n ...resolved.videos.map(mediaPartToInteractionsContent),\n ]\n if (resolved.text) {\n content.push({ type: 'text', text: resolved.text })\n }\n if (content.length === 0) {\n throw new Error(\n `${this.name}.createVideoJob: the prompt produced no content to send (model: ${this.model}).`,\n )\n }\n\n // Reject out-of-range durations locally rather than snapping (which\n // would silently change the clip length the caller asked for) or\n // letting the live API reject them after the round trip.\n const durations = this.availableDurations()\n if (\n duration !== undefined &&\n durations.kind === 'range' &&\n (duration < durations.min || duration > durations.max)\n ) {\n throw new Error(\n `${this.name}.createVideoJob: duration ${duration}s is outside the ${durations.min}–${durations.max}s range supported by ${this.model}. Use snapDuration() to snap arbitrary values into range.`,\n )\n }\n\n // Aspect ratio, clip length, and resolution ride on `response_format`.\n // Duration is a `\"<seconds>s\"` string. Resolution comes from the\n // optional `size` suffix (`'16:9_1080p'`), defaulting to 720p when\n // omitted — https://ai.google.dev/gemini-api/docs/omni\n const parsedSize =\n size !== undefined ? parseGeminiOmniVideoSize(size) : undefined\n const responseFormat =\n parsedSize !== undefined || duration !== undefined\n ? {\n response_format: {\n type: 'video' as const,\n ...(parsedSize !== undefined && {\n aspect_ratio: parsedSize.aspectRatio,\n ...(parsedSize.resolution !== undefined && {\n resolution: parsedSize.resolution,\n }),\n }),\n ...(duration !== undefined && { duration: `${duration}s` }),\n },\n }\n : {}\n\n const interaction = await this.client.interactions.create({\n ...modelOptions,\n model: this.model,\n input: [{ type: 'user_input', content }],\n response_modalities: ['video'],\n background: true,\n ...responseFormat,\n })\n\n if (!interaction.id) {\n throw new Error(\n 'Gemini Omni did not return an interaction id for the video generation job.',\n )\n }\n\n return { jobId: interaction.id, model: this.model }\n } catch (error) {\n logger.errors(`${this.name}.createVideoJob fatal`, {\n error,\n source: `${this.name}.createVideoJob`,\n })\n throw error\n }\n }\n\n /**\n * Route image prompt parts onto Veo's request fields by `metadata.role`.\n */\n private async routeImageParts(\n parts: Array<ImagePart<MediaInputMetadata>>,\n ): Promise<{\n image: Image | undefined\n lastFrame: Image | undefined\n referenceImages: Array<VideoGenerationReferenceImage>\n }> {\n let image: Image | undefined\n let lastFrame: Image | undefined\n const referenceImages: Array<VideoGenerationReferenceImage> = []\n\n for (const part of parts) {\n const role = part.metadata?.role\n switch (role) {\n case 'end_frame': {\n if (lastFrame) {\n throw new Error(\n `${this.name}: Veo accepts at most one 'end_frame' image.`,\n )\n }\n lastFrame = await imagePartToVeoImage(part, this.allowUrlFetch)\n break\n }\n case 'reference':\n case 'character': {\n referenceImages.push({\n image: await imagePartToVeoImage(part, this.allowUrlFetch),\n referenceType: VideoGenerationReferenceType.ASSET,\n })\n break\n }\n case 'start_frame':\n case undefined: {\n if (image) {\n throw new Error(\n `${this.name}: Veo accepts at most one starting image; received multiple 'start_frame'/un-roled images. Use metadata.role ('end_frame', 'reference') to disambiguate the others.`,\n )\n }\n image = await imagePartToVeoImage(part, this.allowUrlFetch)\n break\n }\n case 'mask':\n case 'control':\n throw new Error(\n `${this.name}: unsupported image role \"${role}\" for Veo video generation.`,\n )\n }\n }\n\n return { image, lastFrame, referenceImages }\n }\n\n async getVideoStatus(jobId: string): Promise<VideoStatusResult> {\n if (isInteractionsVideoModel(this.model)) {\n return await this.getInteractionsVideoStatus(jobId)\n }\n const operation = await this.getOperation(jobId)\n\n if (!operation.done) {\n return { jobId, status: 'processing' }\n }\n\n if (operation.error) {\n return {\n jobId,\n status: 'failed',\n error: operationErrorMessage(operation.error),\n }\n }\n\n // The operation can finish \"successfully\" with every sample dropped by\n // Responsible-AI filters — surface that as a failure instead of letting\n // getVideoUrl() throw on an empty response.\n const videos = operation.response?.generatedVideos ?? []\n if (videos.length === 0) {\n const reasons = operation.response?.raiMediaFilteredReasons\n return {\n jobId,\n status: 'failed',\n error: reasons?.length\n ? `Video was filtered by Responsible-AI: ${reasons.join('; ')}`\n : 'Veo returned no generated videos.',\n }\n }\n\n return { jobId, status: 'completed' }\n }\n\n /**\n * Poll an Omni background interaction. `in_progress` maps to\n * 'processing'; a `completed` interaction with no video content (e.g.\n * filtered output) is surfaced as a failure so `getVideoUrl` doesn't\n * throw on an empty response. `requires_action` also fails: the adapter\n * never sends tools, so it can only arise via\n * `previous_interaction_id` chaining onto a tool-bearing interaction —\n * and such an interaction never progresses without a client response,\n * so polling it would spin until timeout.\n */\n private async getInteractionsVideoStatus(\n jobId: string,\n ): Promise<VideoStatusResult> {\n const interaction = await this.getInteraction(jobId)\n const status = interaction.status\n\n if (status === 'in_progress') {\n return { jobId, status: 'processing' }\n }\n if (status === 'requires_action') {\n return {\n jobId,\n status: 'failed',\n error:\n 'Gemini Omni interaction is waiting on a client action (tool response), which the video jobs flow does not support.',\n }\n }\n if (status === 'completed') {\n if (!extractInteractionVideo(interaction)) {\n return {\n jobId,\n status: 'failed',\n error:\n 'Gemini Omni completed the interaction without returning a video (the output may have been filtered).',\n }\n }\n return { jobId, status: 'completed' }\n }\n return {\n jobId,\n status: 'failed',\n error: `Gemini Omni video generation ended with status \"${status}\".`,\n }\n }\n\n async getVideoUrl(jobId: string): Promise<VideoUrlResult> {\n if (isInteractionsVideoModel(this.model)) {\n return await this.getInteractionsVideoUrl(jobId)\n }\n const operation = await this.getOperation(jobId)\n\n if (!operation.done) {\n throw new Error(\n `Video is not ready yet. Check status first. Job ID: ${jobId}`,\n )\n }\n\n if (operation.error) {\n throw new Error(\n `Video generation failed: ${operationErrorMessage(operation.error)}`,\n )\n }\n\n const uri = operation.response?.generatedVideos?.[0]?.video?.uri\n if (!uri) {\n const reasons = operation.response?.raiMediaFilteredReasons\n throw new Error(\n reasons?.length\n ? `Video was filtered by Responsible-AI: ${reasons.join('; ')}`\n : `Video URL not found in operation response. Job ID: ${jobId}`,\n )\n }\n\n return { jobId, url: uri }\n }\n\n /**\n * Extract the finished Omni video. Inline base64 output (the API default)\n * becomes a `data:` URL — matching the OpenAI Sora adapter's inline\n * delivery — and URI delivery passes through (Files API URIs need the API\n * key to download, like Veo). Usage carries the video-modality output\n * tokens (Omni bills per second of video, reported as tokens).\n */\n private async getInteractionsVideoUrl(\n jobId: string,\n ): Promise<VideoUrlResult> {\n const interaction = await this.getInteraction(jobId)\n const status = interaction.status\n\n if (status === 'in_progress') {\n throw new Error(\n `Video is not ready yet. Check status first. Job ID: ${jobId}`,\n )\n }\n if (status !== 'completed') {\n throw new Error(\n `Video generation failed: Gemini Omni interaction ended with status \"${status}\". Job ID: ${jobId}`,\n )\n }\n\n const video = extractInteractionVideo(interaction)\n if (!video) {\n throw new Error(\n `Video not found in interaction response (the output may have been filtered). Job ID: ${jobId}`,\n )\n }\n\n const usage = interactionUsageToTokenUsage(interaction.usage)\n const url = video.uri ?? `data:${video.mimeType};base64,${video.data}`\n return { jobId, url, ...(usage && { usage }) }\n }\n\n override availableDurations(): DurationOptions<\n GeminiVideoModelDurationByName[TModel]\n > {\n return getGeminiVideoDurationOptions(this.model)\n }\n\n override snapDuration(\n seconds: number,\n ): GeminiVideoModelDurationByName[TModel] | undefined {\n return snapToDurationOption(seconds, this.availableDurations())\n }\n\n /**\n * Fetch the long-running operation by name. The SDK's\n * `operations.getVideosOperation` needs a real `GenerateVideosOperation`\n * instance (it calls `_fromAPIResponse` on it), so reconstruct one from\n * the job ID rather than passing an object literal.\n */\n private async getOperation(jobId: string): Promise<GenerateVideosOperation> {\n const operation = new GenerateVideosOperation()\n operation.name = jobId\n return await this.client.operations.getVideosOperation({ operation })\n }\n\n /**\n * Fetch an Omni background interaction by id.\n */\n private async getInteraction(jobId: string): Promise<Interaction> {\n return await this.client.interactions.get(jobId)\n }\n}\n\n/** @deprecated Shuts down 2026-09-30. Use `gemini-omni-1.1-flash`. */\nexport function createGeminiVideo(\n model: 'gemini-omni-flash-preview',\n apiKey: string,\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<'gemini-omni-flash-preview'>\n/**\n * Creates a Gemini video adapter with an explicit API key.\n * Type resolution happens here at the call site.\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @param model - The model name (e.g., 'veo-3.1-generate-preview')\n * @param apiKey - Your Google API key\n * @param config - Optional additional configuration\n * @returns Configured Gemini video adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGeminiVideo('veo-3.1-generate-preview', 'your-api-key');\n *\n * const { jobId } = await generateVideo({\n * adapter,\n * prompt: 'A beautiful sunset over the ocean',\n * duration: adapter.snapDuration(7), // → 6\n * });\n * ```\n */\nexport function createGeminiVideo<TModel extends GeminiVideoModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<TModel>\nexport function createGeminiVideo<TModel extends GeminiVideoModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<TModel> {\n return new GeminiVideoAdapter({ apiKey, ...config }, model)\n}\n\n/** @deprecated Shuts down 2026-09-30. Use `gemini-omni-1.1-flash`. */\nexport function geminiVideo(\n model: 'gemini-omni-flash-preview',\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<'gemini-omni-flash-preview'>\n/**\n * Creates a Gemini video adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `GOOGLE_API_KEY` or `GEMINI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @experimental Video generation is an experimental feature and may change.\n *\n * @param model - The model name (e.g., 'veo-3.1-generate-preview')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Gemini video adapter instance with resolved types\n * @throws Error if GOOGLE_API_KEY or GEMINI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses GOOGLE_API_KEY from environment\n * const adapter = geminiVideo('veo-3.1-generate-preview');\n *\n * // Create a video generation job\n * const { jobId } = await generateVideo({\n * adapter,\n * prompt: 'A cat playing piano'\n * });\n *\n * // Poll for status\n * const status = await getVideoJobStatus({ adapter, jobId });\n * ```\n */\nexport function geminiVideo<TModel extends GeminiVideoModel>(\n model: TModel,\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<TModel>\nexport function geminiVideo<TModel extends GeminiVideoModel>(\n model: TModel,\n config?: Omit<GeminiVideoConfig, 'apiKey'>,\n): GeminiVideoAdapter<TModel> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiVideo(model, apiKey, config)\n}\n"],"mappings":"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"}
|
|
@@ -73,6 +73,7 @@ type ResolveToolCapabilities<TModel extends string> = TModel extends keyof Gemin
|
|
|
73
73
|
export declare class GeminiTextInteractionsAdapter<TModel extends GeminiModels, TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>, TInputModalities extends ReadonlyArray<Modality> = ResolveInputModalities<TModel>, TToolCapabilities extends ReadonlyArray<string> = ResolveToolCapabilities<TModel>> extends BaseTextAdapter<TModel, TProviderOptions, TInputModalities, GeminiMessageMetadataByModality, TToolCapabilities> {
|
|
74
74
|
readonly kind: "text";
|
|
75
75
|
readonly name: "gemini-text-interactions";
|
|
76
|
+
readonly supportsFileSources = true;
|
|
76
77
|
private readonly client;
|
|
77
78
|
private readonly interactionIdByThread;
|
|
78
79
|
constructor(config: GeminiTextInteractionsConfig, model: TModel);
|