Spaces:
Running
Running
| import { | |
| handleModelListRequest, | |
| handleContentGenerationRequest, | |
| API_ACTIONS, | |
| ENDPOINT_TYPE | |
| } from '../utils/common.js'; | |
| import { getProviderPoolManager } from './service-manager.js'; | |
| /** | |
| * Handle API authentication and routing | |
| * @param {string} method - The HTTP method | |
| * @param {string} path - The request path | |
| * @param {http.IncomingMessage} req - The HTTP request object | |
| * @param {http.ServerResponse} res - The HTTP response object | |
| * @param {Object} currentConfig - The current configuration object | |
| * @param {Object} apiService - The API service instance | |
| * @param {Object} providerPoolManager - The provider pool manager instance | |
| * @param {string} promptLogFilename - The prompt log filename | |
| * @returns {Promise<boolean>} - True if the request was handled by API | |
| */ | |
| export async function handleAPIRequests(method, path, req, res, currentConfig, apiService, providerPoolManager, promptLogFilename) { | |
| // Route model list requests | |
| if (method === 'GET') { | |
| if (path === '/v1/models') { | |
| await handleModelListRequest(req, res, apiService, ENDPOINT_TYPE.OPENAI_MODEL_LIST, currentConfig, providerPoolManager, currentConfig.uuid); | |
| return true; | |
| } | |
| if (path === '/v1beta/models') { | |
| await handleModelListRequest(req, res, apiService, ENDPOINT_TYPE.GEMINI_MODEL_LIST, currentConfig, providerPoolManager, currentConfig.uuid); | |
| return true; | |
| } | |
| } | |
| // Route content generation requests | |
| if (method === 'POST') { | |
| if (path === '/v1/chat/completions') { | |
| await handleContentGenerationRequest(req, res, apiService, ENDPOINT_TYPE.OPENAI_CHAT, currentConfig, promptLogFilename, providerPoolManager, currentConfig.uuid, path); | |
| return true; | |
| } | |
| if (path === '/v1/responses') { | |
| await handleContentGenerationRequest(req, res, apiService, ENDPOINT_TYPE.OPENAI_RESPONSES, currentConfig, promptLogFilename, providerPoolManager, currentConfig.uuid, path); | |
| return true; | |
| } | |
| const geminiUrlPattern = new RegExp(`/v1beta/models/(.+?):(${API_ACTIONS.GENERATE_CONTENT}|${API_ACTIONS.STREAM_GENERATE_CONTENT})`); | |
| if (geminiUrlPattern.test(path)) { | |
| await handleContentGenerationRequest(req, res, apiService, ENDPOINT_TYPE.GEMINI_CONTENT, currentConfig, promptLogFilename, providerPoolManager, currentConfig.uuid, path); | |
| return true; | |
| } | |
| if (path === '/v1/messages') { | |
| await handleContentGenerationRequest(req, res, apiService, ENDPOINT_TYPE.CLAUDE_MESSAGE, currentConfig, promptLogFilename, providerPoolManager, currentConfig.uuid, path); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Initialize API management features | |
| * @param {Object} services - The initialized services | |
| * @returns {Function} - The heartbeat and token refresh function | |
| */ | |
| export function initializeAPIManagement(services) { | |
| const providerPoolManager = getProviderPoolManager(); | |
| return async function heartbeatAndRefreshToken() { | |
| console.log(`[Heartbeat] Server is running. Current time: ${new Date().toLocaleString()}`, Object.keys(services)); | |
| // 循环遍历所有已初始化的服务适配器,并尝试刷新令牌 | |
| // if (getProviderPoolManager()) { | |
| // await getProviderPoolManager().performHealthChecks(); // 定期执行健康检查 | |
| // } | |
| for (const providerKey in services) { | |
| const serviceAdapter = services[providerKey]; | |
| try { | |
| // For pooled providers, refreshToken should be handled by individual instances | |
| // For single instances, this remains relevant | |
| if (serviceAdapter.config?.uuid && providerPoolManager) { | |
| providerPoolManager._enqueueRefresh(serviceAdapter.config.MODEL_PROVIDER, { | |
| config: serviceAdapter.config, | |
| uuid: serviceAdapter.config.uuid | |
| }); | |
| } else { | |
| await serviceAdapter.refreshToken(); | |
| } | |
| // console.log(`[Token Refresh] Refreshed token for ${providerKey}`); | |
| } catch (error) { | |
| console.error(`[Token Refresh Error] Failed to refresh token for ${providerKey}: ${error.message}`); | |
| // 如果是号池中的某个实例刷新失败,这里需要捕获并更新其状态 | |
| // 现有的 serviceInstances 存储的是每个配置对应的单例,而非池中的成员 | |
| // 这意味着如果一个池成员的 token 刷新失败,需要找到它并更新其在 poolManager 中的状态 | |
| // 暂时通过捕获错误日志来发现问题,更精细的控制需要在 refreshToken 中抛出更多信息 | |
| } | |
| } | |
| }; | |
| } | |
| /** | |
| * Helper function to read request body | |
| * @param {http.IncomingMessage} req The HTTP request object. | |
| * @returns {Promise<string>} The request body as string. | |
| */ | |
| export function readRequestBody(req) { | |
| return new Promise((resolve, reject) => { | |
| let body = ''; | |
| req.on('data', chunk => { | |
| body += chunk.toString(); | |
| }); | |
| req.on('end', () => { | |
| resolve(body); | |
| }); | |
| req.on('error', err => { | |
| reject(err); | |
| }); | |
| }); | |
| } |