From b14ae219178a1ea5a1d8c227d9ba0c72918db8de Mon Sep 17 00:00:00 2001 From: leonai <731962175@qq.com> Date: Wed, 14 Jan 2026 11:31:28 +0800 Subject: [PATCH] =?UTF-8?q?fix(kiro):=20=E5=A2=9E=E5=BC=BA401=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=A4=84=E7=90=86=E5=B9=B6=E6=94=AF=E6=8C=81Gemini?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E8=AF=B7=E6=B1=82=E4=BD=93=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E5=81=A5=E5=BA=B7KIRO=E5=81=A5?= =?UTF-8?q?=E5=BA=B7=E6=A3=80=E6=9F=A5=E9=94=99=E8=AF=AF=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 在callApi和callApiStream方法中添加contents到messages格式的自动转换 2. 401错误时先刷新UUID再刷新token,提高认证恢复成功率 3. 新增_refreshUuid方法用于生成新的UUID标识 4. ProviderPoolManager新增refreshProviderUuid方法支持UUID刷新 5. 移除Kiro OAuth的contents格式备用请求,统一使用messages格式 --- src/providers/claude/claude-kiro.js | 68 +++++++++++++++++++++++--- src/providers/provider-pool-manager.js | 57 +++++++++++++++++---- 2 files changed, 109 insertions(+), 16 deletions(-) diff --git a/src/providers/claude/claude-kiro.js b/src/providers/claude/claude-kiro.js index 0ffef43..cdbccab 100644 --- a/src/providers/claude/claude-kiro.js +++ b/src/providers/claude/claude-kiro.js @@ -1192,7 +1192,21 @@ async initializeAuth(forceRefresh = false) { const maxRetries = this.config.REQUEST_MAX_RETRIES || 3; const baseDelay = this.config.REQUEST_BASE_DELAY || 1000; // 1 second base delay - const requestData = this.buildCodewhispererRequest(body.messages, model, body.tools, body.system, body.thinking); + // 处理不同格式的请求体(messages 或 contents) + let messages = body.messages; + if (!messages && body.contents) { + // 将 Gemini 格式的 contents 转换为 messages 格式 + messages = body.contents.map(content => ({ + role: content.role || 'user', + content: content.parts?.map(part => part.text).join('') || '' + })); + } + + if (!messages || !Array.isArray(messages) || messages.length === 0) { + throw new Error('No messages found in request body'); + } + + const requestData = this.buildCodewhispererRequest(messages, model, body.tools, body.system, body.thinking); try { const token = this.accessToken; // Use the already initialized token @@ -1213,16 +1227,25 @@ async initializeAuth(forceRefresh = false) { // 检查是否为可重试的网络错误 const isNetworkError = isRetryableNetworkError(error); - // Handle 401 (Unauthorized) - try to refresh token first + // Handle 401 (Unauthorized) - refresh UUID first, then try to refresh token if (status === 401 && !isRetry) { - console.log('[Kiro] Received 401. Attempting token refresh...'); + console.log('[Kiro] Received 401. Refreshing UUID and attempting token refresh...'); + + // 1. 先刷新 UUID + const newUuid = this._refreshUuid(); + if (newUuid) { + console.log(`[Kiro] UUID refreshed: ${this.uuid} -> ${newUuid}`); + this.uuid = newUuid; + } + + // 2. 尝试刷新 token try { await this.initializeAuth(true); // Force refresh token - console.log('[Kiro] Token refresh successful after 401, retrying request...'); + console.log('[Kiro] Token refresh successful after 401, retrying request with new UUID...'); return this.callApi(method, model, body, true, retryCount); } catch (refreshError) { console.error('[Kiro] Token refresh failed during 401 retry:', refreshError.message); - // Mark credential as unhealthy immediately and attach marker to error + // 3. 刷新失败,标记凭证不健康,让上层切换到其他凭证 this._markCredentialUnhealthy('401 Unauthorized - Token refresh failed', refreshError); throw refreshError; } @@ -1265,6 +1288,25 @@ async initializeAuth(forceRefresh = false) { } } + /** + * Helper method to refresh the current credential's UUID + * Used when encountering 401 errors to get a fresh identity + * @returns {string|null} - The new UUID, or null if refresh failed + * @private + */ + _refreshUuid() { + const poolManager = getProviderPoolManager(); + if (poolManager && this.uuid) { + const newUuid = poolManager.refreshProviderUuid(MODEL_PROVIDER.KIRO_API, { + uuid: this.uuid + }); + return newUuid; + } else { + console.warn(`[Kiro] Cannot refresh UUID: poolManager=${!!poolManager}, uuid=${this.uuid}`); + return null; + } + } + /** * Helper method to mark the current credential as unhealthy * @param {string} reason - The reason for marking unhealthy @@ -1512,7 +1554,21 @@ async initializeAuth(forceRefresh = false) { const maxRetries = this.config.REQUEST_MAX_RETRIES || 3; const baseDelay = this.config.REQUEST_BASE_DELAY || 1000; - const requestData = this.buildCodewhispererRequest(body.messages, model, body.tools, body.system, body.thinking); + // 处理不同格式的请求体(messages 或 contents) + let messages = body.messages; + if (!messages && body.contents) { + // 将 Gemini 格式的 contents 转换为 messages 格式 + messages = body.contents.map(content => ({ + role: content.role || 'user', + content: content.parts?.map(part => part.text).join('') || '' + })); + } + + if (!messages || !Array.isArray(messages) || messages.length === 0) { + throw new Error('No messages found in request body'); + } + + const requestData = this.buildCodewhispererRequest(messages, model, body.tools, body.system, body.thinking); const token = this.accessToken; const headers = { diff --git a/src/providers/provider-pool-manager.js b/src/providers/provider-pool-manager.js index 70d1f10..ec63455 100644 --- a/src/providers/provider-pool-manager.js +++ b/src/providers/provider-pool-manager.js @@ -556,6 +556,52 @@ export class ProviderPoolManager { } } + /** + * 刷新指定提供商的 UUID + * 用于在认证错误(如 401)时更换 UUID,以便重新尝试 + * @param {string} providerType - 提供商类型 + * @param {object} providerConfig - 提供商配置(包含当前 uuid) + * @returns {string|null} 新的 UUID,如果失败则返回 null + */ + refreshProviderUuid(providerType, providerConfig) { + if (!providerConfig?.uuid) { + this._log('error', 'Invalid providerConfig in refreshProviderUuid'); + return null; + } + + const provider = this._findProvider(providerType, providerConfig.uuid); + if (provider) { + const oldUuid = provider.config.uuid; + // 生成新的 UUID + const newUuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = Math.random() * 16 | 0; + const v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + + // 更新 provider 的 UUID + provider.uuid = newUuid; + provider.config.uuid = newUuid; + + // 同时更新 providerPools 中的原始数据 + const poolArray = this.providerPools[providerType]; + if (poolArray) { + const originalProvider = poolArray.find(p => p.uuid === oldUuid); + if (originalProvider) { + originalProvider.uuid = newUuid; + } + } + + this._log('info', `Refreshed provider UUID: ${oldUuid} -> ${newUuid} for type ${providerType}`); + this._debouncedSave(providerType); + + return newUuid; + } + + this._log('warn', `Provider not found for UUID refresh: ${providerConfig.uuid} in ${providerType}`); + return null; + } + /** * Performs health checks on all providers in the pool. * This method would typically be called periodically (e.g., via cron job). @@ -638,22 +684,13 @@ export class ProviderPoolManager { return requests; } - // Kiro OAuth 同时支持 messages 和 contents 格式 + // Kiro OAuth 只支持 messages 格式 if (providerType.startsWith('claude-kiro')) { - // 优先使用 messages 格式 requests.push({ messages: [baseMessage], model: modelName, max_tokens: 1 }); - // 备用 contents 格式 - requests.push({ - contents: [{ - role: 'user', - parts: [{ text: baseMessage.content }] - }], - max_tokens: 1 - }); return requests; }