@juspay/neurolink 12.14.8 → 12.14.10

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 (35) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/dist/adapters/providerImageAdapter.js +1 -0
  3. package/dist/browser/neurolink.min.js +364 -364
  4. package/dist/cli/commands/config.js +2 -2
  5. package/dist/cli/commands/setup-huggingface.js +19 -11
  6. package/dist/core/modelConfiguration.d.ts +3 -3
  7. package/dist/core/modelConfiguration.js +6 -4
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +11 -0
  10. package/dist/mcp/servers/aiProviders/aiAnalysisTools.js +43 -30
  11. package/dist/models/manifests/huggingface.d.ts +6 -3
  12. package/dist/models/manifests/huggingface.js +7 -4
  13. package/dist/neurolink.d.ts +4 -1
  14. package/dist/neurolink.js +41 -6
  15. package/dist/providers/azureOpenai.js +1 -1
  16. package/dist/providers/catalog/loader.d.ts +1 -1
  17. package/dist/providers/catalog/loader.js +30 -1
  18. package/dist/providers/cohere.js +1 -1
  19. package/dist/providers/deepseek.js +1 -1
  20. package/dist/providers/googleAiStudio/client.js +6 -5
  21. package/dist/providers/googleVertex/client.d.ts +11 -30
  22. package/dist/providers/googleVertex/client.js +49 -131
  23. package/dist/providers/huggingFace/client.d.ts +30 -7
  24. package/dist/providers/huggingFace/client.js +45 -27
  25. package/dist/providers/llamaCpp.js +1 -1
  26. package/dist/providers/lmStudio.js +1 -1
  27. package/dist/providers/nvidiaNim/client.js +1 -1
  28. package/dist/providers/ollama/client.js +1 -1
  29. package/dist/providers/openRouter/client.js +1 -1
  30. package/dist/providers/openaiCompatible/client.js +1 -1
  31. package/dist/utils/providerSetupMessages.js +2 -2
  32. package/dist/utils/timeout.d.ts +13 -2
  33. package/dist/utils/timeout.js +14 -3
  34. package/docs-site/static/search-index.json +1 -1
  35. package/package.json +1 -1
@@ -977,9 +977,13 @@ export class GoogleVertexProvider extends BaseProvider {
977
977
  async createVertexGenAIClient(regionOverride) {
978
978
  warnGoogleSdkIgnoresProxy("GoogleVertex");
979
979
  const expressApiKey = this.resolveExpressApiKey();
980
- // Resolved only on the ADC path: getVertexProjectId() throws when no
981
- // project is configured, which an Express request legitimately has none of.
982
- const project = expressApiKey ? "" : getVertexProjectId();
980
+ // Resolved only on the ADC path, and from the per-instance projectId the
981
+ // constructor already settled (credentials.projectId if supplied, else
982
+ // the same ambient getVertexProjectId() fallback) — not re-read from
983
+ // ambient env here, or a per-request projectId override would be built
984
+ // into the client with the wrong project while call sites elsewhere
985
+ // (generateContent's `project:` param) correctly used this.projectId.
986
+ const project = expressApiKey ? "" : this.projectId;
983
987
  const location = regionOverride || this.location || getVertexLocation();
984
988
  const mod = await import("@google/genai");
985
989
  const ctor = mod.GoogleGenAI;
@@ -1060,94 +1064,6 @@ export class GoogleVertexProvider extends BaseProvider {
1060
1064
  ].some((name) => (process.env[name] ?? "").trim().length > 0);
1061
1065
  return projectConfigured ? undefined : fromEnv;
1062
1066
  }
1063
- /**
1064
- * Convert one AI-SDK tool into a Vertex Gemini function declaration.
1065
- * Single source for the pre-loop snapshot AND the mid-turn discovery
1066
- * refresh, so tools hydrated by search_tools get the exact same schema
1067
- * treatment (inline, typed, additionalProperties stripped).
1068
- */
1069
- buildGeminiFunctionDeclaration(name, tool) {
1070
- const decl = {
1071
- name,
1072
- description: tool.description || `Tool: ${name}`,
1073
- };
1074
- // Access legacy `parameters` (AI SDK v3/v4) or current `inputSchema` (v6)
1075
- const legacyTool = tool;
1076
- const toolParams = legacyTool.parameters || tool.inputSchema;
1077
- if (toolParams) {
1078
- // Convert and inline schema to resolve $ref/definitions
1079
- const rawSchema = convertZodToJsonSchema(toolParams, "openApi3");
1080
- const inlinedSchema = inlineJsonSchema(rawSchema);
1081
- // Remove $schema if present - @google/genai doesn't need it
1082
- if (inlinedSchema.$schema) {
1083
- delete inlinedSchema.$schema;
1084
- }
1085
- // CRITICAL: Google Vertex AI requires ALL nested schemas to have a type field
1086
- // ensureNestedSchemaTypes recursively adds missing type fields to tool schemas
1087
- // Note: convertZodToJsonSchema now uses openApi3 target which produces nullable: true
1088
- const typedSchema = ensureNestedSchemaTypes(inlinedSchema);
1089
- // Strip `additionalProperties` recursively — Vertex Gemini's
1090
- // function-call validator rejects it on object schemas (returns
1091
- // 400 INVALID_ARGUMENT) even though it's valid OpenAPI 3. The
1092
- // field has no semantic meaning to the model, so dropping it
1093
- // before send is safe for every caller.
1094
- stripAdditionalPropertiesDeep(typedSchema);
1095
- decl.parametersJsonSchema = typedSchema;
1096
- }
1097
- return decl;
1098
- }
1099
- /**
1100
- * Mid-turn tool sync for the native Gemini loops. `search_tools` hydrates
1101
- * discovered tools into the live `options.tools` record between steps, but
1102
- * the loop's declarations + executeMap are a pre-loop snapshot — without
1103
- * this refresh, a tool discovered this turn stays invisible to the rest of
1104
- * the turn and every call to it dies as TOOL_NOT_FOUND. Mutates the
1105
- * declaration array in place (the request config holds it by reference)
1106
- * and clears breaker strikes accrued while the tool was still deferred.
1107
- */
1108
- refreshGeminiToolDeclarations(liveTools, declarations, executeMap, failedTools) {
1109
- if (!liveTools || !declarations) {
1110
- return;
1111
- }
1112
- const declared = new Set(declarations.map((d) => d.name));
1113
- for (const [name, tool] of Object.entries(liveTools)) {
1114
- if (declared.has(name)) {
1115
- continue;
1116
- }
1117
- declarations.push(this.buildGeminiFunctionDeclaration(name, tool));
1118
- if (tool.execute) {
1119
- executeMap.set(name, tool.execute);
1120
- }
1121
- failedTools.delete(name);
1122
- logger.info(`[GoogleVertex] Tool "${name}" hydrated mid-turn via discovery — added to Gemini declarations.`);
1123
- }
1124
- }
1125
- /**
1126
- * Dispatch-miss recovery for the native Gemini loops: the model called a
1127
- * name missing from the executeMap snapshot. Re-read the live record (a
1128
- * tool hydrated by search_tools in this very step batch) or auto-hydrate a
1129
- * deferred catalog tool the model called directly by its advertised name.
1130
- * Returns the dedup-wrapped executor, or undefined when the name is
1131
- * genuinely unknown — callers keep TOOL_NOT_FOUND for that case.
1132
- */
1133
- resolveGeminiToolOnMiss(name, liveTools, declarations, executeMap, failedTools) {
1134
- if (!declarations) {
1135
- return undefined;
1136
- }
1137
- const tool = resolveLiveTool(liveTools, name);
1138
- if (!tool?.execute) {
1139
- return undefined;
1140
- }
1141
- if (!declarations.some((d) => d.name === name)) {
1142
- declarations.push(this.buildGeminiFunctionDeclaration(name, tool));
1143
- }
1144
- executeMap.set(name, tool.execute);
1145
- // NOT_FOUND strikes accrued while the tool was deferred are snapshot
1146
- // artifacts, not real failures — reset so the breaker starts clean.
1147
- failedTools.delete(name);
1148
- logger.info(`[GoogleVertex] Tool "${name}" resolved mid-turn via discovery — executing.`);
1149
- return executeMap.get(name);
1150
- }
1151
1067
  /**
1152
1068
  * Convert one AI-SDK tool into an Anthropic (Claude-on-Vertex) tool
1153
1069
  * declaration. Single source for the pre-loop snapshot AND the mid-turn
@@ -1179,8 +1095,7 @@ export class GoogleVertexProvider extends BaseProvider {
1179
1095
  return anthropicTool;
1180
1096
  }
1181
1097
  /**
1182
- * Mid-turn tool sync for the Claude-on-Vertex loops — the Anthropic
1183
- * counterpart of refreshGeminiToolDeclarations. Claude only calls tools
1098
+ * Mid-turn tool sync for the Claude-on-Vertex loops. Claude only calls tools
1184
1099
  * present in the request's `tools` array, so without this per-step refresh
1185
1100
  * a tool discovered via search_tools is unreachable for the rest of the
1186
1101
  * turn. Mutates the array in place (requestParams holds it by reference).
@@ -1203,8 +1118,7 @@ export class GoogleVertexProvider extends BaseProvider {
1203
1118
  }
1204
1119
  }
1205
1120
  /**
1206
- * Dispatch-miss recovery for the Claude-on-Vertex loops — the Anthropic
1207
- * counterpart of resolveGeminiToolOnMiss.
1121
+ * Dispatch-miss recovery for the Claude-on-Vertex loops.
1208
1122
  */
1209
1123
  resolveAnthropicToolOnMiss(name, liveTools, declarations, executeMap, failedTools) {
1210
1124
  if (!declarations) {
@@ -1222,6 +1136,31 @@ export class GoogleVertexProvider extends BaseProvider {
1222
1136
  logger.info(`[GoogleVertex] Tool "${name}" resolved mid-turn via discovery — executing.`);
1223
1137
  return executeMap.get(name);
1224
1138
  }
1139
+ /**
1140
+ * Build the synthetic `final_result` tool used to force structured output
1141
+ * on Claude-on-Vertex. Anthropic has no native responseSchema, so a schema
1142
+ * is instead enforced by requiring the model to call this tool with
1143
+ * matching arguments. Single source for the non-streaming and streaming
1144
+ * paths — both previously built this object independently from their own
1145
+ * schema variable, byte-identical apart from that variable's name.
1146
+ */
1147
+ buildFinalResultTool(schema) {
1148
+ const schemaAsJson = convertZodToJsonSchema(schema);
1149
+ const inlinedSchema = inlineJsonSchema(schemaAsJson);
1150
+ if (inlinedSchema.$schema) {
1151
+ delete inlinedSchema.$schema;
1152
+ }
1153
+ const typedSchema = ensureNestedSchemaTypes(inlinedSchema);
1154
+ return {
1155
+ name: "final_result",
1156
+ description: "Return the final structured result. You MUST call this tool when you have gathered all information and are ready to provide the final answer. The arguments should contain the structured data matching the expected schema.",
1157
+ input_schema: {
1158
+ type: "object",
1159
+ properties: typedSchema.properties || typedSchema,
1160
+ required: typedSchema.required || [],
1161
+ },
1162
+ };
1163
+ }
1225
1164
  /**
1226
1165
  * Execute stream using native @google/genai SDK for Gemini 3 models on Vertex AI
1227
1166
  * This bypasses @ai-sdk/google-vertex to properly handle thought_signature
@@ -3132,23 +3071,8 @@ export class GoogleVertexProvider extends BaseProvider {
3132
3071
  let schemaSystemPromptSuffix = "";
3133
3072
  if (streamOptions.schema) {
3134
3073
  useFinalResultTool = true;
3135
- // Convert schema to JSON schema format
3136
- const schemaAsJson = convertZodToJsonSchema(streamOptions.schema);
3137
- const inlinedSchema = inlineJsonSchema(schemaAsJson);
3138
- if (inlinedSchema.$schema) {
3139
- delete inlinedSchema.$schema;
3140
- }
3141
- const typedSchema = ensureNestedSchemaTypes(inlinedSchema);
3142
3074
  // Create final_result tool
3143
- const finalResultTool = {
3144
- name: "final_result",
3145
- description: "Return the final structured result. You MUST call this tool when you have gathered all information and are ready to provide the final answer. The arguments should contain the structured data matching the expected schema.",
3146
- input_schema: {
3147
- type: "object",
3148
- properties: typedSchema.properties || typedSchema,
3149
- required: typedSchema.required || [],
3150
- },
3151
- };
3075
+ const finalResultTool = this.buildFinalResultTool(streamOptions.schema);
3152
3076
  // Add to tools array or create new array
3153
3077
  if (!tools) {
3154
3078
  tools = [];
@@ -3158,7 +3082,7 @@ export class GoogleVertexProvider extends BaseProvider {
3158
3082
  schemaSystemPromptSuffix =
3159
3083
  "\n\nIMPORTANT: You MUST call the 'final_result' tool to return your response in the required structured format. Do not respond with plain text - always use the final_result tool.";
3160
3084
  logger.debug("[GoogleVertex] Added final_result tool for Anthropic structured output (stream)", {
3161
- schemaKeys: Object.keys(typedSchema),
3085
+ schemaKeys: Object.keys(finalResultTool.input_schema.properties ?? {}),
3162
3086
  totalTools: tools.length,
3163
3087
  });
3164
3088
  }
@@ -3719,10 +3643,19 @@ export class GoogleVertexProvider extends BaseProvider {
3719
3643
  // with text in step 2 would look unfinished and fall into terminal
3720
3644
  // handling. The finish reason is the per-turn signal — the engine
3721
3645
  // reports "tool-calls" only when the cap was hit with tools still
3722
- // pending.
3646
+ // pending. That signal is not enough on its own, though: Anthropic
3647
+ // reports stop_reason "tool_use" — which maps to finishReason
3648
+ // "tool-calls" — for ANY tool_use block, including a genuine
3649
+ // terminal `final_result` call. Without the third clause, a turn
3650
+ // that called a real tool earlier (toolCalls.length > 0) and then
3651
+ // legitimately finished via final_result reads as unfinished and
3652
+ // falls into the terminal-handling block below, which forces a
3653
+ // redundant second final_result call and pushes its JSON onto the
3654
+ // stream a second time.
3723
3655
  modelFinished =
3724
3656
  engineResult.toolCalls.length === 0 ||
3725
- engineResult.finishReason !== "tool-calls";
3657
+ engineResult.finishReason !== "tool-calls" ||
3658
+ (useFinalResultTool && structuredOutputRef.value !== undefined);
3726
3659
  // Replace in place: the terminal block and the finalization call both
3727
3660
  // read `currentMessages`.
3728
3661
  currentMessages.length = 0;
@@ -4361,23 +4294,8 @@ export class GoogleVertexProvider extends BaseProvider {
4361
4294
  let schemaSystemPromptSuffix = "";
4362
4295
  if (options.schema) {
4363
4296
  useFinalResultTool = true;
4364
- // Convert schema to JSON schema format
4365
- const schemaAsJson = convertZodToJsonSchema(options.schema);
4366
- const inlinedSchema = inlineJsonSchema(schemaAsJson);
4367
- if (inlinedSchema.$schema) {
4368
- delete inlinedSchema.$schema;
4369
- }
4370
- const typedSchema = ensureNestedSchemaTypes(inlinedSchema);
4371
4297
  // Create final_result tool
4372
- const finalResultTool = {
4373
- name: "final_result",
4374
- description: "Return the final structured result. You MUST call this tool when you have gathered all information and are ready to provide the final answer. The arguments should contain the structured data matching the expected schema.",
4375
- input_schema: {
4376
- type: "object",
4377
- properties: typedSchema.properties || typedSchema,
4378
- required: typedSchema.required || [],
4379
- },
4380
- };
4298
+ const finalResultTool = this.buildFinalResultTool(options.schema);
4381
4299
  // Add to tools array or create new array
4382
4300
  if (!tools) {
4383
4301
  tools = [];
@@ -4387,7 +4305,7 @@ export class GoogleVertexProvider extends BaseProvider {
4387
4305
  schemaSystemPromptSuffix =
4388
4306
  "\n\nIMPORTANT: You MUST call the 'final_result' tool to return your response in the required structured format. Do not respond with plain text - always use the final_result tool.";
4389
4307
  logger.debug("[GoogleVertex] Added final_result tool for Anthropic structured output (generate)", {
4390
- schemaKeys: Object.keys(typedSchema),
4308
+ schemaKeys: Object.keys(finalResultTool.input_schema.properties ?? {}),
4391
4309
  totalTools: tools.length,
4392
4310
  });
4393
4311
  }
@@ -19,13 +19,36 @@ export declare class HuggingFaceProvider extends OpenAIChatCompletionsProvider {
19
19
  protected getDefaultModel(): string;
20
20
  protected getFallbackModelName(): string;
21
21
  /**
22
- * HuggingFace serves a huge variety of models, many of which reject the
23
- * OpenAI `tools` field. The base reports supportsTools() === true
24
- * unconditionally, which would merge tools into requests for models
25
- * (including the default DialoGPT-medium) that don't accept them. Preserve
26
- * the pre-migration allowlist: only known tool-calling-capable model
27
- * families opt in; everything else runs tool-free.
22
+ * Tool support is resolved by the base through MODEL_REGISTRY's
23
+ * `modelSupports()` facade, which defaults unknown models to supported.
24
+ *
25
+ * This class used to override that with a 13-entry allowlist of model-name
26
+ * substrings, written for the old per-model Inference API where many
27
+ * endpoints rejected the OpenAI `tools` field. The router replaced that, and
28
+ * the list did not keep up. Measured against the 142 models
29
+ * `GET router.huggingface.co/v1/models` served on 2026-09-13 (the
30
+ * catalogue drifts, so treat the totals as a snapshot; the ratio is the
31
+ * point):
32
+ *
33
+ * allowlist entries matching ANY served model 1 of 13
34
+ * served models the allowlist ADMITS 1 of 142
35
+ * served models the allowlist BLOCKS 141
36
+ *
37
+ * So it suppressed tool calling for 99% of the catalogue, including
38
+ * Qwen/Qwen2.5-72B-Instruct, which the router demonstrably tool-calls. A
39
+ * spread of the blocked models — GLM-5.3-Flash, gemma-4-31B-it,
40
+ * DeepSeek-V4-Flash — all accept `tools` with HTTP 200 and return
41
+ * `tool_calls`; none rejected the field.
42
+ *
43
+ * The allowlist's own comment named `microsoft/DialoGPT-medium` — which
44
+ * `getDefaultModel()`, not `getFallbackModelName()`, used to return — as a
45
+ * model that rejects `tools`. The router does not serve it under any
46
+ * request shape, so that path was never reachable; the default now points
47
+ * at a served model (see `getDefaultHuggingFaceModel`).
48
+ *
49
+ * A model that genuinely cannot use tools simply does not emit `tool_calls`,
50
+ * which the loop already handles, so the optimistic default costs nothing
51
+ * that the allowlist was protecting.
28
52
  */
29
- supportsTools(): boolean;
30
53
  protected formatProviderError(error: unknown): Error;
31
54
  }
@@ -1,3 +1,4 @@
1
+ import { HuggingFaceModels } from "../../constants/enums.js";
1
2
  import { AuthenticationError, InvalidModelError, ProviderError, RateLimitError, } from "../../types/index.js";
2
3
  import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../../utils/errorClassifier.js";
3
4
  import { logger } from "../../utils/logger.js";
@@ -6,7 +7,19 @@ import { createHuggingFaceConfig, getProviderModel, validateApiKey, } from "../.
6
7
  import { OpenAIChatCompletionsProvider } from "../openaiChatCompletionsBase.js";
7
8
  const HUGGINGFACE_DEFAULT_BASE_URL = "https://router.huggingface.co/v1";
8
9
  const getHuggingFaceApiKey = () => validateApiKey(createHuggingFaceConfig());
9
- const getDefaultHuggingFaceModel = () => getProviderModel("HUGGINGFACE_MODEL", "microsoft/DialoGPT-medium");
10
+ /**
11
+ * Must stay the same id `providerRegistry` passes as the registration default
12
+ * (`process.env.HUGGINGFACE_MODEL || HuggingFaceModels.QWEN_2_5_72B_INSTRUCT`),
13
+ * hence the shared constant rather than a second literal.
14
+ *
15
+ * The registry supplies `modelName` on every factory-created provider, so this
16
+ * only decides a directly-constructed `new HuggingFaceProvider()`. It still
17
+ * matters: it previously read `microsoft/DialoGPT-medium`, which the router
18
+ * does not serve under any request shape — it answers 400 "not supported by
19
+ * any provider you have enabled" with or without `tools` — and that stale id
20
+ * was what the docs and setup guidance advertised as the default.
21
+ */
22
+ const getDefaultHuggingFaceModel = () => getProviderModel("HUGGINGFACE_MODEL", HuggingFaceModels.QWEN_2_5_72B_INSTRUCT);
10
23
  /**
11
24
  * HuggingFace Provider — direct HTTP, no AI SDK.
12
25
  *
@@ -47,32 +60,37 @@ export class HuggingFaceProvider extends OpenAIChatCompletionsProvider {
47
60
  return "meta-llama/Llama-3.1-8B-Instruct";
48
61
  }
49
62
  /**
50
- * HuggingFace serves a huge variety of models, many of which reject the
51
- * OpenAI `tools` field. The base reports supportsTools() === true
52
- * unconditionally, which would merge tools into requests for models
53
- * (including the default DialoGPT-medium) that don't accept them. Preserve
54
- * the pre-migration allowlist: only known tool-calling-capable model
55
- * families opt in; everything else runs tool-free.
63
+ * Tool support is resolved by the base through MODEL_REGISTRY's
64
+ * `modelSupports()` facade, which defaults unknown models to supported.
65
+ *
66
+ * This class used to override that with a 13-entry allowlist of model-name
67
+ * substrings, written for the old per-model Inference API where many
68
+ * endpoints rejected the OpenAI `tools` field. The router replaced that, and
69
+ * the list did not keep up. Measured against the 142 models
70
+ * `GET router.huggingface.co/v1/models` served on 2026-09-13 (the
71
+ * catalogue drifts, so treat the totals as a snapshot; the ratio is the
72
+ * point):
73
+ *
74
+ * allowlist entries matching ANY served model 1 of 13
75
+ * served models the allowlist ADMITS 1 of 142
76
+ * served models the allowlist BLOCKS 141
77
+ *
78
+ * So it suppressed tool calling for 99% of the catalogue, including
79
+ * Qwen/Qwen2.5-72B-Instruct, which the router demonstrably tool-calls. A
80
+ * spread of the blocked models — GLM-5.3-Flash, gemma-4-31B-it,
81
+ * DeepSeek-V4-Flash — all accept `tools` with HTTP 200 and return
82
+ * `tool_calls`; none rejected the field.
83
+ *
84
+ * The allowlist's own comment named `microsoft/DialoGPT-medium` — which
85
+ * `getDefaultModel()`, not `getFallbackModelName()`, used to return — as a
86
+ * model that rejects `tools`. The router does not serve it under any
87
+ * request shape, so that path was never reachable; the default now points
88
+ * at a served model (see `getDefaultHuggingFaceModel`).
89
+ *
90
+ * A model that genuinely cannot use tools simply does not emit `tool_calls`,
91
+ * which the loop already handles, so the optimistic default costs nothing
92
+ * that the allowlist was protecting.
56
93
  */
57
- supportsTools() {
58
- const modelName = this.modelName.toLowerCase();
59
- const toolCapableModels = [
60
- "llama-3.1-8b-instruct",
61
- "llama-3.1-70b-instruct",
62
- "llama-3.1-405b-instruct",
63
- "llama-3.1-nemotron-ultra",
64
- "hermes-3-llama-3.2",
65
- "hermes-2-pro",
66
- "codellama-34b-instruct",
67
- "codellama-13b-instruct",
68
- "mistral-7b-instruct-v0.3",
69
- "mistral-8x7b-instruct",
70
- "nous-hermes",
71
- "openchat",
72
- "wizardcoder",
73
- ];
74
- return toolCapableModels.some((capable) => modelName.includes(capable));
75
- }
76
94
  formatProviderError(error) {
77
95
  const rules = [
78
96
  {
@@ -97,6 +115,6 @@ export class HuggingFaceProvider extends OpenAIChatCompletionsProvider {
97
115
  },
98
116
  ...DEFAULT_ERROR_RULES,
99
117
  ];
100
- return classifyProviderError(error, rules, "huggingface", this.modelName);
118
+ return classifyProviderError(error, rules, this.providerName, this.modelName);
101
119
  }
102
120
  }
@@ -74,6 +74,6 @@ export class LlamaCppProvider extends OpenAIChatCompletionsProvider {
74
74
  },
75
75
  ...DEFAULT_ERROR_RULES,
76
76
  ];
77
- return classifyProviderError(error, rules, "llamacpp", this.modelName);
77
+ return classifyProviderError(error, rules, this.providerName, this.modelName);
78
78
  }
79
79
  }
@@ -69,7 +69,7 @@ export class LMStudioProvider extends OpenAIChatCompletionsProvider {
69
69
  },
70
70
  ...DEFAULT_ERROR_RULES,
71
71
  ];
72
- return classifyProviderError(error, rules, "lm-studio", this.modelName);
72
+ return classifyProviderError(error, rules, this.providerName, this.modelName);
73
73
  }
74
74
  async validateConfiguration() {
75
75
  return this.probeModelsEndpoint(this.config.apiKey && this.config.apiKey !== LM_STUDIO_PLACEHOLDER_KEY
@@ -281,7 +281,7 @@ export class NvidiaNimProvider extends OpenAIChatCompletionsProvider {
281
281
  },
282
282
  ...DEFAULT_ERROR_RULES,
283
283
  ];
284
- return classifyProviderError(error, rules, "nvidia-nim", this.modelName);
284
+ return classifyProviderError(error, rules, this.providerName, this.modelName);
285
285
  }
286
286
  async validateConfiguration() {
287
287
  return (typeof this.config.apiKey === "string" &&
@@ -136,7 +136,7 @@ export class OllamaProvider extends OpenAIChatCompletionsProvider {
136
136
  },
137
137
  ...DEFAULT_ERROR_RULES,
138
138
  ];
139
- return classifyProviderError(error, rules, "ollama", this.modelName);
139
+ return classifyProviderError(error, rules, this.providerName, this.modelName);
140
140
  }
141
141
  // ===========================================================================
142
142
  // Optional hooks — Ollama-specific behaviour
@@ -149,7 +149,7 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
149
149
  },
150
150
  ...DEFAULT_ERROR_RULES,
151
151
  ];
152
- return classifyProviderError(error, rules, "openrouter", this.modelName);
152
+ return classifyProviderError(error, rules, this.providerName, this.modelName);
153
153
  }
154
154
  // ===========================================================================
155
155
  // Optional hooks — provider-specific quirks
@@ -85,6 +85,6 @@ export class OpenAICompatibleProvider extends OpenAIChatCompletionsProvider {
85
85
  },
86
86
  ...DEFAULT_ERROR_RULES,
87
87
  ];
88
- return classifyProviderError(error, rules, "openai-compatible", this.modelName);
88
+ return classifyProviderError(error, rules, this.providerName, this.modelName);
89
89
  }
90
90
  }
@@ -71,8 +71,8 @@ export function getProviderSetupMessage(provider, missingVars) {
71
71
  envVars: [
72
72
  'HUGGINGFACE_API_KEY="hf_your_huggingface_token"',
73
73
  "# Optional:",
74
- 'HUGGINGFACE_MODEL="microsoft/DialoGPT-medium"',
75
- 'HUGGINGFACE_ENDPOINT="https://api-inference.huggingface.co"',
74
+ 'HUGGINGFACE_MODEL="Qwen/Qwen2.5-72B-Instruct"',
75
+ 'HUGGINGFACE_BASE_URL="https://router.huggingface.co/v1"',
76
76
  ],
77
77
  },
78
78
  mistral: {
@@ -5,10 +5,21 @@
5
5
  * Supports multiple time formats: milliseconds, seconds, minutes, hours.
6
6
  */
7
7
  import type { TimeoutConfig, TimeoutResult } from "../types/index.js";
8
+ import { TimeoutError as BaseTimeoutError } from "./async/withTimeout.js";
8
9
  /**
9
- * Custom error class for timeout operations
10
+ * Custom error class for timeout operations.
11
+ *
12
+ * Extends the base {@link BaseTimeoutError} (from `./async/withTimeout.js`)
13
+ * rather than `Error` directly, merging what were two independent internal
14
+ * `TimeoutError` classes onto one prototype chain — an `instanceof` check
15
+ * against either module's `TimeoutError` now recognizes errors thrown by
16
+ * this class. Constructor signature, `.name`, and the extra
17
+ * `timeout`/`provider`/`operation` fields are unchanged so all 15 existing
18
+ * `instanceof TimeoutError` call sites keep working exactly as before;
19
+ * `super(message, timeout)` additionally populates the base class's
20
+ * `timeoutMs` field (equal to `timeout`) as a byproduct.
10
21
  */
11
- export declare class TimeoutError extends Error {
22
+ export declare class TimeoutError extends BaseTimeoutError {
12
23
  readonly timeout: number;
13
24
  readonly provider?: string | undefined;
14
25
  readonly operation?: "generate" | "stream" | undefined;
@@ -4,15 +4,26 @@
4
4
  * Provides flexible timeout parsing and error handling for AI operations.
5
5
  * Supports multiple time formats: milliseconds, seconds, minutes, hours.
6
6
  */
7
+ import { TimeoutError as BaseTimeoutError } from "./async/withTimeout.js";
7
8
  /**
8
- * Custom error class for timeout operations
9
+ * Custom error class for timeout operations.
10
+ *
11
+ * Extends the base {@link BaseTimeoutError} (from `./async/withTimeout.js`)
12
+ * rather than `Error` directly, merging what were two independent internal
13
+ * `TimeoutError` classes onto one prototype chain — an `instanceof` check
14
+ * against either module's `TimeoutError` now recognizes errors thrown by
15
+ * this class. Constructor signature, `.name`, and the extra
16
+ * `timeout`/`provider`/`operation` fields are unchanged so all 15 existing
17
+ * `instanceof TimeoutError` call sites keep working exactly as before;
18
+ * `super(message, timeout)` additionally populates the base class's
19
+ * `timeoutMs` field (equal to `timeout`) as a byproduct.
9
20
  */
10
- export class TimeoutError extends Error {
21
+ export class TimeoutError extends BaseTimeoutError {
11
22
  timeout;
12
23
  provider;
13
24
  operation;
14
25
  constructor(message, timeout, provider, operation) {
15
- super(message);
26
+ super(message, timeout);
16
27
  this.timeout = timeout;
17
28
  this.provider = provider;
18
29
  this.operation = operation;