@tanstack/ai-gemini 0.33.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 CHANGED
@@ -240,6 +240,7 @@ Official adapters include:
240
240
  | [`@tanstack/ai-byteplus`](https://tanstack.com/ai/latest/docs/adapters/byteplus) | BytePlus Seed chat, Seedance video, Seedream image, and Seed Speech TTS/ASR |
241
241
  | [`@tanstack/ai-fal`](https://tanstack.com/ai/latest/docs/adapters/fal) | fal.ai image, video, audio, speech, and transcription models |
242
242
  | [`@tanstack/ai-reactor`](https://tanstack.com/ai/latest/docs/adapters/reactor) | Reactor live world and video generation (Orbis, Happy Oyster, LingBot, Helios, FastH3) |
243
+ | [`@tanstack/ai-worldlabs`](https://tanstack.com/ai/latest/docs/adapters/worldlabs) | World Labs Marble persistent 3D world generation |
243
244
  | [`@tanstack/ai-cloudflare`](https://tanstack.com/ai/latest/docs/adapters/cloudflare) | Cloudflare Workers AI chat, embeddings, image, speech, transcription, and AI Gateway |
244
245
 
245
246
  The adapter system is tree-shakeable by activity. Import `openaiText` for chat,
@@ -17,6 +17,23 @@ var DEFAULT_MEDIA_MIME_TYPES = {
17
17
  video: "video/mp4",
18
18
  document: "application/pdf"
19
19
  };
20
+ function getGroundingSources(metadata) {
21
+ const sources = /* @__PURE__ */ new Map();
22
+ for (const chunk of metadata.groundingChunks ?? []) {
23
+ const url = chunk.web?.uri;
24
+ if (!url) continue;
25
+ const existing = sources.get(url);
26
+ if (existing) {
27
+ if (!existing.title && chunk.web?.title) existing.title = chunk.web.title;
28
+ continue;
29
+ }
30
+ sources.set(url, {
31
+ url,
32
+ ...chunk.web?.title ? { title: chunk.web.title } : {}
33
+ });
34
+ }
35
+ return [...sources.values()];
36
+ }
20
37
  /** True when any message carries a video part requesting agentic processing. */
21
38
  function hasAgenticVideo(messages) {
22
39
  return messages.some((msg) => Array.isArray(msg.content) && msg.content.some((part) => part.type === "video" && part.metadata?.processing === "agentic"));
@@ -347,6 +364,41 @@ var GeminiTextAdapter = class extends BaseTextAdapter {
347
364
  let hasEmittedRunStarted = false;
348
365
  let hasEmittedTextMessageStart = false;
349
366
  let hasEmittedStepStarted = false;
367
+ let groundingMetadata;
368
+ let groundingCallEmitted = false;
369
+ const adapterName = this.name;
370
+ const emitGroundingToolCall = function* () {
371
+ if (!groundingMetadata || groundingCallEmitted) return;
372
+ const sources = getGroundingSources(groundingMetadata);
373
+ if (!(sources.length > 0 || (groundingMetadata.webSearchQueries?.length ?? 0) > 0 || groundingMetadata.searchEntryPoint !== void 0)) return;
374
+ groundingCallEmitted = true;
375
+ const toolCallId = generateId(adapterName);
376
+ const metadata = {
377
+ providerExecuted: true,
378
+ sources,
379
+ gemini: { groundingMetadata }
380
+ };
381
+ yield {
382
+ type: EventType.TOOL_CALL_START,
383
+ toolCallId,
384
+ toolCallName: "google_search",
385
+ toolName: "google_search",
386
+ parentMessageId: messageId,
387
+ model,
388
+ timestamp: Date.now(),
389
+ index: toolCallMap.size,
390
+ metadata
391
+ };
392
+ yield {
393
+ type: EventType.TOOL_CALL_END,
394
+ toolCallId,
395
+ toolCallName: "google_search",
396
+ toolName: "google_search",
397
+ model,
398
+ timestamp: Date.now(),
399
+ input: { ...groundingMetadata.webSearchQueries && { queries: groundingMetadata.webSearchQueries } }
400
+ };
401
+ };
350
402
  for await (const chunk of result) {
351
403
  logger.provider(`provider=gemini`, { chunk });
352
404
  if (!hasEmittedRunStarted) {
@@ -360,6 +412,7 @@ var GeminiTextAdapter = class extends BaseTextAdapter {
360
412
  parentRunId: options.parentRunId
361
413
  };
362
414
  }
415
+ if (chunk.candidates?.[0]?.groundingMetadata) groundingMetadata = chunk.candidates[0].groundingMetadata;
363
416
  if (chunk.candidates?.[0]?.content?.parts) {
364
417
  const parts = chunk.candidates[0].content.parts;
365
418
  for (const part of parts) {
@@ -521,6 +574,7 @@ var GeminiTextAdapter = class extends BaseTextAdapter {
521
574
  };
522
575
  }
523
576
  if (chunk.candidates?.[0]?.finishReason) {
577
+ yield* emitGroundingToolCall();
524
578
  const finishReason = chunk.candidates[0].finishReason;
525
579
  for (const [toolCallId, toolCallData] of toolCallMap.entries()) {
526
580
  let parsedInput = {};
@@ -585,6 +639,7 @@ var GeminiTextAdapter = class extends BaseTextAdapter {
585
639
  };
586
640
  }
587
641
  }
642
+ yield* emitGroundingToolCall();
588
643
  }
589
644
  convertContentPartToGemini(part) {
590
645
  switch (part.type) {
@@ -624,13 +679,15 @@ var GeminiTextAdapter = class extends BaseTextAdapter {
624
679
  if (Array.isArray(msg.content)) for (const contentPart of msg.content) parts.push(this.convertContentPartToGemini(contentPart));
625
680
  else if (msg.content && msg.role !== "tool") parts.push({ text: msg.content });
626
681
  if (msg.role === "assistant" && msg.toolCalls?.length) for (const toolCall of msg.toolCalls) {
682
+ const metadata = toolCall.metadata;
683
+ if (metadata?.providerExecuted) continue;
627
684
  let parsedArgs = {};
628
685
  try {
629
686
  parsedArgs = toolCall.function.arguments ? JSON.parse(toolCall.function.arguments) : {};
630
687
  } catch {
631
688
  parsedArgs = {};
632
689
  }
633
- const thoughtSignature = toolCall.metadata?.thoughtSignature;
690
+ const thoughtSignature = metadata?.thoughtSignature;
634
691
  const part = { functionCall: {
635
692
  id: toolCall.id,
636
693
  name: toolCall.function.name,
@@ -1 +1 @@
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 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 // 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\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 // 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 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 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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"}
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":";;;;;;;;;;;;;AA0DA,IAAM,2BAA2B;CAC/B,OAAO;CACP,OAAO;CACP,OAAO;CACP,UAAU;AACZ;AAEA,SAAS,oBACP,UACmC;CACnC,MAAM,0BAAU,IAAI,IAAwC;CAC5D,KAAK,MAAM,SAAS,SAAS,mBAAmB,CAAC,GAAG;EAClD,MAAM,MAAM,MAAM,KAAK;EACvB,IAAI,CAAC,KAAK;EACV,MAAM,WAAW,QAAQ,IAAI,GAAG;EAChC,IAAI,UAAU;GACZ,IAAI,CAAC,SAAS,SAAS,MAAM,KAAK,OAAO,SAAS,QAAQ,MAAM,IAAI;GACpE;EACF;EACA,QAAQ,IAAI,KAAK;GACf;GACA,GAAI,MAAM,KAAK,QAAQ,EAAE,OAAO,MAAM,IAAI,MAAM,IAAI,CAAC;EACvD,CAAC;CACH;CACA,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC;AAC7B;;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;CAGxD,MAAM,OAAO,aAAa,MAAM,IAC5B;EAAE,KAAK,iBAAiB,QAAQ,QAAQ;EAAG,WAAW;CAAS,IAC/D,OAAO,SAAS,SACd;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,sBAAwC;CAExC;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;EAC5B,IAAI;EACJ,IAAI,uBAAuB;EAC3B,MAAM,cAAc,KAAK;EAEzB,MAAM,wBAAwB,aAA2C;GACvE,IAAI,CAAC,qBAAqB,sBAAsB;GAChD,MAAM,UAAU,oBAAoB,iBAAiB;GAKrD,IAAI,EAHF,QAAQ,SAAS,MAChB,kBAAkB,kBAAkB,UAAU,KAAK,KACpD,kBAAkB,qBAAqB,KAAA,IACX;GAE9B,uBAAuB;GACvB,MAAM,aAAa,WAAW,WAAW;GACzC,MAAM,WAAmC;IACvC,kBAAkB;IAClB;IACA,QAAQ,EAAE,kBAAkB;GAC9B;GACA,MAAM;IACJ,MAAM,UAAU;IAChB;IACA,cAAc;IACd,UAAU;IACV,iBAAiB;IACjB;IACA,WAAW,KAAK,IAAI;IACpB,OAAO,YAAY;IACnB;GACF;GACA,MAAM;IACJ,MAAM,UAAU;IAChB;IACA,cAAc;IACd,UAAU;IACV;IACA,WAAW,KAAK,IAAI;IACpB,OAAO,EACL,GAAI,kBAAkB,oBAAoB,EACxC,SAAS,kBAAkB,iBAC7B,EACF;GACF;EACF;EAEA,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,mBACzB,oBAAoB,MAAM,WAAW,EAAE,CAAC;GAG1C,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,OAAO,sBAAsB;IAC7B,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;EAEA,OAAO,sBAAsB;CAC/B;CAEA,2BAAmC,MAAyB;EAC1D,QAAQ,KAAK,MAAb;GACE,KAAK,QACH,OAAO,EAAE,MAAM,KAAK,QAAQ;GAC9B,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,YAAY;IAKf,MAAM,UAAU,aAAa,KAAK,MAAM,IACpC,iBAAiB,KAAK,QAAQ,KAAK,IAAI,IACvC,KAAK,OAAO;IAChB,MAAM,aACJ,KAAK,OAAO,SAAS,SACjB,EACE,YAAY;KACV,MAAM,KAAK,OAAO;KAClB,UAAU,KAAK,OAAO;IACxB,EACF,IACA,EACE,UAAU;KACR;KAGA,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,MAAM,WAAW,SAAS;IAG1B,IAAI,UAAU,kBAAkB;IAEhC,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,mBAAmB,UAAU;IAKnC,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,UAAU,aAAa,KAAK,MAAM,IACpC,iBAAiB,KAAK,QAAQ,KAAK,IAAI,IACvC,KAAK,OAAO;MAChB,MAAM,kBAAkB;OACtB,OAAO;OACP,OAAO;OACP,OAAO;OACP,UAAU;MACZ,EAAE,KAAK;MACP,WAAW,KAAK,EACd,UAAU;OACR;OACA,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,3 +1,5 @@
1
+ import { ProviderExecutedToolMetadata } from '@tanstack/ai';
2
+ import { GroundingMetadata } from '@google/genai';
1
3
  /**
2
4
  * Gemini-specific metadata types for multimodal content parts.
3
5
  * These types extend the base ContentPart metadata with Gemini-specific options.
@@ -135,6 +137,9 @@ export interface GeminiMessageMetadataByModality {
135
137
  *
136
138
  * @see https://ai.google.dev/gemini-api/docs/thinking
137
139
  */
138
- export interface GeminiToolCallMetadata {
140
+ export interface GeminiToolCallMetadata extends ProviderExecutedToolMetadata {
139
141
  thoughtSignature?: string;
142
+ gemini?: {
143
+ groundingMetadata: GroundingMetadata;
144
+ };
140
145
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-gemini",
3
- "version": "0.33.1",
3
+ "version": "0.34.1",
4
4
  "description": "Google Gemini adapter for TanStack AI chat, images, speech, audio generation, and structured outputs.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -63,13 +63,13 @@
63
63
  "@tanstack/ai-utils": "^0.4.1"
64
64
  },
65
65
  "peerDependencies": {
66
- "@tanstack/ai": "^0.61.0"
66
+ "@tanstack/ai": "^0.63.0"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@vitest/coverage-v8": "4.1.10",
70
70
  "vite": "^8.2.1",
71
71
  "zod": "^4.2.0",
72
- "@tanstack/ai": "0.61.0"
72
+ "@tanstack/ai": "0.63.0"
73
73
  },
74
74
  "scripts": {
75
75
  "build": "vite build",
@@ -77,6 +77,7 @@
77
77
  "lint:fix": "oxlint src --type-aware --fix",
78
78
  "test:build": "publint --strict",
79
79
  "test:oxlint": "oxlint src --type-aware",
80
+ "test:coverage": "vitest run --coverage --coverage.include='src/**' --coverage.reporter=text-summary --coverage.reporter=json-summary",
80
81
  "test:lib": "vitest",
81
82
  "test:lib:dev": "pnpm test:lib --watch",
82
83
  "test:types": "tsc"
@@ -31,6 +31,7 @@ import type {
31
31
  GenerateContentParameters,
32
32
  GenerateContentResponse,
33
33
  GoogleGenAI,
34
+ GroundingMetadata,
34
35
  Part,
35
36
  ThinkingLevel,
36
37
  VideoMetadata,
@@ -40,6 +41,7 @@ import type {
40
41
  Modality,
41
42
  ModelMessage,
42
43
  AdapterYieldChunk,
44
+ ProviderExecutedToolSource,
43
45
  TextOptions,
44
46
  } from '@tanstack/ai'
45
47
  import type { ExternalTextProviderOptions } from '../text/text-provider-options'
@@ -61,6 +63,26 @@ const DEFAULT_MEDIA_MIME_TYPES = {
61
63
  document: 'application/pdf',
62
64
  } as const
63
65
 
66
+ function getGroundingSources(
67
+ metadata: GroundingMetadata,
68
+ ): Array<ProviderExecutedToolSource> {
69
+ const sources = new Map<string, ProviderExecutedToolSource>()
70
+ for (const chunk of metadata.groundingChunks ?? []) {
71
+ const url = chunk.web?.uri
72
+ if (!url) continue
73
+ const existing = sources.get(url)
74
+ if (existing) {
75
+ if (!existing.title && chunk.web?.title) existing.title = chunk.web.title
76
+ continue
77
+ }
78
+ sources.set(url, {
79
+ url,
80
+ ...(chunk.web?.title ? { title: chunk.web.title } : {}),
81
+ })
82
+ }
83
+ return [...sources.values()]
84
+ }
85
+
64
86
  /**
65
87
  * Content block shape for an Interactions API `input` step. The installed
66
88
  * @google/genai types predate the video `processing` field, so we model the
@@ -604,6 +626,51 @@ export class GeminiTextAdapter<
604
626
  let hasEmittedRunStarted = false
605
627
  let hasEmittedTextMessageStart = false
606
628
  let hasEmittedStepStarted = false
629
+ let groundingMetadata: GroundingMetadata | undefined
630
+ let groundingCallEmitted = false
631
+ const adapterName = this.name
632
+
633
+ const emitGroundingToolCall = function* (): Generator<AdapterYieldChunk> {
634
+ if (!groundingMetadata || groundingCallEmitted) return
635
+ const sources = getGroundingSources(groundingMetadata)
636
+ const hasGoogleSearchEvidence =
637
+ sources.length > 0 ||
638
+ (groundingMetadata.webSearchQueries?.length ?? 0) > 0 ||
639
+ groundingMetadata.searchEntryPoint !== undefined
640
+ if (!hasGoogleSearchEvidence) return
641
+
642
+ groundingCallEmitted = true
643
+ const toolCallId = generateId(adapterName)
644
+ const metadata: GeminiToolCallMetadata = {
645
+ providerExecuted: true,
646
+ sources,
647
+ gemini: { groundingMetadata },
648
+ }
649
+ yield {
650
+ type: EventType.TOOL_CALL_START,
651
+ toolCallId,
652
+ toolCallName: 'google_search',
653
+ toolName: 'google_search',
654
+ parentMessageId: messageId,
655
+ model,
656
+ timestamp: Date.now(),
657
+ index: toolCallMap.size,
658
+ metadata,
659
+ }
660
+ yield {
661
+ type: EventType.TOOL_CALL_END,
662
+ toolCallId,
663
+ toolCallName: 'google_search',
664
+ toolName: 'google_search',
665
+ model,
666
+ timestamp: Date.now(),
667
+ input: {
668
+ ...(groundingMetadata.webSearchQueries && {
669
+ queries: groundingMetadata.webSearchQueries,
670
+ }),
671
+ },
672
+ }
673
+ }
607
674
 
608
675
  for await (const chunk of result) {
609
676
  logger.provider(`provider=gemini`, { chunk })
@@ -620,6 +687,10 @@ export class GeminiTextAdapter<
620
687
  }
621
688
  }
622
689
 
690
+ if (chunk.candidates?.[0]?.groundingMetadata) {
691
+ groundingMetadata = chunk.candidates[0].groundingMetadata
692
+ }
693
+
623
694
  if (chunk.candidates?.[0]?.content?.parts) {
624
695
  const parts = chunk.candidates[0].content.parts
625
696
 
@@ -830,6 +901,7 @@ export class GeminiTextAdapter<
830
901
  }
831
902
 
832
903
  if (chunk.candidates?.[0]?.finishReason) {
904
+ yield* emitGroundingToolCall()
833
905
  const finishReason = chunk.candidates[0].finishReason
834
906
 
835
907
  // Emit TOOL_CALL_END for all tracked tool calls. functionCall parts on
@@ -920,6 +992,8 @@ export class GeminiTextAdapter<
920
992
  }
921
993
  }
922
994
  }
995
+
996
+ yield* emitGroundingToolCall()
923
997
  }
924
998
 
925
999
  private convertContentPartToGemini(part: ContentPart): Part {
@@ -1011,6 +1085,11 @@ export class GeminiTextAdapter<
1011
1085
 
1012
1086
  if (msg.role === 'assistant' && msg.toolCalls?.length) {
1013
1087
  for (const toolCall of msg.toolCalls) {
1088
+ const metadata = toolCall.metadata as
1089
+ | GeminiToolCallMetadata
1090
+ | undefined
1091
+ if (metadata?.providerExecuted) continue
1092
+
1014
1093
  let parsedArgs: Record<string, unknown> = {}
1015
1094
  try {
1016
1095
  parsedArgs = toolCall.function.arguments
@@ -1023,9 +1102,7 @@ export class GeminiTextAdapter<
1023
1102
  parsedArgs = {}
1024
1103
  }
1025
1104
 
1026
- const thoughtSignature = (
1027
- toolCall.metadata as GeminiToolCallMetadata | undefined
1028
- )?.thoughtSignature
1105
+ const thoughtSignature = metadata?.thoughtSignature
1029
1106
  // Gemini requires thoughtSignature at the Part level (sibling of
1030
1107
  // functionCall), not nested inside functionCall. Nesting it causes
1031
1108
  // the API to reject the next turn with
@@ -1,3 +1,6 @@
1
+ import type { ProviderExecutedToolMetadata } from '@tanstack/ai'
2
+ import type { GroundingMetadata } from '@google/genai'
3
+
1
4
  /**
2
5
  * Gemini-specific metadata types for multimodal content parts.
3
6
  * These types extend the base ContentPart metadata with Gemini-specific options.
@@ -178,6 +181,9 @@ export interface GeminiMessageMetadataByModality {
178
181
  *
179
182
  * @see https://ai.google.dev/gemini-api/docs/thinking
180
183
  */
181
- export interface GeminiToolCallMetadata {
184
+ export interface GeminiToolCallMetadata extends ProviderExecutedToolMetadata {
182
185
  thoughtSignature?: string
186
+ gemini?: {
187
+ groundingMetadata: GroundingMetadata
188
+ }
183
189
  }