(force = false)
| 16 | return config.modelsCacheTtl > 0 && Date.now() - cachedModelsAt > config.modelsCacheTtl * 1000 |
| 17 | } |
| 18 | |
| 19 | const getLatestModels = async (force = false) => { |
| 20 | // 如果有缓存、未过期且不强制刷新,直接返回 |
| 21 | if (cachedModels && !force && !isCacheExpired()) { |
| 22 | return cachedModels |
| 23 | } |
| 24 | |
| 25 | // 如果正在获取,返回当前的 Promise |
| 26 | if (fetchPromise) { |
| 27 | return fetchPromise |
| 28 | } |
| 29 | |
| 30 | const chatBaseUrl = getChatBaseUrl() |
| 31 | // 一次取出账户对象,token 与 proxy 走同一个账号,避免 round-robin 错位 |
| 32 | const account = accountManager.getAccount() |
| 33 | const proxyAgent = getProxyAgent(account) |
| 34 | |
| 35 | // Antidetect: per-account fingerprint headers replace static block |
| 36 | const ssxmod = getSsxmodForAccount(account) |
| 37 | const headers = buildRequestHeaders(account, { |
| 38 | chatBaseUrl, |
| 39 | token: account?.token, |
| 40 | ssxmodItna: ssxmod.ssxmod_itna, |
| 41 | ssxmodItna2: ssxmod.ssxmod_itna2, |
| 42 | accept: '*/*', |
| 43 | extra: { |
| 44 | 'x-request-id': generateUUID() |
| 45 | } |
| 46 | }) |
| 47 | |
| 48 | const requestConfig = { |
| 49 | headers |
| 50 | } |
| 51 | |
| 52 | // 添加代理配置 |
| 53 | if (proxyAgent) { |
| 54 | requestConfig.httpsAgent = proxyAgent |
| 55 | requestConfig.proxy = false |
| 56 | } |
| 57 | |
| 58 | fetchPromise = axios.get(`${chatBaseUrl}/api/models`, requestConfig).then(response => { |
| 59 | cachedModels = response.data.data |
| 60 | cachedModelsAt = Date.now() |
| 61 | fetchPromise = null |
| 62 | return cachedModels |
| 63 | }).catch(error => { |
| 64 | logger.error(`获取模型列表失败: ${error.message}`, 'MODEL') |
| 65 | fetchPromise = null |
| 66 | // 刷新失败时回退到旧缓存,避免返回空列表;重置时间戳以免每个请求都重试 |
| 67 | if (cachedModels) { |
| 68 | cachedModelsAt = Date.now() |
| 69 | return cachedModels |
| 70 | } |
| 71 | return [] |
| 72 | }) |
| 73 | |
| 74 | return fetchPromise |
| 75 | } |
no test coverage detected