UNPKG

route-claudecode

Version:

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

570 lines 23.6 kB
#!/usr/bin/env node "use strict"; /** * Provider管理器 - 集成动态模型发现到rcc命令 * 项目所有者: Jason Zhang */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProviderManager = void 0; exports.createProviderManager = createProviderManager; const promises_1 = require("fs/promises"); const fs_1 = require("fs"); const path_1 = require("path"); const os_1 = require("os"); const chalk_1 = __importDefault(require("chalk")); const dynamic_model_discovery_1 = require("../utils/dynamic-model-discovery"); /** * Provider管理器主类 */ class ProviderManager { configBaseDir; dynamicConfigDir; constructor() { this.configBaseDir = (0, path_1.join)((0, os_1.homedir)(), '.route-claude-code', 'config'); this.dynamicConfigDir = (0, path_1.join)(this.configBaseDir, 'dynamic'); } /** * 主要的provider更新功能 */ async updateAllProviders(options = {}) { const startTime = Date.now(); console.log(chalk_1.default.cyan('🚀 Starting Provider Update Process...')); console.log(chalk_1.default.gray(`📁 Config directory: ${this.configBaseDir}`)); console.log(chalk_1.default.gray(`📁 Dynamic config directory: ${this.dynamicConfigDir}`)); // 确保dynamic目录存在 await this.ensureDynamicDirectoryExists(); // 扫描所有配置文件 console.log(chalk_1.default.cyan('\n🔍 Scanning configuration files...')); const configFiles = await this.scanConfigurationFiles(); if (configFiles.length === 0) { throw new Error('No configuration files found'); } console.log(chalk_1.default.green(`✅ Found ${configFiles.length} configuration files`)); configFiles.forEach(file => { console.log(chalk_1.default.gray(` 📄 ${file}`)); }); // 提取所有唯一的providers console.log(chalk_1.default.cyan('\n📋 Extracting providers...')); const allProviders = await this.extractAllProviders(configFiles); if (allProviders.size === 0) { throw new Error('No providers found in configuration files'); } console.log(chalk_1.default.green(`✅ Found ${allProviders.size} unique providers`)); Array.from(allProviders.keys()).forEach(providerId => { const provider = allProviders.get(providerId); console.log(chalk_1.default.gray(` 🔌 ${providerId} (${provider.type}): ${provider.endpoint}`)); }); // 初始化更新结果 const summary = { totalProviders: allProviders.size, successfulUpdates: 0, totalModelsDiscovered: 0, totalAvailableModels: 0, configFilesGenerated: [], errors: [], recommendations: [] }; const results = []; // 批量更新每个provider console.log(chalk_1.default.cyan('\n🔄 Starting provider discovery and testing...')); for (const [providerId, providerConfig] of allProviders) { try { console.log(chalk_1.default.yellow(`\n📡 Processing provider: ${providerId}`)); const result = await this.updateSingleProvider(providerId, providerConfig, options); results.push(result); if (result.success) { summary.successfulUpdates++; summary.totalModelsDiscovered += result.totalModelsFound; summary.totalAvailableModels += result.availableModels.length; summary.configFilesGenerated.push(...Object.values(result.configFiles)); console.log(chalk_1.default.green(` ✅ ${providerId}: ${result.availableModels.length} models available`)); if (result.availableModels.length > 0) { console.log(chalk_1.default.gray(` Models: ${result.availableModels.slice(0, 3).join(', ')}${result.availableModels.length > 3 ? ` and ${result.availableModels.length - 3} more` : ''}`)); } } else { console.log(chalk_1.default.red(` ❌ ${providerId}: Failed`)); result.errors.forEach(error => { console.log(chalk_1.default.red(` Error: ${error}`)); }); } summary.errors.push(...result.errors); } catch (error) { const errorMessage = `Failed to update provider ${providerId}: ${error instanceof Error ? error.message : String(error)}`; summary.errors.push(errorMessage); console.log(chalk_1.default.red(` ❌ ${providerId}: ${errorMessage}`)); } // 提供商间延迟,避免API限制 if (allProviders.size > 1) { await this.delay(2000); } } // 生成综合配置文件 console.log(chalk_1.default.cyan('\n🔧 Generating comprehensive configurations...')); await this.generateComprehensiveConfigs(results, summary); // 生成建议 this.generateRecommendations(summary, results); // 输出最终结果 const duration = Date.now() - startTime; console.log(chalk_1.default.cyan('\n' + '='.repeat(60))); console.log(chalk_1.default.cyan('📊 PROVIDER UPDATE SUMMARY')); console.log(chalk_1.default.cyan('='.repeat(60))); console.log(`\n⏰ Duration: ${Math.round(duration / 1000)}s`); console.log(`📊 Total providers processed: ${summary.totalProviders}`); console.log(`✅ Successful updates: ${summary.successfulUpdates}`); console.log(`❌ Failed updates: ${summary.totalProviders - summary.successfulUpdates}`); console.log(`📋 Total models discovered: ${summary.totalModelsDiscovered}`); console.log(`🎯 Total available models: ${summary.totalAvailableModels}`); console.log(`📄 Config files generated: ${summary.configFilesGenerated.length}`); if (summary.configFilesGenerated.length > 0) { console.log(chalk_1.default.green('\n📁 Generated configuration files:')); summary.configFilesGenerated.forEach(file => { console.log(chalk_1.default.gray(` 📄 ${file}`)); }); } if (summary.recommendations.length > 0) { console.log(chalk_1.default.yellow('\n💡 Recommendations:')); summary.recommendations.forEach(rec => { console.log(chalk_1.default.yellow(` • ${rec}`)); }); } if (summary.errors.length > 0) { console.log(chalk_1.default.red('\n⚠️ Errors encountered:')); summary.errors.slice(0, 5).forEach(error => { console.log(chalk_1.default.red(` • ${error}`)); }); if (summary.errors.length > 5) { console.log(chalk_1.default.red(` • ... and ${summary.errors.length - 5} more errors`)); } } console.log(chalk_1.default.cyan('\n✅ Provider update completed!')); return summary; } /** * 更新单个provider */ async updateSingleProvider(providerId, providerConfig, options) { const result = { providerId, totalModelsFound: 0, availableModels: [], unavailableModels: [], configFiles: { single: '' }, averageResponseTime: 0, errors: [], success: false }; try { // 创建动态模型发现实例 const discoveryConfig = { provider: providerConfig, providerId, maxRetries: options.maxRetries || 3, retryDelay: 1000, requestTimeout: options.timeout || 30000, testPrompt: 'Hello', maxTokens: 10 }; const discovery = (0, dynamic_model_discovery_1.createDynamicModelDiscovery)(discoveryConfig); // 执行模型发现 const discoveryResult = await discovery.discoverModels(); result.totalModelsFound = discoveryResult.totalModels; result.availableModels = discoveryResult.availableModels; result.unavailableModels = discoveryResult.unavailableModels; result.averageResponseTime = discoveryResult.averageResponseTime; result.errors = discoveryResult.errors; // 生成单个provider的配置文件 if (result.availableModels.length > 0) { const singleConfigPath = await this.generateSingleProviderConfig(providerId, providerConfig, result); result.configFiles.single = singleConfigPath; result.success = true; } else { result.errors.push('No available models found'); } return result; } catch (error) { result.errors.push(error instanceof Error ? error.message : String(error)); return result; } } /** * 扫描配置文件 */ async scanConfigurationFiles() { const configFiles = []; // 扫描主配置目录 const directories = [ 'single-provider', 'load-balancing', 'production-ready' ]; for (const dir of directories) { const dirPath = (0, path_1.join)(this.configBaseDir, dir); if ((0, fs_1.existsSync)(dirPath)) { const files = await (0, promises_1.readdir)(dirPath); for (const file of files) { if (file.endsWith('.json') && !file.includes('.backup')) { configFiles.push((0, path_1.join)(dirPath, file)); } } } } return configFiles; } /** * 提取所有唯一的providers */ async extractAllProviders(configFiles) { const providersMap = new Map(); for (const configFile of configFiles) { try { const content = await (0, promises_1.readFile)(configFile, 'utf-8'); const config = JSON.parse(content); if (config.providers) { for (const [providerId, providerConfig] of Object.entries(config.providers)) { if (!providersMap.has(providerId)) { providersMap.set(providerId, providerConfig); } } } } catch (error) { console.log(chalk_1.default.red(`⚠️ Failed to parse config file ${configFile}: ${error instanceof Error ? error.message : String(error)}`)); } } return providersMap; } /** * 生成单个provider配置 */ async generateSingleProviderConfig(providerId, providerConfig, result) { const configFileName = `config-${providerId}-dynamic.json`; const configPath = (0, path_1.join)(this.dynamicConfigDir, configFileName); // 构建配置对象 const dynamicConfig = { name: `Dynamic ${providerId} Configuration`, description: `Auto-generated config for ${providerId} with ${result.availableModels.length} available models`, generatedAt: new Date().toISOString(), server: { port: 6690 + Math.floor(Math.random() * 100), host: "0.0.0.0" }, providers: { [providerId]: { ...providerConfig, models: result.availableModels, defaultModel: result.availableModels[0] || null } }, routing: { default: { provider: providerId, model: result.availableModels[0] || null } }, loadBalancing: { enabled: false }, failover: { enabled: false }, debug: { enabled: true, logLevel: "info", traceRequests: true, saveRequests: false, logDir: (0, path_1.join)((0, os_1.homedir)(), '.route-claude-code', 'logs') }, hooks: [] }; await (0, promises_1.writeFile)(configPath, JSON.stringify(dynamicConfig, null, 2)); return configPath; } /** * 生成综合配置文件 */ async generateComprehensiveConfigs(results, summary) { const successfulResults = results.filter(r => r.success && r.availableModels.length > 0); if (successfulResults.length === 0) { return; } // 1. 生成负载均衡配置(所有provider) await this.generateLoadBalancingConfig(successfulResults, summary); // 2. 生成OpenAI兼容混合配置 await this.generateOpenAIMixedConfig(successfulResults, summary); } /** * 生成负载均衡配置 */ async generateLoadBalancingConfig(results, summary) { const configPath = (0, path_1.join)(this.dynamicConfigDir, 'config-load-balancing-comprehensive.json'); const providers = {}; const routingProviders = []; for (const result of results) { // 查找原始provider配置 const originalProvider = await this.findOriginalProviderConfig(result.providerId); if (!originalProvider) continue; providers[result.providerId] = { ...originalProvider, models: result.availableModels, defaultModel: result.availableModels[0] }; routingProviders.push({ provider: result.providerId, model: result.availableModels[0], weight: Math.max(1, Math.floor(result.availableModels.length / 2)) }); } const loadBalancingConfig = { name: "Comprehensive Load Balancing Configuration", description: `Auto-generated load balancing config with ${results.length} providers`, generatedAt: new Date().toISOString(), server: { port: 6690, host: "0.0.0.0" }, providers, routing: { default: { providers: routingProviders, loadBalancing: { enabled: true, strategy: "health_based" }, failover: { enabled: true, triggers: [ { type: "http_status", codes: [429, 500, 502, 503, 504], blacklistDuration: 300 } ] } } }, loadBalancing: { enabled: true, strategy: "health_based_with_blacklist" }, failover: { enabled: true, triggers: [ { type: "http_status", codes: [429, 500, 502, 503, 504], blacklistDuration: 300 } ] }, debug: { enabled: true, logLevel: "info", traceRequests: true, saveRequests: false, logDir: (0, path_1.join)((0, os_1.homedir)(), '.route-claude-code', 'logs') }, hooks: [] }; await (0, promises_1.writeFile)(configPath, JSON.stringify(loadBalancingConfig, null, 2)); summary.configFilesGenerated.push(configPath); } /** * 生成OpenAI兼容混合配置 */ async generateOpenAIMixedConfig(results, summary) { // 只选择OpenAI兼容的providers const openaiCompatibleResults = results.filter(result => { return result.providerId.includes('openai') || result.providerId.includes('shuaihong') || result.providerId.includes('lmstudio') || result.providerId.includes('modelscope'); }); if (openaiCompatibleResults.length === 0) { return; } const configPath = (0, path_1.join)(this.dynamicConfigDir, 'config-openai-compatible-mixed.json'); // 合并所有OpenAI兼容provider的模型 const allModels = []; const maxTokensMap = {}; let primaryProvider = null; for (const result of openaiCompatibleResults) { allModels.push(...result.availableModels); // 设置默认max_tokens result.availableModels.forEach(model => { maxTokensMap[model] = this.inferMaxTokens(model); }); if (!primaryProvider) { primaryProvider = await this.findOriginalProviderConfig(result.providerId); } } // 去重模型 const uniqueModels = Array.from(new Set(allModels)); const mixedConfig = { name: "OpenAI Compatible Mixed Configuration", description: `Auto-generated OpenAI mixed config with ${uniqueModels.length} models from ${openaiCompatibleResults.length} providers`, generatedAt: new Date().toISOString(), server: { port: 6691, host: "0.0.0.0" }, providers: { "openai-mixed": { ...primaryProvider, models: uniqueModels, defaultModel: uniqueModels[0], maxTokens: maxTokensMap } }, routing: { default: { provider: "openai-mixed", model: uniqueModels[0] }, background: { provider: "openai-mixed", model: uniqueModels.find(m => m.includes('mini') || m.includes('flash')) || uniqueModels[0] }, thinking: { provider: "openai-mixed", model: uniqueModels.find(m => m.includes('gpt-4') || m.includes('claude-3')) || uniqueModels[0] }, longcontext: { provider: "openai-mixed", model: uniqueModels.find(m => m.includes('128k') || m.includes('long')) || uniqueModels[0] }, search: { provider: "openai-mixed", model: uniqueModels.find(m => m.includes('search')) || uniqueModels[0] } }, loadBalancing: { enabled: false }, failover: { enabled: false }, debug: { enabled: true, logLevel: "info", traceRequests: true, saveRequests: false, logDir: (0, path_1.join)((0, os_1.homedir)(), '.route-claude-code', 'logs') }, hooks: [] }; await (0, promises_1.writeFile)(configPath, JSON.stringify(mixedConfig, null, 2)); summary.configFilesGenerated.push(configPath); } /** * 查找原始provider配置 */ async findOriginalProviderConfig(providerId) { const configFiles = await this.scanConfigurationFiles(); for (const configFile of configFiles) { try { const content = await (0, promises_1.readFile)(configFile, 'utf-8'); const config = JSON.parse(content); if (config.providers && config.providers[providerId]) { return config.providers[providerId]; } } catch (error) { // 忽略解析错误 } } return null; } /** * 推断模型最大token数 */ inferMaxTokens(modelId) { const lowerId = modelId.toLowerCase(); if (lowerId.includes('128k') || lowerId.includes('long')) { return 131072; } else if (lowerId.includes('32k')) { return 32768; } else if (lowerId.includes('16k')) { return 16384; } else if (lowerId.includes('qwen') && lowerId.includes('coder')) { return 262144; } else if (lowerId.includes('deepseek')) { return 131072; } else if (lowerId.includes('gpt-4')) { return 131072; } else if (lowerId.includes('claude-3')) { return 262144; } else if (lowerId.includes('gemini')) { return 131072; } return 8192; // 默认值 } /** * 生成建议 */ generateRecommendations(summary, results) { // 成功率建议 const successRate = summary.successfulUpdates / summary.totalProviders; if (successRate < 0.5) { summary.recommendations.push('Low success rate detected - check API keys and network connectivity'); } // 模型数量建议 const avgModelsPerProvider = summary.totalAvailableModels / summary.successfulUpdates; if (avgModelsPerProvider < 3) { summary.recommendations.push('Consider adding more models or providers for better load balancing'); } // 性能建议 const highResponseTimeProviders = results.filter(r => r.averageResponseTime > 5000); if (highResponseTimeProviders.length > 0) { summary.recommendations.push(`${highResponseTimeProviders.length} providers have high response times - consider optimization`); } // 错误分析建议 const authErrors = summary.errors.filter(e => e.includes('401') || e.includes('403') || e.includes('unauthorized')); if (authErrors.length > 0) { summary.recommendations.push('Authentication errors detected - verify API keys and permissions'); } const rateLimitErrors = summary.errors.filter(e => e.includes('429') || e.includes('rate limit')); if (rateLimitErrors.length > 0) { summary.recommendations.push('Rate limiting detected - consider implementing delays or multiple API keys'); } } /** * 确保dynamic目录存在 */ async ensureDynamicDirectoryExists() { if (!(0, fs_1.existsSync)(this.dynamicConfigDir)) { await (0, promises_1.mkdir)(this.dynamicConfigDir, { recursive: true }); console.log(chalk_1.default.green(`✅ Created dynamic config directory: ${this.dynamicConfigDir}`)); } } /** * 延迟函数 */ delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } exports.ProviderManager = ProviderManager; /** * 工厂函数 */ function createProviderManager() { return new ProviderManager(); } //# sourceMappingURL=provider-manager.js.map