UNPKG

route-claudecode

Version:

Advanced routing and transformation system for Claude Code outputs to multiple AI providers

123 lines 4.91 kB
"use strict"; /** * Gemini API Client Module * 纯API调用客户端,遵循零硬编码、零fallback原则 * Project owner: Jason Zhang */ Object.defineProperty(exports, "__esModule", { value: true }); exports.GeminiApiClient = void 0; const genai_1 = require("@google/genai"); const logger_1 = require("@/utils/logger"); /** * Gemini API客户端 * 职责:纯API调用,无转换逻辑 */ class GeminiApiClient { config; genAI; timeout; constructor(config) { this.config = config; if (!config.apiKey) { throw new Error('GeminiApiClient: apiKey is required - no fallback allowed'); } this.genAI = new genai_1.GoogleGenAI({ apiKey: config.apiKey // Note: GoogleGenAI doesn't support custom baseUrl in constructor // Custom endpoint handling would need to be implemented differently }); if (config.timeout === undefined) { throw new Error('GeminiApiClient: timeout must be specified - no default fallback allowed'); } this.timeout = config.timeout; logger_1.logger.debug('Gemini API client initialized', { hasApiKey: !!config.apiKey, timeout: this.timeout, baseUrl: config.baseUrl }); } /** * 执行非流式API调用 * 🔧 Critical: Zero-fallback, strict error handling */ async generateContent(request, requestId) { if (!request) { throw new Error('GeminiApiClient: request is required'); } if (!request.model) { throw new Error('GeminiApiClient: model is required in request'); } logger_1.logger.debug('Executing Gemini API call', { model: request.model, hasContents: !!request.contents, contentCount: request.contents?.length ?? 0, hasTools: !!request.tools, timeout: this.timeout }, requestId, 'gemini-api-client'); try { // 使用Promise.race实现超时控制 const result = await Promise.race([ this.genAI.models.generateContent({ ...request, model: request.model || 'gemini-1.5-pro' }), new Promise((_, reject) => setTimeout(() => reject(new Error(`Gemini API timeout after ${this.timeout}ms`)), this.timeout)) ]); if (!result) { throw new Error('GeminiApiClient: API returned null response'); } if (!result.candidates || result.candidates.length === 0) { throw new Error('GeminiApiClient: API response has no candidates'); } logger_1.logger.debug('Gemini API call succeeded', { candidatesCount: result.candidates.length, hasUsageMetadata: !!result.usageMetadata, finishReason: result.candidates[0]?.finishReason }, requestId, 'gemini-api-client'); return result; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger_1.logger.error('Gemini API call failed', { model: request.model, error: errorMessage, timeout: this.timeout }, requestId, 'gemini-api-client'); // 重新抛出错误,不做任何fallback处理 throw error; } } /** * 健康检查 - 零硬编码原则:使用配置的默认模型而非硬编码模型 */ async healthCheck(requestId, healthCheckModel) { try { // 🚫 零硬编码原则:健康检查应该使用配置的默认模型 if (!healthCheckModel) { throw new Error('GeminiApiClient: healthCheckModel must be provided - no hardcoded defaults allowed'); } const testRequest = { model: healthCheckModel, contents: [{ role: 'user', parts: [{ text: 'Hi' }] }] }; const result = await this.generateContent(testRequest, requestId); const isHealthy = !!(result && result.candidates && result.candidates.length > 0); logger_1.logger.debug('Gemini API health check completed', { isHealthy, candidatesCount: result?.candidates?.length ?? 0 }, requestId, 'gemini-api-client'); return isHealthy; } catch (error) { logger_1.logger.warn('Gemini API health check failed', { error: error instanceof Error ? error.message : String(error) }, requestId, 'gemini-api-client'); return false; } } } exports.GeminiApiClient = GeminiApiClient; //# sourceMappingURL=api-client.js.map