Spaces:
Running
Running
File size: 5,370 Bytes
ceb3821 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 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);
});
});
} |