From ecd240f6363e8e605090a80e99197cda595e399f Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Tue, 30 Dec 2025 01:02:29 +0100 Subject: [PATCH] Define own GoogleThinkingLevel type instead of importing from @google/genai - Add GoogleThinkingLevel type mirroring Google's ThinkingLevel enum - Update GoogleGeminiCliOptions and GoogleOptions to use our type - Cast to any when assigning to Google SDK's ThinkingConfig --- .../ai/src/providers/google-gemini-cli.ts | 13 +++++-- packages/ai/src/providers/google.ts | 7 ++-- packages/ai/src/stream.ts | 38 +++++++++++-------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/packages/ai/src/providers/google-gemini-cli.ts b/packages/ai/src/providers/google-gemini-cli.ts index cbeb60a2..85d644dd 100644 --- a/packages/ai/src/providers/google-gemini-cli.ts +++ b/packages/ai/src/providers/google-gemini-cli.ts @@ -4,7 +4,7 @@ * Uses the Cloud Code Assist API endpoint to access Gemini and Claude models. */ -import type { Content, ThinkingConfig, ThinkingLevel } from "@google/genai"; +import type { Content, ThinkingConfig } from "@google/genai"; import { calculateCost } from "../models.js"; import type { Api, @@ -21,6 +21,12 @@ import { AssistantMessageEventStream } from "../utils/event-stream.js"; import { sanitizeSurrogates } from "../utils/sanitize-unicode.js"; import { convertMessages, convertTools, mapStopReasonString, mapToolChoice } from "./google-shared.js"; +/** + * Thinking level for Gemini 3 models. + * Mirrors Google's ThinkingLevel enum values. + */ +export type GoogleThinkingLevel = "THINKING_LEVEL_UNSPECIFIED" | "MINIMAL" | "LOW" | "MEDIUM" | "HIGH"; + export interface GoogleGeminiCliOptions extends StreamOptions { toolChoice?: "auto" | "none" | "any"; /** @@ -35,7 +41,7 @@ export interface GoogleGeminiCliOptions extends StreamOptions { /** Thinking budget in tokens. Use for Gemini 2.x models. */ budgetTokens?: number; /** Thinking level. Use for Gemini 3 models (LOW/HIGH for Pro, MINIMAL/LOW/MEDIUM/HIGH for Flash). */ - level?: ThinkingLevel; + level?: GoogleThinkingLevel; }; projectId?: string; } @@ -436,7 +442,8 @@ function buildRequest( }; // Gemini 3 models use thinkingLevel, older models use thinkingBudget if (options.thinking.level !== undefined) { - generationConfig.thinkingConfig.thinkingLevel = options.thinking.level; + // Cast to any since our GoogleThinkingLevel mirrors Google's ThinkingLevel enum values + generationConfig.thinkingConfig.thinkingLevel = options.thinking.level as any; } else if (options.thinking.budgetTokens !== undefined) { generationConfig.thinkingConfig.thinkingBudget = options.thinking.budgetTokens; } diff --git a/packages/ai/src/providers/google.ts b/packages/ai/src/providers/google.ts index 29370a68..67893eef 100644 --- a/packages/ai/src/providers/google.ts +++ b/packages/ai/src/providers/google.ts @@ -3,7 +3,6 @@ import { type GenerateContentParameters, GoogleGenAI, type ThinkingConfig, - type ThinkingLevel, } from "@google/genai"; import { calculateCost } from "../models.js"; import { getEnvApiKey } from "../stream.js"; @@ -20,6 +19,7 @@ import type { } from "../types.js"; import { AssistantMessageEventStream } from "../utils/event-stream.js"; import { sanitizeSurrogates } from "../utils/sanitize-unicode.js"; +import type { GoogleThinkingLevel } from "./google-gemini-cli.js"; import { convertMessages, convertTools, mapStopReason, mapToolChoice } from "./google-shared.js"; export interface GoogleOptions extends StreamOptions { @@ -27,7 +27,7 @@ export interface GoogleOptions extends StreamOptions { thinking?: { enabled: boolean; budgetTokens?: number; // -1 for dynamic, 0 to disable - level?: ThinkingLevel; + level?: GoogleThinkingLevel; }; } @@ -299,7 +299,8 @@ function buildParams( if (options.thinking?.enabled && model.reasoning) { const thinkingConfig: ThinkingConfig = { includeThoughts: true }; if (options.thinking.level !== undefined) { - thinkingConfig.thinkingLevel = options.thinking.level; + // Cast to any since our GoogleThinkingLevel mirrors Google's ThinkingLevel enum values + thinkingConfig.thinkingLevel = options.thinking.level as any; } else if (options.thinking.budgetTokens !== undefined) { thinkingConfig.thinkingBudget = options.thinking.budgetTokens; } diff --git a/packages/ai/src/stream.ts b/packages/ai/src/stream.ts index fb8f5ca3..f68d5f60 100644 --- a/packages/ai/src/stream.ts +++ b/packages/ai/src/stream.ts @@ -1,8 +1,11 @@ -import { ThinkingLevel } from "@google/genai"; import { supportsXhigh } from "./models.js"; import { type AnthropicOptions, streamAnthropic } from "./providers/anthropic.js"; import { type GoogleOptions, streamGoogle } from "./providers/google.js"; -import { type GoogleGeminiCliOptions, streamGoogleGeminiCli } from "./providers/google-gemini-cli.js"; +import { + type GoogleGeminiCliOptions, + type GoogleThinkingLevel, + streamGoogleGeminiCli, +} from "./providers/google-gemini-cli.js"; import { type OpenAICompletionsOptions, streamOpenAICompletions } from "./providers/openai-completions.js"; import { type OpenAIResponsesOptions, streamOpenAIResponses } from "./providers/openai-responses.js"; import type { @@ -256,53 +259,56 @@ function isGemini3FlashModel(model: Model<"google-generative-ai">): boolean { return model.id.includes("3-flash"); } -function getGemini3ThinkingLevel(effort: ClampedReasoningEffort, model: Model<"google-generative-ai">): ThinkingLevel { +function getGemini3ThinkingLevel( + effort: ClampedReasoningEffort, + model: Model<"google-generative-ai">, +): GoogleThinkingLevel { if (isGemini3ProModel(model)) { // Gemini 3 Pro only supports LOW/HIGH (for now) switch (effort) { case "minimal": case "low": - return ThinkingLevel.LOW; + return "LOW"; case "medium": case "high": - return ThinkingLevel.HIGH; + return "HIGH"; } } // Gemini 3 Flash supports all four levels switch (effort) { case "minimal": - return ThinkingLevel.MINIMAL; + return "MINIMAL"; case "low": - return ThinkingLevel.LOW; + return "LOW"; case "medium": - return ThinkingLevel.MEDIUM; + return "MEDIUM"; case "high": - return ThinkingLevel.HIGH; + return "HIGH"; } } -function getGeminiCliThinkingLevel(effort: ClampedReasoningEffort, modelId: string): ThinkingLevel { +function getGeminiCliThinkingLevel(effort: ClampedReasoningEffort, modelId: string): GoogleThinkingLevel { if (modelId.includes("3-pro")) { // Gemini 3 Pro only supports LOW/HIGH (for now) switch (effort) { case "minimal": case "low": - return ThinkingLevel.LOW; + return "LOW"; case "medium": case "high": - return ThinkingLevel.HIGH; + return "HIGH"; } } // Gemini 3 Flash supports all four levels switch (effort) { case "minimal": - return ThinkingLevel.MINIMAL; + return "MINIMAL"; case "low": - return ThinkingLevel.LOW; + return "LOW"; case "medium": - return ThinkingLevel.MEDIUM; + return "MEDIUM"; case "high": - return ThinkingLevel.HIGH; + return "HIGH"; } }