refactor(provider-pool): 重构提供者池管理器的日志和健康检查逻辑
- 添加日志级别控制和统一的日志输出方法 - 重构健康检查逻辑,使用默认模型配置和标准化的请求构建 - 优化批量保存机制,减少文件I/O操作 - 添加参数校验和错误处理 - 重命名方法以更准确表达其功能
This commit is contained in:
parent
08881ae144
commit
1c22b8c5c9
1 changed files with 220 additions and 174 deletions
|
|
@ -6,15 +6,28 @@ import { MODEL_PROVIDER } from './common.js';
|
|||
* Manages a pool of API service providers, handling their health and selection.
|
||||
*/
|
||||
export class ProviderPoolManager {
|
||||
// 默认健康检查模型配置
|
||||
static DEFAULT_HEALTH_CHECK_MODELS = {
|
||||
'gemini-cli': 'gemini-2.5-flash',
|
||||
'openai-custom': 'gpt-3.5-turbo',
|
||||
'claude-custom': 'claude-3-7-sonnet-20250219',
|
||||
'kiro-api': 'claude-3-7-sonnet-20250219',
|
||||
'qwen-api': 'qwen3-coder-flash',
|
||||
'openai-custom-responses': 'gpt-5-low'
|
||||
};
|
||||
|
||||
constructor(providerPools, options = {}) {
|
||||
this.providerPools = providerPools;
|
||||
this.globalConfig = options.globalConfig || {}; // 存储全局配置
|
||||
this.providerStatus = {}; // Tracks health and usage for each provider instance
|
||||
this.roundRobinIndex = {}; // Tracks the current index for round-robin selection for each provider type
|
||||
this.maxErrorCount = options.maxErrorCount || 3; // Default to 1 errors before marking unhealthy
|
||||
this.maxErrorCount = options.maxErrorCount || 3; // Default to 3 errors before marking unhealthy
|
||||
this.healthCheckInterval = options.healthCheckInterval || 10 * 60 * 1000; // Default to 10 minutes
|
||||
|
||||
// 优化1: 添加防抖机制,避免频繁的文件 I/O 操作
|
||||
// 日志级别控制
|
||||
this.logLevel = options.logLevel || 'info'; // 'debug', 'info', 'warn', 'error'
|
||||
|
||||
// 添加防抖机制,避免频繁的文件 I/O 操作
|
||||
this.saveDebounceTime = options.saveDebounceTime || 1000; // 默认1秒防抖
|
||||
this.saveTimer = null;
|
||||
this.pendingSaves = new Set(); // 记录待保存的 providerType
|
||||
|
|
@ -22,6 +35,31 @@ export class ProviderPoolManager {
|
|||
this.initializeProviderStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志输出方法,支持日志级别控制
|
||||
* @private
|
||||
*/
|
||||
_log(level, message) {
|
||||
const levels = { debug: 0, info: 1, warn: 2, error: 3 };
|
||||
if (levels[level] >= levels[this.logLevel]) {
|
||||
const logMethod = level === 'debug' ? 'log' : level;
|
||||
console[logMethod](`[ProviderPoolManager] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定的 provider
|
||||
* @private
|
||||
*/
|
||||
_findProvider(providerType, uuid) {
|
||||
if (!providerType || !uuid) {
|
||||
this._log('error', `Invalid parameters: providerType=${providerType}, uuid=${uuid}`);
|
||||
return null;
|
||||
}
|
||||
const pool = this.providerStatus[providerType];
|
||||
return pool?.find(p => p.uuid === uuid) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the status for each provider in the pools.
|
||||
* Initially, all providers are considered healthy and have zero usage.
|
||||
|
|
@ -49,7 +87,7 @@ export class ProviderPoolManager {
|
|||
});
|
||||
});
|
||||
}
|
||||
console.log('[ProviderPoolManager] Initialized provider statuses: ok');
|
||||
this._log('info', 'Initialized provider statuses: ok');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -59,17 +97,23 @@ export class ProviderPoolManager {
|
|||
* @returns {object|null} The selected provider's configuration, or null if no healthy provider is found.
|
||||
*/
|
||||
selectProvider(providerType) {
|
||||
// 参数校验
|
||||
if (!providerType || typeof providerType !== 'string') {
|
||||
this._log('error', `Invalid providerType: ${providerType}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const availableProviders = this.providerStatus[providerType] || [];
|
||||
const availableAndHealthyProviders = availableProviders.filter(p =>
|
||||
p.config.isHealthy && !p.config.isDisabled
|
||||
);
|
||||
|
||||
if (availableAndHealthyProviders.length === 0) {
|
||||
console.warn(`[ProviderPoolManager] No available and healthy providers for type: ${providerType}`);
|
||||
this._log('warn', `No available and healthy providers for type: ${providerType}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 优化3: 简化轮询逻辑,移除不必要的循环
|
||||
// 简化轮询逻辑
|
||||
const currentIndex = this.roundRobinIndex[providerType] || 0;
|
||||
const providerIndex = currentIndex % availableAndHealthyProviders.length;
|
||||
const selected = availableAndHealthyProviders[providerIndex];
|
||||
|
|
@ -81,9 +125,9 @@ export class ProviderPoolManager {
|
|||
selected.config.lastUsed = new Date().toISOString();
|
||||
selected.config.usageCount++;
|
||||
|
||||
console.log(`[ProviderPoolManager] Selected provider for ${providerType} (round-robin): ${JSON.stringify(selected.config)}`);
|
||||
this._log('debug', `Selected provider for ${providerType} (round-robin): ${selected.config.uuid}`);
|
||||
|
||||
// 优化1: 使用防抖保存
|
||||
// 使用防抖保存
|
||||
this._debouncedSave(providerType);
|
||||
|
||||
return selected.config;
|
||||
|
|
@ -95,23 +139,24 @@ export class ProviderPoolManager {
|
|||
* @param {object} providerConfig - The configuration of the provider to mark.
|
||||
*/
|
||||
markProviderUnhealthy(providerType, providerConfig) {
|
||||
const pool = this.providerStatus[providerType];
|
||||
if (pool) {
|
||||
const provider = pool.find(p => p.uuid === providerConfig.uuid);
|
||||
if (provider) {
|
||||
provider.config.errorCount++;
|
||||
provider.config.lastErrorTime = new Date().toISOString(); // Update last error time in config
|
||||
if (!providerConfig?.uuid) {
|
||||
this._log('error', 'Invalid providerConfig in markProviderUnhealthy');
|
||||
return;
|
||||
}
|
||||
|
||||
if (provider.config.errorCount >= this.maxErrorCount) {
|
||||
provider.config.isHealthy = false;
|
||||
console.warn(`[ProviderPoolManager] Marked provider as unhealthy: ${providerConfig.uuid} for type ${providerType}. Total errors: ${provider.config.errorCount}`);
|
||||
} else {
|
||||
console.warn(`[ProviderPoolManager] Provider ${providerConfig.uuid} for type ${providerType} error count: ${provider.config.errorCount}/${this.maxErrorCount}. Still healthy.`);
|
||||
}
|
||||
|
||||
// 优化1: 使用防抖保存
|
||||
this._debouncedSave(providerType);
|
||||
const provider = this._findProvider(providerType, providerConfig.uuid);
|
||||
if (provider) {
|
||||
provider.config.errorCount++;
|
||||
provider.config.lastErrorTime = new Date().toISOString();
|
||||
|
||||
if (provider.config.errorCount >= this.maxErrorCount) {
|
||||
provider.config.isHealthy = false;
|
||||
this._log('warn', `Marked provider as unhealthy: ${providerConfig.uuid} for type ${providerType}. Total errors: ${provider.config.errorCount}`);
|
||||
} else {
|
||||
this._log('warn', `Provider ${providerConfig.uuid} for type ${providerType} error count: ${provider.config.errorCount}/${this.maxErrorCount}. Still healthy.`);
|
||||
}
|
||||
|
||||
this._debouncedSave(providerType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,43 +164,46 @@ export class ProviderPoolManager {
|
|||
* Marks a provider as healthy.
|
||||
* @param {string} providerType - The type of the provider.
|
||||
* @param {object} providerConfig - The configuration of the provider to mark.
|
||||
* @param {boolean} isInit - Whether to reset usage count (optional, default: false).
|
||||
*/
|
||||
markProviderHealthy(isInit, providerType, providerConfig) {
|
||||
const pool = this.providerStatus[providerType];
|
||||
if (pool) {
|
||||
const provider = pool.find(p => p.uuid === providerConfig.uuid);
|
||||
if (provider) {
|
||||
provider.config.isHealthy = true;
|
||||
provider.config.errorCount = 0; // Reset error count on health recovery
|
||||
provider.config.lastErrorTime = null; // Reset lastErrorTime when healthy
|
||||
if (isInit) {
|
||||
provider.config.usageCount = 0; // Reset usage count on health recovery
|
||||
}
|
||||
console.log(`[ProviderPoolManager] Marked provider as healthy: ${provider.config.uuid} for type ${providerType}`);
|
||||
|
||||
// 优化1: 使用防抖保存
|
||||
this._debouncedSave(providerType);
|
||||
markProviderHealthy(providerType, providerConfig, isInit = false) {
|
||||
if (!providerConfig?.uuid) {
|
||||
this._log('error', 'Invalid providerConfig in markProviderHealthy');
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = this._findProvider(providerType, providerConfig.uuid);
|
||||
if (provider) {
|
||||
provider.config.isHealthy = true;
|
||||
provider.config.errorCount = 0;
|
||||
provider.config.lastErrorTime = null;
|
||||
if (isInit) {
|
||||
provider.config.usageCount = 0;
|
||||
}
|
||||
this._log('info', `Marked provider as healthy: ${provider.config.uuid} for type ${providerType}`);
|
||||
|
||||
this._debouncedSave(providerType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a provider as healthy.
|
||||
/**
|
||||
* 重置提供商的计数器(错误计数和使用计数)
|
||||
* @param {string} providerType - The type of the provider.
|
||||
* @param {object} providerConfig - The configuration of the provider to mark.
|
||||
*/
|
||||
markProviderZero(providerType, providerConfig) {
|
||||
const pool = this.providerStatus[providerType];
|
||||
if (pool) {
|
||||
const provider = pool.find(p => p.uuid === providerConfig.uuid);
|
||||
if (provider) {
|
||||
provider.config.errorCount = 0; // Reset error count on health recovery
|
||||
provider.config.usageCount = 0; // Reset usage count on health recovery
|
||||
console.log(`[ProviderPoolManager] Marked provider as zero: ${provider.config.uuid} for type ${providerType}`);
|
||||
|
||||
// 优化1: 使用防抖保存
|
||||
this._debouncedSave(providerType);
|
||||
}
|
||||
resetProviderCounters(providerType, providerConfig) {
|
||||
if (!providerConfig?.uuid) {
|
||||
this._log('error', 'Invalid providerConfig in resetProviderCounters');
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = this._findProvider(providerType, providerConfig.uuid);
|
||||
if (provider) {
|
||||
provider.config.errorCount = 0;
|
||||
provider.config.usageCount = 0;
|
||||
this._log('info', `Reset provider counters: ${provider.config.uuid} for type ${providerType}`);
|
||||
|
||||
this._debouncedSave(providerType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,16 +213,16 @@ export class ProviderPoolManager {
|
|||
* @param {object} providerConfig - 提供商配置
|
||||
*/
|
||||
disableProvider(providerType, providerConfig) {
|
||||
const pool = this.providerStatus[providerType];
|
||||
if (pool) {
|
||||
const provider = pool.find(p => p.uuid === providerConfig.uuid);
|
||||
if (provider) {
|
||||
provider.config.isDisabled = true;
|
||||
console.log(`[ProviderPoolManager] Disabled provider: ${providerConfig.uuid} for type ${providerType}`);
|
||||
|
||||
// 使用防抖保存
|
||||
this._debouncedSave(providerType);
|
||||
}
|
||||
if (!providerConfig?.uuid) {
|
||||
this._log('error', 'Invalid providerConfig in disableProvider');
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = this._findProvider(providerType, providerConfig.uuid);
|
||||
if (provider) {
|
||||
provider.config.isDisabled = true;
|
||||
this._log('info', `Disabled provider: ${providerConfig.uuid} for type ${providerType}`);
|
||||
this._debouncedSave(providerType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -184,16 +232,16 @@ export class ProviderPoolManager {
|
|||
* @param {object} providerConfig - 提供商配置
|
||||
*/
|
||||
enableProvider(providerType, providerConfig) {
|
||||
const pool = this.providerStatus[providerType];
|
||||
if (pool) {
|
||||
const provider = pool.find(p => p.uuid === providerConfig.uuid);
|
||||
if (provider) {
|
||||
provider.config.isDisabled = false;
|
||||
console.log(`[ProviderPoolManager] Enabled provider: ${providerConfig.uuid} for type ${providerType}`);
|
||||
|
||||
// 使用防抖保存
|
||||
this._debouncedSave(providerType);
|
||||
}
|
||||
if (!providerConfig?.uuid) {
|
||||
this._log('error', 'Invalid providerConfig in enableProvider');
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = this._findProvider(providerType, providerConfig.uuid);
|
||||
if (provider) {
|
||||
provider.config.isDisabled = false;
|
||||
this._log('info', `Enabled provider: ${providerConfig.uuid} for type ${providerType}`);
|
||||
this._debouncedSave(providerType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,8 +250,9 @@ export class ProviderPoolManager {
|
|||
* This method would typically be called periodically (e.g., via cron job).
|
||||
*/
|
||||
async performHealthChecks(isInit = false) {
|
||||
console.log('[ProviderPoolManager] Performing health checks on all providers...');
|
||||
this._log('info', 'Performing health checks on all providers...');
|
||||
const now = new Date();
|
||||
|
||||
for (const providerType in this.providerStatus) {
|
||||
for (const providerStatus of this.providerStatus[providerType]) {
|
||||
const providerConfig = providerStatus.config;
|
||||
|
|
@ -211,37 +260,38 @@ export class ProviderPoolManager {
|
|||
// Only attempt to health check unhealthy providers after a certain interval
|
||||
if (!providerStatus.config.isHealthy && providerStatus.config.lastErrorTime &&
|
||||
(now.getTime() - new Date(providerStatus.config.lastErrorTime).getTime() < this.healthCheckInterval)) {
|
||||
console.log(`[ProviderPoolManager] Skipping health check for ${providerConfig.uuid} (${providerType}). Last error too recent.`);
|
||||
this._log('debug', `Skipping health check for ${providerConfig.uuid} (${providerType}). Last error too recent.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Perform actual health check based on provider type
|
||||
const isHealthy = await this._checkProviderHealth(providerType, providerConfig);
|
||||
if(isHealthy === null){
|
||||
console.log(`[ProviderPoolManager] Health check for ${providerConfig.uuid} (${providerType}) skipped: Check not implemented.`);
|
||||
this.markProviderZero(providerType, providerConfig);
|
||||
|
||||
if (isHealthy === null) {
|
||||
this._log('debug', `Health check for ${providerConfig.uuid} (${providerType}) skipped: Check not implemented.`);
|
||||
this.resetProviderCounters(providerType, providerConfig);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isHealthy) {
|
||||
if (!providerStatus.config.isHealthy) {
|
||||
// Provider was unhealthy but is now healthy
|
||||
this.markProviderHealthy(isInit, providerType, providerConfig);
|
||||
console.log(`[ProviderPoolManager] Health check for ${providerConfig.uuid} (${providerType}): Marked Healthy (actual check)`);
|
||||
this.markProviderHealthy(providerType, providerConfig, isInit);
|
||||
this._log('info', `Health check for ${providerConfig.uuid} (${providerType}): Marked Healthy (actual check)`);
|
||||
} else {
|
||||
// Provider was already healthy and still is
|
||||
this.markProviderHealthy(isInit, providerType, providerConfig);
|
||||
console.log(`[ProviderPoolManager] Health check for ${providerConfig.uuid} (${providerType}): Still Healthy`);
|
||||
this.markProviderHealthy(providerType, providerConfig, isInit);
|
||||
this._log('debug', `Health check for ${providerConfig.uuid} (${providerType}): Still Healthy`);
|
||||
}
|
||||
} else {
|
||||
// Provider is not healthy
|
||||
console.warn(`[ProviderPoolManager] Health check for ${providerConfig.uuid} (${providerType}) failed: Provider is not responding correctly.`);
|
||||
this._log('warn', `Health check for ${providerConfig.uuid} (${providerType}) failed: Provider is not responding correctly.`);
|
||||
this.markProviderUnhealthy(providerType, providerConfig);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`[ProviderPoolManager] Health check for ${providerConfig.uuid} (${providerType}) failed: ${error.message}`);
|
||||
this._log('error', `Health check for ${providerConfig.uuid} (${providerType}) failed: ${error.message}`);
|
||||
// If a health check fails, mark it unhealthy, which will update error count and lastErrorTime
|
||||
this.markProviderUnhealthy(providerType, providerConfig);
|
||||
}
|
||||
|
|
@ -249,88 +299,85 @@ export class ProviderPoolManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建健康检查请求
|
||||
* @private
|
||||
*/
|
||||
_buildHealthCheckRequest(providerType, modelName) {
|
||||
const baseMessage = { role: 'user', content: 'Hello, are you ok?' };
|
||||
|
||||
// Gemini 使用不同的请求格式
|
||||
if (providerType === 'gemini-cli') {
|
||||
return {
|
||||
contents: [{
|
||||
role: 'user',
|
||||
parts: [{ text: baseMessage.content }]
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
// OpenAI Custom Responses 使用特殊格式
|
||||
if (providerType === 'openai-custom-responses') {
|
||||
return {
|
||||
input: [baseMessage],
|
||||
model: modelName
|
||||
};
|
||||
}
|
||||
|
||||
// 其他提供商(OpenAI、Claude、Kiro、Qwen)使用标准格式
|
||||
return {
|
||||
messages: [baseMessage],
|
||||
model: modelName
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an actual health check for a specific provider.
|
||||
* @param {string} providerType - The type of the provider.
|
||||
* @param {object} providerConfig - The configuration of the provider to check.
|
||||
* @returns {Promise<boolean>} - True if the provider is healthy, false otherwise.
|
||||
* @returns {Promise<boolean|null>} - True if healthy, false if unhealthy, null if check not implemented.
|
||||
*/
|
||||
async _checkProviderHealth(providerType, providerConfig) {
|
||||
try {
|
||||
// Create a temporary service adapter for health check
|
||||
// 合并全局配置和 provider 配置
|
||||
const tempConfig = {
|
||||
// ...this.globalConfig,
|
||||
...providerConfig,
|
||||
MODEL_PROVIDER: providerType,
|
||||
USE_SYSTEM_PROXY_GEMINI: this.globalConfig.USE_SYSTEM_PROXY_GEMINI,
|
||||
USE_SYSTEM_PROXY_OPENAI: this.globalConfig.USE_SYSTEM_PROXY_OPENAI,
|
||||
USE_SYSTEM_PROXY_CLAUDE: this.globalConfig.USE_SYSTEM_PROXY_CLAUDE,
|
||||
USE_SYSTEM_PROXY_QWEN: this.globalConfig.USE_SYSTEM_PROXY_QWEN,
|
||||
USE_SYSTEM_PROXY_KIRO: this.globalConfig.USE_SYSTEM_PROXY_KIRO,
|
||||
};
|
||||
const serviceAdapter = getServiceAdapter(tempConfig);
|
||||
if(!providerConfig.checkHealth){
|
||||
// 如果未启用健康检查,返回 null
|
||||
if (!providerConfig.checkHealth) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine a suitable model name for health check
|
||||
// First, try to get it from the provider configuration
|
||||
let modelName = providerConfig.checkModelName;
|
||||
|
||||
// If not specified in config, use default model names based on provider type
|
||||
if (!modelName) {
|
||||
switch (providerType) {
|
||||
case MODEL_PROVIDER.GEMINI_CLI:
|
||||
modelName = 'gemini-2.5-flash'; // Example model name for Gemini
|
||||
break;
|
||||
case MODEL_PROVIDER.OPENAI_CUSTOM:
|
||||
modelName = 'gpt-3.5-turbo'; // Example model name for OpenAI
|
||||
break;
|
||||
case MODEL_PROVIDER.CLAUDE_CUSTOM:
|
||||
modelName = 'claude-3-7-sonnet-20250219'; // Example model name for Claude
|
||||
break;
|
||||
case MODEL_PROVIDER.KIRO_API:
|
||||
modelName = 'claude-3-7-sonnet-20250219'; // Example model name for Kiro API
|
||||
break;
|
||||
case MODEL_PROVIDER.QWEN_API:
|
||||
modelName = 'qwen3-coder-flash'; // Example model name for Qwen
|
||||
break;
|
||||
case MODEL_PROVIDER.OPENAI_CUSTOM_RESPONSES:
|
||||
modelName = 'gpt-5-low'; // Example model name for OpenAI Custom Responses
|
||||
break;
|
||||
default:
|
||||
console.warn(`[ProviderPoolManager] Unknown provider type for health check: ${providerType}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform a lightweight API call to check health
|
||||
const healthCheckRequest = {
|
||||
contents: [{
|
||||
role: 'user',
|
||||
parts: [{ text: 'Hello, are you ok?' }]
|
||||
}]
|
||||
|
||||
// 合并全局配置和 provider 配置(简化代理配置)
|
||||
const proxyKeys = ['GEMINI', 'OPENAI', 'CLAUDE', 'QWEN', 'KIRO'];
|
||||
const tempConfig = {
|
||||
...providerConfig,
|
||||
MODEL_PROVIDER: providerType
|
||||
};
|
||||
|
||||
if (providerType === MODEL_PROVIDER.OPENAI_CUSTOM_RESPONSES) {
|
||||
healthCheckRequest.input = [{ role: 'user', content: 'Hello, are you ok?' }];
|
||||
healthCheckRequest.model = modelName;
|
||||
delete healthCheckRequest.contents;
|
||||
// 动态添加代理配置
|
||||
proxyKeys.forEach(key => {
|
||||
const proxyKey = `USE_SYSTEM_PROXY_${key}`;
|
||||
if (this.globalConfig[proxyKey] !== undefined) {
|
||||
tempConfig[proxyKey] = this.globalConfig[proxyKey];
|
||||
}
|
||||
});
|
||||
|
||||
const serviceAdapter = getServiceAdapter(tempConfig);
|
||||
|
||||
// 确定健康检查使用的模型名称
|
||||
const modelName = providerConfig.checkModelName ||
|
||||
ProviderPoolManager.DEFAULT_HEALTH_CHECK_MODELS[providerType];
|
||||
|
||||
if (!modelName) {
|
||||
this._log('warn', `Unknown provider type for health check: ${providerType}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// For OpenAI and Claude providers, we need a different request format
|
||||
if (providerType === MODEL_PROVIDER.OPENAI_CUSTOM || providerType === MODEL_PROVIDER.CLAUDE_CUSTOM || providerType === MODEL_PROVIDER.KIRO_API || providerType === MODEL_PROVIDER.QWEN_API) {
|
||||
healthCheckRequest.messages = [{ role: 'user', content: 'Hello, are you ok?' }];
|
||||
healthCheckRequest.model = modelName;
|
||||
delete healthCheckRequest.contents;
|
||||
}
|
||||
// 构建健康检查请求
|
||||
const healthCheckRequest = this._buildHealthCheckRequest(providerType, modelName);
|
||||
|
||||
// console.log(`[ProviderPoolManager] Health check request for ${modelName}: ${JSON.stringify(healthCheckRequest)}`);
|
||||
this._log('debug', `Health check request for ${modelName}: ${JSON.stringify(healthCheckRequest)}`);
|
||||
await serviceAdapter.generateContent(modelName, healthCheckRequest);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`[ProviderPoolManager] Health check failed for ${providerType}: ${error.message}`);
|
||||
this._log('error', `Health check failed for ${providerType}: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -356,57 +403,56 @@ export class ProviderPoolManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* 优化1: 批量保存所有待保存的 providerType
|
||||
* 批量保存所有待保存的 providerType(优化为单次文件写入)
|
||||
* @private
|
||||
*/
|
||||
async _flushPendingSaves() {
|
||||
const typesToSave = Array.from(this.pendingSaves);
|
||||
if (typesToSave.length === 0) return;
|
||||
|
||||
this.pendingSaves.clear();
|
||||
this.saveTimer = null;
|
||||
|
||||
for (const providerType of typesToSave) {
|
||||
await this._saveProviderPoolsToJson(providerType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current provider pools configuration to the JSON file.
|
||||
* @private
|
||||
*/
|
||||
async _saveProviderPoolsToJson(providerTypeToUpdate) {
|
||||
try {
|
||||
const filePath = this.globalConfig.PROVIDER_POOLS_FILE_PATH || 'provider_pools.json';
|
||||
let currentPools = {};
|
||||
|
||||
// 一次性读取文件
|
||||
try {
|
||||
const fileContent = await fs.promises.readFile(filePath, 'utf8');
|
||||
currentPools = JSON.parse(fileContent);
|
||||
} catch (readError) {
|
||||
if (readError.code === 'ENOENT') {
|
||||
console.log('[ProviderPoolManager] provider_pools.json does not exist, creating new file.');
|
||||
this._log('info', 'provider_pools.json does not exist, creating new file.');
|
||||
} else {
|
||||
throw readError;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.providerStatus[providerTypeToUpdate]) {
|
||||
currentPools[providerTypeToUpdate] = this.providerStatus[providerTypeToUpdate].map(p => {
|
||||
// Convert Date objects to ISOString if they exist
|
||||
if (p.config.lastUsed instanceof Date) {
|
||||
p.config.lastUsed = p.config.lastUsed.toISOString();
|
||||
}
|
||||
if (p.config.lastErrorTime instanceof Date) {
|
||||
p.config.lastErrorTime = p.config.lastErrorTime.toISOString();
|
||||
}
|
||||
return p.config;
|
||||
});
|
||||
} else {
|
||||
console.warn(`[ProviderPoolManager] Attempted to save unknown providerType: ${providerTypeToUpdate}`);
|
||||
// 更新所有待保存的 providerType
|
||||
for (const providerType of typesToSave) {
|
||||
if (this.providerStatus[providerType]) {
|
||||
currentPools[providerType] = this.providerStatus[providerType].map(p => {
|
||||
// Convert Date objects to ISOString if they exist
|
||||
const config = { ...p.config };
|
||||
if (config.lastUsed instanceof Date) {
|
||||
config.lastUsed = config.lastUsed.toISOString();
|
||||
}
|
||||
if (config.lastErrorTime instanceof Date) {
|
||||
config.lastErrorTime = config.lastErrorTime.toISOString();
|
||||
}
|
||||
return config;
|
||||
});
|
||||
} else {
|
||||
this._log('warn', `Attempted to save unknown providerType: ${providerType}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 一次性写入文件
|
||||
await fs.promises.writeFile(filePath, JSON.stringify(currentPools, null, 2), 'utf8');
|
||||
console.log(`[ProviderPoolManager] provider_pools.json for ${providerTypeToUpdate} updated successfully.`);
|
||||
this._log('info', `provider_pools.json updated successfully for types: ${typesToSave.join(', ')}`);
|
||||
} catch (error) {
|
||||
console.error('[ProviderPoolManager] Failed to write provider_pools.json:', error);
|
||||
this._log('error', `Failed to write provider_pools.json: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue