@juspay/neurolink 7.27.0 → 7.28.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.
@@ -5,6 +5,7 @@
5
5
  import { logger } from "./logger.js";
6
6
  import { AIProviderName } from "../core/types.js";
7
7
  import { basename } from "path";
8
+ import { createProxyFetch } from "../proxy/proxyFetch.js";
8
9
  export class ProviderHealthChecker {
9
10
  static healthCache = new Map();
10
11
  static DEFAULT_TIMEOUT = 5000; // 5 seconds
@@ -150,6 +151,67 @@ export class ProviderHealthChecker {
150
151
  * Check API key validity (format validation)
151
152
  */
152
153
  static async checkApiKeyValidity(providerName, healthStatus) {
154
+ // 🎯 SPECIAL HANDLING FOR VERTEX AI: Check both auth methods
155
+ if (providerName === AIProviderName.VERTEX) {
156
+ logger.debug("Vertex AI authentication check starting", {
157
+ providerName,
158
+ });
159
+ // Method 1: Check GOOGLE_APPLICATION_CREDENTIALS (file-based)
160
+ const credentialsFile = process.env.GOOGLE_APPLICATION_CREDENTIALS;
161
+ let fileBasedAuthValid = false;
162
+ if (credentialsFile) {
163
+ logger.debug("Checking GOOGLE_APPLICATION_CREDENTIALS file");
164
+ try {
165
+ const { promises: fs } = await import("fs");
166
+ try {
167
+ await fs.access(credentialsFile);
168
+ fileBasedAuthValid = true;
169
+ }
170
+ catch {
171
+ fileBasedAuthValid = false;
172
+ }
173
+ logger.debug("File auth check result", {
174
+ fileExists: fileBasedAuthValid,
175
+ });
176
+ }
177
+ catch (error) {
178
+ logger.debug("File auth check error", {
179
+ error: String(error),
180
+ });
181
+ fileBasedAuthValid = false;
182
+ }
183
+ }
184
+ // Method 2: Check individual environment variables
185
+ const hasIndividualAuth = !!(process.env.GOOGLE_AUTH_CLIENT_EMAIL &&
186
+ process.env.GOOGLE_AUTH_PRIVATE_KEY);
187
+ logger.debug("Individual auth check", {
188
+ hasClientEmail: !!process.env.GOOGLE_AUTH_CLIENT_EMAIL,
189
+ hasPrivateKey: !!process.env.GOOGLE_AUTH_PRIVATE_KEY,
190
+ hasIndividualAuth,
191
+ });
192
+ // Vertex is valid if EITHER auth method works
193
+ const hasValidAuth = fileBasedAuthValid || hasIndividualAuth;
194
+ logger.debug("Vertex auth final result", {
195
+ fileBasedAuthValid,
196
+ hasIndividualAuth,
197
+ hasValidAuth,
198
+ });
199
+ if (hasValidAuth) {
200
+ healthStatus.hasApiKey = true;
201
+ logger.debug("Vertex auth SUCCESS", {
202
+ authMethod: fileBasedAuthValid ? "file-based" : "individual-env-vars",
203
+ });
204
+ }
205
+ else {
206
+ healthStatus.hasApiKey = false;
207
+ healthStatus.configurationIssues.push(`Vertex AI authentication not found: neither GOOGLE_APPLICATION_CREDENTIALS file nor individual credentials (GOOGLE_AUTH_CLIENT_EMAIL + GOOGLE_AUTH_PRIVATE_KEY) are properly configured`);
208
+ logger.debug("Vertex auth FAILED", {
209
+ reason: "No valid auth method found",
210
+ });
211
+ }
212
+ return;
213
+ }
214
+ // 🔧 STANDARD HANDLING FOR OTHER PROVIDERS
153
215
  const apiKeyVar = this.getApiKeyEnvironmentVariable(providerName);
154
216
  const apiKey = process.env[apiKeyVar];
155
217
  if (!apiKey) {
@@ -180,13 +242,24 @@ export class ProviderHealthChecker {
180
242
  try {
181
243
  const controller = new AbortController();
182
244
  const timeoutId = setTimeout(() => controller.abort(), timeout);
183
- const response = await fetch(endpoint, {
245
+ const proxyFetch = createProxyFetch();
246
+ let response = await proxyFetch(endpoint, {
184
247
  method: "HEAD",
185
248
  signal: controller.signal,
186
249
  headers: {
187
250
  "User-Agent": "NeuroLink-HealthCheck/1.0",
188
251
  },
189
252
  });
253
+ // Fallback to GET if HEAD returns 405 (Method Not Allowed) for restrictive gateways
254
+ if (response.status === 405) {
255
+ response = await proxyFetch(endpoint, {
256
+ method: "GET",
257
+ signal: controller.signal,
258
+ headers: {
259
+ "User-Agent": "NeuroLink-HealthCheck/1.0",
260
+ },
261
+ });
262
+ }
190
263
  clearTimeout(timeoutId);
191
264
  if (!response.ok) {
192
265
  healthStatus.configurationIssues.push(`Connectivity test failed: HTTP ${response.status}`);
@@ -233,11 +306,18 @@ export class ProviderHealthChecker {
233
306
  const commonModels = this.getCommonModelsForProvider(providerName);
234
307
  if (commonModels.length > 0) {
235
308
  if (providerName === AIProviderName.VERTEX) {
236
- // Provide more detailed information for Vertex AI
237
- healthStatus.recommendations.push(`Available models for ${providerName}:\n` +
238
- ` Google Models: gemini-1.5-pro, gemini-1.5-flash\n` +
239
- ` Claude Models: claude-3-5-sonnet-20241022, claude-3-sonnet-20240229, claude-3-haiku-20240307, claude-3-opus-20240229\n` +
240
- ` Note: Claude models require Anthropic integration to be enabled in your Google Cloud project`);
309
+ // Provide detailed information about dual provider architecture
310
+ healthStatus.recommendations.push(`Available models for ${providerName} (using dual provider architecture):\n` +
311
+ ` Google Models (via vertex provider):\n` +
312
+ ` gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite\n` +
313
+ ` gemini-2.0-flash-001, gemini-1.5-pro, gemini-1.5-flash\n` +
314
+ ` Anthropic Models (via vertexAnthropic provider):\n` +
315
+ ` • claude-sonnet-4@20250514, claude-opus-4@20250514\n` +
316
+ ` • claude-3-5-sonnet-20241022, claude-3-5-haiku-20241022\n` +
317
+ ` • claude-3-sonnet-20240229, claude-3-haiku-20240307, claude-3-opus-20240229\n` +
318
+ ` Implementation: Uses @ai-sdk/google-vertex with dual provider setup\n` +
319
+ ` Authentication: Requires Google Cloud project with Vertex AI API enabled\n` +
320
+ ` Note: Anthropic models require Anthropic integration in your Google Cloud project`);
241
321
  }
242
322
  else {
243
323
  healthStatus.recommendations.push(`Common models for ${providerName}: ${commonModels.slice(0, 3).join(", ")}`);
@@ -263,6 +343,8 @@ export class ProviderHealthChecker {
263
343
  return ["GOOGLE_AI_API_KEY"];
264
344
  case AIProviderName.BEDROCK:
265
345
  return ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION"];
346
+ case AIProviderName.AZURE:
347
+ return ["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"];
266
348
  case AIProviderName.OLLAMA:
267
349
  return []; // Ollama typically doesn't require API keys
268
350
  default:
@@ -284,6 +366,8 @@ export class ProviderHealthChecker {
284
366
  return "GOOGLE_AI_API_KEY";
285
367
  case AIProviderName.BEDROCK:
286
368
  return "AWS_ACCESS_KEY_ID";
369
+ case AIProviderName.AZURE:
370
+ return "AZURE_OPENAI_API_KEY";
287
371
  case AIProviderName.OLLAMA:
288
372
  return "OLLAMA_API_BASE";
289
373
  default:
@@ -305,6 +389,8 @@ export class ProviderHealthChecker {
305
389
  return apiKey.endsWith(".json") || apiKey.includes("type"); // JSON key format
306
390
  case AIProviderName.BEDROCK:
307
391
  return apiKey.length >= 20; // AWS access key length
392
+ case AIProviderName.AZURE:
393
+ return apiKey.length >= 32; // Azure OpenAI API key length
308
394
  case AIProviderName.OLLAMA:
309
395
  return true; // Ollama usually doesn't require specific format
310
396
  default:
@@ -338,47 +424,135 @@ export class ProviderHealthChecker {
338
424
  static async checkProviderSpecificConfig(providerName, healthStatus) {
339
425
  switch (providerName) {
340
426
  case AIProviderName.VERTEX: {
427
+ logger.debug("Starting Vertex AI health check", {
428
+ providerName,
429
+ });
341
430
  // Check for Google Cloud project ID (with fallbacks)
342
431
  const projectId = process.env.GOOGLE_PROJECT_ID ||
343
432
  process.env.GOOGLE_CLOUD_PROJECT_ID ||
344
433
  process.env.GOOGLE_VERTEX_PROJECT ||
345
434
  process.env.GOOGLE_CLOUD_PROJECT ||
346
435
  process.env.VERTEX_PROJECT_ID;
436
+ logger.debug("Project ID validation", {
437
+ hasProjectId: !!projectId,
438
+ });
347
439
  if (!projectId) {
348
440
  healthStatus.configurationIssues.push("Google Cloud project ID not set");
349
441
  healthStatus.recommendations.push("Set one of: GOOGLE_VERTEX_PROJECT, GOOGLE_CLOUD_PROJECT_ID, GOOGLE_PROJECT_ID, or GOOGLE_CLOUD_PROJECT");
350
442
  }
351
- // Check for authentication (either credentials file OR individual credentials)
352
- const hasCredentialsFile = !!process.env.GOOGLE_APPLICATION_CREDENTIALS;
353
- const hasServiceAccountKey = !!process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
354
- const hasIndividualCredentials = !!(process.env.GOOGLE_AUTH_CLIENT_EMAIL &&
355
- process.env.GOOGLE_AUTH_PRIVATE_KEY);
356
- if (!hasCredentialsFile &&
357
- !hasServiceAccountKey &&
358
- !hasIndividualCredentials) {
359
- healthStatus.configurationIssues.push("Google Cloud authentication not configured");
360
- healthStatus.recommendations.push("Set either GOOGLE_APPLICATION_CREDENTIALS (file path), GOOGLE_SERVICE_ACCOUNT_KEY (base64), or both GOOGLE_AUTH_CLIENT_EMAIL and GOOGLE_AUTH_PRIVATE_KEY");
443
+ // Check for authentication with proper file existence validation
444
+ // This aligns with the authentication logic fix in googleVertex.ts
445
+ let hasValidAuth = false;
446
+ logger.debug("Authentication validation starting", {
447
+ hasGoogleApplicationCredentials: !!process.env.GOOGLE_APPLICATION_CREDENTIALS,
448
+ });
449
+ // Check for principal account authentication first (recommended for production)
450
+ // BUT CRITICALLY: Also verify the file actually exists
451
+ if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
452
+ const credentialsPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
453
+ logger.debug("Checking GOOGLE_APPLICATION_CREDENTIALS file");
454
+ // Check if the credentials file actually exists
455
+ let fileExists = false;
456
+ try {
457
+ const { promises: fs } = await import("fs");
458
+ try {
459
+ await fs.access(credentialsPath);
460
+ fileExists = true;
461
+ }
462
+ catch {
463
+ fileExists = false;
464
+ }
465
+ logger.debug("File existence check completed", {
466
+ fileExists,
467
+ });
468
+ }
469
+ catch (error) {
470
+ logger.debug("File existence check failed", {
471
+ error: String(error),
472
+ });
473
+ healthStatus.warning = `Failed to check credentials file existence: ${error}`;
474
+ fileExists = false;
475
+ }
476
+ if (fileExists) {
477
+ // Validate file format
478
+ const fileName = basename(credentialsPath);
479
+ const jsonFilePattern = /\.json(\.\w+)?$/;
480
+ if (!jsonFilePattern.test(fileName)) {
481
+ healthStatus.warning =
482
+ "GOOGLE_APPLICATION_CREDENTIALS should point to a JSON file (e.g., 'credentials.json' or 'key.json.backup')";
483
+ }
484
+ hasValidAuth = true;
485
+ healthStatus.hasApiKey = true;
486
+ logger.debug("GOOGLE_APPLICATION_CREDENTIALS file validated", {
487
+ fileName,
488
+ hasValidAuth,
489
+ });
490
+ }
491
+ else {
492
+ healthStatus.warning = `GOOGLE_APPLICATION_CREDENTIALS file does not exist: ${credentialsPath}`;
493
+ logger.debug("GOOGLE_APPLICATION_CREDENTIALS file missing, falling back to individual env vars");
494
+ // Fall through to check individual environment variables
495
+ }
361
496
  }
362
497
  else {
363
- healthStatus.hasApiKey = true; // At least one auth method is configured
498
+ logger.debug("GOOGLE_APPLICATION_CREDENTIALS not set, checking individual env vars");
364
499
  }
365
- // Validate credentials file if provided
366
- if (hasCredentialsFile) {
367
- const credPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
368
- const fileName = basename(credPath);
369
- // Use regex to match .json files with optional backup extensions
370
- const jsonFilePattern = /\.json(\.\w+)?$/;
371
- if (!jsonFilePattern.test(fileName)) {
372
- healthStatus.warning =
373
- "GOOGLE_APPLICATION_CREDENTIALS should point to a JSON file (e.g., 'credentials.json' or 'key.json.backup')";
500
+ // Fallback to individual credentials for development and production
501
+ // Enhanced to check ALL required fields from the .env file configuration
502
+ if (!hasValidAuth) {
503
+ const hasServiceAccountKey = !!process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
504
+ const hasIndividualCredentials = !!(process.env.GOOGLE_AUTH_CLIENT_EMAIL &&
505
+ process.env.GOOGLE_AUTH_PRIVATE_KEY);
506
+ logger.debug("Individual credentials check", {
507
+ hasServiceAccountKey,
508
+ hasGoogleAuthClientEmail: !!process.env.GOOGLE_AUTH_CLIENT_EMAIL,
509
+ hasGoogleAuthPrivateKey: !!process.env.GOOGLE_AUTH_PRIVATE_KEY,
510
+ hasIndividualCredentials,
511
+ });
512
+ if (hasServiceAccountKey || hasIndividualCredentials) {
513
+ hasValidAuth = true;
514
+ healthStatus.hasApiKey = true;
515
+ logger.debug("Individual credentials validated successfully", {
516
+ hasServiceAccountKey,
517
+ hasIndividualCredentials,
518
+ hasValidAuth,
519
+ });
520
+ }
521
+ else {
522
+ logger.debug("Individual credentials validation failed", {
523
+ hasServiceAccountKey,
524
+ hasIndividualCredentials,
525
+ });
374
526
  }
375
527
  }
528
+ // Final validation
529
+ if (!hasValidAuth) {
530
+ healthStatus.configurationIssues.push("Google Cloud authentication not configured or credentials file missing");
531
+ healthStatus.recommendations.push("Set either GOOGLE_APPLICATION_CREDENTIALS (valid file path), GOOGLE_SERVICE_ACCOUNT_KEY (base64), or both GOOGLE_AUTH_CLIENT_EMAIL and GOOGLE_AUTH_PRIVATE_KEY");
532
+ logger.debug("Final auth validation FAILED", {
533
+ hasValidAuth,
534
+ });
535
+ }
536
+ else {
537
+ logger.debug("Final auth validation SUCCESS", {
538
+ hasValidAuth,
539
+ });
540
+ }
376
541
  // Mark as configured if we have both project ID and auth
377
- if (projectId &&
378
- (hasCredentialsFile ||
379
- hasServiceAccountKey ||
380
- hasIndividualCredentials)) {
542
+ if (projectId && hasValidAuth) {
381
543
  healthStatus.isConfigured = true;
544
+ logger.debug("Vertex AI health check PASSED", {
545
+ hasProjectId: !!projectId,
546
+ hasValidAuth,
547
+ isConfigured: healthStatus.isConfigured,
548
+ });
549
+ }
550
+ else {
551
+ logger.debug("Vertex AI health check FAILED", {
552
+ hasProjectId: !!projectId,
553
+ hasValidAuth,
554
+ isConfigured: healthStatus.isConfigured,
555
+ });
382
556
  }
383
557
  break;
384
558
  }
@@ -389,6 +563,20 @@ export class ProviderHealthChecker {
389
563
  healthStatus.recommendations.push("Set AWS_REGION (e.g., us-east-1)");
390
564
  }
391
565
  break;
566
+ case AIProviderName.AZURE: {
567
+ // Check Azure OpenAI endpoint
568
+ const azureEndpoint = process.env.AZURE_OPENAI_ENDPOINT;
569
+ if (azureEndpoint && !azureEndpoint.startsWith("https://")) {
570
+ healthStatus.configurationIssues.push("Invalid AZURE_OPENAI_ENDPOINT format");
571
+ healthStatus.recommendations.push("Set AZURE_OPENAI_ENDPOINT to a valid URL (e.g., https://your-resource.openai.azure.com/)");
572
+ }
573
+ // Check for deployment name
574
+ if (!process.env.AZURE_OPENAI_DEPLOYMENT_NAME) {
575
+ healthStatus.configurationIssues.push("AZURE_OPENAI_DEPLOYMENT_NAME not set");
576
+ healthStatus.recommendations.push("Set AZURE_OPENAI_DEPLOYMENT_NAME to your deployment name");
577
+ }
578
+ break;
579
+ }
392
580
  case AIProviderName.OLLAMA: {
393
581
  // Check if custom endpoint is set
394
582
  const ollamaBase = process.env.OLLAMA_API_BASE || "http://localhost:11434";
@@ -417,9 +605,18 @@ export class ProviderHealthChecker {
417
605
  return ["gemini-1.5-pro", "gemini-1.5-flash", "gemini-pro"];
418
606
  case AIProviderName.VERTEX:
419
607
  return [
608
+ // Google models (via vertex provider)
609
+ "gemini-2.5-pro",
610
+ "gemini-2.5-flash",
611
+ "gemini-2.5-flash-lite",
612
+ "gemini-2.0-flash-001",
420
613
  "gemini-1.5-pro",
421
614
  "gemini-1.5-flash",
615
+ // Anthropic models (via vertexAnthropic provider)
616
+ "claude-sonnet-4@20250514",
617
+ "claude-opus-4@20250514",
422
618
  "claude-3-5-sonnet-20241022",
619
+ "claude-3-5-haiku-20241022",
423
620
  "claude-3-sonnet-20240229",
424
621
  "claude-3-haiku-20240307",
425
622
  "claude-3-opus-20240229",
@@ -429,6 +626,8 @@ export class ProviderHealthChecker {
429
626
  "anthropic.claude-3-sonnet-20240229-v1:0",
430
627
  "anthropic.claude-3-haiku-20240307-v1:0",
431
628
  ];
629
+ case AIProviderName.AZURE:
630
+ return ["gpt-4o", "gpt-4o-mini", "gpt-35-turbo"];
432
631
  case AIProviderName.OLLAMA:
433
632
  return ["llama3.2:latest", "llama3.1:latest", "mistral:latest"];
434
633
  default:
@@ -450,6 +649,409 @@ export class ProviderHealthChecker {
450
649
  }
451
650
  return cached.status;
452
651
  }
652
+ /**
653
+ * Check if Vertex AI supports Anthropic models (dual provider architecture)
654
+ */
655
+ static async checkVertexAnthropicSupport() {
656
+ const result = {
657
+ isSupported: false,
658
+ hasCreateVertexAnthropic: false,
659
+ hasCorrectTypes: false,
660
+ hasValidProject: false,
661
+ hasRegionalSupport: false,
662
+ hasNetworkAccess: false,
663
+ hasAnthropicModels: false,
664
+ authentication: {
665
+ isValid: false,
666
+ method: "none",
667
+ issues: [],
668
+ },
669
+ projectConfiguration: {
670
+ isValid: false,
671
+ projectId: undefined,
672
+ region: undefined,
673
+ issues: [],
674
+ },
675
+ modelSupport: {
676
+ availableModels: [],
677
+ recommendedModels: [
678
+ "claude-sonnet-4@20250514",
679
+ "claude-opus-4@20250514",
680
+ "claude-3-5-sonnet-20241022",
681
+ "claude-3-5-haiku-20241022",
682
+ "claude-3-sonnet-20240229",
683
+ "claude-3-haiku-20240307",
684
+ ],
685
+ deprecatedModels: [
686
+ "claude-3-opus-20240229", // Still available but newer versions preferred
687
+ ],
688
+ },
689
+ recommendations: [],
690
+ troubleshooting: [],
691
+ };
692
+ logger.debug("Starting comprehensive Vertex Anthropic support verification");
693
+ try {
694
+ // 1. Check SDK module availability
695
+ logger.debug("Checking @ai-sdk/google-vertex/anthropic module availability");
696
+ const anthropicModule = await import("@ai-sdk/google-vertex/anthropic");
697
+ result.hasCreateVertexAnthropic =
698
+ typeof anthropicModule.createVertexAnthropic === "function";
699
+ result.hasCorrectTypes = true; // Types are bundled with the function
700
+ if (!result.hasCreateVertexAnthropic) {
701
+ result.troubleshooting.push("📦 Update @ai-sdk/google-vertex to latest version with Anthropic support", "🔄 Run: npm install @ai-sdk/google-vertex@latest", "📖 See: https://sdk.vercel.ai/providers/ai-sdk-providers/google-vertex#anthropic-models");
702
+ return result;
703
+ }
704
+ logger.debug("SDK module verified successfully");
705
+ // 2. Comprehensive Authentication Validation
706
+ logger.debug("Starting authentication validation");
707
+ result.authentication = await this.validateVertexAuthentication();
708
+ if (!result.authentication.isValid) {
709
+ result.troubleshooting.push("🔐 Fix authentication configuration:", " Option 1: Set GOOGLE_APPLICATION_CREDENTIALS to valid service account file", " Option 2: Set individual env vars: GOOGLE_AUTH_CLIENT_EMAIL, GOOGLE_AUTH_PRIVATE_KEY", "📖 See: https://cloud.google.com/docs/authentication/provide-credentials-adc");
710
+ }
711
+ // 3. Project Configuration Validation
712
+ logger.debug("Starting project configuration validation");
713
+ result.projectConfiguration =
714
+ await this.validateVertexProjectConfiguration();
715
+ result.hasValidProject = result.projectConfiguration.isValid;
716
+ if (!result.hasValidProject) {
717
+ result.troubleshooting.push("🏗️ Fix project configuration:", " Set GOOGLE_VERTEX_PROJECT or GOOGLE_CLOUD_PROJECT environment variable", " Ensure project exists and has Vertex AI API enabled", "📖 See: https://console.cloud.google.com/apis/library/aiplatform.googleapis.com");
718
+ }
719
+ // 4. Regional Support Validation
720
+ logger.debug("Starting regional support validation");
721
+ result.hasRegionalSupport = await this.checkVertexRegionalSupport(result.projectConfiguration.region);
722
+ if (!result.hasRegionalSupport) {
723
+ result.troubleshooting.push("🌍 Regional support issues:", " Anthropic models may not be available in your region", " Try regions: us-central1, us-east4, europe-west1, asia-southeast1", " Set GOOGLE_CLOUD_LOCATION environment variable");
724
+ }
725
+ // 5. Network Connectivity Check (non-blocking)
726
+ logger.debug("Starting network connectivity check");
727
+ result.hasNetworkAccess = await this.checkVertexNetworkConnectivity(result.projectConfiguration.region || "us-central1");
728
+ if (!result.hasNetworkAccess) {
729
+ result.troubleshooting.push("🌐 Network connectivity issues:", " Check proxy configuration if behind corporate firewall", " Verify DNS resolution for *.googleapis.com", " Ensure firewall allows HTTPS to Google Cloud endpoints");
730
+ }
731
+ // 6. Anthropic Model Integration Check
732
+ logger.debug("Starting Anthropic model integration check");
733
+ result.hasAnthropicModels = await this.checkAnthropicModelIntegration(result.projectConfiguration.projectId, result.projectConfiguration.region);
734
+ if (!result.hasAnthropicModels) {
735
+ result.troubleshooting.push("🤖 Anthropic model integration issues:", " Enable Anthropic integration in Google Cloud Console", " Navigate to: Vertex AI > Model Garden > Anthropic", " Accept terms and enable Claude model access", "📖 See: https://console.cloud.google.com/vertex-ai/publishers/anthropic");
736
+ }
737
+ // Calculate overall support status
738
+ result.isSupported =
739
+ result.hasCreateVertexAnthropic &&
740
+ result.authentication.isValid &&
741
+ result.hasValidProject &&
742
+ result.hasRegionalSupport;
743
+ // Note: Network and model integration are nice-to-have but not blocking
744
+ // Generate comprehensive recommendations
745
+ if (result.isSupported) {
746
+ result.recommendations.push("✅ Vertex Anthropic support is fully configured", "✅ Claude models are available via vertexAnthropic provider", `✅ Authentication: ${result.authentication.method}`, `✅ Project: ${result.projectConfiguration.projectId}`, `✅ Region: ${result.projectConfiguration.region}`);
747
+ if (result.hasNetworkAccess) {
748
+ result.recommendations.push("✅ Network connectivity verified");
749
+ }
750
+ else {
751
+ result.recommendations.push("⚠️ Network connectivity not verified (may still work)");
752
+ }
753
+ if (result.hasAnthropicModels) {
754
+ result.recommendations.push("✅ Anthropic model integration verified");
755
+ }
756
+ else {
757
+ result.recommendations.push("⚠️ Anthropic model integration not verified");
758
+ }
759
+ result.recommendations.push("", "🎯 Recommended Claude models:", ...result.modelSupport.recommendedModels.map((model) => ` • ${model}`), "", "📚 Usage example:", ' const vertex = new GoogleVertexProvider("claude-3-5-sonnet-20241022")', ' const result = await vertex.generate("Hello, Claude!")');
760
+ logger.info("Vertex Anthropic support verification: FULLY_SUPPORTED");
761
+ }
762
+ else {
763
+ const missingComponents = [];
764
+ if (!result.hasCreateVertexAnthropic) {
765
+ missingComponents.push("SDK module");
766
+ }
767
+ if (!result.authentication.isValid) {
768
+ missingComponents.push("authentication");
769
+ }
770
+ if (!result.hasValidProject) {
771
+ missingComponents.push("project configuration");
772
+ }
773
+ if (!result.hasRegionalSupport) {
774
+ missingComponents.push("regional support");
775
+ }
776
+ result.recommendations.push(`⚠️ Vertex Anthropic support partially available`, `❌ Missing: ${missingComponents.join(", ")}`, "", "🔧 Quick fixes needed:");
777
+ result.recommendations.push(...result.troubleshooting);
778
+ logger.warn("Vertex Anthropic support verification: PARTIALLY_SUPPORTED", {
779
+ missingComponents,
780
+ hasBasicSupport: result.hasCreateVertexAnthropic,
781
+ authenticationValid: result.authentication.isValid,
782
+ projectValid: result.hasValidProject,
783
+ });
784
+ }
785
+ }
786
+ catch (error) {
787
+ logger.error("Vertex Anthropic support check failed", {
788
+ error: error instanceof Error ? error.message : String(error),
789
+ stack: error instanceof Error ? error.stack : undefined,
790
+ });
791
+ result.recommendations.push("❌ Comprehensive Anthropic support check failed", `🐛 Error: ${error instanceof Error ? error.message : String(error)}`, "", "🔧 Troubleshooting steps:", "1. Update @ai-sdk/google-vertex to latest version", "2. Verify Google Cloud authentication setup", "3. Check project ID and region configuration", "4. Enable Vertex AI API in Google Cloud Console", "5. Enable Anthropic integration in Vertex AI Model Garden");
792
+ }
793
+ return result;
794
+ }
795
+ /**
796
+ * Validate Vertex AI authentication configuration
797
+ */
798
+ static async validateVertexAuthentication() {
799
+ const result = {
800
+ isValid: false,
801
+ method: "none",
802
+ issues: [],
803
+ };
804
+ try {
805
+ // Check for service account file authentication (preferred)
806
+ if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
807
+ const credentialsPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
808
+ try {
809
+ const { promises: fs } = await import("fs");
810
+ try {
811
+ await fs.access(credentialsPath);
812
+ // Validate JSON structure
813
+ const credentialsContent = await fs.readFile(credentialsPath, "utf8");
814
+ const credentials = JSON.parse(credentialsContent);
815
+ if (credentials.type === "service_account" &&
816
+ credentials.project_id &&
817
+ credentials.client_email &&
818
+ credentials.private_key) {
819
+ result.isValid = true;
820
+ result.method = "service_account_file";
821
+ return result;
822
+ }
823
+ else {
824
+ result.issues.push("Service account file missing required fields");
825
+ }
826
+ }
827
+ catch {
828
+ result.issues.push(`Service account file not found: ${credentialsPath}`);
829
+ }
830
+ }
831
+ catch (fileError) {
832
+ result.issues.push(`Service account file validation failed: ${fileError}`);
833
+ }
834
+ }
835
+ // Check for individual environment variables
836
+ if (process.env.GOOGLE_AUTH_CLIENT_EMAIL &&
837
+ process.env.GOOGLE_AUTH_PRIVATE_KEY) {
838
+ const email = process.env.GOOGLE_AUTH_CLIENT_EMAIL;
839
+ const privateKey = process.env.GOOGLE_AUTH_PRIVATE_KEY;
840
+ if (email.includes("@") && privateKey.includes("BEGIN PRIVATE KEY")) {
841
+ result.isValid = true;
842
+ result.method = "environment_variables";
843
+ return result;
844
+ }
845
+ else {
846
+ result.issues.push("Individual credentials format validation failed");
847
+ }
848
+ }
849
+ else {
850
+ result.issues.push("Missing individual credential environment variables");
851
+ }
852
+ // Check for Application Default Credentials (ADC)
853
+ try {
854
+ // This is a simple heuristic - in real implementation you'd use Google Auth library
855
+ if (process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT) {
856
+ result.method = "application_default_credentials";
857
+ result.isValid = true; // Assume valid if environment suggests ADC
858
+ return result;
859
+ }
860
+ }
861
+ catch (adcError) {
862
+ result.issues.push(`ADC check failed: ${adcError}`);
863
+ }
864
+ if (!result.isValid) {
865
+ result.issues.push("No valid authentication method found");
866
+ }
867
+ }
868
+ catch (error) {
869
+ result.issues.push(`Authentication validation error: ${error}`);
870
+ }
871
+ return result;
872
+ }
873
+ /**
874
+ * Validate Vertex AI project configuration
875
+ */
876
+ static async validateVertexProjectConfiguration() {
877
+ const result = {
878
+ isValid: false,
879
+ projectId: undefined,
880
+ region: undefined,
881
+ issues: [],
882
+ };
883
+ // Check project ID
884
+ const projectId = process.env.GOOGLE_VERTEX_PROJECT ||
885
+ process.env.GOOGLE_CLOUD_PROJECT_ID ||
886
+ process.env.GOOGLE_PROJECT_ID ||
887
+ process.env.GOOGLE_CLOUD_PROJECT;
888
+ if (projectId) {
889
+ result.projectId = projectId;
890
+ // Validate project ID format
891
+ const projectIdPattern = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/;
892
+ if (projectIdPattern.test(projectId)) {
893
+ result.isValid = true;
894
+ }
895
+ else {
896
+ result.issues.push(`Invalid project ID format: ${projectId}`);
897
+ }
898
+ }
899
+ else {
900
+ result.issues.push("No project ID configured");
901
+ }
902
+ // Check region/location
903
+ const region = process.env.GOOGLE_CLOUD_LOCATION ||
904
+ process.env.VERTEX_LOCATION ||
905
+ process.env.GOOGLE_VERTEX_LOCATION ||
906
+ "us-central1";
907
+ result.region = region;
908
+ // Validate region format
909
+ const regionPattern = /^[a-z]+-[a-z]+\d+$/;
910
+ if (!regionPattern.test(region)) {
911
+ result.issues.push(`Invalid region format: ${region}`);
912
+ result.isValid = false;
913
+ }
914
+ return result;
915
+ }
916
+ /**
917
+ * Check if the specified region supports Anthropic models
918
+ */
919
+ static async checkVertexRegionalSupport(region = "us-central1") {
920
+ // Based on Google Cloud documentation, these regions support Anthropic models
921
+ const supportedRegions = [
922
+ "us-central1",
923
+ "us-east4",
924
+ "us-west1",
925
+ "us-west4",
926
+ "europe-west1",
927
+ "europe-west4",
928
+ "asia-southeast1",
929
+ "asia-northeast1",
930
+ ];
931
+ const isSupported = supportedRegions.includes(region);
932
+ logger.debug("Regional support check", {
933
+ region,
934
+ isSupported,
935
+ supportedRegions,
936
+ });
937
+ return isSupported;
938
+ }
939
+ /**
940
+ * Check network connectivity to Vertex AI endpoints
941
+ */
942
+ static async checkVertexNetworkConnectivity(region = "us-central1") {
943
+ try {
944
+ const endpoint = `https://${region}-aiplatform.googleapis.com`;
945
+ // Simple connectivity check with timeout
946
+ const controller = new AbortController();
947
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
948
+ const proxyFetch = createProxyFetch();
949
+ let response = await proxyFetch(endpoint, {
950
+ method: "HEAD",
951
+ signal: controller.signal,
952
+ });
953
+ // Fallback to GET if HEAD returns 405 (Method Not Allowed) for restrictive gateways
954
+ if (response.status === 405) {
955
+ response = await proxyFetch(endpoint, {
956
+ method: "GET",
957
+ signal: controller.signal,
958
+ });
959
+ }
960
+ clearTimeout(timeoutId);
961
+ // Any response (even 401/403) indicates network connectivity
962
+ const isConnected = response.status !== undefined;
963
+ logger.debug("Network connectivity check", {
964
+ endpoint,
965
+ status: response.status,
966
+ isConnected,
967
+ });
968
+ return isConnected;
969
+ }
970
+ catch (error) {
971
+ logger.debug("Network connectivity check failed", {
972
+ region,
973
+ error: error instanceof Error ? error.message : String(error),
974
+ });
975
+ return false;
976
+ }
977
+ }
978
+ /**
979
+ * Check if Anthropic model integration is enabled in the project
980
+ */
981
+ static async checkAnthropicModelIntegration(projectId, region = "us-central1") {
982
+ if (!projectId) {
983
+ logger.debug("Cannot check Anthropic integration without project ID");
984
+ return false;
985
+ }
986
+ try {
987
+ // This is a simplified check - in a real implementation, you would:
988
+ // 1. Use Google Cloud APIs to check model availability
989
+ // 2. Verify Anthropic integration status
990
+ // 3. Check model access permissions
991
+ // For now, we'll do a basic endpoint check
992
+ const modelEndpoint = `https://${region}-aiplatform.googleapis.com/v1/projects/${projectId}/locations/${region}/publishers/anthropic/models`;
993
+ const controller = new AbortController();
994
+ const timeoutId = setTimeout(() => controller.abort(), 3000);
995
+ const proxyFetch = createProxyFetch();
996
+ let response = await proxyFetch(modelEndpoint, {
997
+ method: "HEAD",
998
+ signal: controller.signal,
999
+ });
1000
+ // Fallback to GET if HEAD returns 405 (Method Not Allowed) for restrictive gateways
1001
+ if (response.status === 405) {
1002
+ response = await proxyFetch(modelEndpoint, {
1003
+ method: "GET",
1004
+ signal: controller.signal,
1005
+ });
1006
+ }
1007
+ clearTimeout(timeoutId);
1008
+ // Status 200 or 401/403 suggests the endpoint exists (integration enabled)
1009
+ // Status 404 suggests integration not enabled
1010
+ const integrationEnabled = response.status !== 404;
1011
+ logger.debug("Anthropic integration check", {
1012
+ projectId,
1013
+ region,
1014
+ endpoint: modelEndpoint,
1015
+ status: response.status,
1016
+ integrationEnabled,
1017
+ });
1018
+ return integrationEnabled;
1019
+ }
1020
+ catch (error) {
1021
+ logger.debug("Anthropic integration check failed", {
1022
+ projectId,
1023
+ region,
1024
+ error: error instanceof Error ? error.message : String(error),
1025
+ });
1026
+ // Assume integration might be enabled if we can't verify
1027
+ return true;
1028
+ }
1029
+ }
1030
+ /**
1031
+ * Initialize health checks in the background (NON-BLOCKING)
1032
+ * Starts background health monitoring without blocking initialization
1033
+ */
1034
+ static initializeBackgroundHealthChecks() {
1035
+ // Run health checks in the background without awaiting
1036
+ const backgroundHealthCheck = async () => {
1037
+ try {
1038
+ logger.debug("Starting background health check initialization");
1039
+ await this.checkAllProvidersHealth({
1040
+ includeConnectivityTest: false,
1041
+ cacheResults: true,
1042
+ timeout: 2000, // 2-second timeout for background checks
1043
+ });
1044
+ logger.debug("Background health check initialization completed");
1045
+ }
1046
+ catch (error) {
1047
+ logger.warn("Background health check initialization failed", {
1048
+ error: error instanceof Error ? error.message : String(error),
1049
+ });
1050
+ }
1051
+ };
1052
+ // Start background check without blocking
1053
+ backgroundHealthCheck();
1054
+ }
453
1055
  /**
454
1056
  * Clear health cache for a provider or all providers
455
1057
  */
@@ -464,8 +1066,9 @@ export class ProviderHealthChecker {
464
1066
  }
465
1067
  }
466
1068
  /**
467
- * Get the best healthy provider from a list of options
1069
+ * Get the best healthy provider from a list of options (NON-BLOCKING)
468
1070
  * Prioritizes healthy providers over configured but unhealthy ones
1071
+ * Uses fast, cached health checks to avoid blocking initialization
469
1072
  */
470
1073
  static async getBestHealthyProvider(preferredProviders = [
471
1074
  "openai",
@@ -478,6 +1081,7 @@ export class ProviderHealthChecker {
478
1081
  const healthStatuses = await this.checkAllProvidersHealth({
479
1082
  includeConnectivityTest: false, // Quick config check only
480
1083
  cacheResults: true,
1084
+ timeout: 1000, // Fast 1-second timeout to avoid blocking
481
1085
  });
482
1086
  // First try to find a healthy provider in order of preference
483
1087
  for (const provider of preferredProviders) {
@@ -507,11 +1111,12 @@ export class ProviderHealthChecker {
507
1111
  */
508
1112
  static async checkAllProvidersHealth(options = {}) {
509
1113
  const providers = [
510
- AIProviderName.ANTHROPIC,
511
- AIProviderName.OPENAI,
512
1114
  AIProviderName.VERTEX,
513
1115
  AIProviderName.GOOGLE_AI,
1116
+ AIProviderName.ANTHROPIC,
1117
+ AIProviderName.OPENAI,
514
1118
  AIProviderName.BEDROCK,
1119
+ AIProviderName.AZURE,
515
1120
  AIProviderName.OLLAMA,
516
1121
  ];
517
1122
  const healthChecks = providers.map((provider) => this.checkProviderHealth(provider, options));