UNPKG

@callmedayz/ai-prompt-toolkit

Version:

Professional AI prompt engineering toolkit with advanced template features, real-time dashboards, conditional logic, template inheritance, live monitoring, OpenRouter integration, and 310+ model support

198 lines 7.46 kB
"use strict"; /** * Enhanced error handling and retry logic for OpenRouter API */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CircuitBreaker = exports.DEFAULT_RETRY_CONFIG = exports.OpenRouterError = exports.ErrorType = void 0; exports.parseError = parseError; exports.calculateRetryDelay = calculateRetryDelay; exports.retryWithBackoff = retryWithBackoff; /** * Types of errors that can occur */ var ErrorType; (function (ErrorType) { ErrorType["AUTHENTICATION"] = "authentication"; ErrorType["RATE_LIMIT"] = "rate_limit"; ErrorType["QUOTA_EXCEEDED"] = "quota_exceeded"; ErrorType["MODEL_UNAVAILABLE"] = "model_unavailable"; ErrorType["NETWORK"] = "network"; ErrorType["TIMEOUT"] = "timeout"; ErrorType["VALIDATION"] = "validation"; ErrorType["SERVER_ERROR"] = "server_error"; ErrorType["UNKNOWN"] = "unknown"; })(ErrorType || (exports.ErrorType = ErrorType = {})); /** * Enhanced error class with retry information */ class OpenRouterError extends Error { constructor(message, type, statusCode, retryable = false, retryAfter, originalError) { super(message); this.name = 'OpenRouterError'; this.type = type; this.statusCode = statusCode; this.retryable = retryable; this.retryAfter = retryAfter; this.originalError = originalError; } } exports.OpenRouterError = OpenRouterError; /** * Default retry configuration */ exports.DEFAULT_RETRY_CONFIG = { maxRetries: 3, baseDelay: 1000, maxDelay: 30000, exponentialBase: 2, jitter: true, retryableErrors: [ ErrorType.NETWORK, ErrorType.TIMEOUT, ErrorType.SERVER_ERROR, ErrorType.RATE_LIMIT ] }; /** * Parse HTTP response and create appropriate error */ function parseError(response, responseText) { const statusCode = response.status; try { const errorData = JSON.parse(responseText); const message = errorData.error?.message || errorData.message || response.statusText; switch (statusCode) { case 401: return new OpenRouterError(`Authentication failed: ${message}`, ErrorType.AUTHENTICATION, statusCode, false); case 429: const retryAfter = response.headers.get('retry-after'); return new OpenRouterError(`Rate limit exceeded: ${message}`, ErrorType.RATE_LIMIT, statusCode, true, retryAfter ? parseInt(retryAfter) * 1000 : undefined); case 402: return new OpenRouterError(`Quota exceeded: ${message}`, ErrorType.QUOTA_EXCEEDED, statusCode, false); case 400: if (message.toLowerCase().includes('model')) { return new OpenRouterError(`Model unavailable: ${message}`, ErrorType.MODEL_UNAVAILABLE, statusCode, false); } return new OpenRouterError(`Validation error: ${message}`, ErrorType.VALIDATION, statusCode, false); case 500: case 502: case 503: case 504: return new OpenRouterError(`Server error: ${message}`, ErrorType.SERVER_ERROR, statusCode, true); default: return new OpenRouterError(`HTTP ${statusCode}: ${message}`, ErrorType.UNKNOWN, statusCode, statusCode >= 500); } } catch (parseError) { return new OpenRouterError(`HTTP ${statusCode}: ${response.statusText}`, statusCode >= 500 ? ErrorType.SERVER_ERROR : ErrorType.UNKNOWN, statusCode, statusCode >= 500); } } /** * Calculate delay for retry with exponential backoff and jitter */ function calculateRetryDelay(attempt, config, retryAfter) { // Use retry-after header if provided if (retryAfter) { return Math.min(retryAfter, config.maxDelay); } // Calculate exponential backoff const exponentialDelay = config.baseDelay * Math.pow(config.exponentialBase, attempt - 1); // Apply jitter to avoid thundering herd let delay = exponentialDelay; if (config.jitter) { delay = exponentialDelay * (0.5 + Math.random() * 0.5); } return Math.min(delay, config.maxDelay); } /** * Enhanced retry function with sophisticated error handling */ async function retryWithBackoff(operation, config = exports.DEFAULT_RETRY_CONFIG) { let lastError; for (let attempt = 1; attempt <= config.maxRetries + 1; attempt++) { try { return await operation(); } catch (error) { // Convert to OpenRouterError if needed if (!(error instanceof OpenRouterError)) { if (error instanceof Error) { if (error.name === 'AbortError' || error.message.includes('timeout')) { lastError = new OpenRouterError('Request timeout', ErrorType.TIMEOUT, undefined, true, undefined, error); } else if (error.message.includes('fetch')) { lastError = new OpenRouterError('Network error', ErrorType.NETWORK, undefined, true, undefined, error); } else { lastError = new OpenRouterError(error.message, ErrorType.UNKNOWN, undefined, false, undefined, error); } } else { lastError = new OpenRouterError('Unknown error', ErrorType.UNKNOWN, undefined, false); } } else { lastError = error; } // Don't retry if this is the last attempt or error is not retryable if (attempt > config.maxRetries || !lastError.retryable || !config.retryableErrors.includes(lastError.type)) { throw lastError; } // Calculate delay and wait const delay = calculateRetryDelay(attempt, config, lastError.retryAfter); await new Promise(resolve => setTimeout(resolve, delay)); } } throw lastError; } /** * Circuit breaker for preventing cascading failures */ class CircuitBreaker { constructor(failureThreshold = 5, recoveryTimeout = 60000) { this.failureThreshold = failureThreshold; this.recoveryTimeout = recoveryTimeout; this.failures = 0; this.lastFailureTime = 0; this.state = 'closed'; } async execute(operation) { if (this.state === 'open') { if (Date.now() - this.lastFailureTime > this.recoveryTimeout) { this.state = 'half-open'; } else { throw new OpenRouterError('Circuit breaker is open', ErrorType.SERVER_ERROR, undefined, false); } } try { const result = await operation(); this.onSuccess(); return result; } catch (error) { this.onFailure(); throw error; } } onSuccess() { this.failures = 0; this.state = 'closed'; } onFailure() { this.failures++; this.lastFailureTime = Date.now(); if (this.failures >= this.failureThreshold) { this.state = 'open'; } } getState() { return { state: this.state, failures: this.failures, lastFailureTime: this.lastFailureTime }; } } exports.CircuitBreaker = CircuitBreaker; //# sourceMappingURL=error-handling.js.map