Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | import dotenv from 'dotenv'; import path from 'path'; import fs from 'fs'; // 使用 process.cwd() 获取项目根目录(server目录) const projectRoot = process.cwd(); dotenv.config({ path: path.resolve(projectRoot, '.env') }); // 加载模型配置 const modelsConfig = JSON.parse(fs.readFileSync(path.join(__dirname, 'models.json'), 'utf-8')); // 获取所有模型(扁平化) function getAllModels() { const models: any[] = []; for (const [vendorKey, vendor] of Object.entries(modelsConfig.vendors) as [string, any][]) { for (const model of vendor.models) { models.push({ ...model, vendor: vendorKey, vendorName: vendor.name, baseUrl: vendor.baseUrl, apiKey: vendor.apiKey, apiType: vendor.apiType, }); } } return models; } // 获取启用的模型 function getEnabledModels() { return getAllModels().filter((m: any) => m.enabled); } // 根据 ID 获取模型配置 function getModel(id: string) { return getAllModels().find((m: any) => m.id === id); } // 根据类型获取模型列表 (text, tts, image, video) function getModelsByType(type: string) { return getAllModels().filter((m: any) => m.input?.includes(type) && m.enabled); } // 检查模型是否可切换(根据错误类型判断是否需要切换) function shouldSwitchModel(error: any): boolean { if (!error) return false; const message = (error?.message || error?.error?.message || '').toLowerCase(); const status = error?.status || error?.response?.status || 0; // 不可切换的错误:认证/权限/参数问题,换供应商也没用 const nonSwitchablePatterns = [ 'invalid api key', 'invalid api-key', 'authentication', 'unauthorized', 'invalid token', 'token expired', 'permission denied', 'access denied', 'invalid request', 'bad request', 'invalidparameter', 'invalid_parameter', // TTS API 参数错误(如文本过短) ]; if (nonSwitchablePatterns.some(p => message.includes(p))) return false; if (status === 401) return false; // 认证失败 // 可切换的错误:限流、余额不足、服务不可用、模型不存在 const switchablePatterns = [ 'rate limit', 'rate_limit', 'too many requests', '请求过于频繁', 'quota', 'balance', 'insufficient', 'usage limit', 'model not found', 'model not support', 'does not exist', 'invalid model', 'service unavailable', 'bad gateway', 'gateway timeout', 'internal server error', ]; if (switchablePatterns.some(p => message.includes(p))) return true; // HTTP 状态码判断 if ([429, 502, 503, 504, 500].includes(status)) return true; if (status === 403) return true; // 403 多数是配额/权限,换Key可能有效 if (status === 404) return true; // 模型不存在,换供应商 // 状态码文本匹配(兜底) if (['429', '502', '503', '504'].some(c => message.includes(c))) return true; return false; } // 获取下一个可用模型(用于自动切换) function getNextModel(currentModelId: string, type: string): string | null { const models = getModelsByType(type); const currentIndex = models.findIndex((m: any) => m.id === currentModelId); if (currentIndex === -1 || currentIndex >= models.length - 1) { return null; // 没有下一个模型 } return models[currentIndex + 1].id; } export const config = { port: parseInt(process.env.PORT || process.env.SERVER_PORT || '3000', 10), nodeEnv: process.env.NODE_ENV || 'development', mongodb: { uri: process.env.MONGODB_URI || 'mongodb://localhost:27017/audio-book', }, jwt: { // 临时硬编码,确保登录和验证一致 secret: 'my-jwt-secret-key-2024', expiresIn: process.env.JWT_EXPIRES_IN || '7d', }, // 模型配置(统一管理,所有 TTS/LLM 配置均从 models.json 读取) models: { vendors: modelsConfig.vendors, list: getAllModels(), enabled: getEnabledModels(), getModel, getModelsByType, shouldSwitchModel, getNextModel, textGeneration: { defaultModel: modelsConfig.textGeneration.defaultModel, }, tts: { defaultModel: modelsConfig.tts.defaultModel, defaultVoice: modelsConfig.tts.defaultVoice, }, }, upload: { dir: path.join(process.cwd(), 'uploads'), maxSize: 50 * 1024 * 1024, // 50MB }, }; |