UNPKG

route-claudecode

Version:

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

303 lines 11.8 kB
"use strict"; /** * 错误系统诊断工具 * 检测和修复静默失败问题 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ErrorSystemDiagnostics = void 0; exports.diagnoseAndHandleError = diagnoseAndHandleError; const logger_1 = require("@/utils/logger"); class ErrorSystemDiagnostics { static diagnosticsLog = []; static maxLogSize = 1000; /** * 诊断错误处理是否正确执行 */ static diagnoseError(error, reply, context) { const diagnostics = { port: context.port, requestId: context.requestId, stage: context.stage, errorType: this.classifyError(error), isSilentFailure: false, hasClientNotification: false, errorDetails: { message: error?.message, status: error?.response?.status || error?.status, provider: context.providerId, isStreaming: context.isStreaming, timestamp: new Date().toISOString() } }; // 检查是否为静默失败 diagnostics.isSilentFailure = this.detectSilentFailure(error, reply, context); // 检查客户端是否收到通知 diagnostics.hasClientNotification = this.checkClientNotification(reply, context); // 记录诊断信息 this.logDiagnostics(diagnostics); // 如果发现静默失败,强制修复 if (diagnostics.isSilentFailure) { this.forceFix(error, reply, context, diagnostics); } return diagnostics; } /** * 检测静默失败 */ static detectSilentFailure(error, reply, context) { // 检查1: 有错误但响应状态码仍为200 if (error && (reply.statusCode === 200 || !reply.statusCode)) { console.error(`🚨 [SILENT FAILURE DETECTED] Error occurred but status code is ${reply.statusCode}`); return true; } // 检查2: 流式请求错误但连接未关闭 if (context.isStreaming && error && !reply.sent) { console.error(`🚨 [SILENT FAILURE DETECTED] Streaming error but connection not closed`); return true; } // 检查3: 特定端口的已知问题 if (context.port === 6689 && error?.response?.status >= 400) { console.error(`🚨 [SILENT FAILURE DETECTED] Port 6689 API error not properly handled`); return true; } // 检查4: Provider错误但没有设置错误状态 if (error?.message?.includes('Provider') && reply.statusCode < 400) { console.error(`🚨 [SILENT FAILURE DETECTED] Provider error but no error status set`); return true; } return false; } /** * 检查客户端通知 */ static checkClientNotification(reply, context) { // 检查响应是否已发送 if (reply.sent) { return true; } // 检查状态码是否设置 if (reply.statusCode && reply.statusCode >= 400) { return true; } // 对于流式请求,检查是否写入了错误事件 if (context.isStreaming) { // 这里需要检查是否写入了error事件,但由于技术限制,我们假设未通知 return false; } return false; } /** * 强制修复静默失败 */ static forceFix(error, reply, context, diagnostics) { console.error(`🔧 [FORCE FIX] Attempting to fix silent failure for request ${context.requestId}`); try { // 确定适当的错误状态码 const statusCode = this.determineStatusCode(error); if (context.isStreaming) { // 流式请求修复 this.fixStreamingError(error, reply, context, statusCode); } else { // 常规请求修复 this.fixRegularError(error, reply, context, statusCode); } console.error(`✅ [FORCE FIX] Silent failure fixed with status ${statusCode}`); } catch (fixError) { console.error(`❌ [FORCE FIX] Failed to fix silent failure: ${fixError instanceof Error ? fixError.message : String(fixError)}`); // 最后的保险措施:至少设置500状态码 try { if (!reply.sent) { reply.code(500).send({ error: { type: 'internal_server_error', message: 'Request failed due to internal error', requestId: context.requestId, port: context.port, stage: context.stage, timestamp: new Date().toISOString() } }); } } catch (lastResortError) { console.error(`❌ [LAST RESORT] Even last resort fix failed: ${lastResortError instanceof Error ? lastResortError.message : String(lastResortError)}`); } } } /** * 修复流式错误 */ static fixStreamingError(error, reply, context, statusCode) { if (reply.sent) return; reply.code(statusCode); const errorEvent = { type: 'error', error: { type: this.mapStatusCodeToType(statusCode), message: error?.message || 'Request failed', code: statusCode.toString(), requestId: context.requestId, port: context.port, provider: context.providerId, timestamp: new Date().toISOString() } }; // 写入SSE错误事件 reply.raw.write(`event: error\n`); reply.raw.write(`data: ${JSON.stringify(errorEvent)}\n\n`); reply.raw.end(); console.error(`🔚 [STREAMING FIX] Connection closed with error event, status ${statusCode}`); } /** * 修复常规错误 */ static fixRegularError(error, reply, context, statusCode) { if (reply.sent) return; const errorResponse = { error: { type: this.mapStatusCodeToType(statusCode), message: error?.message || 'Request failed', code: statusCode.toString(), requestId: context.requestId, port: context.port, provider: context.providerId, stage: context.stage, timestamp: new Date().toISOString(), details: { originalError: error?.response?.data || error?.details, diagnostics: 'Silent failure detected and fixed' } } }; reply.code(statusCode).send(errorResponse); console.error(`🔚 [REGULAR FIX] Error response sent with status ${statusCode}`); } /** * 确定错误状态码 */ static determineStatusCode(error) { if (error?.response?.status) return error.response.status; if (error?.status) return error.status; if (error?.code === 'ECONNREFUSED') return 503; if (error?.code === 'ETIMEDOUT') return 504; if (error?.message?.includes('401')) return 401; if (error?.message?.includes('403')) return 403; if (error?.message?.includes('404')) return 404; if (error?.message?.includes('429')) return 429; return 500; // 默认内部服务器错误 } /** * 错误分类 */ static classifyError(error) { if (error?.response?.status) return `http_${error.response.status}`; if (error?.code) return `network_${error.code}`; if (error?.name) return `js_${error.name}`; return 'unknown_error'; } /** * 状态码到错误类型映射 */ static mapStatusCodeToType(statusCode) { switch (statusCode) { case 400: return 'bad_request'; case 401: return 'authentication_error'; case 403: return 'permission_error'; case 404: return 'not_found'; case 429: return 'rate_limit_exceeded'; case 500: return 'internal_server_error'; case 502: return 'bad_gateway'; case 503: return 'service_unavailable'; case 504: return 'gateway_timeout'; default: return statusCode >= 500 ? 'server_error' : 'client_error'; } } /** * 记录诊断信息 */ static logDiagnostics(diagnostics) { this.diagnosticsLog.push(diagnostics); // 保持日志大小限制 if (this.diagnosticsLog.length > this.maxLogSize) { this.diagnosticsLog = this.diagnosticsLog.slice(-this.maxLogSize); } // 记录到系统日志 logger_1.logger.error('Error diagnostics', { port: diagnostics.port, requestId: diagnostics.requestId, stage: diagnostics.stage, errorType: diagnostics.errorType, isSilentFailure: diagnostics.isSilentFailure, hasClientNotification: diagnostics.hasClientNotification, errorDetails: diagnostics.errorDetails }, diagnostics.requestId, 'error-diagnostics'); // 如果是静默失败,强制控制台输出 if (diagnostics.isSilentFailure) { console.error(`🚨 [SILENT FAILURE] Port ${diagnostics.port}, Request ${diagnostics.requestId}`); console.error(` Stage: ${diagnostics.stage}`); console.error(` Error Type: ${diagnostics.errorType}`); console.error(` Details: ${JSON.stringify(diagnostics.errorDetails, null, 2)}`); } } /** * 获取诊断统计 */ static getDiagnosticsStats() { const stats = { totalErrors: this.diagnosticsLog.length, silentFailures: this.diagnosticsLog.filter(d => d.isSilentFailure).length, silentFailureRate: 0, errorsByPort: {}, errorsByType: {}, recentSilentFailures: [] }; stats.silentFailureRate = stats.totalErrors > 0 ? (stats.silentFailures / stats.totalErrors) * 100 : 0; // 统计各端口错误 for (const diag of this.diagnosticsLog) { stats.errorsByPort[diag.port] = (stats.errorsByPort[diag.port] || 0) + 1; stats.errorsByType[diag.errorType] = (stats.errorsByType[diag.errorType] || 0) + 1; } // 获取最近的静默失败 stats.recentSilentFailures = this.diagnosticsLog .filter(d => d.isSilentFailure) .slice(-10); return stats; } /** * 清理旧的诊断记录 */ static clearOldDiagnostics(maxAgeMs = 24 * 60 * 60 * 1000) { const cutoffTime = Date.now() - maxAgeMs; const originalLength = this.diagnosticsLog.length; this.diagnosticsLog = this.diagnosticsLog.filter(diag => { const diagTime = new Date(diag.errorDetails.timestamp).getTime(); return diagTime > cutoffTime; }); return originalLength - this.diagnosticsLog.length; } } exports.ErrorSystemDiagnostics = ErrorSystemDiagnostics; /** * 便捷函数:诊断并处理错误 */ function diagnoseAndHandleError(error, reply, context) { return ErrorSystemDiagnostics.diagnoseError(error, reply, context); } //# sourceMappingURL=error-system-diagnostics.js.map