route-claudecode
Version:
Advanced routing and transformation system for Claude Code outputs to multiple AI providers
128 lines • 5.62 kB
JavaScript
;
/**
* Gemini Provider
* Integration with Google's Gemini API via OpenAI-compatible interface
* Project owner: Jason Zhang
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeminiProvider = void 0;
const logger_1 = require("../../utils/logger");
const client_1 = require("./client");
class GeminiProvider {
config;
name;
type;
client;
static PROVIDER_TYPE = 'gemini';
constructor(config, providerId) {
this.config = config;
if (!providerId) {
throw new Error('GeminiProvider: providerId is required - no default fallback allowed');
}
this.name = providerId;
// Zero Hardcode Principle: type from config or static constant
this.type = config.type || GeminiProvider.PROVIDER_TYPE;
if (config.type !== GeminiProvider.PROVIDER_TYPE) {
throw new Error(`Invalid provider type: ${config.type}, expected '${GeminiProvider.PROVIDER_TYPE}'`);
}
this.client = new client_1.GeminiClient(config, this.name);
logger_1.logger.info(`Initialized Gemini provider: ${config.endpoint}`, {
provider: this.name,
endpoint: config.endpoint
});
}
async isHealthy() {
try {
return await this.client.healthCheck();
}
catch (error) {
logger_1.logger.error('Gemini provider health check failed', {
provider: this.name,
error: error instanceof Error ? error.message : String(error)
});
return false;
}
}
async sendRequest(request) {
try {
if (!request.metadata?.requestId) {
throw new Error('GeminiProvider: request.metadata.requestId is required');
}
logger_1.logger.trace(request.metadata.requestId, 'provider', 'Sending request to Gemini', {
provider: this.name,
model: request.model,
endpoint: this.config.endpoint
});
const response = await this.client.createCompletion(request);
logger_1.logger.debug('Received response from Gemini', {
provider: this.name,
model: response.model,
requestId: request.metadata?.requestId,
inputTokens: response.usage?.input_tokens,
outputTokens: response.usage?.output_tokens
});
return response;
}
catch (error) {
const isRateLimited = error?.status === 429 ||
error?.message?.includes('429') ||
error?.message?.includes('RESOURCE_EXHAUSTED') ||
error?.message?.includes('quota');
// 获取当前使用的key信息(如果可用)
const currentKeyInfo = this.client?.getRotationStats?.() ?? {};
// 强制控制台输出429错误,包含详细的key和模型降级信息
if (isRateLimited) {
console.error(`🚨 [429 RATE LIMIT] Gemini API quota exhausted:`);
console.error(` Provider: ${this.name}`);
console.error(` Requested Model: ${request.model}`);
console.error(` Request ID: ${request.metadata?.requestId}`);
console.error(` Current Key: ${currentKeyInfo.keyIndex !== undefined ? `key-${currentKeyInfo.keyIndex + 1}` : 'unknown'}`);
console.error(` Key Suffix: ${currentKeyInfo.keySuffix ?? 'unknown'}`);
console.error(` Total Keys: ${currentKeyInfo.totalKeys ?? 'unknown'}`);
console.error(` Error Details: ${error instanceof Error ? error.message : String(error)}`);
console.error(` Zero Fallback Principle: No model fallback - routing layer must handle alternatives`);
console.error(` Next Key Available: ${currentKeyInfo.nextKeyIndex !== undefined ? `key-${currentKeyInfo.nextKeyIndex + 1}` : 'checking...'}`);
}
logger_1.logger.error('Gemini API request failed', {
provider: this.name,
model: request.model,
requestId: request.metadata?.requestId,
error: error instanceof Error ? error.message : String(error),
isRateLimited,
keyInfo: currentKeyInfo,
httpStatus: error?.status,
errorCode: error?.code
});
throw error;
}
}
async *sendStreamRequest(request) {
try {
logger_1.logger.debug('Starting streaming request to Gemini', {
provider: this.name,
model: request.model,
requestId: request.metadata?.requestId
});
const stream = this.client.streamCompletion(request);
for await (const chunk of stream) {
yield chunk;
}
logger_1.logger.debug('Completed streaming request to Gemini', {
provider: this.name,
model: request.model,
requestId: request.metadata?.requestId
});
}
catch (error) {
logger_1.logger.error('Gemini streaming request failed', {
provider: this.name,
model: request.model,
requestId: request.metadata?.requestId,
error: error instanceof Error ? error.message : String(error)
});
throw error;
}
}
}
exports.GeminiProvider = GeminiProvider;
//# sourceMappingURL=index.js.map