UNPKG

route-claudecode

Version:

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

123 lines 5.18 kB
"use strict"; /** * Enhanced OpenAI-Compatible API Key Manager * Provides intelligent, stateful handling of API keys, including rate-limit-aware cooldowns. * This is an adaptation of the advanced manager built for the Gemini provider. * Project Owner: Jason Zhang */ Object.defineProperty(exports, "__esModule", { value: true }); exports.EnhancedOpenAIKeyManager = void 0; const logger_1 = require("@/utils/logger"); class EnhancedOpenAIKeyManager { keys = []; providerId; roundRobinIndex = 0; cooldownMs = 60000; // 60-second cooldown on 429 immediateFailureThreshold = 62000; // 62 seconds maxFailures = 5; // Blacklist after 5 consecutive failures constructor(apiKeys, providerId) { this.providerId = providerId; this.keys = apiKeys.map((key, index) => ({ key, index, cooldownUntil: 0, lastFailure: 0, consecutiveFailures: 0, blacklisted: false, })); logger_1.logger.info(`Enhanced API Key Manager initialized for ${this.providerId}`, { keyCount: this.keys.length, strategy: 'cooldown_aware_round_robin' }); } /** * Selects the next available API key that is not in cooldown or blacklisted. */ getNextAvailableKey(requestId) { const now = Date.now(); const availableKeys = this.keys.filter(k => !k.blacklisted && now >= k.cooldownUntil); if (availableKeys.length === 0) { // All keys are currently in cooldown or blacklisted. Find the one that will be available soonest. let soonestAvailableTime = Infinity; for (const key of this.keys) { if (!key.blacklisted && key.cooldownUntil < soonestAvailableTime) { soonestAvailableTime = key.cooldownUntil; } } const waitTime = soonestAvailableTime === Infinity ? 'N/A' : Math.ceil((soonestAvailableTime - now) / 1000); throw new Error(`All API keys for provider ${this.providerId} are currently rate-limited or blacklisted. Please try again in about ${waitTime} seconds.`); } // Simple round-robin among available keys this.roundRobinIndex = (this.roundRobinIndex + 1) % availableKeys.length; const selectedKey = availableKeys[this.roundRobinIndex]; logger_1.logger.debug(`Selected API key for ${this.providerId}`, { keyIndex: selectedKey.index, requestId }); return selectedKey.key; } /** * Reports a 429 error for a specific API key, triggering a cooldown. */ report429Error(apiKey, requestId) { const keyState = this.keys.find(k => k.key === apiKey); if (!keyState) return; const now = Date.now(); // Check if it failed again right after a cooldown ended const isImmediateFailure = (now - keyState.lastFailure) < this.immediateFailureThreshold && keyState.lastFailure > 0; keyState.consecutiveFailures++; keyState.lastFailure = now; if (isImmediateFailure || keyState.consecutiveFailures >= this.maxFailures) { // Blacklist the key if it fails repeatedly or immediately after cooldown keyState.blacklisted = true; logger_1.logger.warn(`Blacklisted API key for ${this.providerId}`, { reason: isImmediateFailure ? 'Immediate re-failure' : 'Exceeded max failures', keyIndex: keyState.index, requestId }); } else { // Otherwise, just put it in a normal cooldown keyState.cooldownUntil = now + this.cooldownMs; logger_1.logger.warn(`API key for ${this.providerId} put into cooldown for 60s`, { keyIndex: keyState.index, requestId }); } } /** * Reports a generic, non-429 error. */ reportGenericError(apiKey, requestId) { const keyState = this.keys.find(k => k.key === apiKey); if (!keyState) return; keyState.consecutiveFailures++; if (keyState.consecutiveFailures >= this.maxFailures) { keyState.blacklisted = true; logger_1.logger.warn(`Blacklisted API key for ${this.providerId} due to generic errors`, { keyIndex: keyState.index, requestId }); } } /** * Reports a successful request, resetting the failure counter for the key. */ reportSuccess(apiKey, requestId) { const keyState = this.keys.find(k => k.key === apiKey); if (keyState) { if (keyState.consecutiveFailures > 0) { logger_1.logger.info(`API key for ${this.providerId} is healthy again`, { keyIndex: keyState.index, requestId }); } keyState.consecutiveFailures = 0; keyState.cooldownUntil = 0; // Clear cooldown on success } } } exports.EnhancedOpenAIKeyManager = EnhancedOpenAIKeyManager; //# sourceMappingURL=enhanced-api-key-manager.js.map