108 lines
No EOL
4.8 KiB
JavaScript
108 lines
No EOL
4.8 KiB
JavaScript
import {
|
|
handleModelListRequest,
|
|
handleContentGenerationRequest,
|
|
API_ACTIONS,
|
|
ENDPOINT_TYPE
|
|
} from './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);
|
|
return true;
|
|
}
|
|
if (path === '/v1/responses') {
|
|
await handleContentGenerationRequest(req, res, apiService, ENDPOINT_TYPE.OPENAI_RESPONSES, currentConfig, promptLogFilename, providerPoolManager, currentConfig.uuid);
|
|
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);
|
|
return true;
|
|
}
|
|
if (path === '/v1/messages') {
|
|
await handleContentGenerationRequest(req, res, apiService, ENDPOINT_TYPE.CLAUDE_MESSAGE, currentConfig, promptLogFilename, providerPoolManager, currentConfig.uuid);
|
|
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) {
|
|
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
|
|
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);
|
|
});
|
|
});
|
|
} |