/** * iFlow API Service * * iFlow 是一个 AI 服务平台,提供 OpenAI 兼容的 API 接口。 * 使用 Token 文件方式认证 - 从文件读取 API Key * * 支持的模型: * - Qwen 系列: qwen3-max, qwen3-coder-plus, qwen3-vl-plus, qwen3-235b 等 * - Kimi 系列: kimi-k2, kimi-k2-0905 * - DeepSeek 系列: deepseek-v3, deepseek-v3.2, deepseek-r1 * - GLM 系列: glm-4.6 * * 支持的特殊模型配置: * - GLM-4.x: 使用 chat_template_kwargs.enable_thinking * - Qwen thinking 模型: 内置推理能力 * - DeepSeek R1: 内置推理能力 */ import axios from 'axios'; import * as http from 'http'; import * as https from 'https'; import { promises as fs } from 'fs'; import * as path from 'path'; import * as os from 'os'; import { configureAxiosProxy } from '../proxy-utils.js'; import { isRetryableNetworkError } from '../common.js'; // iFlow API 端点 const IFLOW_API_BASE_URL = 'https://apis.iflow.cn/v1'; const IFLOW_USER_AGENT = 'iFlow-Cli'; const IFLOW_OAUTH_TOKEN_ENDPOINT = 'https://iflow.cn/oauth/token'; const IFLOW_USER_INFO_ENDPOINT = 'https://iflow.cn/api/oauth/getUserInfo'; 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' ]; // 支持 thinking 的模型前缀 const THINKING_MODEL_PREFIXES = ['glm-4', 'qwen3-235b-a22b-thinking', 'deepseek-r1']; // ==================== Token 管理 ==================== /** * iFlow Token 存储类 */ class IFlowTokenStorage { constructor(data = {}) { this.accessToken = data.accessToken || data.access_token || ''; this.refreshToken = data.refreshToken || data.refresh_token || ''; this.expiryDate = data.expiryDate || data.expiry_date || ''; this.apiKey = data.apiKey || data.api_key || ''; this.tokenType = data.tokenType || data.token_type || ''; this.scope = data.scope || ''; } /** * 转换为 JSON 对象 */ toJSON() { return { access_token: this.accessToken, refresh_token: this.refreshToken, expiry_date: this.expiryDate, token_type: this.tokenType, scope: this.scope, apiKey: this.apiKey }; } /** * 从 JSON 对象创建实例 */ static fromJSON(json) { return new IFlowTokenStorage(json); } } /** * 从文件加载 Token * @param {string} filePath - Token 文件路径 * @returns {Promise} */ async function loadTokenFromFile(filePath) { try { const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(process.cwd(), filePath); const data = await fs.readFile(absolutePath, 'utf-8'); const json = JSON.parse(data); return IFlowTokenStorage.fromJSON(json); } catch (error) { if (error.code === 'ENOENT') { console.warn(`[iFlow] Token file not found: ${filePath}`); return null; } throw new Error(`[iFlow] Failed to load token from file: ${error.message}`); } } /** * 保存 Token 到文件 * @param {string} filePath - Token 文件路径 * @param {IFlowTokenStorage} tokenStorage - Token 存储对象 */ async function saveTokenToFile(filePath, tokenStorage) { try { const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(process.cwd(), filePath); // 确保目录存在 const dir = path.dirname(absolutePath); await fs.mkdir(dir, { recursive: true }); // 写入文件 const json = tokenStorage.toJSON(); await fs.writeFile(absolutePath, JSON.stringify(json, null, 2), 'utf-8'); console.log(`[iFlow] Token saved to: ${filePath}`); } catch (error) { throw new Error(`[iFlow] Failed to save token to file: ${error.message}`); } } // ==================== Token 刷新逻辑 ==================== /** * 使用 refresh_token 刷新 OAuth Token * @param {string} refreshToken - 刷新令牌 * @param {Object} axiosInstance - axios 实例(可选,用于代理配置) * @returns {Promise} - 新的 Token 数据 */ async function refreshOAuthTokens(refreshToken, axiosInstance = null) { if (!refreshToken || refreshToken.trim() === '') { throw new Error('[iFlow] refresh_token is empty'); } console.log('[iFlow] Refreshing OAuth tokens...'); // 构建请求参数 const params = new URLSearchParams(); params.append('grant_type', 'refresh_token'); params.append('refresh_token', refreshToken); params.append('client_id', IFLOW_OAUTH_CLIENT_ID); params.append('client_secret', IFLOW_OAUTH_CLIENT_SECRET); // 构建 Basic Auth header const basicAuth = Buffer.from(`${IFLOW_OAUTH_CLIENT_ID}:${IFLOW_OAUTH_CLIENT_SECRET}`).toString('base64'); const requestConfig = { method: 'POST', url: IFLOW_OAUTH_TOKEN_ENDPOINT, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', 'Authorization': `Basic ${basicAuth}` }, data: params.toString(), timeout: 30000 }; try { const response = axiosInstance ? await axiosInstance.request(requestConfig) : await axios.request(requestConfig); const tokenResp = response.data; // console.log('[iFlow] Token response:', JSON.stringify(tokenResp)); if (!tokenResp.access_token) { console.error('[iFlow] Token response:', JSON.stringify(tokenResp)); throw new Error('[iFlow] Missing access_token in response'); } // 计算过期时间(毫秒级时间戳) const expiresIn = tokenResp.expires_in || 3600; const expireTimestamp = Date.now() + expiresIn * 1000; const tokenData = { accessToken: tokenResp.access_token, refreshToken: tokenResp.refresh_token || refreshToken, tokenType: tokenResp.token_type || 'Bearer', scope: tokenResp.scope || '', expiryDate: expireTimestamp // 毫秒级时间戳 }; console.log('[iFlow] OAuth tokens refreshed successfully'); // 获取用户信息以获取 API Key const userInfo = await fetchUserInfo(tokenData.accessToken, axiosInstance); if (userInfo && userInfo.apiKey) { tokenData.apiKey = userInfo.apiKey; tokenData.email = userInfo.email || userInfo.phone || ''; } return tokenData; } catch (error) { const status = error.response?.status; const data = error.response?.data; console.error(`[iFlow] OAuth token refresh failed (Status: ${status}):`, data || error.message); throw error; } } /** * 获取用户信息(包含 API Key) * @param {string} accessToken - 访问令牌 * @param {Object} axiosInstance - axios 实例(可选) * @returns {Promise} - 用户信息 */ async function fetchUserInfo(accessToken, axiosInstance = null) { if (!accessToken || accessToken.trim() === '') { throw new Error('[iFlow] access_token is empty'); } const url = `${IFLOW_USER_INFO_ENDPOINT}?accessToken=${encodeURIComponent(accessToken)}`; const requestConfig = { method: 'GET', url, headers: { 'Accept': 'application/json' }, timeout: 30000 }; try { const response = axiosInstance ? await axiosInstance.request(requestConfig) : await axios.request(requestConfig); const result = response.data; // console.log('[iFlow] User info response:', JSON.stringify(result)); if (!result.success) { throw new Error('[iFlow] User info request not successful'); } if (!result.data || !result.data.apiKey) { throw new Error('[iFlow] Missing apiKey in user info response'); } return { apiKey: result.data.apiKey, email: result.data.email || '', phone: result.data.phone || '' }; } catch (error) { const status = error.response?.status; const data = error.response?.data; console.error(`[iFlow] Fetch user info failed (Status: ${status}):`, data || error.message); throw error; } } // ==================== 请求处理工具函数 ==================== /** * 检查模型是否支持 thinking 配置 * @param {string} model - 模型名称 * @returns {boolean} */ function isThinkingModel(model) { if (!model) return false; const lowerModel = model.toLowerCase(); return THINKING_MODEL_PREFIXES.some(prefix => lowerModel.startsWith(prefix)); } /** * 应用 iFlow 特定的 thinking 配置 * 将 reasoning_effort 转换为模型特定的配置 * * @param {Object} body - 请求体 * @param {string} model - 模型名称 * @returns {Object} - 处理后的请求体 */ function applyIFlowThinkingConfig(body, model) { if (!body || !model) return body; const lowerModel = model.toLowerCase(); const reasoningEffort = body.reasoning_effort; // 如果没有 reasoning_effort,直接返回 if (reasoningEffort === undefined) return body; const enableThinking = reasoningEffort !== 'none' && reasoningEffort !== ''; // 创建新对象,移除 reasoning_effort 和 thinking const newBody = { ...body }; delete newBody.reasoning_effort; delete newBody.thinking; // GLM-4.x: 使用 chat_template_kwargs if (lowerModel.startsWith('glm-4')) { newBody.chat_template_kwargs = { ...(newBody.chat_template_kwargs || {}), enable_thinking: enableThinking }; if (enableThinking) { newBody.chat_template_kwargs.clear_thinking = false; } return newBody; } // Qwen thinking 模型: 保持 thinking 配置 if (lowerModel.includes('thinking')) { // Qwen thinking 模型默认启用 thinking,不需要额外配置 return newBody; } // DeepSeek R1: 推理模型,不需要额外配置 if (lowerModel.startsWith('deepseek-r1')) { return newBody; } return newBody; } /** * 保留消息历史中的 reasoning_content * 对于支持 thinking 的模型,保留 assistant 消息中的 reasoning_content * * @param {Object} body - 请求体 * @param {string} model - 模型名称 * @returns {Object} - 处理后的请求体 */ function preserveReasoningContentInMessages(body, model) { if (!body || !model) return body; const lowerModel = model.toLowerCase(); // 只对支持 thinking 的模型应用 const needsPreservation = lowerModel.startsWith('glm-4') || lowerModel.includes('thinking') || lowerModel.startsWith('deepseek-r1'); if (!needsPreservation) return body; const messages = body.messages; if (!Array.isArray(messages)) return body; // 检查是否有 assistant 消息包含 reasoning_content const hasReasoningContent = messages.some(msg => msg.role === 'assistant' && msg.reasoning_content && msg.reasoning_content !== '' ); if (hasReasoningContent) { console.log(`[iFlow] reasoning_content found in message history for ${model}`); } return body; } /** * 确保 tools 数组存在(避免某些模型的问题) * 如果 tools 是空数组,添加一个占位工具 * * @param {Object} body - 请求体 * @returns {Object} - 处理后的请求体 */ function ensureToolsArray(body) { if (!body || !body.tools) return body; if (Array.isArray(body.tools) && body.tools.length === 0) { return { ...body, tools: [{ type: 'function', function: { name: 'noop', description: 'Placeholder tool to stabilise streaming', parameters: { type: 'object' } } }] }; } return body; } /** * 预处理请求体 * @param {Object} body - 原始请求体 * @param {string} model - 模型名称 * @returns {Object} - 处理后的请求体 */ function preprocessRequestBody(body, model) { let processedBody = { ...body }; // 确保模型名称正确 processedBody.model = model; // 应用 iFlow thinking 配置 processedBody = applyIFlowThinkingConfig(processedBody, model); // 保留 reasoning_content processedBody = preserveReasoningContentInMessages(processedBody, model); // 确保 tools 数组 processedBody = ensureToolsArray(processedBody); return processedBody; } // ==================== API 服务 ==================== /** * iFlow API 服务类 */ // 默认 Token 文件路径 const DEFAULT_TOKEN_FILE_PATH = path.join(os.homedir(), '.iflow', 'oauth_creds.json'); export class IFlowApiService { constructor(config) { this.config = config; this.apiKey = null; this.baseUrl = config.IFLOW_BASE_URL || IFLOW_API_BASE_URL; this.tokenFilePath = config.IFLOW_TOKEN_FILE_PATH || DEFAULT_TOKEN_FILE_PATH; this.isInitialized = false; this.tokenStorage = null; // 配置 HTTP/HTTPS agent const httpAgent = new http.Agent({ keepAlive: true, maxSockets: 100, maxFreeSockets: 5, timeout: 120000, }); const httpsAgent = new https.Agent({ keepAlive: true, maxSockets: 100, maxFreeSockets: 5, timeout: 120000, }); const axiosConfig = { baseURL: this.baseUrl, httpAgent, httpsAgent, headers: { 'Content-Type': 'application/json', 'User-Agent': IFLOW_USER_AGENT, }, }; // 配置自定义代理 configureAxiosProxy(axiosConfig, config, 'openai-iflow'); this.axiosInstance = axios.create(axiosConfig); } /** * 初始化服务 */ async initialize() { if (this.isInitialized) return; console.log('[iFlow] Initializing iFlow API Service...'); await this.initializeAuth(); this.isInitialized = true; console.log('[iFlow] Initialization complete.'); } /** * 初始化认证 * @param {boolean} forceRefresh - 是否强制刷新 Token */ async initializeAuth(forceRefresh = false) { // 如果已有 API Key 且不强制刷新,直接返回 if (this.apiKey && !forceRefresh) return; // 从 Token 文件加载 API Key if (!this.tokenFilePath) { throw new Error('[iFlow] IFLOW_TOKEN_FILE_PATH is required.'); } try { this.tokenStorage = await loadTokenFromFile(this.tokenFilePath); if (this.tokenStorage && this.tokenStorage.apiKey) { this.apiKey = this.tokenStorage.apiKey; console.log('[iFlow Auth] Authentication configured successfully from file.'); if (forceRefresh) { console.log('[iFlow Auth] Forcing token refresh...'); await this._refreshOAuthTokens(); console.log('[iFlow Auth] Token refreshed and saved successfully.'); } } else { throw new Error('[iFlow] Token file does not contain a valid API key.'); } } catch (error) { console.error('[iFlow Auth] Error initializing authentication:', error.code || error.message); if (error.code === 'ENOENT') { console.log(`[iFlow Auth] Credentials file '${this.tokenFilePath}' not found.`); throw new Error(`[iFlow Auth] Credentials file not found. Please run OAuth flow first.`); } else { console.error('[iFlow Auth] Failed to initialize authentication from file:', error.message); throw new Error(`[iFlow Auth] Failed to load OAuth credentials.`); } } // 更新 axios 实例的 Authorization header this.axiosInstance.defaults.headers['Authorization'] = `Bearer ${this.apiKey}`; } /** * 检查是否需要刷新 Token 并执行刷新 * @returns {Promise} - 是否执行了刷新 */ async _checkAndRefreshTokenIfNeeded() { if (!this.tokenStorage) { return false; } // 检查是否有 refresh_token if (!this.tokenStorage.refreshToken || this.tokenStorage.refreshToken.trim() === '') { console.log('[iFlow] No refresh_token available, skipping token refresh check'); return false; } // 使用 isExpiryDateNear 检查过期时间 if (!this.isExpiryDateNear()) { console.log('[iFlow] Token is valid, no refresh needed'); return false; } console.log('[iFlow] Token is expiring soon, attempting refresh...'); try { await this._refreshOAuthTokens(); return true; } catch (error) { console.error('[iFlow] Token refresh failed:', error.message); // 刷新失败不抛出异常,继续使用现有 Token return false; } } /** * 使用 refresh_token 刷新 OAuth Token * @returns {Promise} */ async _refreshOAuthTokens() { if (!this.tokenStorage || !this.tokenStorage.refreshToken) { throw new Error('[iFlow] No refresh_token available'); } const oldAccessToken = this.tokenStorage.accessToken; if (oldAccessToken) { console.log(`[iFlow] Refreshing access token, old: ${this._maskToken(oldAccessToken)}`); } // 调用刷新函数 const tokenData = await refreshOAuthTokens(this.tokenStorage.refreshToken, this.axiosInstance); // 更新 tokenStorage this.tokenStorage.accessToken = tokenData.accessToken; if (tokenData.refreshToken) { this.tokenStorage.refreshToken = tokenData.refreshToken; } if (tokenData.apiKey) { this.tokenStorage.apiKey = tokenData.apiKey; this.apiKey = tokenData.apiKey; } this.tokenStorage.expiryDate = tokenData.expiryDate; this.tokenStorage.tokenType = tokenData.tokenType || 'Bearer'; this.tokenStorage.scope = tokenData.scope || ''; if (tokenData.email) { this.tokenStorage.email = tokenData.email; } // 更新 axios 实例的 Authorization header this.axiosInstance.defaults.headers['Authorization'] = `Bearer ${this.apiKey}`; // 保存到文件 await saveTokenToFile(this.tokenFilePath, this.tokenStorage); console.log(`[iFlow] Token refresh successful, new: ${this._maskToken(tokenData.accessToken)}`); } /** * 掩码 Token(只显示前后几个字符) * @param {string} token - Token 字符串 * @returns {string} - 掩码后的 Token */ _maskToken(token) { if (!token || token.length < 10) { return '***'; } return `${token.substring(0, 4)}...${token.substring(token.length - 4)}`; } /** * 手动刷新 Token(供外部调用) * @returns {Promise} - 是否刷新成功 */ async refreshToken() { if (!this.isInitialized) { await this.initialize(); } try { await this._refreshOAuthTokens(); return true; } catch (error) { console.error('[iFlow] Manual token refresh failed:', error.message); return false; } } /** * Checks if the given expiry date is within the threshold from now or already expired. * @returns {boolean} True if the expiry date is within the threshold or already expired, false otherwise. */ isExpiryDateNear() { try { if (!this.tokenStorage || !this.tokenStorage.expiryDate) { return false; } const currentTime = Date.now(); // 默认 10 分钟,可通过配置覆盖 const cronNearMinutes = this.config.CRON_NEAR_MINUTES || 10; const cronNearMinutesInMillis = cronNearMinutes * 60 * 1000; // 解析过期时间 let expireTime; const expireValue = this.tokenStorage.expiryDate; // 检查是否为数字(毫秒时间戳) if (typeof expireValue === 'number') { expireTime = expireValue; } else if (typeof expireValue === 'string') { // 检查是否为纯数字字符串(毫秒时间戳) if (/^\d+$/.test(expireValue)) { expireTime = parseInt(expireValue, 10); } else if (expireValue.includes('T')) { // ISO 8601 格式 expireTime = new Date(expireValue).getTime(); } else { // 格式:2006-01-02 15:04 expireTime = new Date(expireValue.replace(' ', 'T') + ':00').getTime(); } } else { console.error(`[iFlow] Invalid expiry date type: ${typeof expireValue}`); return false; } if (isNaN(expireTime)) { console.error(`[iFlow] Error parsing expiry date: ${expireValue}`); return false; } // 计算剩余时间 const timeRemaining = expireTime - currentTime; // 判断是否已过期或接近过期 // 已过期:timeRemaining <= 0 // 接近过期:timeRemaining > 0 && timeRemaining <= cronNearMinutesInMillis const isExpired = timeRemaining <= 0; const isNear = timeRemaining > 0 && timeRemaining <= cronNearMinutesInMillis; const needsRefresh = isExpired || isNear; const expireDateStr = new Date(expireTime).toISOString(); const timeRemainingMinutes = Math.floor(timeRemaining / 60000); const timeRemainingHours = (timeRemaining / 3600000).toFixed(2); console.log(`[iFlow] Token expiry check: Expiry=${expireDateStr}, Remaining=${timeRemainingHours}h (${timeRemainingMinutes}min), Threshold=${cronNearMinutes}min, Expired=${isExpired}, Near=${isNear}, NeedsRefresh=${needsRefresh}`); return needsRefresh; } catch (error) { console.error(`[iFlow] Error checking expiry date: ${error.message}`); return false; } } /** * 获取请求头 * @param {boolean} stream - 是否为流式请求 * @returns {Object} - 请求头 */ _getHeaders(stream = false) { const headers = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}`, 'User-Agent': IFLOW_USER_AGENT, }; if (stream) { headers['Accept'] = 'text/event-stream'; } else { headers['Accept'] = 'application/json'; } return headers; } /** * 调用 API */ async callApi(endpoint, body, model, retryCount = 0) { const maxRetries = this.config.REQUEST_MAX_RETRIES || 3; const baseDelay = this.config.REQUEST_BASE_DELAY || 1000; // 预处理请求体 const processedBody = preprocessRequestBody(body, model); try { const response = await this.axiosInstance.post(endpoint, processedBody, { headers: this._getHeaders(false) }); return response.data; } catch (error) { const status = error.response?.status; const data = error.response?.data; const errorCode = error.code; const errorMessage = error.message || ''; // 检查是否为可重试的网络错误 const isNetworkError = isRetryableNetworkError(error); if (status === 401 || status === 403) { console.error(`[iFlow] Received ${status}. API Key might be invalid or expired.`); throw error; } // Handle 429 (Too Many Requests) with exponential backoff if (status === 429 && retryCount < maxRetries) { 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); } // Handle other retryable errors (5xx server errors) if (status >= 500 && status < 600 && retryCount < maxRetries) { 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); } // Handle network errors (ECONNRESET, ETIMEDOUT, etc.) with exponential backoff if (isNetworkError && retryCount < maxRetries) { const delay = baseDelay * Math.pow(2, retryCount); 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); } console.error(`[iFlow] Error calling API (Status: ${status}, Code: ${errorCode}):`, data || error.message); throw error; } } /** * 流式调用 API * * - 使用大缓冲区处理长行 * - 逐行处理 SSE 数据 * - 正确处理 data: 前缀和 [DONE] 标记 */ async *streamApi(endpoint, body, model, retryCount = 0) { const maxRetries = this.config.REQUEST_MAX_RETRIES || 3; const baseDelay = this.config.REQUEST_BASE_DELAY || 1000; // 预处理请求体并设置 stream: true const processedBody = preprocessRequestBody({ ...body, stream: true }, model); try { const response = await this.axiosInstance.post(endpoint, processedBody, { responseType: 'stream', headers: this._getHeaders(true) }); const stream = response.data; let buffer = ''; for await (const chunk of stream) { // 将 chunk 转换为字符串并追加到缓冲区 buffer += chunk.toString(); // 逐行处理 let newlineIndex; while ((newlineIndex = buffer.indexOf('\n')) !== -1) { // 提取一行(不包含换行符) const line = buffer.substring(0, newlineIndex); buffer = buffer.substring(newlineIndex + 1); // 去除行首尾空白(处理 \r\n 情况) const trimmedLine = line.trim(); // 跳过空行(SSE 格式中的分隔符) if (trimmedLine === '') { continue; } // 处理 SSE data: 前缀 if (trimmedLine.startsWith('data:')) { // 提取 data: 后的内容(注意:data: 后可能有空格也可能没有) let jsonData = trimmedLine.substring(5); // 去除前导空格 if (jsonData.startsWith(' ')) { jsonData = jsonData.substring(1); } jsonData = jsonData.trim(); // 检查流结束标记 if (jsonData === '[DONE]') { return; // 流结束 } // 跳过空数据 if (jsonData === '') { continue; } try { const parsedChunk = JSON.parse(jsonData); yield parsedChunk; } catch (e) { // JSON 解析失败,记录警告但继续处理 console.warn("[iFlow] Failed to parse stream chunk JSON:", e.message, "Data:", jsonData.substring(0, 200)); } } // 忽略其他 SSE 字段(如 event:, id:, retry: 等) } } // 处理缓冲区中剩余的数据(如果有的话) if (buffer.trim() !== '') { const trimmedLine = buffer.trim(); if (trimmedLine.startsWith('data:')) { let jsonData = trimmedLine.substring(5); if (jsonData.startsWith(' ')) { jsonData = jsonData.substring(1); } jsonData = jsonData.trim(); if (jsonData !== '[DONE]' && jsonData !== '') { try { const parsedChunk = JSON.parse(jsonData); yield parsedChunk; } catch (e) { console.warn("[iFlow] Failed to parse final stream chunk JSON:", e.message); } } } } } catch (error) { const status = error.response?.status; const data = error.response?.data; const errorCode = error.code; const errorMessage = error.message || ''; // 检查是否为可重试的网络错误 const isNetworkError = isRetryableNetworkError(error); if (status === 401 || status === 403) { console.error(`[iFlow] Received ${status} during stream. API Key might be invalid or expired.`); throw error; } // Handle 429 (Too Many Requests) with exponential backoff if (status === 429 && retryCount < maxRetries) { 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); return; } // Handle other retryable errors (5xx server errors) if (status >= 500 && status < 600 && retryCount < maxRetries) { 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); return; } // Handle network errors (ECONNRESET, ETIMEDOUT, etc.) with exponential backoff if (isNetworkError && retryCount < maxRetries) { const delay = baseDelay * Math.pow(2, retryCount); 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); return; } console.error(`[iFlow] Error calling streaming API (Status: ${status}, Code: ${errorCode}):`, data || error.message); throw error; } } /** * 生成内容 */ async generateContent(model, requestBody) { if (!this.isInitialized) { await this.initialize(); } // 在 API 调用前检查是否需要刷新 Token await this._checkAndRefreshTokenIfNeeded(); return this.callApi('/chat/completions', requestBody, model); } /** * 流式生成内容 */ async *generateContentStream(model, requestBody) { if (!this.isInitialized) { await this.initialize(); } // 在 API 调用前检查是否需要刷新 Token await this._checkAndRefreshTokenIfNeeded(); yield* this.streamApi('/chat/completions', requestBody, model); } /** * 列出可用模型 */ async listModels() { if (!this.isInitialized) { await this.initialize(); } try { const response = await this.axiosInstance.get('/models', { headers: this._getHeaders(false) }); // 检查返回数据中是否包含 glm-4.7,如果没有则添加 const modelsData = response.data; if (modelsData && modelsData.data && Array.isArray(modelsData.data)) { const hasGlm47 = modelsData.data.some(model => model.id === 'glm-4.7'); if (!hasGlm47) { // 添加 glm-4.7 模型到返回列表 modelsData.data.push({ id: 'glm-4.7', object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'iflow' }); console.log('[iFlow] Added glm-4.7 to models list'); } } return modelsData; } catch (error) { console.warn('[iFlow] Failed to fetch models from API, using default list:', error.message); // 返回默认模型列表,确保包含 glm-4.7 const defaultModels = [...IFLOW_MODELS]; if (!defaultModels.includes('glm-4.7')) { defaultModels.push('glm-4.7'); } return { object: 'list', data: defaultModels.map(id => ({ id, object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'iflow' })) }; } } } export { IFLOW_MODELS, IFLOW_USER_AGENT, IFlowTokenStorage, loadTokenFromFile, saveTokenToFile, refreshOAuthTokens, fetchUserInfo, isThinkingModel, applyIFlowThinkingConfig, preserveReasoningContentInMessages, ensureToolsArray, preprocessRequestBody, };