From fe131b33d223ae5bc02dd3dfc65799a194fa680e Mon Sep 17 00:00:00 2001 From: hex2077 Date: Tue, 20 Jan 2026 00:10:04 +0800 Subject: [PATCH] =?UTF-8?q?fix(provider):=20=E4=BC=98=E5=8C=96=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=88=B7=E6=96=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改节点刷新逻辑,当健康节点少于5个时立即刷新 - 移除刷新时的随机延迟以避免并发问题 - 更新Claude Kiro版本号至0.8.140 - 在初始化API服务时添加isReady参数控制预热逻辑 - 在modal.js中排除更多字段 --- src/providers/claude/claude-kiro.js | 6 ++-- src/providers/provider-pool-manager.js | 43 ++++++++------------------ src/services/api-server.js | 2 +- src/services/service-manager.js | 22 +++++++------ static/app/modal.js | 2 +- 5 files changed, 30 insertions(+), 45 deletions(-) diff --git a/src/providers/claude/claude-kiro.js b/src/providers/claude/claude-kiro.js index 252fcff..2c9996c 100644 --- a/src/providers/claude/claude-kiro.js +++ b/src/providers/claude/claude-kiro.js @@ -31,7 +31,7 @@ const KIRO_CONSTANTS = { AXIOS_TIMEOUT: 120000, // 2 minutes timeout for normal requests TOKEN_REFRESH_TIMEOUT: 15000, // 15 seconds timeout for token refresh (shorter to avoid blocking) USER_AGENT: 'KiroIDE', - KIRO_VERSION: '0.7.5', + KIRO_VERSION: '0.8.140', CONTENT_TYPE_JSON: 'application/json', ACCEPT_JSON: 'application/json', AUTH_METHOD_SOCIAL: 'social', @@ -403,7 +403,7 @@ export class KiroApiService { // 配置 HTTP/HTTPS agent 限制连接池大小,避免资源泄漏 const httpAgent = new http.Agent({ keepAlive: true, - maxSockets: 100, // 每个主机最多 10 个连接 + maxSockets: 100, // 每个主机最多 100 个连接 maxFreeSockets: 5, // 最多保留 5 个空闲连接 timeout: KIRO_CONSTANTS.AXIOS_TIMEOUT, }); @@ -2735,7 +2735,7 @@ async saveCredentialsToFile(filePath, newData) { } const fullUrl = `${usageLimitsUrl}?${params.toString()}`; - // 构建请求头 + // 动态生成 headers const machineId = generateMachineIdFromConfig({ uuid: this.uuid, profileArn: this.profileArn, diff --git a/src/providers/provider-pool-manager.js b/src/providers/provider-pool-manager.js index 8d4e290..d182bc3 100644 --- a/src/providers/provider-pool-manager.js +++ b/src/providers/provider-pool-manager.js @@ -108,32 +108,7 @@ export class ProviderPoolManager { if (configPath && fs.existsSync(configPath)) { try { - const fileContent = fs.readFileSync(configPath, 'utf8'); - const data = JSON.parse(fileContent); - - // 获取对应的适配器 - const tempConfig = { - ...config, - MODEL_PROVIDER: providerType - }; - const serviceAdapter = getServiceAdapter(tempConfig); - - // 调用提供商适配器内的 isExpiryDateNear 方法 - let needsRefresh = false; - if (typeof serviceAdapter.isExpiryDateNear === 'function') { - // 适配器内部自行判断,不传参 - needsRefresh = serviceAdapter.isExpiryDateNear(); - this._log('info', `Node ${providerStatus.uuid} (${providerType}) isExpiryDateNear: ${needsRefresh}`); - } else { - // 兜底逻辑:如果适配器没实现,使用配置数据进行判断 - const expiryDate = data.expiry_date || data.expires_at || data.expiry; - if (expiryDate) { - const expiry = new Date(expiryDate).getTime(); - needsRefresh = (expiry - Date.now()) < 24 * 60 * 60 * 1000; - } - } - - if (needsRefresh) { + if (true) { this._log('warn', `Node ${providerStatus.uuid} (${providerType}) is near expiration. Enqueuing refresh...`); this._enqueueRefresh(providerType, providerStatus); } @@ -141,7 +116,7 @@ export class ProviderPoolManager { this._log('error', `Failed to check expiry for node ${providerStatus.uuid}: ${err.message}`); } } else { - this._log('debug', `Node ${providerStatus.uuid} (${providerType}) has no valid config file path or file does not exist.`); + this._log('debug', `Node ${providerStatus.uuid} (${providerType}) has no valid config file path or file does not exist.`); } } } @@ -203,6 +178,14 @@ export class ProviderPoolManager { return; } + // 判断提供商池内的总可用节点数,小于5个时,不等待缓冲,直接加入刷新队列 + const healthyCount = this.getHealthyCount(providerType); + if (healthyCount < 5) { + this._log('info', `Provider ${providerType} has only ${healthyCount} healthy nodes. Bypassing buffer and enqueuing refresh for ${uuid} immediately.`); + this._enqueueRefreshImmediate(providerType, providerStatus, force); + return; + } + // 初始化缓冲队列 if (!this.refreshBufferQueues[providerType]) { this.refreshBufferQueues[providerType] = new Map(); // 使用 Map 自动去重 @@ -383,9 +366,9 @@ export class ProviderPoolManager { } // 添加5秒内的随机等待时间,避免并发刷新时的冲突 - const randomDelay = Math.floor(Math.random() * 5000); - this._log('info', `Starting token refresh for node ${providerStatus.uuid} (${providerType}) with ${randomDelay}ms delay`); - await new Promise(resolve => setTimeout(resolve, randomDelay)); + // const randomDelay = Math.floor(Math.random() * 5000); + // this._log('info', `Starting token refresh for node ${providerStatus.uuid} (${providerType}) with ${randomDelay}ms delay`); + // await new Promise(resolve => setTimeout(resolve, randomDelay)); try { // 增加刷新计数 diff --git a/src/services/api-server.js b/src/services/api-server.js index d357987..ebdbb85 100644 --- a/src/services/api-server.js +++ b/src/services/api-server.js @@ -242,7 +242,7 @@ async function startServer() { } // Initialize API services - const services = await initApiService(CONFIG); + const services = await initApiService(CONFIG, true); // Initialize UI management features initializeUIManagement(CONFIG); diff --git a/src/services/service-manager.js b/src/services/service-manager.js index ce3b154..84786d2 100644 --- a/src/services/service-manager.js +++ b/src/services/service-manager.js @@ -165,7 +165,7 @@ async function scanProviderDirectory(dirPath, linkedPaths, newProviders, options * @param {Object} config - The server configuration * @returns {Promise} The initialized services */ -export async function initApiService(config) { +export async function initApiService(config, isReady = false) { if (config.providerPools && Object.keys(config.providerPools).length > 0) { providerPoolManager = new ProviderPoolManager(config.providerPools, { @@ -175,16 +175,18 @@ export async function initApiService(config) { }); console.log('[Initialization] ProviderPoolManager initialized with configured pools.'); - // --- V2: 触发系统预热 --- - // 预热逻辑是异步的,不会阻塞服务器启动 - providerPoolManager.warmupNodes().catch(err => { - console.error(`[Initialization] Warmup failed: ${err.message}`); - }); + if(isReady){ + // --- V2: 触发系统预热 --- + // 预热逻辑是异步的,不会阻塞服务器启动 + providerPoolManager.warmupNodes().catch(err => { + console.error(`[Initialization] Warmup failed: ${err.message}`); + }); - // 检查并刷新即将过期的节点(异步调用,不阻塞启动) - providerPoolManager.checkAndRefreshExpiringNodes().catch(err => { - console.error(`[Initialization] Check and refresh expiring nodes failed: ${err.message}`); - }); + // 检查并刷新即将过期的节点(异步调用,不阻塞启动) + providerPoolManager.checkAndRefreshExpiringNodes().catch(err => { + console.error(`[Initialization] Check and refresh expiring nodes failed: ${err.message}`); + }); + } // 健康检查将在服务器完全启动后执行 } else { diff --git a/static/app/modal.js b/static/app/modal.js index bc839cc..1c0a26f 100644 --- a/static/app/modal.js +++ b/static/app/modal.js @@ -674,7 +674,7 @@ function getFieldOrder(provider) { const excludedFields = [ 'isHealthy', 'lastUsed', 'usageCount', 'errorCount', 'lastErrorTime', 'uuid', 'isDisabled', 'lastHealthCheckTime', 'lastHealthCheckModel', 'lastErrorMessage', - 'notSupportedModels' + 'notSupportedModels', 'refreshCount', 'needsRefresh' ]; // 从 getProviderTypeFields 获取字段顺序映射