From 2828166b18cb9a26339ff24b663812bd4401ca4e Mon Sep 17 00:00:00 2001 From: hex2077 Date: Thu, 8 Jan 2026 12:46:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(api):=20=E4=BF=AE=E5=A4=8D401/400=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=97=B6=E7=9A=84=E8=AE=A4=E8=AF=81=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E5=B9=B6=E4=BC=98=E5=8C=96=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为API调用和流式请求添加401/400错误时的认证刷新重试机制 修改antigravity-core中ensureRolesInContents方法,仅对非图像模型设置systemInstruction --- src/gemini/antigravity-core.js | 22 +++++++++--------- src/openai/iflow-core.js | 41 +++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/gemini/antigravity-core.js b/src/gemini/antigravity-core.js index 4c615d9..d3a96ba 100644 --- a/src/gemini/antigravity-core.js +++ b/src/gemini/antigravity-core.js @@ -702,18 +702,20 @@ function toGeminiApiResponse(antigravityResponse) { * @param {Object} requestBody - 请求体 * @returns {Object} 处理后的请求体 */ -function ensureRolesInContents(requestBody) { +function ensureRolesInContents(requestBody, modelName) { delete requestBody.model; if (requestBody.system_instruction) { delete requestBody.system_instruction; } - // 强制设置 systemInstruction - requestBody.systemInstruction = { - role: 'user', - parts: [{ text: ANTIGRAVITY_SYSTEM_PROMPT }] - }; + // 只有非图像模型才强制设置 systemInstruction + if (!isImageModel(modelName)) { + requestBody.systemInstruction = { + role: 'user', + parts: [{ text: ANTIGRAVITY_SYSTEM_PROMPT }] + }; + } if (requestBody.contents && Array.isArray(requestBody.contents)) { requestBody.contents.forEach(content => { @@ -1262,9 +1264,9 @@ export class AntigravityApiService { selectedModel = this.availableModels[0]; } - // 深拷贝请求体 - const processedRequestBody = ensureRolesInContents(JSON.parse(JSON.stringify(requestBody))); const actualModelName = alias2ModelName(selectedModel); + // 深拷贝请求体 + const processedRequestBody = ensureRolesInContents(JSON.parse(JSON.stringify(requestBody)), actualModelName); const isClaudeModel = isClaude(actualModelName); // 将处理后的请求体转换为 Antigravity 格式 @@ -1318,9 +1320,9 @@ export class AntigravityApiService { selectedModel = this.availableModels[0]; } - // 深拷贝请求体 - const processedRequestBody = ensureRolesInContents(JSON.parse(JSON.stringify(requestBody))); const actualModelName = alias2ModelName(selectedModel); + // 深拷贝请求体 + const processedRequestBody = ensureRolesInContents(JSON.parse(JSON.stringify(requestBody)), actualModelName); // 将处理后的请求体转换为 Antigravity 格式 const payload = geminiToAntigravity(actualModelName, { request: processedRequestBody }, this.projectId); diff --git a/src/openai/iflow-core.js b/src/openai/iflow-core.js index 841c382..345a199 100644 --- a/src/openai/iflow-core.js +++ b/src/openai/iflow-core.js @@ -722,7 +722,7 @@ export class IFlowApiService { /** * 调用 API */ - async callApi(endpoint, body, model, retryCount = 0) { + async callApi(endpoint, body, model, isRetry = false, retryCount = 0) { const maxRetries = this.config.REQUEST_MAX_RETRIES || 3; const baseDelay = this.config.REQUEST_BASE_DELAY || 1000; @@ -743,6 +743,18 @@ export class IFlowApiService { // 检查是否为可重试的网络错误 const isNetworkError = isRetryableNetworkError(error); + // Handle 401/400 - refresh auth and retry once + if ((status === 400 || status === 401) && !isRetry) { + console.log(`[iFlow] Received ${status}. Refreshing auth and retrying...`); + try { + await this.initializeAuth(true); + return this.callApi(endpoint, body, model, true, retryCount); + } catch (authError) { + console.error('[iFlow] Failed to refresh auth during retry:', authError.message); + throw error; // throw original error if refresh fails + } + } + if (status === 401 || status === 403) { console.error(`[iFlow] Received ${status}. API Key might be invalid or expired.`); throw error; @@ -753,7 +765,7 @@ export class IFlowApiService { const delay = baseDelay * Math.pow(2, retryCount); console.log(`[iFlow] Received 429 (Too Many Requests). Retrying in ${delay}ms... (attempt ${retryCount + 1}/${maxRetries})`); await new Promise(resolve => setTimeout(resolve, delay)); - return this.callApi(endpoint, body, model, retryCount + 1); + return this.callApi(endpoint, body, model, isRetry, retryCount + 1); } // Handle other retryable errors (5xx server errors) @@ -761,7 +773,7 @@ export class IFlowApiService { const delay = baseDelay * Math.pow(2, retryCount); console.log(`[iFlow] Received ${status} server error. Retrying in ${delay}ms... (attempt ${retryCount + 1}/${maxRetries})`); await new Promise(resolve => setTimeout(resolve, delay)); - return this.callApi(endpoint, body, model, retryCount + 1); + return this.callApi(endpoint, body, model, isRetry, retryCount + 1); } // Handle network errors (ECONNRESET, ETIMEDOUT, etc.) with exponential backoff @@ -770,7 +782,7 @@ export class IFlowApiService { const errorIdentifier = errorCode || errorMessage.substring(0, 50); console.log(`[iFlow] Network error (${errorIdentifier}). Retrying in ${delay}ms... (attempt ${retryCount + 1}/${maxRetries})`); await new Promise(resolve => setTimeout(resolve, delay)); - return this.callApi(endpoint, body, model, retryCount + 1); + return this.callApi(endpoint, body, model, isRetry, retryCount + 1); } console.error(`[iFlow] Error calling API (Status: ${status}, Code: ${errorCode}):`, data || error.message); @@ -785,7 +797,7 @@ export class IFlowApiService { * - 逐行处理 SSE 数据 * - 正确处理 data: 前缀和 [DONE] 标记 */ - async *streamApi(endpoint, body, model, retryCount = 0) { + async *streamApi(endpoint, body, model, isRetry = false, retryCount = 0) { const maxRetries = this.config.REQUEST_MAX_RETRIES || 3; const baseDelay = this.config.REQUEST_BASE_DELAY || 1000; @@ -881,6 +893,19 @@ export class IFlowApiService { // 检查是否为可重试的网络错误 const isNetworkError = isRetryableNetworkError(error); + // Handle 401/400 during stream - refresh auth and retry once + if ((status === 400 || status === 401) && !isRetry) { + console.log(`[iFlow] Received ${status} during stream. Refreshing auth and retrying...`); + try { + await this.initializeAuth(true); + yield* this.streamApi(endpoint, body, model, true, retryCount); + return; + } catch (authError) { + console.error('[iFlow] Failed to refresh auth during stream retry:', authError.message); + throw error; + } + } + if (status === 401 || status === 403) { console.error(`[iFlow] Received ${status} during stream. API Key might be invalid or expired.`); throw error; @@ -891,7 +916,7 @@ export class IFlowApiService { const delay = baseDelay * Math.pow(2, retryCount); console.log(`[iFlow] Received 429 (Too Many Requests) during stream. Retrying in ${delay}ms... (attempt ${retryCount + 1}/${maxRetries})`); await new Promise(resolve => setTimeout(resolve, delay)); - yield* this.streamApi(endpoint, body, model, retryCount + 1); + yield* this.streamApi(endpoint, body, model, isRetry, retryCount + 1); return; } @@ -900,7 +925,7 @@ export class IFlowApiService { const delay = baseDelay * Math.pow(2, retryCount); console.log(`[iFlow] Received ${status} server error during stream. Retrying in ${delay}ms... (attempt ${retryCount + 1}/${maxRetries})`); await new Promise(resolve => setTimeout(resolve, delay)); - yield* this.streamApi(endpoint, body, model, retryCount + 1); + yield* this.streamApi(endpoint, body, model, isRetry, retryCount + 1); return; } @@ -910,7 +935,7 @@ export class IFlowApiService { const errorIdentifier = errorCode || errorMessage.substring(0, 50); console.log(`[iFlow] Network error (${errorIdentifier}) during stream. Retrying in ${delay}ms... (attempt ${retryCount + 1}/${maxRetries})`); await new Promise(resolve => setTimeout(resolve, delay)); - yield* this.streamApi(endpoint, body, model, retryCount + 1); + yield* this.streamApi(endpoint, body, model, isRetry, retryCount + 1); return; }