UNPKG

ai-sdk-provider-gemini-cli

Version:

Community AI SDK provider for Google Gemini using the official CLI/SDK

1,140 lines (1,129 loc) 37.4 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { ThinkingLevel: () => ThinkingLevel, createGeminiCliCoreProvider: () => createGeminiProvider, createGeminiProvider: () => createGeminiProvider }); module.exports = __toCommonJS(index_exports); // src/gemini-provider.ts var import_provider2 = require("@ai-sdk/provider"); // src/gemini-language-model.ts var import_node_crypto2 = require("crypto"); // src/client.ts var import_node_crypto = require("crypto"); var import_gemini_cli_core = require("@google/gemini-cli-core"); async function initializeGeminiClient(options, modelId) { let authType; if (options.authType === "api-key" || options.authType === "gemini-api-key") { authType = import_gemini_cli_core.AuthType.USE_GEMINI; } else if (options.authType === "vertex-ai") { authType = import_gemini_cli_core.AuthType.USE_VERTEX_AI; } else if (options.authType === "oauth" || options.authType === "oauth-personal") { authType = import_gemini_cli_core.AuthType.LOGIN_WITH_GOOGLE; } else if (options.authType === "google-auth-library") { authType = import_gemini_cli_core.AuthType.USE_GEMINI; } const sessionId = (0, import_node_crypto.randomUUID)(); const baseConfig = { // Required methods (currently working) getModel: () => modelId, getProxy: () => options.proxy || process.env.HTTP_PROXY || process.env.HTTPS_PROXY || void 0, getUsageStatisticsEnabled: () => false, // Disable telemetry by default getContentGeneratorConfig: () => ({ authType, // Keep as AuthType | undefined for consistency model: modelId, apiKey: "apiKey" in options ? options.apiKey : void 0, vertexai: options.authType === "vertex-ai" ? true : void 0, proxy: options.proxy }), // Core safety methods - most likely to be called getSessionId: () => sessionId, getDebugMode: () => false, getTelemetryEnabled: () => false, getTargetDir: () => process.cwd(), getFullContext: () => false, getIdeMode: () => false, getCoreTools: () => [], getExcludeTools: () => [], getMaxSessionTurns: () => 100, getFileFilteringRespectGitIgnore: () => true, // OAuth-specific methods (required for LOGIN_WITH_GOOGLE auth) isBrowserLaunchSuppressed: () => false, // Allow browser launch for OAuth flow // NEW in 0.20.0 - JIT Context & Memory getContextManager: () => void 0, getGlobalMemory: () => "", getEnvironmentMemory: () => "", // NEW in 0.20.0 - Hook System getHookSystem: () => void 0, // NEW in 0.20.0 - Model Availability Service (replaces getUseModelRouter) getModelAvailabilityService: () => void 0, // NEW in 0.20.0 - Shell Timeout (default: 2 minutes) getShellToolInactivityTimeout: () => 12e4, // NEW in 0.20.0 - Experiments (async getter) getExperimentsAsync: () => Promise.resolve(void 0) }; const configMock = new Proxy(baseConfig, { get(target, prop) { if (prop in target) { return target[prop]; } if (typeof prop === "string") { if (prop.startsWith("get") || prop.startsWith("is") || prop.startsWith("has")) { if (process.env.DEBUG) { console.warn( `[ai-sdk-provider-gemini-cli] Unknown config method called: ${prop}()` ); } return () => { if (prop.startsWith("is") || prop.startsWith("has")) { return false; } if (prop.startsWith("get")) { if (prop.includes("Enabled") || prop.includes("Mode")) { return false; } if (prop.includes("Registry") || prop.includes("Client") || prop.includes("Service") || prop.includes("Manager")) { return void 0; } if (prop.includes("Memory")) { return ""; } if (prop.includes("Timeout")) { return 12e4; } if (prop.includes("Config") || prop.includes("Options")) { return {}; } if (prop.includes("Command") || prop.includes("Path")) { return void 0; } return void 0; } return void 0; }; } } return void 0; } }); const config = await (0, import_gemini_cli_core.createContentGeneratorConfig)( configMock, authType ); if ((options.authType === "api-key" || options.authType === "gemini-api-key") && options.apiKey) { config.apiKey = options.apiKey; } else if (options.authType === "vertex-ai" && options.vertexAI) { config.vertexai = true; } const client = await (0, import_gemini_cli_core.createContentGenerator)( config, configMock, sessionId ); return { client, config, sessionId }; } // src/message-mapper.ts function mapPromptToGeminiFormat(options) { const messages = options.prompt; const contents = []; let systemInstruction; for (const message of messages) { switch (message.role) { case "system": systemInstruction = { role: "user", parts: [{ text: message.content }] }; break; case "user": contents.push(mapUserMessage(message)); break; case "assistant": contents.push(mapAssistantMessage(message)); break; case "tool": { const parts = []; for (const part of message.content) { if (part.type === "tool-result") { const output = part.output; let resultValue; if (output.type === "text" || output.type === "error-text") { resultValue = { result: output.value }; } else if (output.type === "json" || output.type === "error-json") { const jsonValue = output.value; if (jsonValue !== null && typeof jsonValue === "object" && !Array.isArray(jsonValue)) { resultValue = jsonValue; } else { resultValue = { result: jsonValue }; } } else if (output.type === "execution-denied") { resultValue = { result: `[Execution denied${output.reason ? `: ${output.reason}` : ""}]` }; } else if (output.type === "content") { const textContent = output.value.filter( (p) => p.type === "text" ).map((p) => p.text).join("\n"); resultValue = { result: textContent }; } else { resultValue = { result: "[Unknown output type]" }; } parts.push({ functionResponse: { name: part.toolName, response: resultValue } }); } } contents.push({ role: "user", parts }); break; } } } return { contents, systemInstruction }; } function mapUserMessage(message) { const parts = []; for (const part of message.content) { switch (part.type) { case "text": parts.push({ text: part.text }); break; case "file": { const mediaType = part.mediaType || "application/octet-stream"; if (mediaType.startsWith("image/") || mediaType.startsWith("audio/") || mediaType.startsWith("video/") || mediaType === "application/pdf") { parts.push(mapFilePart(part)); } else { throw new Error(`Unsupported file type: ${mediaType}`); } break; } } } return { role: "user", parts }; } function mapAssistantMessage(message) { const parts = []; for (const part of message.content) { switch (part.type) { case "text": parts.push({ text: part.text }); break; case "tool-call": parts.push({ functionCall: { name: part.toolName, args: part.input || {} } }); break; } } return { role: "model", parts }; } function mapFilePart(part) { if (part.data instanceof URL) { throw new Error( "URL files are not supported by Gemini CLI Core. Please provide base64-encoded data." ); } const mimeType = part.mediaType || "application/octet-stream"; let base64Data; if (typeof part.data === "string") { base64Data = part.data; } else if (part.data instanceof Uint8Array) { base64Data = Buffer.from(part.data).toString("base64"); } else { throw new Error("Unsupported file format"); } return { inlineData: { mimeType, data: base64Data } }; } // src/tool-mapper.ts var import_genai = require("@google/genai"); var import_zod = require("zod"); function mapToolsToGeminiFormat(tools) { const functionDeclarations = []; for (const tool of tools) { functionDeclarations.push({ name: tool.name, description: tool.description, parameters: convertToolParameters(tool.inputSchema) }); } return [{ functionDeclarations }]; } function convertZodToJsonSchema(zodSchema) { const zodWithToJSONSchema = import_zod.z; if (zodWithToJSONSchema.toJSONSchema && typeof zodWithToJSONSchema.toJSONSchema === "function") { try { return zodWithToJSONSchema.toJSONSchema(zodSchema); } catch { } } try { const zodToJsonSchemaModule = require("zod-to-json-schema"); return zodToJsonSchemaModule.zodToJsonSchema(zodSchema); } catch { } console.warn( "Unable to convert Zod schema to JSON Schema. For Zod v3, install zod-to-json-schema. For Zod v4, use z.toJSONSchema() function." ); return { type: "object" }; } function convertToolParameters(parameters) { if (isJsonSchema(parameters)) { return cleanJsonSchema(parameters); } if (isZodSchema(parameters)) { const jsonSchema = convertZodToJsonSchema(parameters); return cleanJsonSchema(jsonSchema); } return parameters; } function isJsonSchema(obj) { return typeof obj === "object" && obj !== null && ("type" in obj || "properties" in obj || "$schema" in obj); } function isZodSchema(obj) { return typeof obj === "object" && obj !== null && "_def" in obj && typeof obj._def === "object"; } function cleanJsonSchema(schema) { if (typeof schema !== "object" || schema === null) { return schema; } const cleaned = { ...schema }; delete cleaned.$schema; delete cleaned.$ref; delete cleaned.$defs; delete cleaned.definitions; if (cleaned.properties && typeof cleaned.properties === "object") { const cleanedProps = {}; for (const [key, value] of Object.entries(cleaned.properties)) { cleanedProps[key] = cleanJsonSchema(value); } cleaned.properties = cleanedProps; } if (cleaned.items) { cleaned.items = cleanJsonSchema(cleaned.items); } if (cleaned.additionalProperties && typeof cleaned.additionalProperties === "object") { cleaned.additionalProperties = cleanJsonSchema( cleaned.additionalProperties ); } for (const key of ["allOf", "anyOf", "oneOf"]) { const arrayProp = cleaned[key]; if (Array.isArray(arrayProp)) { cleaned[key] = arrayProp.map( (item) => cleanJsonSchema(item) ); } } if (cleaned.properties && cleaned.type === void 0) { cleaned.type = "object"; } return cleaned; } function mapGeminiToolConfig(options) { if (options.toolChoice) { const allowedFunctionNames = options.toolChoice.type === "tool" ? [options.toolChoice.toolName] : void 0; return { functionCallingConfig: { allowedFunctionNames, mode: mapToolChoiceToGeminiFormat(options.toolChoice) } }; } return void 0; } function mapToolChoiceToGeminiFormat(toolChoice) { switch (toolChoice.type) { case "auto": return import_genai.FunctionCallingConfigMode.AUTO; case "none": return import_genai.FunctionCallingConfigMode.NONE; case "required": case "tool": return import_genai.FunctionCallingConfigMode.ANY; default: return import_genai.FunctionCallingConfigMode.MODE_UNSPECIFIED; } } // src/error.ts var import_provider = require("@ai-sdk/provider"); function createAPICallError({ message, code, exitCode, stderr, promptExcerpt, isRetryable = false, statusCode = 500 }) { return new import_provider.APICallError({ url: "gemini-cli-core://command", requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : {}, statusCode, responseHeaders: {}, message, data: { code, exitCode, stderr }, isRetryable }); } function createAuthenticationError({ message }) { return new import_provider.LoadAPIKeyError({ message }); } function createTimeoutError({ message, promptExcerpt }) { return createAPICallError({ message, code: "TIMEOUT", promptExcerpt, isRetryable: true, statusCode: 504 }); } function isAuthenticationError(error) { if (error instanceof import_provider.LoadAPIKeyError) { return true; } if (error instanceof Error) { const message = error.message.toLowerCase(); return message.includes("unauthorized") || message.includes("authentication") || message.includes("api key") || message.includes("credentials"); } return false; } function isTimeoutError(error) { if (error instanceof import_provider.APICallError) { return error.statusCode === 504 || error.data?.code === "TIMEOUT"; } if (error instanceof Error) { const message = error.message.toLowerCase(); return message.includes("timeout") || message.includes("timed out"); } return false; } function mapGeminiError(error) { if (error instanceof Error) { if (error.name === "AbortError") { throw error; } const message = error.message.toLowerCase(); if (isAuthenticationError(error)) { return createAuthenticationError({ message: error.message }); } if (message.includes("rate limit") || message.includes("quota")) { return createAPICallError({ message: error.message, code: "RATE_LIMIT", isRetryable: true, statusCode: 429 }); } if (isTimeoutError(error)) { return createTimeoutError({ message: error.message }); } if (message.includes("not found") || message.includes("no such model") || message.includes("model") && (message.includes("invalid") || message.includes("not found"))) { return createAPICallError({ message: error.message, code: "MODEL_NOT_FOUND", isRetryable: false, statusCode: 404 }); } if (message.includes("invalid") || message.includes("bad request")) { return createAPICallError({ message: error.message, code: "INVALID_REQUEST", isRetryable: false, statusCode: 400 }); } return createAPICallError({ message: error.message, code: "INTERNAL_ERROR", isRetryable: true, statusCode: 500 }); } return createAPICallError({ message: "An unknown error occurred", code: "UNKNOWN_ERROR", isRetryable: true, statusCode: 500 }); } // src/logger.ts var defaultLogger = { debug: (message) => console.debug(`[DEBUG] ${message}`), info: (message) => console.info(`[INFO] ${message}`), warn: (message) => console.warn(`[WARN] ${message}`), error: (message) => console.error(`[ERROR] ${message}`) }; var noopLogger = { debug: () => { }, info: () => { }, warn: () => { }, error: () => { } }; function getLogger(logger) { if (logger === false) { return noopLogger; } if (logger === void 0) { return defaultLogger; } return logger; } function createVerboseLogger(logger, verbose = false) { if (verbose) { return logger; } return { debug: () => { }, // Suppressed in non-verbose mode info: () => { }, // Suppressed in non-verbose mode warn: logger.warn.bind(logger), error: logger.error.bind(logger) }; } // src/gemini-language-model.ts var ThinkingLevel = /* @__PURE__ */ ((ThinkingLevel2) => { ThinkingLevel2["LOW"] = "LOW"; ThinkingLevel2["MEDIUM"] = "MEDIUM"; ThinkingLevel2["HIGH"] = "HIGH"; ThinkingLevel2["MINIMAL"] = "MINIMAL"; return ThinkingLevel2; })(ThinkingLevel || {}); function normalizeThinkingLevel(level) { const normalized = level.toUpperCase(); switch (normalized) { case "LOW": return "LOW" /* LOW */; case "MEDIUM": return "MEDIUM" /* MEDIUM */; case "HIGH": return "HIGH" /* HIGH */; case "MINIMAL": return "MINIMAL" /* MINIMAL */; default: return void 0; } } function mapGeminiFinishReason(geminiReason) { switch (geminiReason) { case "STOP": return { unified: "stop", raw: geminiReason }; case "MAX_TOKENS": return { unified: "length", raw: geminiReason }; case "SAFETY": case "RECITATION": return { unified: "content-filter", raw: geminiReason }; case "OTHER": return { unified: "other", raw: geminiReason }; default: return { unified: "other", raw: geminiReason }; } } function buildThinkingConfig(input) { const config = {}; if (input.thinkingLevel !== void 0) { if (typeof input.thinkingLevel === "string") { const normalized = normalizeThinkingLevel(input.thinkingLevel); if (normalized !== void 0) { config.thinkingLevel = normalized; } } else { config.thinkingLevel = input.thinkingLevel; } } if (input.thinkingBudget !== void 0) { config.thinkingBudget = input.thinkingBudget; } if (input.includeThoughts !== void 0) { config.includeThoughts = input.includeThoughts; } return config; } function prepareGenerationConfig(options, settings) { const warnings = []; const responseFormat = options.responseFormat; const isJsonMode = responseFormat?.type === "json"; const schema = isJsonMode ? responseFormat.schema : void 0; const hasSchema = isJsonMode && schema !== void 0; if (isJsonMode && !hasSchema) { warnings.push({ type: "unsupported", feature: "responseFormat", details: "JSON response format without a schema is not supported. Treating as plain text. Provide a schema for structured output." }); } const settingsThinkingConfig = settings?.thinkingConfig; const optionsThinkingConfig = options.thinkingConfig; let effectiveOptionsThinking = optionsThinkingConfig; if (optionsThinkingConfig?.thinkingLevel !== void 0 && typeof optionsThinkingConfig.thinkingLevel === "string") { const normalized = normalizeThinkingLevel( optionsThinkingConfig.thinkingLevel ); if (normalized === void 0) { const { thinkingLevel: _, ...rest } = optionsThinkingConfig; effectiveOptionsThinking = Object.keys(rest).length > 0 ? rest : void 0; } } const mergedThinkingConfig = settingsThinkingConfig || effectiveOptionsThinking ? { ...settingsThinkingConfig, ...effectiveOptionsThinking } : void 0; const thinkingConfig = mergedThinkingConfig ? buildThinkingConfig(mergedThinkingConfig) : void 0; const generationConfig = { temperature: options.temperature ?? settings?.temperature, topP: options.topP ?? settings?.topP, topK: options.topK ?? settings?.topK, maxOutputTokens: options.maxOutputTokens ?? settings?.maxOutputTokens, stopSequences: options.stopSequences, // Only use application/json when we have a schema to enforce it responseMimeType: hasSchema ? "application/json" : "text/plain", // Pass schema directly to Gemini API for native structured output responseJsonSchema: hasSchema ? schema : void 0, toolConfig: mapGeminiToolConfig(options), // Pass thinkingConfig for Gemini 3 (thinkingLevel) or Gemini 2.5 (thinkingBudget) // Cast needed because our ThinkingLevel enum isn't recognized by @google/genai v1.30.0 types thinkingConfig }; return { generationConfig, warnings }; } var GeminiLanguageModel = class { constructor(options) { this.specificationVersion = "v3"; this.provider = "gemini-cli-core"; this.defaultObjectGenerationMode = "json"; this.supportsImageUrls = false; // CLI Core uses base64 data, not URLs this.supportedUrls = {}; // No native URL support this.supportsStructuredOutputs = true; this.modelId = options.modelId; this.providerOptions = options.providerOptions; this.settings = options.settings; const baseLogger = getLogger(options.settings?.logger); this.logger = createVerboseLogger( baseLogger, options.settings?.verbose ?? false ); } async ensureInitialized() { if (this.contentGenerator && this.config) { return { contentGenerator: this.contentGenerator, config: this.config }; } if (!this.initPromise) { this.initPromise = this.initialize(); } await this.initPromise; return { contentGenerator: this.contentGenerator, config: this.config }; } async initialize() { try { const { client, config } = await initializeGeminiClient( this.providerOptions, this.modelId ); this.contentGenerator = client; this.config = config; } catch (error) { throw new Error(`Failed to initialize Gemini model: ${String(error)}`); } } /** * Non-streaming generation method */ async doGenerate(options) { this.logger.debug( `[gemini-cli] Starting doGenerate request with model: ${this.modelId}` ); try { const { contentGenerator } = await this.ensureInitialized(); const { contents, systemInstruction } = mapPromptToGeminiFormat(options); this.logger.debug( `[gemini-cli] Request mode: ${options.responseFormat?.type === "json" ? "object-json" : "regular"}, response format: ${options.responseFormat?.type ?? "none"}` ); this.logger.debug( `[gemini-cli] Converted ${options.prompt.length} messages` ); const { generationConfig, warnings } = prepareGenerationConfig( options, this.settings ); let tools; if (options.tools) { const functionTools = options.tools.filter( (tool) => tool.type === "function" ); if (functionTools.length > 0) { tools = mapToolsToGeminiFormat(functionTools); } } const request = { model: this.modelId, contents, config: { ...generationConfig, systemInstruction, tools } }; let abortListener; if (options.abortSignal) { if (options.abortSignal.aborted) { const abortError = new Error("Request aborted"); abortError.name = "AbortError"; throw abortError; } abortListener = () => { }; options.abortSignal.addEventListener("abort", abortListener, { once: true }); } let response; const startTime = Date.now(); try { this.logger.debug("[gemini-cli] Executing generateContent request"); response = await contentGenerator.generateContent( request, (0, import_node_crypto2.randomUUID)() ); const duration = Date.now() - startTime; this.logger.info( `[gemini-cli] Request completed - Duration: ${duration}ms` ); if (options.abortSignal?.aborted) { const abortError = new Error("Request aborted"); abortError.name = "AbortError"; throw abortError; } } finally { if (options.abortSignal && abortListener) { options.abortSignal.removeEventListener("abort", abortListener); } } const candidate = response.candidates?.[0]; const responseContent = candidate?.content; const content = []; let hasToolCalls = false; if (responseContent?.parts) { for (const part of responseContent.parts) { if (part.text) { content.push({ type: "text", text: part.text }); } else if (part.functionCall) { hasToolCalls = true; content.push({ type: "tool-call", toolCallId: (0, import_node_crypto2.randomUUID)(), toolName: part.functionCall.name || "", input: JSON.stringify(part.functionCall.args || {}) }); } } } const inputTokens = response.usageMetadata?.promptTokenCount || 0; const outputTokens = response.usageMetadata?.candidatesTokenCount || 0; const totalTokens = inputTokens + outputTokens; const usage = { inputTokens: { total: inputTokens, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: outputTokens, text: void 0, reasoning: void 0 } }; this.logger.debug( `[gemini-cli] Token usage - Input: ${inputTokens}, Output: ${outputTokens}, Total: ${totalTokens}` ); const finishReason = hasToolCalls ? { unified: "tool-calls", raw: candidate?.finishReason } : mapGeminiFinishReason(candidate?.finishReason); this.logger.debug(`[gemini-cli] Finish reason: ${finishReason.unified}`); return { content, finishReason, usage, rawCall: { rawPrompt: { contents, systemInstruction, generationConfig, tools }, rawSettings: generationConfig }, rawResponse: { body: response }, response: { id: (0, import_node_crypto2.randomUUID)(), timestamp: /* @__PURE__ */ new Date(), modelId: this.modelId }, warnings }; } catch (error) { this.logger.debug( `[gemini-cli] Error during doGenerate: ${error instanceof Error ? error.message : String(error)}` ); throw mapGeminiError(error); } } /** * Streaming generation method */ async doStream(options) { this.logger.debug( `[gemini-cli] Starting doStream request with model: ${this.modelId}` ); try { const { contentGenerator } = await this.ensureInitialized(); const { contents, systemInstruction } = mapPromptToGeminiFormat(options); this.logger.debug( `[gemini-cli] Stream mode: ${options.responseFormat?.type === "json" ? "object-json" : "regular"}, response format: ${options.responseFormat?.type ?? "none"}` ); this.logger.debug( `[gemini-cli] Converted ${options.prompt.length} messages for streaming` ); const { generationConfig, warnings } = prepareGenerationConfig( options, this.settings ); let tools; if (options.tools) { const functionTools = options.tools.filter( (tool) => tool.type === "function" ); if (functionTools.length > 0) { tools = mapToolsToGeminiFormat(functionTools); } } const request = { model: this.modelId, contents, config: { ...generationConfig, systemInstruction, tools } }; let abortListener; if (options.abortSignal) { if (options.abortSignal.aborted) { const abortError = new Error("Request aborted"); abortError.name = "AbortError"; throw abortError; } abortListener = () => { }; options.abortSignal.addEventListener("abort", abortListener, { once: true }); } let streamResponse; try { this.logger.debug( "[gemini-cli] Starting generateContentStream request" ); streamResponse = await contentGenerator.generateContentStream( request, (0, import_node_crypto2.randomUUID)() ); if (options.abortSignal?.aborted) { const abortError = new Error("Request aborted"); abortError.name = "AbortError"; throw abortError; } } catch (error) { if (options.abortSignal && abortListener) { options.abortSignal.removeEventListener("abort", abortListener); } throw error; } const modelId = this.modelId; const logger = this.logger; const streamWarnings = warnings; const stream = new ReadableStream({ async start(controller) { try { if (options.abortSignal?.aborted) { const abortError = new Error("Request aborted"); abortError.name = "AbortError"; controller.error(abortError); return; } let totalInputTokens = 0; let totalOutputTokens = 0; let textPartId; let hasToolCalls = false; controller.enqueue({ type: "stream-start", warnings: streamWarnings }); const streamStartTime = Date.now(); logger.debug("[gemini-cli] Stream started, processing chunks"); for await (const chunk of streamResponse) { if (options.abortSignal?.aborted) { const abortError = new Error("Request aborted"); abortError.name = "AbortError"; controller.error(abortError); return; } const candidate = chunk.candidates?.[0]; const content = candidate?.content; if (chunk.usageMetadata) { totalInputTokens = chunk.usageMetadata.promptTokenCount || 0; totalOutputTokens = chunk.usageMetadata.candidatesTokenCount || 0; } if (content?.parts) { for (const part of content.parts) { if (part.text) { if (!textPartId) { textPartId = (0, import_node_crypto2.randomUUID)(); controller.enqueue({ type: "text-start", id: textPartId }); } controller.enqueue({ type: "text-delta", id: textPartId, delta: part.text }); } else if (part.functionCall) { hasToolCalls = true; controller.enqueue({ type: "tool-call", toolCallId: (0, import_node_crypto2.randomUUID)(), toolName: part.functionCall.name || "", input: JSON.stringify(part.functionCall.args || {}) }); } } } if (candidate?.finishReason) { const duration = Date.now() - streamStartTime; logger.info( `[gemini-cli] Stream completed - Duration: ${duration}ms` ); logger.debug( `[gemini-cli] Stream token usage - Input: ${totalInputTokens}, Output: ${totalOutputTokens}, Total: ${totalInputTokens + totalOutputTokens}` ); if (textPartId) { controller.enqueue({ type: "text-end", id: textPartId }); } const finishReason = hasToolCalls ? { unified: "tool-calls", raw: candidate.finishReason } : mapGeminiFinishReason(candidate.finishReason); logger.debug( `[gemini-cli] Stream finish reason: ${finishReason.unified}` ); controller.enqueue({ type: "response-metadata", id: (0, import_node_crypto2.randomUUID)(), timestamp: /* @__PURE__ */ new Date(), modelId }); controller.enqueue({ type: "finish", finishReason, usage: { inputTokens: { total: totalInputTokens, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: totalOutputTokens, text: void 0, reasoning: void 0 } } }); } } logger.debug("[gemini-cli] Stream finalized, closing stream"); controller.close(); } catch (error) { logger.debug( `[gemini-cli] Error during doStream: ${error instanceof Error ? error.message : String(error)}` ); controller.error(mapGeminiError(error)); } finally { if (options.abortSignal && abortListener) { options.abortSignal.removeEventListener("abort", abortListener); } } }, cancel: () => { if (options.abortSignal && abortListener) { options.abortSignal.removeEventListener("abort", abortListener); } } }); return { stream, rawCall: { rawPrompt: { contents, systemInstruction, generationConfig, tools }, rawSettings: generationConfig } }; } catch (error) { this.logger.debug( `[gemini-cli] Error creating stream: ${error instanceof Error ? error.message : String(error)}` ); throw mapGeminiError(error); } } }; // src/validation.ts function validateAuthOptions(options = {}) { const authType = options.authType || "oauth-personal"; switch (authType) { case "api-key": case "gemini-api-key": if (!("apiKey" in options) || !options.apiKey) { throw new Error(`API key is required for ${authType} auth type`); } return { ...options, authType }; case "vertex-ai": if ("vertexAI" in options && options.vertexAI) { if (!options.vertexAI.projectId || options.vertexAI.projectId.trim() === "") { throw new Error("Project ID is required for vertex-ai auth type"); } if (!options.vertexAI.location || options.vertexAI.location.trim() === "") { throw new Error("Location is required for vertex-ai auth type"); } } else { throw new Error( "Vertex AI configuration is required for vertex-ai auth type" ); } return { ...options, authType }; case "oauth": case "oauth-personal": return { ...options, authType }; case "google-auth-library": if (!("googleAuth" in options) || !options.googleAuth) { throw new Error( "Google Auth Library instance is required for google-auth-library auth type" ); } return { ...options, authType }; default: throw new Error(`Invalid auth type: ${String(authType)}`); } } // src/gemini-provider.ts function createGeminiProvider(options = {}) { const validatedOptions = validateAuthOptions(options); const createLanguageModel = (modelId, settings) => { return new GeminiLanguageModel({ modelId, providerOptions: validatedOptions, settings: { maxOutputTokens: 65536, // 64K output tokens for Gemini 2.5 models ...settings } }); }; const provider = Object.assign( function(modelId, settings) { if (new.target) { throw new Error( "The provider function cannot be called with the new keyword." ); } return createLanguageModel(modelId, settings); }, { specificationVersion: "v3", languageModel: createLanguageModel, chat: createLanguageModel, embeddingModel: (modelId) => { throw new import_provider2.NoSuchModelError({ modelId, modelType: "embeddingModel", message: `Gemini provider does not support embedding models.` }); }, imageModel: (modelId) => { throw new import_provider2.NoSuchModelError({ modelId, modelType: "imageModel", message: `Gemini provider does not support image models.` }); } } ); return provider; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ThinkingLevel, createGeminiCliCoreProvider, createGeminiProvider }); //# sourceMappingURL=index.js.map