@tanstack/ai-gemini 0.10.6 → 0.10.7

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.
@@ -1,5 +1,5 @@
1
1
  import { FinishReason } from "@google/genai";
2
- import { EventType } from "@tanstack/ai";
2
+ import { EventType, normalizeSystemPrompts } from "@tanstack/ai";
3
3
  import { BaseTextAdapter } from "@tanstack/ai/adapters";
4
4
  import { convertToolsToProviderFormat } from "../tools/tool-converter.js";
5
5
  import { createGeminiClient, generateId, getGeminiApiKeyFromEnv } from "../utils/client.js";
@@ -562,7 +562,10 @@ class GeminiTextAdapter extends BaseTextAdapter {
562
562
  thinkingConfig.thinkingLevel
563
563
  ) : void 0
564
564
  } : void 0,
565
- systemInstruction: options.systemPrompts?.join("\n"),
565
+ systemInstruction: (() => {
566
+ const prompts = normalizeSystemPrompts(options.systemPrompts);
567
+ return prompts.length > 0 ? prompts.map((p) => p.content).join("\n") : void 0;
568
+ })(),
566
569
  tools: convertToolsToProviderFormat(options.tools)
567
570
  }
568
571
  };
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { FinishReason } from '@google/genai'\nimport { EventType } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { convertToolsToProviderFormat } from '../tools/tool-converter'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport type {\n GEMINI_MODELS,\n GeminiChatModelProviderOptionsByName,\n GeminiChatModelToolCapabilitiesByName,\n GeminiModelInputModalitiesByName,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type {\n Content,\n GenerateContentParameters,\n GenerateContentResponse,\n GoogleGenAI,\n Part,\n ThinkingLevel,\n} from '@google/genai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type { ExternalTextProviderOptions } from '../text/text-provider-options'\nimport type {\n GeminiMessageMetadataByModality,\n GeminiToolCallMetadata,\n} from '../message-types'\nimport type { GeminiClientConfig } from '../utils'\n\n/**\n * Configuration for Gemini text adapter\n */\nexport interface GeminiTextConfig extends GeminiClientConfig {}\n\n/**\n * Gemini-specific provider options for text/chat\n */\nexport type GeminiTextProviderOptions = ExternalTextProviderOptions\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model.\n * If the model has explicit options in the map, use those; otherwise use base options.\n */\ntype ResolveProviderOptions<TModel extends string> =\n TModel extends keyof GeminiChatModelProviderOptionsByName\n ? GeminiChatModelProviderOptionsByName[TModel]\n : GeminiTextProviderOptions\n\n/**\n * Resolve input modalities for a specific model.\n * If the model has explicit modalities in the map, use those; otherwise use all modalities.\n */\ntype ResolveInputModalities<TModel extends string> =\n TModel extends keyof GeminiModelInputModalitiesByName\n ? GeminiModelInputModalitiesByName[TModel]\n : readonly ['text', 'image', 'audio', 'video', 'document']\n\n/**\n * Resolve tool capabilities for a specific model.\n * If the model has explicit tools in the map, use those; otherwise use empty tuple.\n */\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GeminiChatModelToolCapabilitiesByName\n ? NonNullable<GeminiChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n// ===========================\n// Adapter Implementation\n// ===========================\n\n/**\n * Gemini Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Gemini chat/text completion functionality.\n * Import only what you need for smaller bundle sizes.\n */\nexport class GeminiTextAdapter<\n TModel extends (typeof GEMINI_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GeminiMessageMetadataByModality,\n TToolCapabilities,\n GeminiToolCallMetadata\n> {\n readonly kind = 'text' as const\n readonly name = 'gemini' as const\n\n private client: GoogleGenAI\n\n constructor(config: GeminiTextConfig, model: TModel) {\n super({}, model)\n this.client = createGeminiClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GeminiTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const mappedOptions = this.mapCommonOptionsToGemini(options)\n const { logger } = options\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: 'gemini', model: this.model },\n )\n const result =\n await this.client.models.generateContentStream(mappedOptions)\n\n yield* this.processStreamChunks(result, options, logger)\n } catch (error) {\n 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 error: {\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\n },\n }\n }\n }\n\n /**\n * Generate structured output using Gemini's native JSON response format.\n * Uses responseMimeType: 'application/json' and responseSchema for structured output.\n * The outputSchema is already JSON Schema (converted in the ai layer).\n */\n async structuredOutput(\n options: StructuredOutputOptions<GeminiTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const { logger } = chatOptions\n\n const mappedOptions = this.mapCommonOptionsToGemini(chatOptions)\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,\n { provider: 'gemini', model: this.model },\n )\n // Add structured output configuration\n const result = await this.client.models.generateContent({\n ...mappedOptions,\n config: {\n ...mappedOptions.config,\n responseMimeType: 'application/json',\n responseSchema: outputSchema,\n },\n })\n\n // Extract text content from the response\n const rawText = this.extractTextFromResponse(result)\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n return {\n data: parsed,\n rawText,\n }\n } catch (error) {\n logger.errors('gemini.structuredOutput fatal', {\n error,\n source: 'gemini.structuredOutput',\n })\n throw new Error(\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during structured output generation.',\n )\n }\n }\n\n /**\n * Extract text content from a non-streaming response\n */\n private extractTextFromResponse(response: GenerateContentResponse): string {\n let textContent = ''\n\n if (response.candidates?.[0]?.content?.parts) {\n for (const part of response.candidates[0].content.parts) {\n if (part.text) {\n textContent += part.text\n }\n }\n }\n\n return textContent\n }\n\n private async *processStreamChunks(\n result: AsyncGenerator<GenerateContentResponse, unknown, unknown>,\n options: TextOptions<GeminiTextProviderOptions>,\n logger: InternalLogger,\n ): AsyncIterable<StreamChunk> {\n const model = options.model\n let accumulatedContent = ''\n let accumulatedThinking = ''\n const toolCallMap = new Map<\n string,\n {\n name: string\n args: string\n index: number\n started: boolean\n thoughtSignature?: string\n }\n >()\n let nextToolIndex = 0\n\n // AG-UI lifecycle tracking\n const runId = options.runId ?? generateId(this.name)\n const threadId = options.threadId ?? generateId(this.name)\n const messageId = generateId(this.name)\n let stepId: string | null = null\n let reasoningMessageId: string | null = null\n let hasClosedReasoning = false\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let hasEmittedStepStarted = false\n\n for await (const chunk of result) {\n logger.provider(`provider=gemini`, { chunk })\n // Emit RUN_STARTED on first chunk\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n }\n\n if (chunk.candidates?.[0]?.content?.parts) {\n const parts = chunk.candidates[0].content.parts\n\n for (const part of parts) {\n if (part.text) {\n if (part.thought) {\n // Emit STEP_STARTED and REASONING events on first thinking content\n if (!hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n reasoningMessageId = generateId(this.name)\n\n // Spec REASONING events\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model,\n timestamp: Date.now(),\n }\n\n // Legacy STEP events (kept during transition)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n accumulatedThinking += part.text\n\n // Spec REASONING content event\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 thoughtSignature: partThoughtSignature,\n }\n toolCallMap.set(toolCallId, toolCallData)\n } else {\n if (!toolCallData.thoughtSignature && partThoughtSignature) {\n toolCallData.thoughtSignature = partThoughtSignature\n }\n try {\n const existingArgs = JSON.parse(toolCallData.args)\n const newArgs =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n const mergedArgs = { ...existingArgs, ...newArgs }\n toolCallData.args = JSON.stringify(mergedArgs)\n } catch {\n toolCallData.args =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n }\n }\n\n // Emit TOOL_CALL_START if not already started\n if (!toolCallData.started) {\n toolCallData.started = true\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n index: toolCallData.index,\n ...(toolCallData.thoughtSignature && {\n metadata: {\n thoughtSignature: toolCallData.thoughtSignature,\n } satisfies GeminiToolCallMetadata,\n }),\n }\n }\n\n // Emit TOOL_CALL_ARGS\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n model,\n timestamp: Date.now(),\n delta: toolCallData.args,\n args: toolCallData.args,\n }\n }\n }\n } else if (chunk.data && chunk.data.trim()) {\n // Skip whitespace-only data (e.g. \"\\n\" during auto-continuation)\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += chunk.data\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: chunk.data,\n content: accumulatedContent,\n }\n }\n\n if (chunk.candidates?.[0]?.finishReason) {\n const finishReason = chunk.candidates[0].finishReason\n\n if (finishReason === FinishReason.UNEXPECTED_TOOL_CALL) {\n if (chunk.candidates[0].content?.parts) {\n for (const part of chunk.candidates[0].content.parts) {\n const functionCall = part.functionCall\n if (functionCall) {\n const toolCallId =\n functionCall.id ||\n `${functionCall.name}_${Date.now()}_${nextToolIndex}`\n const functionArgs = functionCall.args || {}\n\n const argsString =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n\n toolCallMap.set(toolCallId, {\n name: functionCall.name || '',\n args: argsString,\n index: nextToolIndex++,\n started: true,\n })\n\n // Emit TOOL_CALL_START\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n index: nextToolIndex - 1,\n }\n\n // Emit TOOL_CALL_END with parsed input\n let parsedInput: unknown = {}\n try {\n const parsed =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n }\n }\n }\n\n // Emit TOOL_CALL_END for all tracked tool calls\n for (const [toolCallId, toolCallData] of toolCallMap.entries()) {\n let parsedInput: unknown = {}\n try {\n const parsed = JSON.parse(toolCallData.args)\n parsedInput = parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n\n // Reset so a new TEXT_MESSAGE_START is emitted if text follows tool calls\n if (toolCallMap.size > 0) {\n hasEmittedTextMessageStart = false\n }\n\n if (finishReason === FinishReason.MAX_TOKENS) {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model,\n timestamp: Date.now(),\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n error: {\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n },\n }\n }\n\n // Close reasoning events if still open\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n finishReason: toolCallMap.size > 0 ? 'tool_calls' : 'stop',\n usage: chunk.usageMetadata\n ? {\n promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,\n completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,\n totalTokens: chunk.usageMetadata.totalTokenCount ?? 0,\n }\n : undefined,\n }\n }\n }\n }\n\n private convertContentPartToGemini(part: ContentPart): Part {\n switch (part.type) {\n case 'text':\n return { text: part.content }\n case 'image':\n case 'audio':\n case 'video':\n case 'document': {\n if (part.source.type === 'data') {\n return {\n inlineData: {\n data: part.source.value,\n mimeType: part.source.mimeType,\n },\n }\n } else {\n // For URL sources, use provided mimeType or fall back to reasonable defaults\n const defaultMimeType = {\n image: 'image/jpeg',\n audio: 'audio/mp3',\n video: 'video/mp4',\n document: 'application/pdf',\n }[part.type]\n\n return {\n fileData: {\n fileUri: part.source.value,\n mimeType: part.source.mimeType ?? defaultMimeType,\n },\n }\n }\n }\n default: {\n const _exhaustiveCheck: never = part\n throw new Error(\n `Unsupported content part type: ${(_exhaustiveCheck as ContentPart).type}`,\n )\n }\n }\n }\n\n private formatMessages(\n messages: Array<ModelMessage>,\n ): GenerateContentParameters['contents'] {\n // Build a lookup from toolCallId → function name so functionResponse uses the\n // correct name instead of the raw call ID.\n const toolCallIdToName = new Map<string, string>()\n for (const msg of messages) {\n if (msg.role === 'assistant' && msg.toolCalls) {\n for (const tc of msg.toolCalls) {\n toolCallIdToName.set(tc.id, tc.function.name)\n }\n }\n }\n\n const formatted = messages.map((msg) => {\n const role: 'user' | 'model' = msg.role === 'assistant' ? 'model' : 'user'\n const parts: Array<Part> = []\n\n if (Array.isArray(msg.content)) {\n for (const contentPart of msg.content) {\n parts.push(this.convertContentPartToGemini(contentPart))\n }\n } else if (msg.content && msg.role !== 'tool') {\n parts.push({ text: msg.content })\n }\n\n if (msg.role === 'assistant' && msg.toolCalls?.length) {\n for (const toolCall of msg.toolCalls) {\n let parsedArgs: Record<string, unknown> = {}\n try {\n parsedArgs = toolCall.function.arguments\n ? (JSON.parse(toolCall.function.arguments) as Record<\n string,\n unknown\n >)\n : {}\n } catch {\n parsedArgs = toolCall.function.arguments as unknown as Record<\n string,\n unknown\n >\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 parts.push({\n functionResponse: {\n id: msg.toolCallId,\n name: functionName,\n response: {\n content: msg.content || '',\n },\n } as any,\n })\n }\n\n return {\n role,\n parts: parts.length > 0 ? parts : [{ text: '' }],\n }\n })\n\n // Post-process: Gemini requires strictly alternating user/model roles.\n // Tool results are mapped to role:'user', which can create consecutive\n // user messages when followed by a new user message. Merge them.\n return this.mergeConsecutiveSameRoleMessages(formatted)\n }\n\n /**\n * Merge consecutive messages of the same role into a single message.\n * Gemini's API requires strictly alternating user/model roles.\n * Tool results are mapped to role:'user', which can collide with actual\n * user messages in multi-turn conversations.\n *\n * Also filters out empty model messages (e.g., from a previous failed request)\n * and deduplicates functionResponse parts with the same name (tool call ID).\n */\n private mergeConsecutiveSameRoleMessages(\n messages: Array<Content>,\n ): Array<Content> {\n const merged: Array<Content> = []\n\n for (const msg of messages) {\n const parts = msg.parts || []\n\n // Skip empty model messages (no parts or only empty text)\n if (msg.role === 'model') {\n const hasContent =\n parts.length > 0 &&\n !parts.every(\n (p) => 'text' in p && (p as { text: string }).text === '',\n )\n if (!hasContent) {\n continue\n }\n }\n\n const prev = merged[merged.length - 1]\n if (prev && prev.role === msg.role) {\n // Merge parts arrays\n prev.parts = [...(prev.parts || []), ...parts]\n } else {\n merged.push({ ...msg, parts: [...parts] })\n }\n }\n\n // Deduplicate functionResponse parts with the same name (tool call ID)\n for (const msg of merged) {\n if (!msg.parts) continue\n const seenFunctionResponseNames = new Set<string>()\n msg.parts = msg.parts.filter((part) => {\n if ('functionResponse' in part && part.functionResponse?.name) {\n if (seenFunctionResponseNames.has(part.functionResponse.name)) {\n return false\n }\n seenFunctionResponseNames.add(part.functionResponse.name)\n }\n return true\n })\n }\n\n return merged\n }\n\n private mapCommonOptionsToGemini(\n options: TextOptions<GeminiTextProviderOptions>,\n ) {\n const modelOpts = options.modelOptions\n const thinkingConfig = modelOpts?.thinkingConfig\n const requestOptions: GenerateContentParameters = {\n model: options.model,\n contents: this.formatMessages(options.messages),\n config: {\n ...modelOpts,\n temperature: options.temperature,\n topP: options.topP,\n maxOutputTokens: options.maxTokens,\n thinkingConfig: thinkingConfig\n ? {\n ...thinkingConfig,\n thinkingLevel: thinkingConfig.thinkingLevel\n ? // Enum is provided by the SDK, we use it for the type but cast it to string constants, here we just cast them back\n (thinkingConfig.thinkingLevel as ThinkingLevel)\n : undefined,\n }\n : undefined,\n systemInstruction: options.systemPrompts?.join('\\n'),\n tools: convertToolsToProviderFormat(options.tools),\n },\n }\n\n return requestOptions\n }\n}\n\n/**\n * Creates a Gemini text adapter with explicit API key.\n * Type resolution happens here at the call site.\n */\nexport function createGeminiChat<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n return new GeminiTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Gemini text adapter with automatic API key detection.\n * Type resolution happens here at the call site.\n */\nexport function geminiText<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiChat(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { FinishReason } from '@google/genai'\nimport { EventType, normalizeSystemPrompts } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { convertToolsToProviderFormat } from '../tools/tool-converter'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport type {\n GEMINI_MODELS,\n GeminiChatModelProviderOptionsByName,\n GeminiChatModelToolCapabilitiesByName,\n GeminiModelInputModalitiesByName,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type {\n Content,\n GenerateContentParameters,\n GenerateContentResponse,\n GoogleGenAI,\n Part,\n ThinkingLevel,\n} from '@google/genai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type { ExternalTextProviderOptions } from '../text/text-provider-options'\nimport type {\n GeminiMessageMetadataByModality,\n GeminiToolCallMetadata,\n} from '../message-types'\nimport type { GeminiClientConfig } from '../utils'\n\n/**\n * Configuration for Gemini text adapter\n */\nexport interface GeminiTextConfig extends GeminiClientConfig {}\n\n/**\n * Gemini-specific provider options for text/chat\n */\nexport type GeminiTextProviderOptions = ExternalTextProviderOptions\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model.\n * If the model has explicit options in the map, use those; otherwise use base options.\n */\ntype ResolveProviderOptions<TModel extends string> =\n TModel extends keyof GeminiChatModelProviderOptionsByName\n ? GeminiChatModelProviderOptionsByName[TModel]\n : GeminiTextProviderOptions\n\n/**\n * Resolve input modalities for a specific model.\n * If the model has explicit modalities in the map, use those; otherwise use all modalities.\n */\ntype ResolveInputModalities<TModel extends string> =\n TModel extends keyof GeminiModelInputModalitiesByName\n ? GeminiModelInputModalitiesByName[TModel]\n : readonly ['text', 'image', 'audio', 'video', 'document']\n\n/**\n * Resolve tool capabilities for a specific model.\n * If the model has explicit tools in the map, use those; otherwise use empty tuple.\n */\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GeminiChatModelToolCapabilitiesByName\n ? NonNullable<GeminiChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n// ===========================\n// Adapter Implementation\n// ===========================\n\n/**\n * Gemini Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Gemini chat/text completion functionality.\n * Import only what you need for smaller bundle sizes.\n */\nexport class GeminiTextAdapter<\n TModel extends (typeof GEMINI_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GeminiMessageMetadataByModality,\n TToolCapabilities,\n GeminiToolCallMetadata\n> {\n readonly kind = 'text' as const\n readonly name = 'gemini' as const\n\n private client: GoogleGenAI\n\n constructor(config: GeminiTextConfig, model: TModel) {\n super({}, model)\n this.client = createGeminiClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GeminiTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const mappedOptions = this.mapCommonOptionsToGemini(options)\n const { logger } = options\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: 'gemini', model: this.model },\n )\n const result =\n await this.client.models.generateContentStream(mappedOptions)\n\n yield* this.processStreamChunks(result, options, logger)\n } catch (error) {\n 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 error: {\n message:\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during the chat stream.',\n },\n }\n }\n }\n\n /**\n * Generate structured output using Gemini's native JSON response format.\n * Uses responseMimeType: 'application/json' and responseSchema for structured output.\n * The outputSchema is already JSON Schema (converted in the ai layer).\n */\n async structuredOutput(\n options: StructuredOutputOptions<GeminiTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const { logger } = chatOptions\n\n const mappedOptions = this.mapCommonOptionsToGemini(chatOptions)\n\n try {\n logger.request(\n `activity=chat provider=gemini model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,\n { provider: 'gemini', model: this.model },\n )\n // Add structured output configuration\n const result = await this.client.models.generateContent({\n ...mappedOptions,\n config: {\n ...mappedOptions.config,\n responseMimeType: 'application/json',\n responseSchema: outputSchema,\n },\n })\n\n // Extract text content from the response\n const rawText = this.extractTextFromResponse(result)\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n return {\n data: parsed,\n rawText,\n }\n } catch (error) {\n logger.errors('gemini.structuredOutput fatal', {\n error,\n source: 'gemini.structuredOutput',\n })\n throw new Error(\n error instanceof Error\n ? error.message\n : 'An unknown error occurred during structured output generation.',\n )\n }\n }\n\n /**\n * Extract text content from a non-streaming response\n */\n private extractTextFromResponse(response: GenerateContentResponse): string {\n let textContent = ''\n\n if (response.candidates?.[0]?.content?.parts) {\n for (const part of response.candidates[0].content.parts) {\n if (part.text) {\n textContent += part.text\n }\n }\n }\n\n return textContent\n }\n\n private async *processStreamChunks(\n result: AsyncGenerator<GenerateContentResponse, unknown, unknown>,\n options: TextOptions<GeminiTextProviderOptions>,\n logger: InternalLogger,\n ): AsyncIterable<StreamChunk> {\n const model = options.model\n let accumulatedContent = ''\n let accumulatedThinking = ''\n const toolCallMap = new Map<\n string,\n {\n name: string\n args: string\n index: number\n started: boolean\n thoughtSignature?: string\n }\n >()\n let nextToolIndex = 0\n\n // AG-UI lifecycle tracking\n const runId = options.runId ?? generateId(this.name)\n const threadId = options.threadId ?? generateId(this.name)\n const messageId = generateId(this.name)\n let stepId: string | null = null\n let reasoningMessageId: string | null = null\n let hasClosedReasoning = false\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n let hasEmittedStepStarted = false\n\n for await (const chunk of result) {\n logger.provider(`provider=gemini`, { chunk })\n // Emit RUN_STARTED on first chunk\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n }\n\n if (chunk.candidates?.[0]?.content?.parts) {\n const parts = chunk.candidates[0].content.parts\n\n for (const part of parts) {\n if (part.text) {\n if (part.thought) {\n // Emit STEP_STARTED and REASONING events on first thinking content\n if (!hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n reasoningMessageId = generateId(this.name)\n\n // Spec REASONING events\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model,\n timestamp: Date.now(),\n }\n\n // Legacy STEP events (kept during transition)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n accumulatedThinking += part.text\n\n // Spec REASONING content event\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 thoughtSignature: partThoughtSignature,\n }\n toolCallMap.set(toolCallId, toolCallData)\n } else {\n if (!toolCallData.thoughtSignature && partThoughtSignature) {\n toolCallData.thoughtSignature = partThoughtSignature\n }\n try {\n const existingArgs = JSON.parse(toolCallData.args)\n const newArgs =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n const mergedArgs = { ...existingArgs, ...newArgs }\n toolCallData.args = JSON.stringify(mergedArgs)\n } catch {\n toolCallData.args =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n }\n }\n\n // Emit TOOL_CALL_START if not already started\n if (!toolCallData.started) {\n toolCallData.started = true\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n index: toolCallData.index,\n ...(toolCallData.thoughtSignature && {\n metadata: {\n thoughtSignature: toolCallData.thoughtSignature,\n } satisfies GeminiToolCallMetadata,\n }),\n }\n }\n\n // Emit TOOL_CALL_ARGS\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId,\n model,\n timestamp: Date.now(),\n delta: toolCallData.args,\n args: toolCallData.args,\n }\n }\n }\n } else if (chunk.data && chunk.data.trim()) {\n // Skip whitespace-only data (e.g. \"\\n\" during auto-continuation)\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += chunk.data\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n model,\n timestamp: Date.now(),\n delta: chunk.data,\n content: accumulatedContent,\n }\n }\n\n if (chunk.candidates?.[0]?.finishReason) {\n const finishReason = chunk.candidates[0].finishReason\n\n if (finishReason === FinishReason.UNEXPECTED_TOOL_CALL) {\n if (chunk.candidates[0].content?.parts) {\n for (const part of chunk.candidates[0].content.parts) {\n const functionCall = part.functionCall\n if (functionCall) {\n const toolCallId =\n functionCall.id ||\n `${functionCall.name}_${Date.now()}_${nextToolIndex}`\n const functionArgs = functionCall.args || {}\n\n const argsString =\n typeof functionArgs === 'string'\n ? functionArgs\n : JSON.stringify(functionArgs)\n\n toolCallMap.set(toolCallId, {\n name: functionCall.name || '',\n args: argsString,\n index: nextToolIndex++,\n started: true,\n })\n\n // Emit TOOL_CALL_START\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n index: nextToolIndex - 1,\n }\n\n // Emit TOOL_CALL_END with parsed input\n let parsedInput: unknown = {}\n try {\n const parsed =\n typeof functionArgs === 'string'\n ? JSON.parse(functionArgs)\n : functionArgs\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: functionCall.name || '',\n toolName: functionCall.name || '',\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n }\n }\n }\n\n // Emit TOOL_CALL_END for all tracked tool calls\n for (const [toolCallId, toolCallData] of toolCallMap.entries()) {\n let parsedInput: unknown = {}\n try {\n const parsed = JSON.parse(toolCallData.args)\n parsedInput = parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n toolCallName: toolCallData.name,\n toolName: toolCallData.name,\n model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n\n // Reset so a new TEXT_MESSAGE_START is emitted if text follows tool calls\n if (toolCallMap.size > 0) {\n hasEmittedTextMessageStart = false\n }\n\n if (finishReason === FinishReason.MAX_TOKENS) {\n yield {\n type: EventType.RUN_ERROR,\n runId,\n model,\n timestamp: Date.now(),\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n error: {\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n },\n }\n }\n\n // Close reasoning events if still open\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n model,\n timestamp: Date.now(),\n }\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: Date.now(),\n finishReason: toolCallMap.size > 0 ? 'tool_calls' : 'stop',\n usage: chunk.usageMetadata\n ? {\n promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,\n completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,\n totalTokens: chunk.usageMetadata.totalTokenCount ?? 0,\n }\n : undefined,\n }\n }\n }\n }\n\n private convertContentPartToGemini(part: ContentPart): Part {\n switch (part.type) {\n case 'text':\n return { text: part.content }\n case 'image':\n case 'audio':\n case 'video':\n case 'document': {\n if (part.source.type === 'data') {\n return {\n inlineData: {\n data: part.source.value,\n mimeType: part.source.mimeType,\n },\n }\n } else {\n // For URL sources, use provided mimeType or fall back to reasonable defaults\n const defaultMimeType = {\n image: 'image/jpeg',\n audio: 'audio/mp3',\n video: 'video/mp4',\n document: 'application/pdf',\n }[part.type]\n\n return {\n fileData: {\n fileUri: part.source.value,\n mimeType: part.source.mimeType ?? defaultMimeType,\n },\n }\n }\n }\n default: {\n const _exhaustiveCheck: never = part\n throw new Error(\n `Unsupported content part type: ${(_exhaustiveCheck as ContentPart).type}`,\n )\n }\n }\n }\n\n private formatMessages(\n messages: Array<ModelMessage>,\n ): GenerateContentParameters['contents'] {\n // Build a lookup from toolCallId → function name so functionResponse uses the\n // correct name instead of the raw call ID.\n const toolCallIdToName = new Map<string, string>()\n for (const msg of messages) {\n if (msg.role === 'assistant' && msg.toolCalls) {\n for (const tc of msg.toolCalls) {\n toolCallIdToName.set(tc.id, tc.function.name)\n }\n }\n }\n\n const formatted = messages.map((msg) => {\n const role: 'user' | 'model' = msg.role === 'assistant' ? 'model' : 'user'\n const parts: Array<Part> = []\n\n if (Array.isArray(msg.content)) {\n for (const contentPart of msg.content) {\n parts.push(this.convertContentPartToGemini(contentPart))\n }\n } else if (msg.content && msg.role !== 'tool') {\n parts.push({ text: msg.content })\n }\n\n if (msg.role === 'assistant' && msg.toolCalls?.length) {\n for (const toolCall of msg.toolCalls) {\n let parsedArgs: Record<string, unknown> = {}\n try {\n parsedArgs = toolCall.function.arguments\n ? (JSON.parse(toolCall.function.arguments) as Record<\n string,\n unknown\n >)\n : {}\n } catch {\n parsedArgs = toolCall.function.arguments as unknown as Record<\n string,\n unknown\n >\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 parts.push({\n functionResponse: {\n id: msg.toolCallId,\n name: functionName,\n response: {\n content: msg.content || '',\n },\n } as any,\n })\n }\n\n return {\n role,\n parts: parts.length > 0 ? parts : [{ text: '' }],\n }\n })\n\n // Post-process: Gemini requires strictly alternating user/model roles.\n // Tool results are mapped to role:'user', which can create consecutive\n // user messages when followed by a new user message. Merge them.\n return this.mergeConsecutiveSameRoleMessages(formatted)\n }\n\n /**\n * Merge consecutive messages of the same role into a single message.\n * Gemini's API requires strictly alternating user/model roles.\n * Tool results are mapped to role:'user', which can collide with actual\n * user messages in multi-turn conversations.\n *\n * Also filters out empty model messages (e.g., from a previous failed request)\n * and deduplicates functionResponse parts with the same name (tool call ID).\n */\n private mergeConsecutiveSameRoleMessages(\n messages: Array<Content>,\n ): Array<Content> {\n const merged: Array<Content> = []\n\n for (const msg of messages) {\n const parts = msg.parts || []\n\n // Skip empty model messages (no parts or only empty text)\n if (msg.role === 'model') {\n const hasContent =\n parts.length > 0 &&\n !parts.every(\n (p) => 'text' in p && (p as { text: string }).text === '',\n )\n if (!hasContent) {\n continue\n }\n }\n\n const prev = merged[merged.length - 1]\n if (prev && prev.role === msg.role) {\n // Merge parts arrays\n prev.parts = [...(prev.parts || []), ...parts]\n } else {\n merged.push({ ...msg, parts: [...parts] })\n }\n }\n\n // Deduplicate functionResponse parts with the same name (tool call ID)\n for (const msg of merged) {\n if (!msg.parts) continue\n const seenFunctionResponseNames = new Set<string>()\n msg.parts = msg.parts.filter((part) => {\n if ('functionResponse' in part && part.functionResponse?.name) {\n if (seenFunctionResponseNames.has(part.functionResponse.name)) {\n return false\n }\n seenFunctionResponseNames.add(part.functionResponse.name)\n }\n return true\n })\n }\n\n return merged\n }\n\n private mapCommonOptionsToGemini(\n options: TextOptions<GeminiTextProviderOptions>,\n ) {\n const modelOpts = options.modelOptions\n const thinkingConfig = modelOpts?.thinkingConfig\n const requestOptions: GenerateContentParameters = {\n model: options.model,\n contents: this.formatMessages(options.messages),\n config: {\n ...modelOpts,\n temperature: options.temperature,\n topP: options.topP,\n maxOutputTokens: options.maxTokens,\n thinkingConfig: thinkingConfig\n ? {\n ...thinkingConfig,\n thinkingLevel: thinkingConfig.thinkingLevel\n ? // Enum is provided by the SDK, we use it for the type but cast it to string constants, here we just cast them back\n (thinkingConfig.thinkingLevel as ThinkingLevel)\n : undefined,\n }\n : undefined,\n systemInstruction: (() => {\n const prompts = normalizeSystemPrompts(options.systemPrompts)\n return prompts.length > 0\n ? prompts.map((p) => p.content).join('\\n')\n : undefined\n })(),\n tools: convertToolsToProviderFormat(options.tools),\n },\n }\n\n return requestOptions\n }\n}\n\n/**\n * Creates a Gemini text adapter with explicit API key.\n * Type resolution happens here at the call site.\n */\nexport function createGeminiChat<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n return new GeminiTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Gemini text adapter with automatic API key detection.\n * Type resolution happens here at the call site.\n */\nexport function geminiText<TModel extends (typeof GEMINI_MODELS)[number]>(\n model: TModel,\n config?: Omit<GeminiTextConfig, 'apiKey'>,\n): GeminiTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n ResolveToolCapabilities<TModel>\n> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiChat(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-gemini",
3
- "version": "0.10.6",
3
+ "version": "0.10.7",
4
4
  "description": "Google Gemini adapter for TanStack AI",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -38,13 +38,13 @@
38
38
  "@tanstack/ai-utils": "0.2.0"
39
39
  },
40
40
  "peerDependencies": {
41
- "@tanstack/ai": "^0.19.0"
41
+ "@tanstack/ai": "^0.20.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@vitest/coverage-v8": "4.0.14",
45
45
  "vite": "^7.3.3",
46
46
  "zod": "^4.2.0",
47
- "@tanstack/ai": "0.19.0"
47
+ "@tanstack/ai": "0.20.0"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "vite build",
@@ -1,5 +1,5 @@
1
1
  import { FinishReason } from '@google/genai'
2
- import { EventType } from '@tanstack/ai'
2
+ import { EventType, normalizeSystemPrompts } from '@tanstack/ai'
3
3
  import { BaseTextAdapter } from '@tanstack/ai/adapters'
4
4
  import { convertToolsToProviderFormat } from '../tools/tool-converter'
5
5
  import {
@@ -838,7 +838,12 @@ export class GeminiTextAdapter<
838
838
  : undefined,
839
839
  }
840
840
  : undefined,
841
- systemInstruction: options.systemPrompts?.join('\n'),
841
+ systemInstruction: (() => {
842
+ const prompts = normalizeSystemPrompts(options.systemPrompts)
843
+ return prompts.length > 0
844
+ ? prompts.map((p) => p.content).join('\n')
845
+ : undefined
846
+ })(),
842
847
  tools: convertToolsToProviderFormat(options.tools),
843
848
  },
844
849
  }