From f27c9d87323ff8f526b0d97541358de4d5b3ef2d Mon Sep 17 00:00:00 2001 From: hex2077 Date: Sat, 14 Feb 2026 19:54:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(providers):=20=E7=BB=9F=E4=B8=80=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=97=A5=E5=BF=97=E6=A0=BC=E5=BC=8F=E5=B9=B6=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=8F=90=E4=BE=9B=E5=95=86=E6=A8=A1=E5=9E=8B=E5=BC=95?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复多个API服务中错误日志格式不一致的问题,统一使用error.message替代data字段 修正多个提供商模型引用方式,使用MODEL_PROVIDER常量替代硬编码字符串 更新Codex API请求头配置,修复流式响应处理逻辑 为iFlow API添加签名验证机制,增强请求安全性 添加GLM-5和MiniMax M2.5等新模型支持 修复400错误状态码检查逻辑,使用error.response?.status替代error.code --- src/providers/claude/claude-core.js | 2 +- src/providers/claude/claude-kiro.js | 25 ++++- src/providers/forward/forward-core.js | 4 +- src/providers/gemini/antigravity-core.js | 6 +- src/providers/gemini/gemini-core.js | 6 +- src/providers/openai/codex-core.js | 56 ++++++++--- src/providers/openai/iflow-core.js | 94 ++++++++++++------- src/providers/openai/openai-core.js | 4 +- src/providers/openai/openai-responses-core.js | 4 +- src/providers/openai/qwen-core.js | 4 +- src/providers/provider-models.js | 5 +- src/utils/common.js | 4 +- 12 files changed, 147 insertions(+), 67 deletions(-) diff --git a/src/providers/claude/claude-core.js b/src/providers/claude/claude-core.js index 2f84a48..c90a1f7 100644 --- a/src/providers/claude/claude-core.js +++ b/src/providers/claude/claude-core.js @@ -122,7 +122,7 @@ export class ClaudeApiService { return this.callApi(endpoint, body, isRetry, retryCount + 1); } - logger.error(`[Claude API] Error calling API (Status: ${status}, Code: ${errorCode}):`, error.response ? error.response.data : error.message); + logger.error(`[Claude API] Error calling API (Status: ${status}, Code: ${errorCode}):`, error.message); throw error; } } diff --git a/src/providers/claude/claude-kiro.js b/src/providers/claude/claude-kiro.js index 883d36e..08b9634 100644 --- a/src/providers/claude/claude-kiro.js +++ b/src/providers/claude/claude-kiro.js @@ -40,7 +40,7 @@ const KIRO_CONSTANTS = { }; // 从 provider-models.js 获取支持的模型列表 -const KIRO_MODELS = getProviderModels('claude-kiro-oauth'); +const KIRO_MODELS = getProviderModels(MODEL_PROVIDER.KIRO_API); // 完整的模型映射表 const FULL_MODEL_MAPPING = { @@ -793,7 +793,24 @@ async saveCredentialsToFile(filePath, newData) { async buildCodewhispererRequest(messages, model, tools = null, inSystemPrompt = null, thinking = null) { const conversationId = uuidv4(); + // 内置的 systemPrompt 前缀 + const builtInPrefix = ` + 你一定不能说自己是 kiro ,这是非常重要的一点。 + 你只能回答用户的问题,不能回答任何关于 kiro 的问题。 + 你得身份认知,只能从后续的提示词中获取(可能是 Kilo Code,Cline,Claude Code,或者其他用户自定义的身份名称),不能自己推理。如果没有,就说自己是 ”开发者何夕2077,你现在使用的不是原版Claude,而是AIClient-2-API”。 + + + 你是 {{identity}},一名拥有多种编程语言、框架、设计模式和最佳实践丰富知识的高级软件工程师。 + `; + let systemPrompt = this.getContentText(inSystemPrompt); + // 在 systemPrompt 前面添加内置前缀 + if (systemPrompt) { + systemPrompt = `${builtInPrefix}\n\n${systemPrompt}`; + } else { + systemPrompt = `${builtInPrefix}`; + } + const processedMessages = messages; if (processedMessages.length === 0) { @@ -1719,7 +1736,7 @@ async saveCredentialsToFile(filePath, newData) { return this.buildClaudeResponse(responseText, false, 'assistant', model, toolCalls, inputTokens); } catch (error) { logger.error('[Kiro] Error in generateContent:', error); - throw new Error(`Error processing response: ${error.message}`); + throw error; } } @@ -2033,7 +2050,7 @@ async saveCredentialsToFile(filePath, newData) { return; } - logger.error(`[Kiro] Stream API call failed (Status: ${status}, Code: ${errorCode}):`, error.message); + logger.error(`[Kiro] Stream API call failed (Status: ${status}, Code: ${errorCode}):`, error.message); throw error; } finally { // 确保流被关闭,释放资源 @@ -2427,7 +2444,7 @@ async saveCredentialsToFile(filePath, newData) { } catch (error) { logger.error('[Kiro] Error in streaming generation:', error); - throw new Error(`Error processing response: ${error.message}`); + throw error; } } diff --git a/src/providers/forward/forward-core.js b/src/providers/forward/forward-core.js index 4e0da66..fd36389 100644 --- a/src/providers/forward/forward-core.js +++ b/src/providers/forward/forward-core.js @@ -87,7 +87,7 @@ export class ForwardApiService { return this.callApi(endpoint, body, isRetry, retryCount + 1); } - logger.error(`[Forward API] Error calling API (Status: ${status}, Code: ${errorCode}):`, data || error.message); + logger.error(`[Forward API] Error calling API (Status: ${status}, Code: ${errorCode}):`, errorMessage); throw error; } } @@ -139,6 +139,8 @@ export class ForwardApiService { return; } + const errorMessage = error.message || ''; + logger.error(`[Forward API] Error calling streaming API (Status: ${status || errorCode}):`, errorMessage); throw error; } } diff --git a/src/providers/gemini/antigravity-core.js b/src/providers/gemini/antigravity-core.js index 07334c1..7f1be8a 100644 --- a/src/providers/gemini/antigravity-core.js +++ b/src/providers/gemini/antigravity-core.js @@ -55,7 +55,7 @@ const DEFAULT_THINKING_MIN = 1024; const DEFAULT_THINKING_MAX = 100000; // 获取 Antigravity 模型列表 -const ANTIGRAVITY_MODELS = getProviderModels('gemini-antigravity'); +const ANTIGRAVITY_MODELS = getProviderModels(MODEL_PROVIDER.ANTIGRAVITY); // 模型别名映射 - 别名 -> 真实模型名 const MODEL_ALIAS_MAP = { @@ -1107,7 +1107,7 @@ export class AntigravityApiService { // 检查是否为可重试的网络错误 const isNetworkError = isRetryableNetworkError(error); - logger.error(`[Antigravity API] Error calling ${method} on ${baseURL}:`, status, error.message); + logger.error(`[Antigravity API] Error calling (Status: ${status}, Code: ${errorCode}):`, error.message); if ((status === 400 || status === 401) && !isRetry) { logger.info('[Antigravity API] Received 401/400. Triggering background refresh via PoolManager...'); @@ -1209,7 +1209,7 @@ export class AntigravityApiService { // 检查是否为可重试的网络错误 const isNetworkError = isRetryableNetworkError(error); - logger.error(`[Antigravity API] Error during stream ${method} on ${baseURL}:`, status, error.message); + logger.error(`[Antigravity API] Error during stream (Status: ${status}, Code: ${errorCode}):`, error.message); if ((status === 400 || status === 401) && !isRetry) { logger.info('[Antigravity API] Received 401/400 during stream. Triggering background refresh via PoolManager...'); diff --git a/src/providers/gemini/gemini-core.js b/src/providers/gemini/gemini-core.js index 6d2614f..ff9eb59 100644 --- a/src/providers/gemini/gemini-core.js +++ b/src/providers/gemini/gemini-core.js @@ -36,7 +36,7 @@ const DEFAULT_CODE_ASSIST_ENDPOINT = 'https://cloudcode-pa.googleapis.com'; const DEFAULT_CODE_ASSIST_API_VERSION = 'v1internal'; const OAUTH_CLIENT_ID = '681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com'; const OAUTH_CLIENT_SECRET = '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'; -const GEMINI_MODELS = getProviderModels('gemini-cli-oauth'); +const GEMINI_MODELS = getProviderModels(MODEL_PROVIDER.GEMINI_CLI); const ANTI_TRUNCATION_MODELS = GEMINI_MODELS.map(model => `anti-${model}`); function is_anti_truncation_model(model) { @@ -489,7 +489,7 @@ export class GeminiApiService { // 检查是否为可重试的网络错误 const isNetworkError = isRetryableNetworkError(error); - logger.error(`[Gemini API] Error calling ${method}:`, status, error.message); + logger.error(`[Gemini API] Error calling (Status: ${status}, Code: ${errorCode}):`, errorMessage); // Handle 401 (Unauthorized) - refresh auth and retry once if ((status === 400 || status === 401) && !isRetry) { @@ -568,7 +568,7 @@ export class GeminiApiService { // 检查是否为可重试的网络错误 const isNetworkError = isRetryableNetworkError(error); - logger.error(`[Gemini API] Error during stream ${method}:`, status, error.message); + logger.error(`[Gemini API] Error during stream (Status: ${status}, Code: ${errorCode}):`, errorMessage); // Handle 401 (Unauthorized) - refresh auth and retry once if ((status === 400 || status === 401) && !isRetry) { diff --git a/src/providers/openai/codex-core.js b/src/providers/openai/codex-core.js index 897b960..73222ca 100644 --- a/src/providers/openai/codex-core.js +++ b/src/providers/openai/codex-core.js @@ -148,11 +148,12 @@ export class CodexApiService { const url = `${this.baseUrl}/responses`; const body = this.prepareRequestBody(model, requestBody, true); - const headers = this.buildHeaders(body.prompt_cache_key); + const headers = this.buildHeaders(body.prompt_cache_key, true); try { const config = { headers, + responseType: 'text', // 确保以文本形式接收 SSE 流 timeout: 120000 // 2 分钟超时 }; @@ -184,8 +185,10 @@ export class CodexApiService { error.shouldSwitchCredential = true; error.skipErrorCount = true; throw error; + } else { + logger.error(`[Codex] Error calling non-stream API (Status: ${error.response?.status}, Code: ${error.code || 'N/A'}):`, error.message); + throw error; } - throw error; } } @@ -216,7 +219,7 @@ export class CodexApiService { const url = `${this.baseUrl}/responses`; const body = this.prepareRequestBody(model, requestBody, true); - const headers = this.buildHeaders(body.prompt_cache_key); + const headers = this.buildHeaders(body.prompt_cache_key, true); try { const config = { @@ -254,6 +257,7 @@ export class CodexApiService { error.skipErrorCount = true; throw error; } else { + logger.error(`[Codex] Error calling streaming API (Status: ${error.response?.status}, Code: ${error.code || 'N/A'}):`, error.message); throw error; } } @@ -262,21 +266,34 @@ export class CodexApiService { /** * 构建请求头 */ - buildHeaders(cacheId) { - return { - 'version': '0.98.0', + buildHeaders(cacheId, stream = true) { + const headers = { + 'version': '0.101.0', 'x-codex-beta-features': 'powershell_utf8', 'x-oai-web-search-eligible': 'true', - 'session_id': cacheId, - 'accept': 'text/event-stream', 'authorization': `Bearer ${this.accessToken}`, 'chatgpt-account-id': this.accountId, 'content-type': 'application/json', - 'user-agent': 'codex_cli_rs/0.89.0 (Windows 10.0.26100; x86_64) WindowsTerminal', + 'user-agent': 'codex_cli_rs/0.101.0 (Windows 10.0.26100; x86_64) WindowsTerminal', 'originator': 'codex_cli_rs', 'host': 'chatgpt.com', - 'Connection': 'close' + 'Connection': 'Keep-Alive' }; + + // 设置 Conversation_id 和 Session_id + if (cacheId) { + headers['Conversation_id'] = cacheId; + headers['Session_id'] = cacheId; + } + + // 根据是否流式设置 Accept 头 + if (stream) { + headers['accept'] = 'text/event-stream'; + } else { + headers['accept'] = 'application/json'; + } + + return headers; } /** @@ -438,22 +455,32 @@ export class CodexApiService { * 解析非流式响应 */ parseNonStreamResponse(data) { + // 确保 data 是字符串 + const responseText = typeof data === 'string' ? data : String(data); + // 从 SSE 流中提取 response.completed 事件 - const lines = data.split('\n'); + const lines = responseText.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const jsonData = line.slice(6).trim(); + if (!jsonData || jsonData === '[DONE]') { + continue; + } try { const parsed = JSON.parse(jsonData); if (parsed.type === 'response.completed') { return parsed; } } catch (e) { - // 继续解析 + // 继续解析下一行 + logger.debug('[Codex] Failed to parse SSE line:', e.message); } } } - throw new Error('No completed response found in Codex response'); + + // 如果没有找到 response.completed,抛出错误 + logger.error('[Codex] No completed response found in Codex response'); + throw new Error('stream error: stream disconnected before completion: stream closed before response.completed'); } /** @@ -472,7 +499,8 @@ export class CodexApiService { { id: 'gpt-5.1-codex-max', object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'openai' }, { id: 'gpt-5.2', object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'openai' }, { id: 'gpt-5.2-codex', object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'openai' }, - { id: 'gpt-5.3-codex', object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'openai' } + { id: 'gpt-5.3-codex', object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'openai' }, + { id: 'gpt-5.3-codex-spark', object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'openai' } ] }; } diff --git a/src/providers/openai/iflow-core.js b/src/providers/openai/iflow-core.js index 696ce83..fe5e6c5 100644 --- a/src/providers/openai/iflow-core.js +++ b/src/providers/openai/iflow-core.js @@ -23,9 +23,11 @@ import * as https from 'https'; import { promises as fs } from 'fs'; import * as path from 'path'; import * as os from 'os'; +import * as crypto from 'crypto'; import { configureAxiosProxy } from '../../utils/proxy-utils.js'; import { isRetryableNetworkError, MODEL_PROVIDER, formatExpiryLog } from '../../utils/common.js'; import { getProviderPoolManager } from '../../services/service-manager.js'; +import { getProviderModels } from '../provider-models.js'; // iFlow API 端点 const IFLOW_API_BASE_URL = 'https://apis.iflow.cn/v1'; @@ -36,32 +38,10 @@ const IFLOW_OAUTH_CLIENT_ID = '10009311001'; const IFLOW_OAUTH_CLIENT_SECRET = '4Z3YjXycVsQvyGF1etiNlIBB4RsqSDtW'; // 默认模型列表 -const IFLOW_MODELS = [ - // iFlow 特有模型 - 'iflow-rome-30ba3b', - // Qwen 模型 - 'qwen3-coder-plus', - 'qwen3-max', - 'qwen3-vl-plus', - 'qwen3-max-preview', - 'qwen3-32b', - 'qwen3-235b-a22b-thinking-2507', - 'qwen3-235b-a22b-instruct', - 'qwen3-235b', - // Kimi 模型 - 'kimi-k2-0905', - 'kimi-k2', - // GLM 模型 - 'glm-4.6', - 'glm-4.7', - // DeepSeek 模型 - 'deepseek-v3.2', - 'deepseek-r1', - 'deepseek-v3' -]; +const IFLOW_MODELS = getProviderModels(MODEL_PROVIDER.IFLOW_API); // 支持 thinking 的模型前缀 -const THINKING_MODEL_PREFIXES = ['glm-4', 'qwen3-235b-a22b-thinking', 'deepseek-r1']; +const THINKING_MODEL_PREFIXES = ['glm-', 'qwen3-235b-a22b-thinking', 'deepseek-r1']; // ==================== Token 管理 ==================== @@ -294,6 +274,33 @@ async function fetchUserInfo(accessToken, axiosInstance = null) { // ==================== 请求处理工具函数 ==================== +/** + * 生成 UUID v4 + * @returns {string} - UUID 字符串 + */ +function generateUUID() { + return crypto.randomUUID(); +} + +/** + * 创建 iFlow 签名 + * 签名格式: HMAC-SHA256(userAgent:sessionId:timestamp, apiKey) + * @param {string} userAgent - User-Agent + * @param {string} sessionID - Session ID + * @param {number} timestamp - 时间戳(毫秒) + * @param {string} apiKey - API Key + * @returns {string} - 十六进制签名 + */ +function createIFlowSignature(userAgent, sessionID, timestamp, apiKey) { + if (!apiKey) { + return ''; + } + const payload = `${userAgent}:${sessionID}:${timestamp}`; + const hmac = crypto.createHmac('sha256', apiKey); + hmac.update(payload); + return hmac.digest('hex'); +} + /** * 检查模型是否支持 thinking 配置 * @param {string} model - 模型名称 @@ -359,6 +366,9 @@ function applyIFlowThinkingConfig(body, model) { * 保留消息历史中的 reasoning_content * 对于支持 thinking 的模型,保留 assistant 消息中的 reasoning_content * + * 对于 GLM-4.6/4.7 和 MiniMax M2/M2.1,建议在消息历史中包含完整的 assistant + * 响应(包括 reasoning_content)以保持更好的上下文连续性。 + * * @param {Object} body - 请求体 * @param {string} model - 模型名称 * @returns {Object} - 处理后的请求体 @@ -368,11 +378,13 @@ function preserveReasoningContentInMessages(body, model) { const lowerModel = model.toLowerCase(); - // 只对支持 thinking 的模型应用 + // 只对支持 thinking 且需要历史保留的模型应用 const needsPreservation = lowerModel.startsWith('glm-4') || - lowerModel.includes('thinking') || - lowerModel.startsWith('deepseek-r1'); - if (!needsPreservation) return body; + lowerModel.startsWith('minimax-m2'); + + if (!needsPreservation) { + return body; + } const messages = body.messages; if (!Array.isArray(messages)) return body; @@ -382,8 +394,10 @@ function preserveReasoningContentInMessages(body, model) { msg.role === 'assistant' && msg.reasoning_content && msg.reasoning_content !== '' ); + // 如果 reasoning content 已经存在,说明消息格式正确 + // 客户端已经正确地在历史中保留了推理内容 if (hasReasoningContent) { - logger.info(`[iFlow] reasoning_content found in message history for ${model}`); + logger.debug(`[iFlow] reasoning_content found in message history for ${model}`); } return body; @@ -735,12 +749,28 @@ export class IFlowApiService { * @returns {Object} - 请求头 */ _getHeaders(stream = false) { + // 生成 session-id + const sessionID = 'session-' + generateUUID(); + + // 生成时间戳(毫秒) + const timestamp = Date.now(); + + // 生成签名 + const signature = createIFlowSignature(IFLOW_USER_AGENT, sessionID, timestamp, this.apiKey); + const headers = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}`, 'User-Agent': IFLOW_USER_AGENT, + 'session-id': sessionID, + 'x-iflow-timestamp': timestamp.toString(), }; + // 只有在签名生成成功时才添加 + if (signature) { + headers['x-iflow-signature'] = signature; + } + if (stream) { headers['Accept'] = 'text/event-stream'; } else { @@ -824,7 +854,7 @@ export class IFlowApiService { return this.callApi(endpoint, body, model, isRetry, retryCount + 1); } - logger.error(`[iFlow] Error calling API (Status: ${status}, Code: ${errorCode}):`, data || error.message); + logger.error(`[iFlow] Error calling API (Status: ${status}, Code: ${errorCode}):`, errorMessage); throw error; } } @@ -985,7 +1015,7 @@ export class IFlowApiService { return; } - logger.error(`[iFlow] Error calling streaming API (Status: ${status}, Code: ${errorCode}):`, data || error.message); + logger.error(`[iFlow] Error calling streaming API (Status: ${status}, Code: ${errorCode}):`, errorMessage); throw error; } } @@ -1055,7 +1085,7 @@ export class IFlowApiService { } // 需要手动添加的模型列表 - const manualModels = ['glm-4.7', 'kimi-k2.5', 'minimax-m2.1']; + const manualModels = ['glm-4.7', 'glm-5', 'kimi-k2.5', 'minimax-m2.1', 'minimax-m2.5']; try { const response = await this.axiosInstance.get('/models', { diff --git a/src/providers/openai/openai-core.js b/src/providers/openai/openai-core.js index 065320f..a1ce70a 100644 --- a/src/providers/openai/openai-core.js +++ b/src/providers/openai/openai-core.js @@ -98,7 +98,7 @@ export class OpenAIApiService { return this.callApi(endpoint, body, isRetry, retryCount + 1); } - logger.error(`[OpenAI API] Error calling API (Status: ${status}, Code: ${errorCode}):`, data || error.message); + logger.error(`[OpenAI API] Error calling API (Status: ${status}, Code: ${errorCode}):`, errorMessage); throw error; } } @@ -183,7 +183,7 @@ export class OpenAIApiService { return; } - logger.error(`[OpenAI API] Error calling streaming API (Status: ${status}, Code: ${errorCode}):`, data || error.message); + logger.error(`[OpenAI API] Error calling streaming API (Status: ${status}, Code: ${errorCode}):`, errorMessage); throw error; } } diff --git a/src/providers/openai/openai-responses-core.js b/src/providers/openai/openai-responses-core.js index bca7a03..210496b 100644 --- a/src/providers/openai/openai-responses-core.js +++ b/src/providers/openai/openai-responses-core.js @@ -82,7 +82,7 @@ export class OpenAIResponsesApiService { return this.callApi(endpoint, body, isRetry, retryCount + 1); } - logger.error(`Error calling OpenAI Responses API (Status: ${status}):`, data || error.message); + logger.error(`Error calling OpenAI Responses API (Status: ${status}):`, error.message); throw error; } } @@ -151,7 +151,7 @@ export class OpenAIResponsesApiService { return; } - logger.error(`Error calling OpenAI Responses streaming API (Status: ${status}):`, data || error.message); + logger.error(`Error calling OpenAI Responses streaming API (Status: ${status}):`, error.message); throw error; } } diff --git a/src/providers/openai/qwen-core.js b/src/providers/openai/qwen-core.js index d566d0e..ae8aa2e 100644 --- a/src/providers/openai/qwen-core.js +++ b/src/providers/openai/qwen-core.js @@ -19,7 +19,7 @@ import { getProviderPoolManager } from '../../services/service-manager.js'; const QWEN_DIR = '.qwen'; const QWEN_CREDENTIAL_FILENAME = 'oauth_creds.json'; // 从 provider-models.js 获取支持的模型列表 -const QWEN_MODELS = getProviderModels('openai-qwen-oauth'); +const QWEN_MODELS = getProviderModels(MODEL_PROVIDER.QWEN_API); const QWEN_MODEL_LIST = QWEN_MODELS.map(id => ({ id: id, name: id.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ') @@ -645,7 +645,7 @@ export class QwenApiService { return this.callApiWithAuthAndRetry(endpoint, body, isStream, retryCount + 1); } - logger.error(`[QwenApiService] Error calling API (Status: ${status}, Code: ${errorCode}):`, data); + logger.error(`[QwenApiService] Error calling API (Status: ${status}, Code: ${errorCode}):`, errorMessage); throw error; } } diff --git a/src/providers/provider-models.js b/src/providers/provider-models.js index abcee8c..1eef5bf 100644 --- a/src/providers/provider-models.js +++ b/src/providers/provider-models.js @@ -66,8 +66,10 @@ export const PROVIDER_MODELS = { 'deepseek-v3', // 手动定义 'glm-4.7', + 'glm-5', 'kimi-k2.5', 'minimax-m2.1', + 'minimax-m2.5', ], 'openai-codex-oauth': [ 'gpt-5', @@ -79,7 +81,8 @@ export const PROVIDER_MODELS = { 'gpt-5.1-codex-max', 'gpt-5.2', 'gpt-5.2-codex', - 'gpt-5.3-codex' + 'gpt-5.3-codex', + 'gpt-5.3-codex-spark' ], 'forward-api': [] }; diff --git a/src/utils/common.js b/src/utils/common.js index 00eaa50..1933d36 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -494,7 +494,7 @@ export async function handleStreamRequest(res, service, model, requestBody, from // 如果底层未标记,且不跳过错误计数,则在此处标记 if (!credentialMarkedUnhealthy && !skipErrorCount && providerPoolManager && pooluuid) { // 400 报错码通常是请求参数问题,不记录为提供商错误 - if (error.code === 400) { + if (error.response?.status === 400) { logger.info(`[Provider Pool] Skipping unhealthy marking for ${toProvider} (${pooluuid}) due to status 400 (client error)`); } else { logger.info(`[Provider Pool] Marking ${toProvider} as unhealthy due to stream error (status: ${status || 'unknown'})`); @@ -692,7 +692,7 @@ export async function handleUnaryRequest(res, service, model, requestBody, fromP // 如果底层未标记,且不跳过错误计数,则在此处标记 if (!credentialMarkedUnhealthy && !skipErrorCount && providerPoolManager && pooluuid) { // 400 报错码通常是请求参数问题,不记录为提供商错误 - if (error.code === 400) { + if (error.response?.status === 400) { logger.info(`[Provider Pool] Skipping unhealthy marking for ${toProvider} (${pooluuid}) due to status 400 (client error)`); } else { logger.info(`[Provider Pool] Marking ${toProvider} as unhealthy due to unary error (status: ${status || 'unknown'})`);