@juspay/neurolink 7.6.0 → 7.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/CHANGELOG.md +14 -2
  2. package/README.md +79 -4
  3. package/dist/cli/commands/config.d.ts +275 -3
  4. package/dist/cli/commands/config.js +121 -0
  5. package/dist/cli/commands/mcp.js +77 -28
  6. package/dist/cli/factories/commandFactory.js +359 -6
  7. package/dist/core/analytics.js +7 -27
  8. package/dist/core/baseProvider.js +43 -4
  9. package/dist/core/constants.d.ts +46 -0
  10. package/dist/core/constants.js +47 -0
  11. package/dist/core/dynamicModels.d.ts +16 -4
  12. package/dist/core/dynamicModels.js +130 -26
  13. package/dist/core/evaluation.js +5 -1
  14. package/dist/core/evaluationProviders.d.ts +6 -2
  15. package/dist/core/evaluationProviders.js +41 -125
  16. package/dist/core/factory.d.ts +5 -0
  17. package/dist/core/factory.js +62 -50
  18. package/dist/core/modelConfiguration.d.ts +246 -0
  19. package/dist/core/modelConfiguration.js +775 -0
  20. package/dist/core/types.d.ts +22 -3
  21. package/dist/core/types.js +5 -1
  22. package/dist/factories/providerRegistry.js +3 -3
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +1 -1
  25. package/dist/lib/core/analytics.js +7 -27
  26. package/dist/lib/core/baseProvider.js +43 -4
  27. package/dist/lib/core/constants.d.ts +46 -0
  28. package/dist/lib/core/constants.js +47 -0
  29. package/dist/lib/core/dynamicModels.d.ts +16 -4
  30. package/dist/lib/core/dynamicModels.js +130 -26
  31. package/dist/lib/core/evaluation.js +5 -1
  32. package/dist/lib/core/evaluationProviders.d.ts +6 -2
  33. package/dist/lib/core/evaluationProviders.js +41 -125
  34. package/dist/lib/core/factory.d.ts +5 -0
  35. package/dist/lib/core/factory.js +63 -50
  36. package/dist/lib/core/modelConfiguration.d.ts +246 -0
  37. package/dist/lib/core/modelConfiguration.js +775 -0
  38. package/dist/lib/core/types.d.ts +22 -3
  39. package/dist/lib/core/types.js +5 -1
  40. package/dist/lib/factories/providerRegistry.js +3 -3
  41. package/dist/lib/index.d.ts +1 -1
  42. package/dist/lib/index.js +1 -1
  43. package/dist/lib/mcp/factory.d.ts +5 -5
  44. package/dist/lib/mcp/factory.js +2 -2
  45. package/dist/lib/mcp/servers/utilities/utilityServer.d.ts +1 -1
  46. package/dist/lib/mcp/servers/utilities/utilityServer.js +1 -1
  47. package/dist/lib/mcp/toolRegistry.js +2 -2
  48. package/dist/lib/neurolink.d.ts +168 -12
  49. package/dist/lib/neurolink.js +685 -123
  50. package/dist/lib/providers/anthropic.js +52 -2
  51. package/dist/lib/providers/googleAiStudio.js +4 -0
  52. package/dist/lib/providers/googleVertex.d.ts +75 -9
  53. package/dist/lib/providers/googleVertex.js +365 -46
  54. package/dist/lib/providers/huggingFace.d.ts +52 -11
  55. package/dist/lib/providers/huggingFace.js +180 -42
  56. package/dist/lib/providers/litellm.d.ts +9 -9
  57. package/dist/lib/providers/litellm.js +103 -16
  58. package/dist/lib/providers/ollama.d.ts +52 -17
  59. package/dist/lib/providers/ollama.js +276 -68
  60. package/dist/lib/sdk/toolRegistration.d.ts +42 -0
  61. package/dist/lib/sdk/toolRegistration.js +269 -27
  62. package/dist/lib/telemetry/telemetryService.d.ts +6 -0
  63. package/dist/lib/telemetry/telemetryService.js +38 -3
  64. package/dist/lib/types/contextTypes.d.ts +75 -11
  65. package/dist/lib/types/contextTypes.js +227 -1
  66. package/dist/lib/types/domainTypes.d.ts +62 -0
  67. package/dist/lib/types/domainTypes.js +5 -0
  68. package/dist/lib/types/generateTypes.d.ts +52 -0
  69. package/dist/lib/types/index.d.ts +1 -0
  70. package/dist/lib/types/mcpTypes.d.ts +1 -1
  71. package/dist/lib/types/mcpTypes.js +1 -1
  72. package/dist/lib/types/streamTypes.d.ts +14 -0
  73. package/dist/lib/types/universalProviderOptions.d.ts +1 -1
  74. package/dist/lib/utils/errorHandling.d.ts +142 -0
  75. package/dist/lib/utils/errorHandling.js +316 -0
  76. package/dist/lib/utils/factoryProcessing.d.ts +74 -0
  77. package/dist/lib/utils/factoryProcessing.js +588 -0
  78. package/dist/lib/utils/optionsConversion.d.ts +54 -0
  79. package/dist/lib/utils/optionsConversion.js +126 -0
  80. package/dist/lib/utils/optionsUtils.d.ts +246 -0
  81. package/dist/lib/utils/optionsUtils.js +960 -0
  82. package/dist/lib/utils/providerHealth.d.ts +107 -0
  83. package/dist/lib/utils/providerHealth.js +507 -0
  84. package/dist/lib/utils/providerUtils.d.ts +17 -0
  85. package/dist/lib/utils/providerUtils.js +271 -16
  86. package/dist/lib/utils/timeout.js +1 -1
  87. package/dist/lib/utils/tokenLimits.d.ts +33 -0
  88. package/dist/lib/utils/tokenLimits.js +118 -0
  89. package/dist/mcp/factory.d.ts +5 -5
  90. package/dist/mcp/factory.js +2 -2
  91. package/dist/mcp/servers/utilities/utilityServer.d.ts +1 -1
  92. package/dist/mcp/servers/utilities/utilityServer.js +1 -1
  93. package/dist/mcp/toolRegistry.js +2 -2
  94. package/dist/neurolink.d.ts +168 -12
  95. package/dist/neurolink.js +685 -123
  96. package/dist/providers/anthropic.js +52 -2
  97. package/dist/providers/googleAiStudio.js +4 -0
  98. package/dist/providers/googleVertex.d.ts +75 -9
  99. package/dist/providers/googleVertex.js +365 -46
  100. package/dist/providers/huggingFace.d.ts +52 -11
  101. package/dist/providers/huggingFace.js +181 -43
  102. package/dist/providers/litellm.d.ts +9 -9
  103. package/dist/providers/litellm.js +103 -16
  104. package/dist/providers/ollama.d.ts +52 -17
  105. package/dist/providers/ollama.js +276 -68
  106. package/dist/sdk/toolRegistration.d.ts +42 -0
  107. package/dist/sdk/toolRegistration.js +269 -27
  108. package/dist/telemetry/telemetryService.d.ts +6 -0
  109. package/dist/telemetry/telemetryService.js +38 -3
  110. package/dist/types/contextTypes.d.ts +75 -11
  111. package/dist/types/contextTypes.js +227 -2
  112. package/dist/types/domainTypes.d.ts +62 -0
  113. package/dist/types/domainTypes.js +5 -0
  114. package/dist/types/generateTypes.d.ts +52 -0
  115. package/dist/types/index.d.ts +1 -0
  116. package/dist/types/mcpTypes.d.ts +1 -1
  117. package/dist/types/mcpTypes.js +1 -1
  118. package/dist/types/streamTypes.d.ts +14 -0
  119. package/dist/types/universalProviderOptions.d.ts +1 -1
  120. package/dist/types/universalProviderOptions.js +0 -1
  121. package/dist/utils/errorHandling.d.ts +142 -0
  122. package/dist/utils/errorHandling.js +316 -0
  123. package/dist/utils/factoryProcessing.d.ts +74 -0
  124. package/dist/utils/factoryProcessing.js +588 -0
  125. package/dist/utils/optionsConversion.d.ts +54 -0
  126. package/dist/utils/optionsConversion.js +126 -0
  127. package/dist/utils/optionsUtils.d.ts +246 -0
  128. package/dist/utils/optionsUtils.js +960 -0
  129. package/dist/utils/providerHealth.d.ts +107 -0
  130. package/dist/utils/providerHealth.js +507 -0
  131. package/dist/utils/providerUtils.d.ts +17 -0
  132. package/dist/utils/providerUtils.js +271 -16
  133. package/dist/utils/timeout.js +1 -1
  134. package/dist/utils/tokenLimits.d.ts +33 -0
  135. package/dist/utils/tokenLimits.js +118 -0
  136. package/package.json +2 -2
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Provider Health Checking System
3
+ * Prevents 500 errors by validating provider availability and configuration
4
+ */
5
+ import { AIProviderName } from "../core/types.js";
6
+ export interface ProviderHealthStatus {
7
+ provider: AIProviderName;
8
+ isHealthy: boolean;
9
+ isConfigured: boolean;
10
+ hasApiKey: boolean;
11
+ lastChecked: Date;
12
+ error?: string;
13
+ warning?: string;
14
+ responseTime?: number;
15
+ configurationIssues: string[];
16
+ recommendations: string[];
17
+ }
18
+ export interface ProviderHealthCheckOptions {
19
+ timeout?: number;
20
+ includeConnectivityTest?: boolean;
21
+ includeModelValidation?: boolean;
22
+ cacheResults?: boolean;
23
+ maxCacheAge?: number;
24
+ }
25
+ export declare class ProviderHealthChecker {
26
+ private static healthCache;
27
+ private static readonly DEFAULT_TIMEOUT;
28
+ private static readonly DEFAULT_CACHE_AGE;
29
+ private static readonly CONSECUTIVE_FAILURE_THRESHOLD;
30
+ private static consecutiveFailures;
31
+ /**
32
+ * Validate and return a safe failure threshold value
33
+ */
34
+ private static getValidatedFailureThreshold;
35
+ /**
36
+ * Comprehensive health check for a provider
37
+ */
38
+ static checkProviderHealth(providerName: AIProviderName, options?: ProviderHealthCheckOptions): Promise<ProviderHealthStatus>;
39
+ /**
40
+ * Check environment configuration for a provider
41
+ */
42
+ private static checkEnvironmentConfiguration;
43
+ /**
44
+ * Check API key validity (format validation)
45
+ */
46
+ private static checkApiKeyValidity;
47
+ /**
48
+ * Check connectivity to provider endpoints
49
+ */
50
+ private static checkConnectivity;
51
+ /**
52
+ * Check model availability (if possible without making API calls)
53
+ */
54
+ private static checkModelAvailability;
55
+ /**
56
+ * Get required environment variables for a provider
57
+ */
58
+ private static getRequiredEnvironmentVariables;
59
+ /**
60
+ * Get API key environment variable for a provider
61
+ */
62
+ private static getApiKeyEnvironmentVariable;
63
+ /**
64
+ * Validate API key format for a provider
65
+ */
66
+ private static validateApiKeyFormat;
67
+ /**
68
+ * Get health check endpoint for connectivity testing
69
+ */
70
+ private static getProviderHealthEndpoint;
71
+ /**
72
+ * Provider-specific configuration checks
73
+ */
74
+ private static checkProviderSpecificConfig;
75
+ /**
76
+ * Get common models for a provider
77
+ */
78
+ private static getCommonModelsForProvider;
79
+ /**
80
+ * Get cached health status if still valid
81
+ */
82
+ private static getCachedHealth;
83
+ /**
84
+ * Clear health cache for a provider or all providers
85
+ */
86
+ static clearHealthCache(providerName?: AIProviderName): void;
87
+ /**
88
+ * Get the best healthy provider from a list of options
89
+ * Prioritizes healthy providers over configured but unhealthy ones
90
+ */
91
+ static getBestHealthyProvider(preferredProviders?: string[]): Promise<string | null>;
92
+ /**
93
+ * Get health status for all registered providers
94
+ */
95
+ static checkAllProvidersHealth(options?: ProviderHealthCheckOptions): Promise<ProviderHealthStatus[]>;
96
+ /**
97
+ * Get a summary of provider health
98
+ */
99
+ static getHealthSummary(healthStatuses: ProviderHealthStatus[]): {
100
+ total: number;
101
+ healthy: number;
102
+ configured: number;
103
+ hasIssues: number;
104
+ healthyProviders: string[];
105
+ unhealthyProviders: string[];
106
+ };
107
+ }
@@ -0,0 +1,507 @@
1
+ /**
2
+ * Provider Health Checking System
3
+ * Prevents 500 errors by validating provider availability and configuration
4
+ */
5
+ import { logger } from "./logger.js";
6
+ import { AIProviderName } from "../core/types.js";
7
+ export class ProviderHealthChecker {
8
+ static healthCache = new Map();
9
+ static DEFAULT_TIMEOUT = 5000; // 5 seconds
10
+ static DEFAULT_CACHE_AGE = 300000; // 5 minutes
11
+ static CONSECUTIVE_FAILURE_THRESHOLD = ProviderHealthChecker.getValidatedFailureThreshold();
12
+ static consecutiveFailures = new Map();
13
+ /**
14
+ * Validate and return a safe failure threshold value
15
+ */
16
+ static getValidatedFailureThreshold() {
17
+ const envValue = process.env.PROVIDER_FAILURE_THRESHOLD;
18
+ if (!envValue) {
19
+ return 3; // default
20
+ }
21
+ const parsed = Number(envValue);
22
+ if (isNaN(parsed) || parsed <= 0 || parsed > 10) {
23
+ console.warn(`Invalid PROVIDER_FAILURE_THRESHOLD: ${envValue} (must be between 1 and 10), using default: 3`);
24
+ return 3;
25
+ }
26
+ return parsed;
27
+ }
28
+ /**
29
+ * Comprehensive health check for a provider
30
+ */
31
+ static async checkProviderHealth(providerName, options = {}) {
32
+ const { timeout = this.DEFAULT_TIMEOUT, includeConnectivityTest = false, includeModelValidation = false, cacheResults = true, maxCacheAge = this.DEFAULT_CACHE_AGE, } = options;
33
+ // Check cache first
34
+ if (cacheResults) {
35
+ const cached = this.getCachedHealth(providerName, maxCacheAge);
36
+ if (cached) {
37
+ logger.debug(`Using cached health status for ${providerName}`);
38
+ return cached;
39
+ }
40
+ }
41
+ // Check if provider has consecutive failures (blacklisting)
42
+ const failureCount = this.consecutiveFailures.get(providerName) || 0;
43
+ if (failureCount >= this.CONSECUTIVE_FAILURE_THRESHOLD) {
44
+ const healthStatus = {
45
+ provider: providerName,
46
+ isHealthy: false,
47
+ isConfigured: false,
48
+ hasApiKey: false,
49
+ lastChecked: new Date(),
50
+ error: `Provider blacklisted after ${failureCount} consecutive failures`,
51
+ warning: "Provider will be retried after cache TTL expires",
52
+ configurationIssues: [
53
+ `Blacklisted due to ${failureCount} consecutive failures`,
54
+ ],
55
+ recommendations: ["Check provider status and configuration"],
56
+ };
57
+ logger.warn(`Provider ${providerName} blacklisted due to consecutive failures`, { failureCount });
58
+ return healthStatus;
59
+ }
60
+ const startTime = Date.now();
61
+ const healthStatus = {
62
+ provider: providerName,
63
+ isHealthy: false,
64
+ isConfigured: false,
65
+ hasApiKey: false,
66
+ lastChecked: new Date(),
67
+ configurationIssues: [],
68
+ recommendations: [],
69
+ };
70
+ try {
71
+ // 1. Check environment configuration
72
+ await this.checkEnvironmentConfiguration(providerName, healthStatus);
73
+ // 2. Check API key validity (basic format validation)
74
+ await this.checkApiKeyValidity(providerName, healthStatus);
75
+ // 3. Optional: Connectivity test
76
+ if (includeConnectivityTest) {
77
+ await this.checkConnectivity(providerName, healthStatus, timeout);
78
+ }
79
+ // 4. Optional: Model validation
80
+ if (includeModelValidation) {
81
+ await this.checkModelAvailability(providerName, healthStatus);
82
+ }
83
+ // 5. Determine overall health
84
+ healthStatus.isHealthy =
85
+ healthStatus.isConfigured &&
86
+ healthStatus.hasApiKey &&
87
+ healthStatus.configurationIssues.length === 0;
88
+ healthStatus.responseTime = Date.now() - startTime;
89
+ // Cache results
90
+ if (cacheResults) {
91
+ this.healthCache.set(providerName, {
92
+ status: healthStatus,
93
+ timestamp: Date.now(),
94
+ });
95
+ }
96
+ // Reset failure count on success
97
+ if (healthStatus.isHealthy) {
98
+ this.consecutiveFailures.delete(providerName);
99
+ }
100
+ else {
101
+ // Track consecutive failures
102
+ const currentFailures = this.consecutiveFailures.get(providerName) || 0;
103
+ this.consecutiveFailures.set(providerName, currentFailures + 1);
104
+ }
105
+ logger.debug(`Health check completed for ${providerName}`, {
106
+ isHealthy: healthStatus.isHealthy,
107
+ responseTime: healthStatus.responseTime,
108
+ issues: healthStatus.configurationIssues.length,
109
+ });
110
+ }
111
+ catch (error) {
112
+ const errorMessage = error instanceof Error ? error.message : String(error);
113
+ healthStatus.error = errorMessage;
114
+ healthStatus.configurationIssues.push(`Health check failed: ${errorMessage}`);
115
+ healthStatus.responseTime = Date.now() - startTime;
116
+ // Track consecutive failures
117
+ const currentFailures = this.consecutiveFailures.get(providerName) || 0;
118
+ this.consecutiveFailures.set(providerName, currentFailures + 1);
119
+ logger.warn(`Health check failed for ${providerName}`, {
120
+ error: errorMessage,
121
+ consecutiveFailures: currentFailures + 1,
122
+ });
123
+ }
124
+ return healthStatus;
125
+ }
126
+ /**
127
+ * Check environment configuration for a provider
128
+ */
129
+ static async checkEnvironmentConfiguration(providerName, healthStatus) {
130
+ const requiredEnvVars = this.getRequiredEnvironmentVariables(providerName);
131
+ let allConfigured = true;
132
+ const missingVars = [];
133
+ for (const envVar of requiredEnvVars) {
134
+ const value = process.env[envVar];
135
+ if (!value || value.trim() === "") {
136
+ allConfigured = false;
137
+ missingVars.push(envVar);
138
+ }
139
+ }
140
+ healthStatus.isConfigured = allConfigured;
141
+ if (!allConfigured) {
142
+ healthStatus.configurationIssues.push(`Missing required environment variables: ${missingVars.join(", ")}`);
143
+ healthStatus.recommendations.push(`Set the following environment variables: ${missingVars.join(", ")}`);
144
+ }
145
+ // Provider-specific configuration checks
146
+ await this.checkProviderSpecificConfig(providerName, healthStatus);
147
+ }
148
+ /**
149
+ * Check API key validity (format validation)
150
+ */
151
+ static async checkApiKeyValidity(providerName, healthStatus) {
152
+ const apiKeyVar = this.getApiKeyEnvironmentVariable(providerName);
153
+ const apiKey = process.env[apiKeyVar];
154
+ if (!apiKey) {
155
+ healthStatus.hasApiKey = false;
156
+ healthStatus.configurationIssues.push(`API key not found in ${apiKeyVar}`);
157
+ return;
158
+ }
159
+ // Basic format validation
160
+ const isValidFormat = this.validateApiKeyFormat(providerName, apiKey);
161
+ if (!isValidFormat) {
162
+ healthStatus.hasApiKey = false;
163
+ healthStatus.configurationIssues.push(`API key format appears invalid for ${providerName}`);
164
+ healthStatus.recommendations.push(`Verify the API key format for ${providerName}`);
165
+ }
166
+ else {
167
+ healthStatus.hasApiKey = true;
168
+ }
169
+ }
170
+ /**
171
+ * Check connectivity to provider endpoints
172
+ */
173
+ static async checkConnectivity(providerName, healthStatus, timeout) {
174
+ const endpoint = this.getProviderHealthEndpoint(providerName);
175
+ if (!endpoint) {
176
+ healthStatus.warning = "No connectivity test available for this provider";
177
+ return;
178
+ }
179
+ try {
180
+ const controller = new AbortController();
181
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
182
+ const response = await fetch(endpoint, {
183
+ method: "HEAD",
184
+ signal: controller.signal,
185
+ headers: {
186
+ "User-Agent": "NeuroLink-HealthCheck/1.0",
187
+ },
188
+ });
189
+ clearTimeout(timeoutId);
190
+ if (!response.ok) {
191
+ healthStatus.configurationIssues.push(`Connectivity test failed: HTTP ${response.status}`);
192
+ }
193
+ }
194
+ catch (error) {
195
+ const errorMessage = error instanceof Error ? error.message : String(error);
196
+ // Provide specific error messages for common network issues
197
+ if (errorMessage.includes("abort")) {
198
+ healthStatus.configurationIssues.push(`Connectivity test timed out after ${timeout}ms`);
199
+ }
200
+ else if (errorMessage.includes("ENOTFOUND") ||
201
+ errorMessage.includes("getaddrinfo")) {
202
+ healthStatus.configurationIssues.push(`DNS resolution failed: Cannot resolve hostname for ${providerName}`);
203
+ }
204
+ else if (errorMessage.includes("ECONNREFUSED")) {
205
+ healthStatus.configurationIssues.push(`Connection refused: ${providerName} service is not accepting connections`);
206
+ }
207
+ else if (errorMessage.includes("ETIMEDOUT")) {
208
+ healthStatus.configurationIssues.push(`Connection timeout: ${providerName} service did not respond`);
209
+ }
210
+ else if (errorMessage.includes("certificate") ||
211
+ errorMessage.includes("SSL") ||
212
+ errorMessage.includes("TLS")) {
213
+ healthStatus.configurationIssues.push(`SSL/TLS certificate error: ${providerName} has certificate issues`);
214
+ }
215
+ else if (errorMessage.includes("ECONNRESET")) {
216
+ healthStatus.configurationIssues.push(`Connection reset: ${providerName} terminated the connection`);
217
+ }
218
+ else if (errorMessage.includes("network") ||
219
+ errorMessage.includes("offline")) {
220
+ healthStatus.configurationIssues.push(`Network error: Check internet connectivity and firewall settings`);
221
+ }
222
+ else {
223
+ healthStatus.configurationIssues.push(`Connectivity test failed: ${errorMessage}`);
224
+ }
225
+ }
226
+ }
227
+ /**
228
+ * Check model availability (if possible without making API calls)
229
+ */
230
+ static async checkModelAvailability(providerName, healthStatus) {
231
+ // For now, we'll do basic model name validation
232
+ // In the future, this could be enhanced with actual API calls
233
+ const commonModels = this.getCommonModelsForProvider(providerName);
234
+ if (commonModels.length > 0) {
235
+ healthStatus.recommendations.push(`Common models for ${providerName}: ${commonModels.slice(0, 3).join(", ")}`);
236
+ }
237
+ }
238
+ /**
239
+ * Get required environment variables for a provider
240
+ */
241
+ static getRequiredEnvironmentVariables(providerName) {
242
+ switch (providerName) {
243
+ case AIProviderName.ANTHROPIC:
244
+ return ["ANTHROPIC_API_KEY"];
245
+ case AIProviderName.OPENAI:
246
+ return ["OPENAI_API_KEY"];
247
+ case AIProviderName.VERTEX:
248
+ return ["GOOGLE_APPLICATION_CREDENTIALS", "GOOGLE_PROJECT_ID"];
249
+ case AIProviderName.GOOGLE_AI:
250
+ return ["GOOGLE_AI_API_KEY"];
251
+ case AIProviderName.BEDROCK:
252
+ return ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION"];
253
+ case AIProviderName.OLLAMA:
254
+ return []; // Ollama typically doesn't require API keys
255
+ default:
256
+ return [];
257
+ }
258
+ }
259
+ /**
260
+ * Get API key environment variable for a provider
261
+ */
262
+ static getApiKeyEnvironmentVariable(providerName) {
263
+ switch (providerName) {
264
+ case AIProviderName.ANTHROPIC:
265
+ return "ANTHROPIC_API_KEY";
266
+ case AIProviderName.OPENAI:
267
+ return "OPENAI_API_KEY";
268
+ case AIProviderName.VERTEX:
269
+ return "GOOGLE_APPLICATION_CREDENTIALS";
270
+ case AIProviderName.GOOGLE_AI:
271
+ return "GOOGLE_AI_API_KEY";
272
+ case AIProviderName.BEDROCK:
273
+ return "AWS_ACCESS_KEY_ID";
274
+ case AIProviderName.OLLAMA:
275
+ return "OLLAMA_API_BASE";
276
+ default:
277
+ return "";
278
+ }
279
+ }
280
+ /**
281
+ * Validate API key format for a provider
282
+ */
283
+ static validateApiKeyFormat(providerName, apiKey) {
284
+ switch (providerName) {
285
+ case AIProviderName.ANTHROPIC:
286
+ return apiKey.startsWith("sk-ant-") && apiKey.length > 20;
287
+ case AIProviderName.OPENAI:
288
+ return apiKey.startsWith("sk-") && apiKey.length > 20;
289
+ case AIProviderName.GOOGLE_AI:
290
+ return apiKey.length > 20; // Basic length check
291
+ case AIProviderName.VERTEX:
292
+ return apiKey.endsWith(".json") || apiKey.includes("type"); // JSON key format
293
+ case AIProviderName.BEDROCK:
294
+ return apiKey.length >= 20; // AWS access key length
295
+ case AIProviderName.OLLAMA:
296
+ return true; // Ollama usually doesn't require specific format
297
+ default:
298
+ return true; // Default to true for unknown providers
299
+ }
300
+ }
301
+ /**
302
+ * Get health check endpoint for connectivity testing
303
+ */
304
+ static getProviderHealthEndpoint(providerName) {
305
+ switch (providerName) {
306
+ case AIProviderName.ANTHROPIC:
307
+ return null; // Anthropic doesn't have a public health endpoint
308
+ case AIProviderName.OPENAI:
309
+ return "https://api.openai.com/v1/models";
310
+ case AIProviderName.GOOGLE_AI:
311
+ return null; // No public health endpoint
312
+ case AIProviderName.VERTEX:
313
+ return null; // Complex authentication required
314
+ case AIProviderName.BEDROCK:
315
+ return null; // AWS endpoints vary by region
316
+ case AIProviderName.OLLAMA:
317
+ return "http://localhost:11434/api/version";
318
+ default:
319
+ return null;
320
+ }
321
+ }
322
+ /**
323
+ * Provider-specific configuration checks
324
+ */
325
+ static async checkProviderSpecificConfig(providerName, healthStatus) {
326
+ switch (providerName) {
327
+ case AIProviderName.VERTEX:
328
+ // Check for Google Cloud specific configuration
329
+ if (!process.env.GOOGLE_PROJECT_ID) {
330
+ healthStatus.configurationIssues.push("GOOGLE_PROJECT_ID not set");
331
+ healthStatus.recommendations.push("Set GOOGLE_PROJECT_ID to your GCP project ID");
332
+ }
333
+ {
334
+ const credPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
335
+ if (credPath && !credPath.includes("json")) {
336
+ healthStatus.warning =
337
+ "GOOGLE_APPLICATION_CREDENTIALS should point to a JSON file";
338
+ }
339
+ }
340
+ break;
341
+ case AIProviderName.BEDROCK:
342
+ // Check AWS region
343
+ if (!process.env.AWS_REGION) {
344
+ healthStatus.configurationIssues.push("AWS_REGION not set");
345
+ healthStatus.recommendations.push("Set AWS_REGION (e.g., us-east-1)");
346
+ }
347
+ break;
348
+ case AIProviderName.OLLAMA: {
349
+ // Check if custom endpoint is set
350
+ const ollamaBase = process.env.OLLAMA_API_BASE || "http://localhost:11434";
351
+ if (!ollamaBase.startsWith("http")) {
352
+ healthStatus.configurationIssues.push("Invalid OLLAMA_API_BASE format");
353
+ healthStatus.recommendations.push("Set OLLAMA_API_BASE to a valid URL (e.g., http://localhost:11434)");
354
+ }
355
+ break;
356
+ }
357
+ }
358
+ }
359
+ /**
360
+ * Get common models for a provider
361
+ */
362
+ static getCommonModelsForProvider(providerName) {
363
+ switch (providerName) {
364
+ case AIProviderName.ANTHROPIC:
365
+ return [
366
+ "claude-3-5-sonnet-20241022",
367
+ "claude-3-haiku-20240307",
368
+ "claude-3-opus-20240229",
369
+ ];
370
+ case AIProviderName.OPENAI:
371
+ return ["gpt-4o", "gpt-4o-mini", "gpt-3.5-turbo"];
372
+ case AIProviderName.GOOGLE_AI:
373
+ return ["gemini-1.5-pro", "gemini-1.5-flash", "gemini-pro"];
374
+ case AIProviderName.VERTEX:
375
+ return ["gemini-1.5-pro", "gemini-1.5-flash"];
376
+ case AIProviderName.BEDROCK:
377
+ return [
378
+ "anthropic.claude-3-sonnet-20240229-v1:0",
379
+ "anthropic.claude-3-haiku-20240307-v1:0",
380
+ ];
381
+ case AIProviderName.OLLAMA:
382
+ return ["llama3.2:latest", "llama3.1:latest", "mistral:latest"];
383
+ default:
384
+ return [];
385
+ }
386
+ }
387
+ /**
388
+ * Get cached health status if still valid
389
+ */
390
+ static getCachedHealth(providerName, maxAge) {
391
+ const cached = this.healthCache.get(providerName);
392
+ if (!cached) {
393
+ return null;
394
+ }
395
+ const age = Date.now() - cached.timestamp;
396
+ if (age > maxAge) {
397
+ this.healthCache.delete(providerName);
398
+ return null;
399
+ }
400
+ return cached.status;
401
+ }
402
+ /**
403
+ * Clear health cache for a provider or all providers
404
+ */
405
+ static clearHealthCache(providerName) {
406
+ if (providerName) {
407
+ this.healthCache.delete(providerName);
408
+ this.consecutiveFailures.delete(providerName);
409
+ }
410
+ else {
411
+ this.healthCache.clear();
412
+ this.consecutiveFailures.clear();
413
+ }
414
+ }
415
+ /**
416
+ * Get the best healthy provider from a list of options
417
+ * Prioritizes healthy providers over configured but unhealthy ones
418
+ */
419
+ static async getBestHealthyProvider(preferredProviders = [
420
+ "openai",
421
+ "anthropic",
422
+ "vertex",
423
+ "bedrock",
424
+ "azure",
425
+ "google-ai",
426
+ ]) {
427
+ const healthStatuses = await this.checkAllProvidersHealth({
428
+ includeConnectivityTest: false, // Quick config check only
429
+ cacheResults: true,
430
+ });
431
+ // First try to find a healthy provider in order of preference
432
+ for (const provider of preferredProviders) {
433
+ const health = healthStatuses.find((h) => h.provider === provider);
434
+ if (health?.isHealthy) {
435
+ logger.debug(`Selected healthy provider: ${provider}`);
436
+ return provider;
437
+ }
438
+ }
439
+ // Fallback to any healthy provider
440
+ const anyHealthy = healthStatuses.find((h) => h.isHealthy);
441
+ if (anyHealthy) {
442
+ logger.info(`Using fallback healthy provider: ${anyHealthy.provider}`);
443
+ return anyHealthy.provider;
444
+ }
445
+ // Last resort: any configured provider
446
+ const anyConfigured = healthStatuses.find((h) => h.isConfigured);
447
+ if (anyConfigured) {
448
+ logger.warn(`Using configured but potentially unhealthy provider: ${anyConfigured.provider}`);
449
+ return anyConfigured.provider;
450
+ }
451
+ logger.error("No healthy or configured providers found");
452
+ return null;
453
+ }
454
+ /**
455
+ * Get health status for all registered providers
456
+ */
457
+ static async checkAllProvidersHealth(options = {}) {
458
+ const providers = [
459
+ AIProviderName.ANTHROPIC,
460
+ AIProviderName.OPENAI,
461
+ AIProviderName.VERTEX,
462
+ AIProviderName.GOOGLE_AI,
463
+ AIProviderName.BEDROCK,
464
+ AIProviderName.OLLAMA,
465
+ ];
466
+ const healthChecks = providers.map((provider) => this.checkProviderHealth(provider, options));
467
+ const results = await Promise.allSettled(healthChecks);
468
+ return results.map((result, index) => {
469
+ if (result.status === "fulfilled") {
470
+ return result.value;
471
+ }
472
+ else {
473
+ // Return a failed health status for rejected promises
474
+ return {
475
+ provider: providers[index],
476
+ isHealthy: false,
477
+ isConfigured: false,
478
+ hasApiKey: false,
479
+ lastChecked: new Date(),
480
+ error: result.reason?.message || "Health check failed",
481
+ configurationIssues: ["Health check promise rejected"],
482
+ recommendations: [
483
+ "Check provider configuration and network connectivity",
484
+ ],
485
+ };
486
+ }
487
+ });
488
+ }
489
+ /**
490
+ * Get a summary of provider health
491
+ */
492
+ static getHealthSummary(healthStatuses) {
493
+ const healthy = healthStatuses.filter((h) => h.isHealthy);
494
+ const configured = healthStatuses.filter((h) => h.isConfigured);
495
+ const hasIssues = healthStatuses.filter((h) => h.configurationIssues.length > 0);
496
+ return {
497
+ total: healthStatuses.length,
498
+ healthy: healthy.length,
499
+ configured: configured.length,
500
+ hasIssues: hasIssues.length,
501
+ healthyProviders: healthy.map((h) => h.provider),
502
+ unhealthyProviders: healthStatuses
503
+ .filter((h) => !h.isHealthy)
504
+ .map((h) => h.provider),
505
+ };
506
+ }
507
+ }
@@ -5,9 +5,26 @@
5
5
  * @returns The best provider name to use
6
6
  */
7
7
  export declare function getBestProvider(requestedProvider?: string): Promise<string>;
8
+ /**
9
+ * Validation results for environment variables
10
+ */
11
+ export interface EnvVarValidationResult {
12
+ isValid: boolean;
13
+ missingVars: string[];
14
+ invalidVars: string[];
15
+ warnings: string[];
16
+ }
17
+ /**
18
+ * Validate environment variable values for a provider
19
+ * Addresses GitHub Copilot comment about adding environment variable validation
20
+ * @param provider - Provider name to validate
21
+ * @returns Validation result with detailed information
22
+ */
23
+ export declare function validateProviderEnvVars(provider: string): EnvVarValidationResult;
8
24
  /**
9
25
  * Check if a provider has the minimum required environment variables
10
26
  * NOTE: This only checks if variables exist, not if they're valid
27
+ * For validation, use validateProviderEnvVars instead
11
28
  * @param provider - Provider name to check
12
29
  * @returns True if the provider has required environment variables
13
30
  */