UNPKG

route-claudecode

Version:

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

268 lines 10.2 kB
"use strict"; /** * 响应统计管理器 - 记录每个provider和模型的响应数量 * Owner: Jason Zhang */ Object.defineProperty(exports, "__esModule", { value: true }); exports.responseStatsManager = exports.ResponseStatsManager = void 0; const logger_1 = require("./logger"); const fs_1 = require("fs"); const path_1 = require("path"); const config_paths_1 = require("./config-paths"); class ResponseStatsManager { stats = {}; startTime = new Date(); logInterval = null; statsFilePath; constructor() { const configPaths = (0, config_paths_1.getConfigPaths)(); this.statsFilePath = (0, path_1.resolve)(configPaths.configDir, 'response-stats.json'); this.loadStats(); // 每5分钟输出一次统计日志 - 已禁用定期输出避免干扰 // this.startPeriodicLogging(); // Save stats on exit process.on('beforeExit', () => this.saveStats()); // Use console.log instead of logger during initialization console.log('ResponseStatsManager initialized'); } /** * 记录成功响应 */ recordSuccess(providerId, model, responseTimeMs, isStreaming = false) { const stat = this.getOrCreateStat(providerId, model); stat.totalResponses++; if (isStreaming) { stat.streamingResponses++; } else { stat.nonStreamingResponses++; } stat.successfulResponses++; stat.lastResponseTime = new Date(); stat.totalResponseTime += responseTimeMs; stat.averageResponseTime = stat.totalResponseTime / stat.totalResponses; logger_1.logger.debug(`Response recorded: ${providerId}/${model}`, { providerId, model, responseTimeMs, isStreaming, totalResponses: stat.totalResponses, successRate: `${((stat.successfulResponses / stat.totalResponses) * 100).toFixed(1)}%` }); } /** * 记录失败响应 */ recordFailure(providerId, model, error, isStreaming = false) { const stat = this.getOrCreateStat(providerId, model); stat.totalResponses++; if (isStreaming) { stat.streamingResponses++; } else { stat.nonStreamingResponses++; } stat.failedResponses++; stat.lastResponseTime = new Date(); logger_1.logger.warn(`Failed response recorded: ${providerId}/${model}`, { providerId, model, error, isStreaming, totalResponses: stat.totalResponses, failureRate: `${((stat.failedResponses / stat.totalResponses) * 100).toFixed(1)}%` }); } /** * 获取或创建统计记录 */ getOrCreateStat(providerId, model) { if (!this.stats[providerId]) { this.stats[providerId] = {}; } if (!this.stats[providerId][model]) { this.stats[providerId][model] = { providerId, model, totalResponses: 0, successfulResponses: 0, failedResponses: 0, streamingResponses: 0, nonStreamingResponses: 0, lastResponseTime: new Date(), averageResponseTime: 0, totalResponseTime: 0, categories: {} }; } return this.stats[providerId][model]; } /** * 获取所有统计数据 */ getAllStats() { return JSON.parse(JSON.stringify(this.stats)); } /** * 获取特定provider的统计 */ getProviderStats(providerId) { return this.stats[providerId] || null; } /** * 获取汇总统计 */ getSummaryStats() { let totalRequests = 0; let totalSuccessful = 0; const providerCounts = {}; const modelCounts = {}; for (const [providerId, providerData] of Object.entries(this.stats)) { for (const [model, stat] of Object.entries(providerData)) { totalRequests += stat.totalResponses; totalSuccessful += stat.successfulResponses; providerCounts[providerId] = (providerCounts[providerId] || 0) + stat.totalResponses; modelCounts[model] = (modelCounts[model] || 0) + stat.totalResponses; } } // 找出使用最多的provider和model const topProvider = Object.entries(providerCounts).length > 0 ? Object.entries(providerCounts).reduce((a, b) => a[1] > b[1] ? a : b) : null; const topModel = Object.entries(modelCounts).length > 0 ? Object.entries(modelCounts).reduce((a, b) => a[1] > b[1] ? a : b) : null; return { totalRequests, totalProviders: Object.keys(this.stats).length, totalModels: Object.keys(modelCounts).length, topProvider: topProvider ? { providerId: topProvider[0], count: topProvider[1] } : null, topModel: topModel ? { model: topModel[0], count: topModel[1] } : null, overallSuccessRate: totalRequests > 0 ? (totalSuccessful / totalRequests) * 100 : 0 }; } /** * 开始定期日志输出 */ startPeriodicLogging() { this.logInterval = setInterval(() => { this.logSummaryStats(); }, 5 * 60 * 1000); // 每5分钟 } /** * 输出汇总统计日志 */ logSummaryStats() { const summary = this.getSummaryStats(); const runtime = new Date().getTime() - this.startTime.getTime(); const runtimeMinutes = Math.floor(runtime / 60000); logger_1.logger.info('📊 Response Statistics Summary', { runtime: `${runtimeMinutes} minutes`, totalRequests: summary.totalRequests, totalProviders: summary.totalProviders, totalModels: summary.totalModels, overallSuccessRate: `${summary.overallSuccessRate.toFixed(1)}%`, topProvider: summary.topProvider ? `${summary.topProvider.providerId} (${summary.topProvider.count} requests)` : 'N/A', topModel: summary.topModel ? `${summary.topModel.model} (${summary.topModel.count} requests)` : 'N/A' }); // 详细的provider分布统计 this.logDetailedStats(); } /** * 输出详细统计日志 */ logDetailedStats() { const providerBreakdown = {}; const modelBreakdown = {}; for (const [providerId, providerData] of Object.entries(this.stats)) { let providerTotal = 0; for (const [model, stat] of Object.entries(providerData)) { providerTotal += stat.totalResponses; modelBreakdown[model] = (modelBreakdown[model] || 0) + stat.totalResponses; } if (providerTotal > 0) { providerBreakdown[providerId] = providerTotal; } } if (Object.keys(providerBreakdown).length > 0) { logger_1.logger.info('🎯 Provider Distribution', providerBreakdown); } if (Object.keys(modelBreakdown).length > 0) { logger_1.logger.info('🤖 Model Distribution', modelBreakdown); } // 输出权重效果验证 this.logWeightEffectiveness(); } /** * 输出权重效果验证日志 */ logWeightEffectiveness() { const effectiveness = {}; for (const [providerId, providerData] of Object.entries(this.stats)) { for (const [model, stat] of Object.entries(providerData)) { if (stat.totalResponses > 0) { effectiveness[`${providerId}/${model}`] = { responses: stat.totalResponses, successRate: `${((stat.successfulResponses / stat.totalResponses) * 100).toFixed(1)}%`, avgResponseTime: `${stat.averageResponseTime.toFixed(0)}ms`, lastActive: stat.lastResponseTime.toISOString().split('T')[1].split('.')[0] }; } } } if (Object.keys(effectiveness).length > 0) { logger_1.logger.info('⚖️ Weight Effectiveness Analysis', effectiveness); } } /** * 重置统计数据 */ reset() { this.stats = {}; this.startTime = new Date(); logger_1.logger.info('Response statistics reset'); } /** * 停止定期日志 */ loadStats() { try { if ((0, fs_1.existsSync)(this.statsFilePath)) { const data = (0, fs_1.readFileSync)(this.statsFilePath, 'utf8'); this.stats = JSON.parse(data); // Use console.log instead of logger during initialization to avoid port dependency console.log('Response stats loaded from file'); } else { console.log('No existing response stats file found, starting fresh.'); } } catch (error) { // Use console.error instead of logger during initialization console.error('Failed to load response stats:', error instanceof Error ? error.message : String(error)); } } saveStats() { try { (0, fs_1.writeFileSync)(this.statsFilePath, JSON.stringify(this.stats, null, 2)); // Use console.log for save operations to avoid logger dependency issues console.log('Response stats saved to file'); } catch (error) { console.error('Failed to save response stats:', error instanceof Error ? error.message : String(error)); } } destroy() { if (this.logInterval) { clearInterval(this.logInterval); this.logInterval = null; } logger_1.logger.info('ResponseStatsManager destroyed'); } } exports.ResponseStatsManager = ResponseStatsManager; // 导出单例实例 exports.responseStatsManager = new ResponseStatsManager(); //# sourceMappingURL=response-stats.js.map