diff --git a/provider_pools.json b/provider_pools.json index 40310f7..49e274e 100644 --- a/provider_pools.json +++ b/provider_pools.json @@ -3,6 +3,7 @@ { "OPENAI_API_KEY": "sk-openai-key1", "OPENAI_BASE_URL": "https://api.openai.com/v1", + "checkModelName": null, "uuid": "2f579c65-d3c5-41b1-9985-9f6e3d7bf39c", "isHealthy": true, "lastUsed": null, @@ -13,6 +14,7 @@ { "OPENAI_API_KEY": "sk-openai-key2", "OPENAI_BASE_URL": "https://api.openai.com/v1", + "checkModelName": null, "uuid": "e284628d-302f-456d-91f3-6095386fb3b8", "isHealthy": true, "lastUsed": null, @@ -25,6 +27,7 @@ { "GEMINI_OAUTH_CREDS_FILE_PATH": "./credentials1.json", "PROJECT_ID": "your-project-id-1", + "checkModelName": null, "uuid": "ac200154-26b8-4f5f-8650-e8cc738b06e3", "isHealthy": true, "lastUsed": null, @@ -35,6 +38,7 @@ { "GEMINI_OAUTH_CREDS_FILE_PATH": "./credentials2.json", "PROJECT_ID": "your-project-id-2", + "checkModelName": null, "uuid": "4f8afcc2-a9bb-4b96-bb50-3b9667a71f54", "isHealthy": true, "lastUsed": null, @@ -47,6 +51,7 @@ { "CLAUDE_API_KEY": "sk-claude-key1", "CLAUDE_BASE_URL": "https://api.anthropic.com", + "checkModelName": null, "uuid": "bb87047a-3b1d-4249-adbb-1087ecd58128", "isHealthy": true, "lastUsed": null, @@ -57,6 +62,7 @@ { "CLAUDE_API_KEY": "sk-claude-key2", "CLAUDE_BASE_URL": "https://api.anthropic.com", + "checkModelName": null, "uuid": "7c2002c6-122a-4db0-af06-8a0ff433801a", "isHealthy": true, "lastUsed": null, @@ -69,6 +75,7 @@ { "KIRO_OAUTH_CREDS_FILE_PATH": "./kiro_creds1.json", "uuid": "2c69d0ac-b86f-43d8-9d17-0d300afc5cfd", + "checkModelName": null, "isHealthy": true, "lastUsed": null, "usageCount": 0, @@ -78,6 +85,7 @@ { "KIRO_OAUTH_CREDS_FILE_PATH": "./kiro_creds2.json", "uuid": "7482abe6-8083-4288-bb7d-d8ecb7c461e2", + "checkModelName": null, "isHealthy": true, "lastUsed": null, "usageCount": 0, diff --git a/src/claude/claude-kiro.js b/src/claude/claude-kiro.js index 9c5f32d..f87fc77 100644 --- a/src/claude/claude-kiro.js +++ b/src/claude/claude-kiro.js @@ -260,7 +260,7 @@ export class KiroApiService { async initialize() { if (this.isInitialized) return; - console.log('[Kiro] Initializing Gemini API Service...'); + console.log('[Kiro] Initializing Kiro API Service...'); await this.initializeAuth(); const macSha256 = await getMacAddressSha256(); this.axiosInstance = axios.create({ diff --git a/src/provider-pool-manager.js b/src/provider-pool-manager.js index bd8ff60..fdae5e5 100644 --- a/src/provider-pool-manager.js +++ b/src/provider-pool-manager.js @@ -1,4 +1,6 @@ import * as fs from 'fs'; // Import fs module +import { getServiceAdapter } from './adapter.js'; +import { MODEL_PROVIDER } from './common.js'; /** * Manages a pool of API service providers, handling their health and selection. @@ -146,17 +148,22 @@ export class ProviderPoolManager { } try { - // TODO: Implement actual health check logic for each provider type - // For now, if a provider was unhealthy and enough time has passed, - // we optimistically mark it healthy and reset error count. - // A more robust system would involve actual API calls or pings here. - if (!providerStatus.config.isHealthy) { - // Only reset and mark healthy if it was unhealthy and we are attempting a check after interval - this.markProviderHealthy(providerType, providerConfig); - console.log(`[ProviderPoolManager] Health check for ${JSON.stringify(providerConfig)} (${providerType}): Marked Healthy (re-evaluation)`); + // Perform actual health check based on provider type + const isHealthy = await this._checkProviderHealth(providerType, providerConfig); + + if (isHealthy) { + if (!providerStatus.config.isHealthy) { + // Provider was unhealthy but is now healthy + this.markProviderHealthy(providerType, providerConfig); + console.log(`[ProviderPoolManager] Health check for ${JSON.stringify(providerConfig)} (${providerType}): Marked Healthy (actual check)`); + } else { + // Provider was already healthy and still is + console.log(`[ProviderPoolManager] Health check for ${JSON.stringify(providerConfig)} (${providerType}): Still Healthy`); + } } else { - // For already healthy providers, just log or perform a lighter check if needed - console.log(`[ProviderPoolManager] Health check for ${JSON.stringify(providerConfig)} (${providerType}): Still Healthy`); + // Provider is not healthy + console.warn(`[ProviderPoolManager] Health check for ${JSON.stringify(providerConfig)} (${providerType}) failed: Provider is not responding correctly.`); + this.markProviderUnhealthy(providerType, providerConfig); } } catch (error) { @@ -167,6 +174,68 @@ export class ProviderPoolManager { } } } + + /** + * 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} - True if the provider is healthy, false otherwise. + */ + async _checkProviderHealth(providerType, providerConfig) { + try { + // Create a temporary service adapter for health check + const tempConfig = { ...providerConfig, MODEL_PROVIDER: providerType }; + const serviceAdapter = getServiceAdapter(tempConfig); + + // 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; + 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?' }] + }] + }; + + // 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) { + healthCheckRequest.messages = [{ role: 'user', content: 'Hello, are you ok?' }]; + healthCheckRequest.model = modelName; + delete healthCheckRequest.contents; + } + + // console.log(`[ProviderPoolManager] 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}`); + return false; + } + } + /** * Saves the current provider pools configuration to the JSON file. * @private