UNPKG

route-claudecode

Version:

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

650 lines 27.2 kB
"use strict"; /** * 智能模型发现系统 v2.0 * 通过/models端点获取模型列表,多次测试避免流控误判,智能更新配置 */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.DEFAULT_DISCOVERY_CONFIG = exports.IntelligentModelDiscovery = void 0; exports.createIntelligentModelDiscovery = createIntelligentModelDiscovery; const logger_1 = require("@/utils/logger"); const types_1 = require("@/types"); const fs_1 = require("fs"); const path = __importStar(require("path")); class IntelligentModelDiscovery { config; cache = new Map(); cacheExpiry = new Map(); testQueue = []; activeTests = new Set(); constructor(config) { this.config = config; } /** * 执行完整的智能模型发现流程 */ async discoverAndUpdateModels() { const startTime = Date.now(); const report = { providerId: this.config.providerId, discoveryTime: startTime, totalModelsFound: 0, modelsFromAPI: 0, modelsFromFallback: 0, availableModels: [], unavailableModels: [], configurationUpdated: false, recommendations: [], warnings: [], errors: [], performance: { totalDuration: 0, apiCallDuration: 0, testingDuration: 0, averageResponseTime: 0 } }; try { logger_1.logger.info('Starting intelligent model discovery', { providerId: this.config.providerId, testCount: this.config.testConfig.testCount, minSuccessRate: this.config.qualityThresholds.minSuccessRate }, 'intelligent-discovery'); // 1. 获取模型列表 const apiStartTime = Date.now(); const models = await this.fetchModelsFromAPI(); report.performance.apiCallDuration = Date.now() - apiStartTime; if (models.length === 0) { report.warnings.push('No models found from API, using fallback models'); const fallbackModels = this.getFallbackModels(); models.push(...fallbackModels); report.modelsFromFallback = fallbackModels.length; } else { report.modelsFromAPI = models.length; } report.totalModelsFound = models.length; // 2. 智能模型测试 const testStartTime = Date.now(); const testResults = await this.intelligentModelTesting(models); report.performance.testingDuration = Date.now() - testStartTime; // 3. 分析测试结果 const { available, unavailable } = this.analyzeTestResults(testResults); report.availableModels = available; report.unavailableModels = unavailable; // 4. 计算性能指标 const allResponseTimes = [...available, ...unavailable] .filter(r => r.responseTime > 0) .map(r => r.responseTime); report.performance.averageResponseTime = allResponseTimes.length > 0 ? allResponseTimes.reduce((sum, time) => sum + time, 0) / allResponseTimes.length : 0; // 5. 生成建议和警告 this.generateRecommendations(report); // 6. 更新配置(如果有可用模型) if (available.length > 0) { const updateSuccess = await this.updateProviderConfiguration(available); report.configurationUpdated = updateSuccess; if (updateSuccess) { report.recommendations.push(`Configuration updated with ${available.length} available models`); } else { report.warnings.push('Failed to update configuration file'); } } else { report.warnings.push('No available models found, configuration not updated'); } report.performance.totalDuration = Date.now() - startTime; logger_1.logger.info('Intelligent model discovery completed', { providerId: this.config.providerId, totalModels: report.totalModelsFound, availableModels: report.availableModels.length, unavailableModels: report.unavailableModels.length, configUpdated: report.configurationUpdated, duration: report.performance.totalDuration }, 'intelligent-discovery'); return report; } catch (error) { const errorMessage = `Discovery failed: ${error instanceof Error ? error.message : String(error)}`; report.errors.push(errorMessage); report.performance.totalDuration = Date.now() - startTime; logger_1.logger.error('Intelligent model discovery failed', { providerId: this.config.providerId, error: error instanceof Error ? error.message : String(error), duration: report.performance.totalDuration }, 'intelligent-discovery'); return report; } } /** * 从API获取模型列表 */ async fetchModelsFromAPI() { const cacheKey = `models:${this.config.providerId}`; // 检查缓存 if (this.isCacheValid(cacheKey)) { logger_1.logger.debug('Using cached model list', { providerId: this.config.providerId }, 'intelligent-discovery'); return this.cache.get(cacheKey); } const modelsEndpoint = this.buildModelsEndpoint(); const headers = this.buildAuthHeaders(); try { logger_1.logger.debug('Fetching models from API', { providerId: this.config.providerId, endpoint: modelsEndpoint }, 'intelligent-discovery'); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.config.testConfig.requestTimeout); const response = await fetch(modelsEndpoint, { method: 'GET', headers, signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); const models = this.parseModelsResponse(data); // 缓存结果 this.cache.set(cacheKey, models); this.cacheExpiry.set(cacheKey, Date.now() + this.config.cacheConfig.modelListTTL); logger_1.logger.info('Successfully fetched models from API', { providerId: this.config.providerId, modelCount: models.length, sampleModels: models.slice(0, 3).map(m => m.id) }, 'intelligent-discovery'); return models; } catch (error) { logger_1.logger.warn('Failed to fetch models from API', { providerId: this.config.providerId, endpoint: modelsEndpoint, error: error instanceof Error ? error.message : String(error) }, 'intelligent-discovery'); throw error; } } /** * 智能模型测试 - 考虑流控和并发限制 */ async intelligentModelTesting(models) { const results = []; const { maxConcurrentTests, testCount, testInterval, rateLimitBackoff } = this.config.testConfig; // 按优先级排序模型(常见模型优先测试) const prioritizedModels = this.prioritizeModels(models); logger_1.logger.info('Starting intelligent model testing', { providerId: this.config.providerId, totalModels: models.length, testCount, maxConcurrentTests }, 'intelligent-discovery'); // 分批测试,控制并发 for (let i = 0; i < prioritizedModels.length; i += maxConcurrentTests) { const batch = prioritizedModels.slice(i, i + maxConcurrentTests); logger_1.logger.debug(`Testing model batch ${Math.floor(i / maxConcurrentTests) + 1}`, { providerId: this.config.providerId, batchSize: batch.length, models: batch.map(m => m.id) }, 'intelligent-discovery'); // 并发测试当前批次 const batchPromises = batch.map(model => this.testModelWithRetryLogic(model, testCount)); const batchResults = await Promise.allSettled(batchPromises); // 处理批次结果 batchResults.forEach((result, index) => { if (result.status === 'fulfilled') { results.push(result.value); } else { // 测试失败,创建失败结果 results.push({ modelId: batch[index].id, isAvailable: false, responseTime: 0, successRate: 0, testCount: 0, errors: [result.reason?.message || 'Test failed'], statusCodes: [], lastTestTime: Date.now(), confidence: 'low' }); } }); // 批次间延迟,避免过快请求 if (i + maxConcurrentTests < prioritizedModels.length) { await this.delay(rateLimitBackoff); } } return results; } /** * 带重试逻辑的模型测试 */ async testModelWithRetryLogic(model, testCount) { const results = []; const { testInterval, rateLimitBackoff } = this.config.testConfig; for (let attempt = 0; attempt < testCount; attempt++) { try { logger_1.logger.debug(`Testing model ${model.id} (${attempt + 1}/${testCount})`, { providerId: this.config.providerId, modelId: model.id, attempt: attempt + 1 }, 'intelligent-discovery'); const startTime = Date.now(); await this.sendTestRequest(model.id); const responseTime = Date.now() - startTime; results.push({ success: true, responseTime }); logger_1.logger.debug(`Model test successful`, { providerId: this.config.providerId, modelId: model.id, attempt: attempt + 1, responseTime }, 'intelligent-discovery'); } catch (error) { const responseTime = Date.now(); const statusCode = error?.response?.status || error?.status; const errorMessage = error instanceof Error ? error.message : String(error); results.push({ success: false, responseTime, error: errorMessage, statusCode }); logger_1.logger.debug(`Model test failed`, { providerId: this.config.providerId, modelId: model.id, attempt: attempt + 1, error: errorMessage, statusCode }, 'intelligent-discovery'); // 如果是429错误,增加延迟 if (statusCode === 429) { await this.delay(rateLimitBackoff * (attempt + 1)); } } // 测试间隔 if (attempt < testCount - 1) { await this.delay(testInterval); } } // 分析测试结果 return this.analyzeModelTestResults(model.id, results); } /** * 分析单个模型的测试结果 */ analyzeModelTestResults(modelId, results) { const successfulTests = results.filter(r => r.success); const failedTests = results.filter(r => !r.success); const successRate = successfulTests.length / results.length; const averageResponseTime = successfulTests.length > 0 ? successfulTests.reduce((sum, r) => sum + r.responseTime, 0) / successfulTests.length : 0; // 错误分析 const rateLimitErrors = failedTests.filter(r => r.statusCode === 429); const authErrors = failedTests.filter(r => r.statusCode === 401 || r.statusCode === 403); const serverErrors = failedTests.filter(r => r.statusCode && r.statusCode >= 500); // 判断可用性和置信度 let isAvailable = false; let confidence = 'low'; if (successRate >= 0.8) { isAvailable = true; confidence = 'high'; } else if (successRate >= 0.6) { isAvailable = true; confidence = 'medium'; } else if (successRate >= 0.4 && rateLimitErrors.length === failedTests.length) { // 如果失败都是429错误,可能是流控,认为模型可用但置信度低 isAvailable = true; confidence = 'low'; } else if (authErrors.length > 0) { // 认证错误,模型不可用 isAvailable = false; confidence = 'high'; } return { modelId, isAvailable, responseTime: averageResponseTime, successRate, testCount: results.length, errors: failedTests.map(r => r.error || 'Unknown error'), statusCodes: failedTests.map(r => r.statusCode || 0).filter(code => code > 0), lastTestTime: Date.now(), confidence }; } /** * 分析所有测试结果 */ analyzeTestResults(results) { const { minSuccessRate, maxResponseTime, minConfidenceLevel } = this.config.qualityThresholds; const confidenceLevels = { low: 0, medium: 1, high: 2 }; const minConfidenceValue = confidenceLevels[minConfidenceLevel]; const available = results.filter(result => { return result.isAvailable && result.successRate >= minSuccessRate && result.responseTime <= maxResponseTime && confidenceLevels[result.confidence] >= minConfidenceValue; }); const unavailable = results.filter(result => !available.includes(result)); // 按质量排序可用模型 available.sort((a, b) => { // 优先级:置信度 > 成功率 > 响应时间 const confidenceDiff = confidenceLevels[b.confidence] - confidenceLevels[a.confidence]; if (confidenceDiff !== 0) return confidenceDiff; const successRateDiff = b.successRate - a.successRate; if (Math.abs(successRateDiff) > 0.1) return successRateDiff; return a.responseTime - b.responseTime; }); return { available, unavailable }; } /** * 更新提供商配置 */ async updateProviderConfiguration(availableModels) { try { // 这里需要根据实际的配置文件结构来实现 // 示例实现:更新配置文件中的模型列表 const configPath = this.findConfigurationFile(); if (!configPath) { logger_1.logger.warn('Configuration file not found', { providerId: this.config.providerId }, 'intelligent-discovery'); return false; } const configContent = await fs_1.promises.readFile(configPath, 'utf-8'); const config = JSON.parse(configContent); // 更新模型列表 const modelIds = availableModels.map(m => m.modelId); const providerConfig = config.providers[this.config.providerId]; if (providerConfig) { // 备份原配置 const backupPath = `${configPath}.backup.${Date.now()}`; await fs_1.promises.writeFile(backupPath, configContent); // 更新模型列表 providerConfig.models = modelIds; // 设置默认模型(选择最高质量的模型) if (modelIds.length > 0) { providerConfig.defaultModel = modelIds[0]; } // 写入更新后的配置 await fs_1.promises.writeFile(configPath, JSON.stringify(config, null, 2)); logger_1.logger.info('Configuration updated successfully', { providerId: this.config.providerId, configPath, backupPath, modelCount: modelIds.length, defaultModel: providerConfig.defaultModel }, 'intelligent-discovery'); return true; } return false; } catch (error) { logger_1.logger.error('Failed to update configuration', { providerId: this.config.providerId, error: error instanceof Error ? error.message : String(error) }, 'intelligent-discovery'); return false; } } /** * 生成建议和警告 */ generateRecommendations(report) { const { availableModels, unavailableModels } = report; // 性能建议 if (report.performance.averageResponseTime > 5000) { report.warnings.push('High average response time detected, consider optimizing requests'); } // 可用性建议 if (availableModels.length === 0) { report.warnings.push('No models are available, check provider configuration and API keys'); } else if (availableModels.length < 3) { report.recommendations.push('Consider adding more models for better load balancing'); } // 质量建议 const lowConfidenceModels = availableModels.filter(m => m.confidence === 'low'); if (lowConfidenceModels.length > 0) { report.warnings.push(`${lowConfidenceModels.length} models have low confidence, monitor their performance`); } // 错误分析 const rateLimitIssues = unavailableModels.filter(m => m.statusCodes.some(code => code === 429)); if (rateLimitIssues.length > 0) { report.recommendations.push('Consider implementing rate limiting or using multiple API keys'); } const authIssues = unavailableModels.filter(m => m.statusCodes.some(code => code === 401 || code === 403)); if (authIssues.length > 0) { report.warnings.push('Authentication issues detected, verify API keys and permissions'); } } // 辅助方法 buildModelsEndpoint() { const { provider } = this.config; const baseUrl = provider.endpoint.replace(/\/v1\/chat\/completions$/, ''); return `${baseUrl}/v1/models`; } buildAuthHeaders() { const { provider } = this.config; const headers = { 'Content-Type': 'application/json', 'User-Agent': 'claude-code-router/2.7.0' }; if (provider.authentication.type !== 'none') { const credentials = provider.authentication.credentials; const apiKey = credentials ? (credentials.apiKey || credentials.api_key) : ''; if (Array.isArray(apiKey)) { headers['Authorization'] = `Bearer ${apiKey[0]}`; } else if (apiKey) { headers['Authorization'] = `Bearer ${apiKey}`; } } return headers; } parseModelsResponse(data) { const models = []; if (data.data && Array.isArray(data.data)) { // OpenAI格式 for (const model of data.data) { models.push({ id: model.id, name: model.id, description: `Model: ${model.id}`, created: model.created, owned_by: model.owned_by, capabilities: this.inferModelCapabilities(model.id) }); } } else if (data.models && Array.isArray(data.models)) { // Gemini格式 for (const model of data.models) { models.push({ id: model.name.replace('models/', ''), name: model.displayName || model.name, description: model.description, capabilities: { text: true, vision: model.supportedGenerationMethods?.includes('generateContent') || false, tools: model.supportedGenerationMethods?.includes('generateContent') || false, streaming: true } }); } } return models; } prioritizeModels(models) { // 常见模型优先级 const priorityPatterns = [ /gpt-4o/i, /gpt-4/i, /claude-3/i, /gemini/i, /qwen/i, /glm/i ]; return models.sort((a, b) => { const aPriority = priorityPatterns.findIndex(pattern => pattern.test(a.id)); const bPriority = priorityPatterns.findIndex(pattern => pattern.test(b.id)); if (aPriority !== -1 && bPriority !== -1) { return aPriority - bPriority; } else if (aPriority !== -1) { return -1; } else if (bPriority !== -1) { return 1; } return a.id.localeCompare(b.id); }); } async sendTestRequest(modelId) { const endpoint = this.config.provider.endpoint; const headers = this.buildAuthHeaders(); const { testPrompt, maxTokens, requestTimeout } = this.config.testConfig; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), requestTimeout); const response = await fetch(endpoint, { method: 'POST', headers, body: JSON.stringify({ model: modelId, messages: [{ role: 'user', content: testPrompt }], max_tokens: maxTokens, stream: false }), signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { throw new types_1.ProviderError(`Test request failed: ${response.statusText}`, this.config.providerId, response.status); } return response.json(); } inferModelCapabilities(modelId) { const lowerId = modelId.toLowerCase(); return { text: true, vision: lowerId.includes('vision') || lowerId.includes('gpt-4') || lowerId.includes('claude-3'), tools: lowerId.includes('gpt-4') || lowerId.includes('claude-3') || lowerId.includes('gemini'), streaming: true }; } getFallbackModels() { const { provider } = this.config; switch (provider.type) { case 'openai': return [ { id: 'gpt-4o-mini', name: 'GPT-4o Mini', capabilities: { text: true, vision: false, tools: true, streaming: true } }, { id: 'gpt-4', name: 'GPT-4', capabilities: { text: true, vision: false, tools: true, streaming: true } } ]; default: return [ { id: 'default-model', name: 'Default Model', capabilities: { text: true, vision: false, tools: false, streaming: true } } ]; } } findConfigurationFile() { // 这里需要根据实际项目结构来实现 // 示例:查找配置文件 const possiblePaths = [ path.join(process.cwd(), 'config.json'), path.join(process.env.HOME || '', '.route-claude-code', 'config', 'current.json') ]; for (const configPath of possiblePaths) { try { if (require('fs').existsSync(configPath)) { return configPath; } } catch (error) { // 忽略错误,继续查找 } } return null; } isCacheValid(cacheKey) { const expiryTime = this.cacheExpiry.get(cacheKey); return expiryTime ? Date.now() < expiryTime : false; } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } exports.IntelligentModelDiscovery = IntelligentModelDiscovery; /** * 创建智能模型发现实例的工厂函数 */ function createIntelligentModelDiscovery(config) { return new IntelligentModelDiscovery(config); } /** * 默认配置 */ exports.DEFAULT_DISCOVERY_CONFIG = { testConfig: { testCount: 3, testInterval: 1000, requestTimeout: 10000, testPrompt: 'Hello', maxTokens: 5, rateLimitBackoff: 2000, maxConcurrentTests: 2 }, qualityThresholds: { minSuccessRate: 0.6, maxResponseTime: 10000, minConfidenceLevel: 'medium' }, cacheConfig: { modelListTTL: 30 * 60 * 1000, // 30分钟 testResultTTL: 10 * 60 * 1000, // 10分钟 enablePersistentCache: true } }; //# sourceMappingURL=intelligent-model-discovery.js.map