@juspay/neurolink 11.0.0 → 11.1.1

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 (121) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/browser/neurolink.min.js +510 -510
  3. package/dist/cli/commands/setup.d.ts +8 -1
  4. package/dist/cli/commands/setup.js +62 -46
  5. package/dist/cli/factories/commandFactory.d.ts +1157 -1
  6. package/dist/cli/factories/commandFactory.js +20 -53
  7. package/dist/constants/networkErrorCodes.d.ts +14 -0
  8. package/dist/constants/networkErrorCodes.js +21 -0
  9. package/dist/factories/providerDescriptors.d.ts +18 -0
  10. package/dist/factories/providerDescriptors.js +546 -0
  11. package/dist/factories/providerFactory.d.ts +25 -14
  12. package/dist/factories/providerFactory.js +46 -23
  13. package/dist/factories/providerRegistry.js +31 -30
  14. package/dist/index.d.ts +10 -1
  15. package/dist/index.js +13 -3
  16. package/dist/lib/constants/networkErrorCodes.d.ts +14 -0
  17. package/dist/lib/constants/networkErrorCodes.js +22 -0
  18. package/dist/lib/factories/providerDescriptors.d.ts +18 -0
  19. package/dist/lib/factories/providerDescriptors.js +547 -0
  20. package/dist/lib/factories/providerFactory.d.ts +25 -14
  21. package/dist/lib/factories/providerFactory.js +46 -23
  22. package/dist/lib/factories/providerRegistry.js +31 -30
  23. package/dist/lib/index.d.ts +10 -1
  24. package/dist/lib/index.js +13 -3
  25. package/dist/lib/neurolink.js +25 -37
  26. package/dist/lib/processors/base/BaseFileProcessor.d.ts +17 -0
  27. package/dist/lib/processors/base/BaseFileProcessor.js +40 -0
  28. package/dist/lib/processors/document/OpenDocumentProcessor.js +12 -2
  29. package/dist/lib/providers/amazonBedrock/client.js +34 -16
  30. package/dist/lib/providers/anthropic/client.js +39 -34
  31. package/dist/lib/providers/azureOpenai.js +17 -15
  32. package/dist/lib/providers/cloudflare.js +12 -21
  33. package/dist/lib/providers/cohere.js +31 -25
  34. package/dist/lib/providers/deepseek.js +23 -26
  35. package/dist/lib/providers/fireworks.js +12 -21
  36. package/dist/lib/providers/googleAiStudio/client.d.ts +2 -0
  37. package/dist/lib/providers/googleAiStudio/client.js +39 -17
  38. package/dist/lib/providers/googleVertex/client.js +107 -99
  39. package/dist/lib/providers/groq.js +19 -22
  40. package/dist/lib/providers/huggingFace/client.js +26 -24
  41. package/dist/lib/providers/litellm/client.js +54 -39
  42. package/dist/lib/providers/llamaCpp.js +21 -18
  43. package/dist/lib/providers/lmStudio.js +22 -19
  44. package/dist/lib/providers/mistral.js +12 -24
  45. package/dist/lib/providers/nvidiaNim/client.js +37 -34
  46. package/dist/lib/providers/ollama/client.js +50 -35
  47. package/dist/lib/providers/openAI/client.js +37 -40
  48. package/dist/lib/providers/openRouter/client.js +53 -47
  49. package/dist/lib/providers/openaiChatCompletionsBase.js +40 -16
  50. package/dist/lib/providers/openaiCompatible/client.js +36 -32
  51. package/dist/lib/providers/perplexity.js +12 -21
  52. package/dist/lib/providers/togetherAi.js +12 -21
  53. package/dist/lib/providers/xai.js +17 -26
  54. package/dist/lib/proxy/proxyFetch.js +1 -9
  55. package/dist/lib/server/errors.d.ts +1 -1
  56. package/dist/lib/server/errors.js +2 -2
  57. package/dist/lib/server/index.d.ts +2 -2
  58. package/dist/lib/server/index.js +5 -3
  59. package/dist/lib/server/middleware/rateLimit.d.ts +0 -4
  60. package/dist/lib/server/middleware/rateLimit.js +0 -4
  61. package/dist/lib/types/cli.d.ts +2 -0
  62. package/dist/lib/types/errors.d.ts +35 -0
  63. package/dist/lib/types/providers.d.ts +79 -0
  64. package/dist/lib/utils/errorClassifier.d.ts +30 -0
  65. package/dist/lib/utils/errorClassifier.js +183 -0
  66. package/dist/lib/utils/fileDetector.js +6 -43
  67. package/dist/lib/utils/providerConfig.d.ts +16 -0
  68. package/dist/lib/utils/providerConfig.js +23 -0
  69. package/dist/lib/utils/providerHealth.d.ts +53 -12
  70. package/dist/lib/utils/providerHealth.js +125 -131
  71. package/dist/lib/utils/providerUtils.js +22 -64
  72. package/dist/neurolink.js +25 -37
  73. package/dist/processors/base/BaseFileProcessor.d.ts +17 -0
  74. package/dist/processors/base/BaseFileProcessor.js +40 -0
  75. package/dist/processors/document/OpenDocumentProcessor.js +12 -2
  76. package/dist/providers/amazonBedrock/client.js +34 -16
  77. package/dist/providers/amazonSagemaker.d.ts +1 -1
  78. package/dist/providers/anthropic/client.js +39 -34
  79. package/dist/providers/azureOpenai.js +17 -15
  80. package/dist/providers/cloudflare.js +12 -21
  81. package/dist/providers/cohere.js +31 -25
  82. package/dist/providers/deepseek.js +23 -26
  83. package/dist/providers/fireworks.js +12 -21
  84. package/dist/providers/googleAiStudio/client.d.ts +2 -0
  85. package/dist/providers/googleAiStudio/client.js +39 -17
  86. package/dist/providers/googleVertex/client.js +107 -99
  87. package/dist/providers/groq.js +19 -22
  88. package/dist/providers/huggingFace/client.js +26 -24
  89. package/dist/providers/litellm/client.js +54 -39
  90. package/dist/providers/llamaCpp.js +21 -18
  91. package/dist/providers/lmStudio.js +22 -19
  92. package/dist/providers/mistral.js +12 -24
  93. package/dist/providers/nvidiaNim/client.js +37 -34
  94. package/dist/providers/ollama/client.js +50 -35
  95. package/dist/providers/openAI/client.js +37 -40
  96. package/dist/providers/openRouter/client.js +53 -47
  97. package/dist/providers/openaiChatCompletionsBase.js +40 -16
  98. package/dist/providers/openaiCompatible/client.js +36 -32
  99. package/dist/providers/perplexity.js +12 -21
  100. package/dist/providers/sagemaker/language-model.d.ts +2 -2
  101. package/dist/providers/togetherAi.js +12 -21
  102. package/dist/providers/xai.js +17 -26
  103. package/dist/proxy/proxyFetch.js +1 -9
  104. package/dist/server/errors.d.ts +1 -1
  105. package/dist/server/errors.js +2 -2
  106. package/dist/server/index.d.ts +2 -2
  107. package/dist/server/index.js +5 -3
  108. package/dist/server/middleware/rateLimit.d.ts +0 -4
  109. package/dist/server/middleware/rateLimit.js +0 -4
  110. package/dist/types/cli.d.ts +2 -0
  111. package/dist/types/errors.d.ts +35 -0
  112. package/dist/types/providers.d.ts +79 -0
  113. package/dist/utils/errorClassifier.d.ts +30 -0
  114. package/dist/utils/errorClassifier.js +182 -0
  115. package/dist/utils/fileDetector.js +6 -43
  116. package/dist/utils/providerConfig.d.ts +16 -0
  117. package/dist/utils/providerConfig.js +23 -0
  118. package/dist/utils/providerHealth.d.ts +53 -12
  119. package/dist/utils/providerHealth.js +125 -131
  120. package/dist/utils/providerUtils.js +22 -64
  121. package/package.json +15 -70
@@ -6,6 +6,7 @@ import { ATTR, tracers, withClientSpan, withClientStreamSpan, withSpan, } from "
6
6
  import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../../types/index.js";
7
7
  import { ERROR_CODES, NeuroLinkError } from "../../utils/errorHandling.js";
8
8
  import { logger } from "../../utils/logger.js";
9
+ import { isToolsSchemaExclusionInForce } from "../../core/modules/structuredOutputPolicy.js";
9
10
  import { GEMINI_ELISION_NOTE, planGeminiLoopReclaim, previewGeminiToolResponseText, } from "../../context/geminiLoopGuard.js";
10
11
  import { getAvailableInputTokens, getContextWindowSize, } from "../../constants/contextWindows.js";
11
12
  import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../../utils/timeout.js";
@@ -18,7 +19,7 @@ import { createProxyFetch } from "../../proxy/proxyFetch.js";
18
19
  // Google AI Live API types now imported from ../types/providerSpecific.js
19
20
  // Import proper types for multimodal message handling
20
21
  // Create Google GenAI client
21
- async function createGoogleGenAIClient(apiKey) {
22
+ async function createGoogleGenAIClient(apiKey, baseURL) {
22
23
  const mod = await import("@google/genai");
23
24
  const ctor = mod.GoogleGenAI;
24
25
  if (!ctor) {
@@ -32,11 +33,17 @@ async function createGoogleGenAIClient(apiKey) {
32
33
  });
33
34
  }
34
35
  const Ctor = ctor;
35
- // Include httpOptions with proxy fetch for corporate network support
36
+ // Include httpOptions with proxy fetch for corporate network support.
37
+ // baseUrl is only included when resolved — verified against
38
+ // @google/genai's ApiClient (dist/node/index.cjs) that it falls back to
39
+ // its own default whenever httpOptions.baseUrl is undefined, so omitting
40
+ // the key and passing `baseUrl: undefined` behave identically; the key is
41
+ // still omitted outright for a cleaner outbound config object.
36
42
  return new Ctor({
37
43
  apiKey,
38
44
  httpOptions: {
39
45
  fetch: createProxyFetch(),
46
+ ...(baseURL ? { baseUrl: baseURL } : {}),
40
47
  },
41
48
  });
42
49
  }
@@ -256,7 +263,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
256
263
  // Use the @google/genai client for image generation
257
264
  let client;
258
265
  try {
259
- client = await createGoogleGenAIClient(apiKey);
266
+ client = await createGoogleGenAIClient(apiKey, this.getBaseURL());
260
267
  }
261
268
  catch {
262
269
  throw new AuthenticationError("Missing '@google/genai'. Install with: npm install @google/genai", this.providerName);
@@ -563,7 +570,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
563
570
  // generate() does, or attached files are dropped on this path alone.
564
571
  await this.preprocessNativeFileInput(options);
565
572
  // Structured output (analysisSchema, JSON format, or schema) is incompatible with tools on Gemini.
566
- const wantsStructuredOutput = analysisSchema || options.output?.format === "json" || options.schema;
573
+ const wantsStructuredOutput = Boolean(analysisSchema || options.output?.format === "json" || options.schema);
567
574
  // Tool filter (a0269210): trust options.tools — caller (BaseProvider.stream)
568
575
  // already merged MCP/built-in tools with user tools and applied any
569
576
  // enabledToolNames filter. Re-attaching getAllTools() here would clobber
@@ -575,12 +582,10 @@ export class GoogleAIStudioProvider extends BaseProvider {
575
582
  ...options,
576
583
  tools: optionTools,
577
584
  };
578
- // Check for tools + JSON schema conflict (Gemini limitation)
585
+ // Tools + JSON conflict (Gemini), via the shared predicate.
579
586
  const wantsJsonOutput = options.output?.format === "json" || options.schema;
580
- if (wantsJsonOutput &&
581
- mergedOptions.tools &&
582
- Object.keys(mergedOptions.tools).length > 0 &&
583
- !mergedOptions.disableTools) {
587
+ const exclusionInForce = isToolsSchemaExclusionInForce(this.providerName, modelName, !mergedOptions.disableTools, Object.keys(mergedOptions.tools ?? {}).length);
588
+ if (wantsJsonOutput && exclusionInForce) {
584
589
  logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request.");
585
590
  mergedOptions = { ...mergedOptions, disableTools: true, tools: {} };
586
591
  }
@@ -618,7 +623,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
618
623
  const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
619
624
  try {
620
625
  const apiKey = this.getApiKey();
621
- const client = await createGoogleGenAIClient(apiKey);
626
+ const client = await createGoogleGenAIClient(apiKey, this.getBaseURL());
622
627
  logger.debug("[GoogleAIStudio] Using native @google/genai for Gemini 3", {
623
628
  model: modelName,
624
629
  hasTools: !!options.tools && Object.keys(options.tools).length > 0,
@@ -956,7 +961,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
956
961
  const timeoutController = createTimeoutController(timeout, this.providerName, "generate");
957
962
  try {
958
963
  const apiKey = this.getApiKey();
959
- const client = await createGoogleGenAIClient(apiKey);
964
+ const client = await createGoogleGenAIClient(apiKey, this.getBaseURL());
960
965
  logger.debug("[GoogleAIStudio] Using native @google/genai for Gemini 3 generate", {
961
966
  model: modelName,
962
967
  hasTools: !!options.tools && Object.keys(options.tools).length > 0,
@@ -985,7 +990,16 @@ export class GoogleAIStudioProvider extends BaseProvider {
985
990
  let originalNameMap = new Map();
986
991
  let declarationsResult;
987
992
  const shouldUseTools = !options.disableTools;
988
- if (shouldUseTools) {
993
+ // Structured output (JSON format or schema) is incompatible with
994
+ // tools on Gemini — routed through the shared predicate so this
995
+ // decision matches stream()'s orchestrator (previously this path
996
+ // had no proactive check and silently dropped the schema instead).
997
+ const wantsNativeJsonRequested = Boolean(options.output?.format === "json" || options.schema);
998
+ const exclusionInForce = isToolsSchemaExclusionInForce(this.providerName, modelName, shouldUseTools, Object.keys(options.tools || {}).length);
999
+ if (wantsNativeJsonRequested && exclusionInForce) {
1000
+ logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request (generate()).");
1001
+ }
1002
+ if (shouldUseTools && !exclusionInForce) {
989
1003
  const tools = options.tools || {};
990
1004
  if (Object.keys(tools).length > 0) {
991
1005
  const result = buildNativeToolDeclarations(tools);
@@ -1002,8 +1016,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
1002
1016
  // Native JSON / schema enforcement (generate path). Mirrors the
1003
1017
  // stream block above; only set when no tools are being sent
1004
1018
  // because Gemini cannot combine function calling with JSON mime.
1005
- const wantsNativeJson = !toolsConfig &&
1006
- (options.output?.format === "json" || !!options.schema);
1019
+ const wantsNativeJson = !toolsConfig && wantsNativeJsonRequested;
1007
1020
  const nativeResponseSchema = wantsNativeJson && options.schema
1008
1021
  ? buildGeminiResponseSchema(options.schema)
1009
1022
  : undefined;
@@ -1332,7 +1345,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
1332
1345
  // Dynamic import to avoid hard dependency unless audio streaming is used
1333
1346
  let client;
1334
1347
  try {
1335
- client = await createGoogleGenAIClient(apiKey);
1348
+ client = await createGoogleGenAIClient(apiKey, this.getBaseURL());
1336
1349
  }
1337
1350
  catch {
1338
1351
  throw new AuthenticationError("Missing '@google/genai'. Install with: pnpm add @google/genai", this.providerName);
@@ -1522,7 +1535,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
1522
1535
  });
1523
1536
  try {
1524
1537
  const apiKey = this.getApiKey();
1525
- const client = await createGoogleGenAIClient(apiKey);
1538
+ const client = await createGoogleGenAIClient(apiKey, this.getBaseURL());
1526
1539
  const result = await client.models.embedContent({
1527
1540
  model: embeddingModelName,
1528
1541
  contents: [text],
@@ -1562,7 +1575,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
1562
1575
  });
1563
1576
  try {
1564
1577
  const apiKey = this.getApiKey();
1565
- const client = await createGoogleGenAIClient(apiKey);
1578
+ const client = await createGoogleGenAIClient(apiKey, this.getBaseURL());
1566
1579
  const result = await client.models.embedContent({
1567
1580
  model: embeddingModelName,
1568
1581
  contents: texts,
@@ -1594,6 +1607,15 @@ export class GoogleAIStudioProvider extends BaseProvider {
1594
1607
  }
1595
1608
  return apiKey;
1596
1609
  }
1610
+ // Mirrors mistral.ts's baseURL precedence (credentials override, then env,
1611
+ // then unset — the SDK's own default applies when unset). Blank/whitespace
1612
+ // values are treated as unset so an empty override can't accidentally
1613
+ // clobber the default.
1614
+ getBaseURL() {
1615
+ const baseURL = this.credentials?.baseURL?.trim() ||
1616
+ process.env.GOOGLE_AI_BASE_URL?.trim();
1617
+ return baseURL && baseURL.length > 0 ? baseURL : undefined;
1618
+ }
1597
1619
  }
1598
1620
  export default GoogleAIStudioProvider;
1599
1621
  //# sourceMappingURL=client.js.map
@@ -13,6 +13,7 @@ import { isSchemaComplexityError } from "../../core/modules/structuredOutputPoli
13
13
  import { redactUrlForError, stringifyContentSafe, } from "../../utils/logSanitize.js";
14
14
  import { createProxyFetch } from "../../proxy/proxyFetch.js";
15
15
  import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../../types/index.js";
16
+ import { classifyProviderError } from "../../utils/errorClassifier.js";
16
17
  import { ERROR_CODES, NeuroLinkError } from "../../utils/errorHandling.js";
17
18
  import { applyVertexAnthropicCacheBreakpoints } from "../../utils/anthropicCacheBreakpoints.js";
18
19
  import { FileDetector } from "../../utils/fileDetector.js";
@@ -6098,110 +6099,117 @@ export class GoogleVertexProvider extends BaseProvider {
6098
6099
  }
6099
6100
  formatProviderError(error) {
6100
6101
  const errorRecord = error;
6101
- if (typeof errorRecord?.name === "string" &&
6102
- errorRecord.name === "TimeoutError") {
6103
- return new NetworkError(`Google Vertex AI request timed out. Consider increasing timeout or using a lighter model.`, this.providerName);
6104
- }
6105
- const message = typeof errorRecord?.message === "string"
6106
- ? errorRecord.message
6107
- : "Unknown error occurred";
6108
6102
  const statusCode = typeof errorRecord?.status === "number"
6109
6103
  ? errorRecord.status
6110
6104
  : typeof errorRecord?.statusCode === "number"
6111
6105
  ? errorRecord.statusCode
6112
6106
  : undefined;
6113
- // Authentication and permission errors
6114
- if (message.includes("PERMISSION_DENIED") ||
6115
- message.includes("UNAUTHENTICATED") ||
6116
- message.includes("Invalid API key") ||
6117
- statusCode === 401 ||
6118
- statusCode === 403) {
6119
- return new AuthenticationError(`Google Vertex AI Permission Denied. Your Google Cloud credentials don't have permission to access Vertex AI. ` +
6120
- `Required Steps: 1. Ensure your service account has Vertex AI User role ` +
6121
- `2. Check if Vertex AI API is enabled in your project ` +
6122
- `3. Verify your project ID is correct ` +
6123
- `4. Confirm your location/region has Vertex AI available`, this.providerName);
6124
- }
6125
- // Model not found errors
6126
- if (message.includes("NOT_FOUND") ||
6127
- message.includes("model not found") ||
6128
- message.includes("Model not found") ||
6129
- statusCode === 404) {
6130
- const modelSuggestions = this.getModelSuggestions(this.modelName);
6131
- return new InvalidModelError(`Model '${this.modelName}' is not available in region ${this.location}. ` +
6132
- `Suggested alternatives: ${modelSuggestions}. ` +
6133
- `Troubleshooting: 1. Check model name spelling and format ` +
6134
- `2. Verify model is available in your region ` +
6135
- `3. Ensure your project has access to the model ` +
6136
- `4. For Claude models, enable Anthropic integration in Google Cloud Console`, this.providerName);
6137
- }
6138
- // Rate limit / quota / capacity errors. Anthropic-on-Vertex capacity
6139
- // exhaustion surfaces as overloaded_error (HTTP 529) — same operational
6140
- // meaning as a 429, so classify it here instead of the generic 5xx branch.
6141
- if (message.includes("QUOTA_EXCEEDED") ||
6142
- message.includes("RATE_LIMIT_EXCEEDED") ||
6143
- message.includes("rate limit") ||
6144
- message.includes("429") ||
6145
- statusCode === 429 ||
6146
- statusCode === 529 ||
6147
- /overloaded/i.test(message)) {
6148
- // Surface retry guidance when the SDK error carries it. @google/genai
6149
- // ApiError nests RetryInfo inside the JSON error body's details array,
6150
- // so fall back to scraping retryDelay out of the raw message.
6151
- const retryDelay = typeof errorRecord?.retryDelay === "string"
6152
- ? errorRecord.retryDelay
6153
- : (/["']?retryDelay["']?\s*[:=]\s*["']?(\d+(?:\.\d+)?s)/.exec(message)?.[1] ?? undefined);
6154
- // Prefer the per-request context the native catches attach to the
6155
- // error (this.modelName can be stale when options.model overrides the
6156
- // instance default). Gemini models are force-routed to the "global"
6157
- // endpoint regardless of configured location — report the region the
6158
- // request actually hit.
6159
- const requestModel = typeof errorRecord?.requestModel === "string"
6160
- ? errorRecord.requestModel
6161
- : this.modelName;
6162
- const effectiveRegion = typeof errorRecord?.requestRegion === "string"
6163
- ? errorRecord.requestRegion
6164
- : resolveVertexRegionForModel(requestModel, this.location);
6165
- return new RateLimitError(`Google Vertex AI rate limit / shared-capacity exhausted (429 RESOURCE_EXHAUSTED / overloaded) ` +
6166
- `for model '${requestModel}' in region '${effectiveRegion}'.` +
6167
- (retryDelay
6168
- ? ` Upstream suggests retrying after ${retryDelay}.`
6169
- : "") +
6170
- ` Solutions: 1. Retry with backoff ` +
6171
- `2. Check your Vertex AI quotas in Google Cloud Console (shared-capacity 429s can occur below quota) ` +
6172
- `3. Try a different region or model ` +
6173
- `4. Request provisioned throughput for sustained load`, this.providerName);
6174
- }
6175
- // Network connectivity errors
6176
- if (message.includes("ECONNRESET") ||
6177
- message.includes("ENOTFOUND") ||
6178
- message.includes("ETIMEDOUT") ||
6179
- message.includes("ECONNREFUSED") ||
6180
- message.includes("network") ||
6181
- message.includes("connection")) {
6182
- return new NetworkError(`Connection error: ${message}`, this.providerName);
6183
- }
6184
- // Server errors (5xx)
6185
- if (message.includes("500") ||
6186
- message.includes("502") ||
6187
- message.includes("503") ||
6188
- message.includes("504") ||
6189
- message.includes("server error") ||
6190
- message.includes("Internal Server Error") ||
6191
- message.includes("INTERNAL") ||
6192
- message.includes("UNAVAILABLE") ||
6193
- (statusCode && statusCode >= 500 && statusCode < 600)) {
6194
- return new ProviderError(`Google Vertex AI server error: ${message}. Please try again later.`, this.providerName);
6195
- }
6196
- // Invalid argument errors
6197
- if (message.includes("INVALID_ARGUMENT")) {
6198
- return new ProviderError(`Google Vertex AI Invalid Request: ${message}. ` +
6199
- `Check: 1. Request parameters are within model limits ` +
6200
- `2. Input text is properly formatted ` +
6201
- `3. Temperature and other settings are valid ` +
6202
- `4. Model supports your request type`, this.providerName);
6203
- }
6204
- return new ProviderError(`Google Vertex AI error: ${message}`, this.providerName);
6107
+ const rules = [
6108
+ {
6109
+ // Duck-typed on .name rather than `instanceof TimeoutError` —
6110
+ // Vertex's own `withTimeout` (../../utils/async/index.js) throws a
6111
+ // TimeoutError class distinct from the one classifyProviderError's
6112
+ // built-in fast path checks (../../utils/timeout.js), so that fast
6113
+ // path never fires for a real Vertex timeout. This rule preserves
6114
+ // the pre-migration duck-typed match (both classes set
6115
+ // `.name = "TimeoutError"`) and the original Vertex-specific
6116
+ // message see task-4-report.md for the full writeup.
6117
+ match: (ctx) => ctx.errorName === "TimeoutError",
6118
+ errorClass: NetworkError,
6119
+ message: "Google Vertex AI request timed out. Consider increasing timeout or using a lighter model.",
6120
+ },
6121
+ {
6122
+ match: (ctx) => /PERMISSION_DENIED|UNAUTHENTICATED|Invalid API key/i.test(ctx.message) ||
6123
+ statusCode === 401 ||
6124
+ statusCode === 403,
6125
+ errorClass: AuthenticationError,
6126
+ message: () => `Google Vertex AI Permission Denied. Your Google Cloud credentials don't have permission to access Vertex AI. ` +
6127
+ `Required Steps: 1. Ensure your service account has Vertex AI User role ` +
6128
+ `2. Check if Vertex AI API is enabled in your project ` +
6129
+ `3. Verify your project ID is correct ` +
6130
+ `4. Confirm your location/region has Vertex AI available`,
6131
+ },
6132
+ {
6133
+ match: (ctx) => /NOT_FOUND|model not found|Model not found/i.test(ctx.message) ||
6134
+ statusCode === 404,
6135
+ errorClass: InvalidModelError,
6136
+ message: () => {
6137
+ const modelSuggestions = this.getModelSuggestions(this.modelName);
6138
+ return (`Model '${this.modelName}' is not available in region ${this.location}. ` +
6139
+ `Suggested alternatives: ${modelSuggestions}. ` +
6140
+ `Troubleshooting: 1. Check model name spelling and format ` +
6141
+ `2. Verify model is available in your region ` +
6142
+ `3. Ensure your project has access to the model ` +
6143
+ `4. For Claude models, enable Anthropic integration in Google Cloud Console`);
6144
+ },
6145
+ },
6146
+ {
6147
+ // Rate limit / quota / capacity errors. Anthropic-on-Vertex capacity
6148
+ // exhaustion surfaces as overloaded_error (HTTP 529) same
6149
+ // operational meaning as a 429, so classify it here instead of the
6150
+ // generic 5xx branch below.
6151
+ match: (ctx) => /QUOTA_EXCEEDED|RATE_LIMIT_EXCEEDED|rate limit|429/i.test(ctx.message) ||
6152
+ statusCode === 429 ||
6153
+ statusCode === 529 ||
6154
+ /overloaded/i.test(ctx.message),
6155
+ errorClass: RateLimitError,
6156
+ message: (ctx) => {
6157
+ // Surface retry guidance when the SDK error carries it.
6158
+ // @google/genai ApiError nests RetryInfo inside the JSON error
6159
+ // body's details array, so fall back to scraping retryDelay out
6160
+ // of the raw message.
6161
+ const retryDelay = typeof errorRecord?.retryDelay === "string"
6162
+ ? errorRecord.retryDelay
6163
+ : (/["']?retryDelay["']?\s*[:=]\s*["']?(\d+(?:\.\d+)?s)/.exec(ctx.message)?.[1] ?? undefined);
6164
+ // Prefer the per-request context the native catches attach to the
6165
+ // error (this.modelName can be stale when options.model overrides
6166
+ // the instance default). Gemini models are force-routed to the
6167
+ // "global" endpoint regardless of configured location — report
6168
+ // the region the request actually hit.
6169
+ const requestModel = typeof errorRecord?.requestModel === "string"
6170
+ ? errorRecord.requestModel
6171
+ : this.modelName;
6172
+ const effectiveRegion = typeof errorRecord?.requestRegion === "string"
6173
+ ? errorRecord.requestRegion
6174
+ : resolveVertexRegionForModel(requestModel, this.location);
6175
+ return (`Google Vertex AI rate limit / shared-capacity exhausted (429 RESOURCE_EXHAUSTED / overloaded) ` +
6176
+ `for model '${requestModel}' in region '${effectiveRegion}'.` +
6177
+ (retryDelay
6178
+ ? ` Upstream suggests retrying after ${retryDelay}.`
6179
+ : "") +
6180
+ ` Solutions: 1. Retry with backoff ` +
6181
+ `2. Check your Vertex AI quotas in Google Cloud Console (shared-capacity 429s can occur below quota) ` +
6182
+ `3. Try a different region or model ` +
6183
+ `4. Request provisioned throughput for sustained load`);
6184
+ },
6185
+ },
6186
+ {
6187
+ match: (ctx) => /ECONNRESET|ENOTFOUND|ETIMEDOUT|ECONNREFUSED|network|connection/i.test(ctx.message),
6188
+ errorClass: NetworkError,
6189
+ message: (ctx) => `Connection error: ${ctx.message}`,
6190
+ },
6191
+ {
6192
+ match: (ctx) => /500|502|503|504|server error|Internal Server Error|INTERNAL|UNAVAILABLE/i.test(ctx.message) ||
6193
+ (statusCode !== undefined && statusCode >= 500 && statusCode < 600),
6194
+ errorClass: ProviderError,
6195
+ message: (ctx) => `Google Vertex AI server error: ${ctx.message}. Please try again later.`,
6196
+ },
6197
+ {
6198
+ match: (ctx) => /INVALID_ARGUMENT/i.test(ctx.message),
6199
+ errorClass: ProviderError,
6200
+ message: (ctx) => `Google Vertex AI Invalid Request: ${ctx.message}. ` +
6201
+ `Check: 1. Request parameters are within model limits ` +
6202
+ `2. Input text is properly formatted ` +
6203
+ `3. Temperature and other settings are valid ` +
6204
+ `4. Model supports your request type`,
6205
+ },
6206
+ {
6207
+ match: () => true,
6208
+ errorClass: ProviderError,
6209
+ message: (ctx) => `Google Vertex AI error: ${ctx.message}`,
6210
+ },
6211
+ ];
6212
+ return classifyProviderError(error, rules, this.providerName, this.modelName);
6205
6213
  }
6206
6214
  /**
6207
6215
  * Memory-safe cache management for model configurations
@@ -1,7 +1,8 @@
1
1
  import { GroqModels } from "../constants/enums.js";
2
- import { AuthenticationError, InvalidModelError, ProviderError, RateLimitError, } from "../types/index.js";
2
+ import { AuthenticationError, InvalidModelError, ProviderError, } from "../types/index.js";
3
3
  import { logger } from "../utils/logger.js";
4
4
  import { redactUrlCredentials } from "../utils/logSanitize.js";
5
+ import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../utils/errorClassifier.js";
5
6
  import { createGroqConfig, getProviderModel, validateApiKey, } from "../utils/providerConfig.js";
6
7
  import { TimeoutError } from "../utils/timeout.js";
7
8
  import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
@@ -57,30 +58,26 @@ export class GroqProvider extends OpenAIChatCompletionsProvider {
57
58
  ];
58
59
  }
59
60
  formatProviderError(error) {
61
+ // Groq's TimeoutError maps to plain ProviderError (not NetworkError, the
62
+ // classifier's built-in default) — intercept before delegating.
60
63
  if (error instanceof TimeoutError) {
61
64
  return new ProviderError(`Groq request timed out: ${error.message}`, "groq");
62
65
  }
63
- const errorRecord = error;
64
- const message = typeof errorRecord?.message === "string"
65
- ? errorRecord.message
66
- : "Unknown error";
67
- if (message.includes("Invalid API key") ||
68
- message.includes("Authentication") ||
69
- message.includes("401") ||
70
- message.includes("invalid_api_key")) {
71
- return new AuthenticationError("Invalid Groq API key. Check GROQ_API_KEY. Get one at https://console.groq.com/keys", "groq");
72
- }
73
- if (message.includes("rate limit") || message.includes("429")) {
74
- return new RateLimitError("Groq rate limit exceeded. Free tier limits are tight; consider upgrading or backing off.", "groq");
75
- }
76
- if (message.includes("model_not_found") ||
77
- message.includes("404") ||
78
- message.includes("model_decommissioned")) {
79
- return new InvalidModelError(message.includes("model_decommissioned")
80
- ? `Groq model '${this.modelName}' was decommissioned. Pick a current model from https://console.groq.com/docs/models.`
81
- : `Groq model '${this.modelName}' not found. See https://console.groq.com/docs/models for the current catalog.`, "groq");
82
- }
83
- return new ProviderError(`Groq error: ${message}`, "groq");
66
+ const rules = [
67
+ {
68
+ match: (ctx) => ctx.statusCode === 401 ||
69
+ /Invalid API key|Authentication|invalid_api_key/i.test(ctx.message),
70
+ errorClass: AuthenticationError,
71
+ message: "Invalid Groq API key. Check GROQ_API_KEY. Get one at https://console.groq.com/keys",
72
+ },
73
+ {
74
+ match: (ctx) => /model_decommissioned/i.test(ctx.message),
75
+ errorClass: InvalidModelError,
76
+ message: (ctx) => `Groq model '${ctx.modelName}' was decommissioned. Pick a current model from https://console.groq.com/docs/models.`,
77
+ },
78
+ ...DEFAULT_ERROR_RULES,
79
+ ];
80
+ return classifyProviderError(error, rules, "groq", this.modelName);
84
81
  }
85
82
  }
86
83
  //# sourceMappingURL=groq.js.map
@@ -1,8 +1,8 @@
1
- import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../../types/index.js";
1
+ import { AuthenticationError, InvalidModelError, ProviderError, RateLimitError, } from "../../types/index.js";
2
+ import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../../utils/errorClassifier.js";
2
3
  import { logger } from "../../utils/logger.js";
3
4
  import { redactUrlCredentials } from "../../utils/logSanitize.js";
4
5
  import { createHuggingFaceConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
5
- import { TimeoutError } from "../../utils/timeout.js";
6
6
  import { OpenAIChatCompletionsProvider } from "../openaiChatCompletionsBase.js";
7
7
  const HUGGINGFACE_DEFAULT_BASE_URL = "https://router.huggingface.co/v1";
8
8
  const getHuggingFaceApiKey = () => validateApiKey(createHuggingFaceConfig());
@@ -74,28 +74,30 @@ export class HuggingFaceProvider extends OpenAIChatCompletionsProvider {
74
74
  return toolCapableModels.some((capable) => modelName.includes(capable));
75
75
  }
76
76
  formatProviderError(error) {
77
- if (error instanceof TimeoutError) {
78
- return new NetworkError(`Request timed out: ${error.message}`, "huggingface");
79
- }
80
- const errorObj = error;
81
- const message = errorObj?.message && typeof errorObj.message === "string"
82
- ? errorObj.message
83
- : "Unknown error";
84
- // Enhanced error messages with tool calling context
85
- if (message.includes("API_TOKEN_INVALID") ||
86
- message.includes("Invalid token")) {
87
- return new AuthenticationError("Invalid HuggingFace API token. Please check your HUGGINGFACE_API_KEY environment variable.", "huggingface");
88
- }
89
- if (message.includes("rate limit")) {
90
- return new RateLimitError("HuggingFace rate limit exceeded. Consider using a paid plan or try again later.", "huggingface");
91
- }
92
- if (message.includes("model") && message.includes("not found")) {
93
- return new InvalidModelError(`HuggingFace model '${this.modelName}' not found.\n\nSuggestions:\n1. Check model name spelling\n2. Ensure model exists on HuggingFace Hub\n3. For tool calling, use: Llama-3.1-8B-Instruct, Hermes-3-Llama-3.2-3B, or CodeLlama-34b-Instruct-hf`, "huggingface");
94
- }
95
- if (message.includes("function") || message.includes("tool")) {
96
- return new ProviderError(`HuggingFace tool calling error: ${message}\n\nNotes:\n1. Ensure you're using a tool-capable model (Llama-3.1+, Hermes-3+, CodeLlama)\n2. Check that your model supports function calling\n3. Verify tool schema format is correct`, "huggingface");
97
- }
98
- return new ProviderError(`HuggingFace Provider Error: ${message}`, "huggingface");
77
+ const rules = [
78
+ {
79
+ match: (ctx) => /API_TOKEN_INVALID|Invalid token/.test(ctx.message),
80
+ errorClass: AuthenticationError,
81
+ message: "Invalid HuggingFace API token. Please check your HUGGINGFACE_API_KEY environment variable.",
82
+ },
83
+ {
84
+ match: (ctx) => /rate limit/.test(ctx.message),
85
+ errorClass: RateLimitError,
86
+ message: "HuggingFace rate limit exceeded. Consider using a paid plan or try again later.",
87
+ },
88
+ {
89
+ match: (ctx) => /model/.test(ctx.message) && /not found/.test(ctx.message),
90
+ errorClass: InvalidModelError,
91
+ message: () => `HuggingFace model '${this.modelName}' not found.\n\nSuggestions:\n1. Check model name spelling\n2. Ensure model exists on HuggingFace Hub\n3. For tool calling, use: Llama-3.1-8B-Instruct, Hermes-3-Llama-3.2-3B, or CodeLlama-34b-Instruct-hf`,
92
+ },
93
+ {
94
+ match: (ctx) => /function|tool/.test(ctx.message),
95
+ errorClass: ProviderError,
96
+ message: (ctx) => `HuggingFace tool calling error: ${ctx.message}\n\nNotes:\n1. Ensure you're using a tool-capable model (Llama-3.1+, Hermes-3+, CodeLlama)\n2. Check that your model supports function calling\n3. Verify tool schema format is correct`,
97
+ },
98
+ ...DEFAULT_ERROR_RULES,
99
+ ];
100
+ return classifyProviderError(error, rules, "huggingface", this.modelName);
99
101
  }
100
102
  }
101
103
  //# sourceMappingURL=client.js.map