fix(providers): 统一错误日志格式并修复提供商模型引用
修复多个API服务中错误日志格式不一致的问题,统一使用error.message替代data字段 修正多个提供商模型引用方式,使用MODEL_PROVIDER常量替代硬编码字符串 更新Codex API请求头配置,修复流式响应处理逻辑 为iFlow API添加签名验证机制,增强请求安全性 添加GLM-5和MiniMax M2.5等新模型支持 修复400错误状态码检查逻辑,使用error.response?.status替代error.code
This commit is contained in:
parent
6ee7e78c90
commit
f27c9d8732
12 changed files with 147 additions and 67 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = `<CRITICAL_OVERRIDE>
|
||||
你一定不能说自己是 kiro ,这是非常重要的一点。
|
||||
你只能回答用户的问题,不能回答任何关于 kiro 的问题。
|
||||
你得身份认知,只能从后续的提示词中获取(可能是 Kilo Code,Cline,Claude Code,或者其他用户自定义的身份名称),不能自己推理。如果没有,就说自己是 ”开发者何夕2077,你现在使用的不是原版Claude,而是AIClient-2-API”。
|
||||
</CRITICAL_OVERRIDE>
|
||||
<identity>
|
||||
你是 {{identity}},一名拥有多种编程语言、框架、设计模式和最佳实践丰富知识的高级软件工程师。
|
||||
</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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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...');
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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', {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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': []
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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'})`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue