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
This commit is contained in:
Mario Zechner 2025-12-30 01:02:29 +01:00
parent 8ed6f6dd85
commit ecd240f636
3 changed files with 36 additions and 22 deletions

View file

@ -4,7 +4,7 @@
* Uses the Cloud Code Assist API endpoint to access Gemini and Claude models. * 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 { calculateCost } from "../models.js";
import type { import type {
Api, Api,
@ -21,6 +21,12 @@ import { AssistantMessageEventStream } from "../utils/event-stream.js";
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js"; import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
import { convertMessages, convertTools, mapStopReasonString, mapToolChoice } from "./google-shared.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 { export interface GoogleGeminiCliOptions extends StreamOptions {
toolChoice?: "auto" | "none" | "any"; toolChoice?: "auto" | "none" | "any";
/** /**
@ -35,7 +41,7 @@ export interface GoogleGeminiCliOptions extends StreamOptions {
/** Thinking budget in tokens. Use for Gemini 2.x models. */ /** Thinking budget in tokens. Use for Gemini 2.x models. */
budgetTokens?: number; budgetTokens?: number;
/** Thinking level. Use for Gemini 3 models (LOW/HIGH for Pro, MINIMAL/LOW/MEDIUM/HIGH for Flash). */ /** Thinking level. Use for Gemini 3 models (LOW/HIGH for Pro, MINIMAL/LOW/MEDIUM/HIGH for Flash). */
level?: ThinkingLevel; level?: GoogleThinkingLevel;
}; };
projectId?: string; projectId?: string;
} }
@ -436,7 +442,8 @@ function buildRequest(
}; };
// Gemini 3 models use thinkingLevel, older models use thinkingBudget // Gemini 3 models use thinkingLevel, older models use thinkingBudget
if (options.thinking.level !== undefined) { 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) { } else if (options.thinking.budgetTokens !== undefined) {
generationConfig.thinkingConfig.thinkingBudget = options.thinking.budgetTokens; generationConfig.thinkingConfig.thinkingBudget = options.thinking.budgetTokens;
} }

View file

@ -3,7 +3,6 @@ import {
type GenerateContentParameters, type GenerateContentParameters,
GoogleGenAI, GoogleGenAI,
type ThinkingConfig, type ThinkingConfig,
type ThinkingLevel,
} from "@google/genai"; } from "@google/genai";
import { calculateCost } from "../models.js"; import { calculateCost } from "../models.js";
import { getEnvApiKey } from "../stream.js"; import { getEnvApiKey } from "../stream.js";
@ -20,6 +19,7 @@ import type {
} from "../types.js"; } from "../types.js";
import { AssistantMessageEventStream } from "../utils/event-stream.js"; import { AssistantMessageEventStream } from "../utils/event-stream.js";
import { sanitizeSurrogates } from "../utils/sanitize-unicode.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"; import { convertMessages, convertTools, mapStopReason, mapToolChoice } from "./google-shared.js";
export interface GoogleOptions extends StreamOptions { export interface GoogleOptions extends StreamOptions {
@ -27,7 +27,7 @@ export interface GoogleOptions extends StreamOptions {
thinking?: { thinking?: {
enabled: boolean; enabled: boolean;
budgetTokens?: number; // -1 for dynamic, 0 to disable budgetTokens?: number; // -1 for dynamic, 0 to disable
level?: ThinkingLevel; level?: GoogleThinkingLevel;
}; };
} }
@ -299,7 +299,8 @@ function buildParams(
if (options.thinking?.enabled && model.reasoning) { if (options.thinking?.enabled && model.reasoning) {
const thinkingConfig: ThinkingConfig = { includeThoughts: true }; const thinkingConfig: ThinkingConfig = { includeThoughts: true };
if (options.thinking.level !== undefined) { 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) { } else if (options.thinking.budgetTokens !== undefined) {
thinkingConfig.thinkingBudget = options.thinking.budgetTokens; thinkingConfig.thinkingBudget = options.thinking.budgetTokens;
} }

View file

@ -1,8 +1,11 @@
import { ThinkingLevel } from "@google/genai";
import { supportsXhigh } from "./models.js"; import { supportsXhigh } from "./models.js";
import { type AnthropicOptions, streamAnthropic } from "./providers/anthropic.js"; import { type AnthropicOptions, streamAnthropic } from "./providers/anthropic.js";
import { type GoogleOptions, streamGoogle } from "./providers/google.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 OpenAICompletionsOptions, streamOpenAICompletions } from "./providers/openai-completions.js";
import { type OpenAIResponsesOptions, streamOpenAIResponses } from "./providers/openai-responses.js"; import { type OpenAIResponsesOptions, streamOpenAIResponses } from "./providers/openai-responses.js";
import type { import type {
@ -256,53 +259,56 @@ function isGemini3FlashModel(model: Model<"google-generative-ai">): boolean {
return model.id.includes("3-flash"); 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)) { if (isGemini3ProModel(model)) {
// Gemini 3 Pro only supports LOW/HIGH (for now) // Gemini 3 Pro only supports LOW/HIGH (for now)
switch (effort) { switch (effort) {
case "minimal": case "minimal":
case "low": case "low":
return ThinkingLevel.LOW; return "LOW";
case "medium": case "medium":
case "high": case "high":
return ThinkingLevel.HIGH; return "HIGH";
} }
} }
// Gemini 3 Flash supports all four levels // Gemini 3 Flash supports all four levels
switch (effort) { switch (effort) {
case "minimal": case "minimal":
return ThinkingLevel.MINIMAL; return "MINIMAL";
case "low": case "low":
return ThinkingLevel.LOW; return "LOW";
case "medium": case "medium":
return ThinkingLevel.MEDIUM; return "MEDIUM";
case "high": 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")) { if (modelId.includes("3-pro")) {
// Gemini 3 Pro only supports LOW/HIGH (for now) // Gemini 3 Pro only supports LOW/HIGH (for now)
switch (effort) { switch (effort) {
case "minimal": case "minimal":
case "low": case "low":
return ThinkingLevel.LOW; return "LOW";
case "medium": case "medium":
case "high": case "high":
return ThinkingLevel.HIGH; return "HIGH";
} }
} }
// Gemini 3 Flash supports all four levels // Gemini 3 Flash supports all four levels
switch (effort) { switch (effort) {
case "minimal": case "minimal":
return ThinkingLevel.MINIMAL; return "MINIMAL";
case "low": case "low":
return ThinkingLevel.LOW; return "LOW";
case "medium": case "medium":
return ThinkingLevel.MEDIUM; return "MEDIUM";
case "high": case "high":
return ThinkingLevel.HIGH; return "HIGH";
} }
} }