@juspay/neurolink 10.12.8 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/browser/neurolink.min.js +399 -401
  3. package/dist/cli/commands/setup.d.ts +16 -1
  4. package/dist/cli/commands/setup.js +141 -3
  5. package/dist/core/baseProvider.js +3 -3
  6. package/dist/core/dynamicModels.d.ts +1 -1
  7. package/dist/core/dynamicModels.js +1 -23
  8. package/dist/core/modelConfiguration.d.ts +1 -70
  9. package/dist/core/modelConfiguration.js +0 -303
  10. package/dist/core/modules/GenerationHandler.js +38 -16
  11. package/dist/core/modules/structuredOutputPolicy.d.ts +2 -2
  12. package/dist/core/modules/structuredOutputPolicy.js +2 -2
  13. package/dist/factories/providerFactory.d.ts +15 -0
  14. package/dist/factories/providerFactory.js +27 -39
  15. package/dist/factories/providerRegistry.js +2 -2
  16. package/dist/lib/core/baseProvider.js +3 -3
  17. package/dist/lib/core/dynamicModels.d.ts +1 -1
  18. package/dist/lib/core/dynamicModels.js +1 -23
  19. package/dist/lib/core/modelConfiguration.d.ts +1 -70
  20. package/dist/lib/core/modelConfiguration.js +0 -303
  21. package/dist/lib/core/modules/GenerationHandler.js +38 -16
  22. package/dist/lib/core/modules/structuredOutputPolicy.d.ts +2 -2
  23. package/dist/lib/core/modules/structuredOutputPolicy.js +2 -2
  24. package/dist/lib/factories/providerFactory.d.ts +15 -0
  25. package/dist/lib/factories/providerFactory.js +27 -39
  26. package/dist/lib/factories/providerRegistry.js +2 -2
  27. package/dist/lib/models/anthropicModels.d.ts +0 -7
  28. package/dist/lib/models/anthropicModels.js +0 -9
  29. package/dist/lib/neurolink.d.ts +15 -0
  30. package/dist/lib/neurolink.js +77 -44
  31. package/dist/lib/providers/anthropic/client.js +17 -1
  32. package/dist/lib/providers/anthropic/constants.d.ts +0 -1
  33. package/dist/lib/providers/anthropic/constants.js +0 -2
  34. package/dist/lib/providers/anthropic/index.d.ts +0 -1
  35. package/dist/lib/providers/anthropic/index.js +0 -1
  36. package/dist/lib/providers/googleAiStudio/index.d.ts +0 -1
  37. package/dist/lib/providers/googleAiStudio/index.js +0 -1
  38. package/dist/lib/providers/googleNativeGemini3/index.d.ts +0 -1
  39. package/dist/lib/providers/googleNativeGemini3/index.js +0 -1
  40. package/dist/lib/providers/googleVertex/client.d.ts +0 -51
  41. package/dist/lib/providers/googleVertex/client.js +0 -678
  42. package/dist/lib/providers/huggingFace/client.d.ts +1 -1
  43. package/dist/lib/providers/huggingFace/client.js +1 -1
  44. package/dist/lib/providers/huggingFace/index.d.ts +0 -1
  45. package/dist/lib/providers/huggingFace/index.js +0 -1
  46. package/dist/lib/providers/jina.d.ts +0 -1
  47. package/dist/lib/providers/jina.js +0 -1
  48. package/dist/lib/providers/litellm/index.d.ts +0 -2
  49. package/dist/lib/providers/litellm/index.js +0 -2
  50. package/dist/lib/providers/llamaCpp.d.ts +1 -0
  51. package/dist/lib/providers/llamaCpp.js +3 -0
  52. package/dist/lib/providers/lmStudio.js +3 -23
  53. package/dist/lib/providers/nvidiaNim/index.d.ts +0 -1
  54. package/dist/lib/providers/nvidiaNim/index.js +0 -1
  55. package/dist/lib/providers/ollama/client.js +1 -23
  56. package/dist/lib/providers/ollama/index.d.ts +0 -1
  57. package/dist/lib/providers/ollama/index.js +0 -1
  58. package/dist/lib/providers/openAI/index.d.ts +0 -2
  59. package/dist/lib/providers/openAI/index.js +0 -2
  60. package/dist/lib/providers/openRouter/utils.d.ts +0 -2
  61. package/dist/lib/providers/openRouter/utils.js +0 -12
  62. package/dist/lib/providers/openaiChatCompletionsBase.d.ts +9 -0
  63. package/dist/lib/providers/openaiChatCompletionsBase.js +33 -0
  64. package/dist/lib/providers/perplexity.d.ts +2 -1
  65. package/dist/lib/providers/perplexity.js +2 -1
  66. package/dist/lib/providers/replicate.d.ts +0 -1
  67. package/dist/lib/providers/replicate.js +22 -9
  68. package/dist/lib/providers/voyage.d.ts +0 -1
  69. package/dist/lib/providers/voyage.js +0 -1
  70. package/dist/lib/types/index.d.ts +0 -1
  71. package/dist/lib/types/index.js +0 -1
  72. package/dist/lib/types/providers.d.ts +2 -11
  73. package/dist/lib/types/utilities.d.ts +29 -0
  74. package/dist/lib/utils/json/coerce.d.ts +21 -1
  75. package/dist/lib/utils/json/coerce.js +148 -11
  76. package/dist/lib/utils/providerConfig.d.ts +0 -8
  77. package/dist/lib/utils/providerConfig.js +0 -37
  78. package/dist/lib/utils/providerUtils.js +1 -25
  79. package/dist/models/anthropicModels.d.ts +0 -7
  80. package/dist/models/anthropicModels.js +0 -9
  81. package/dist/neurolink.d.ts +15 -0
  82. package/dist/neurolink.js +77 -44
  83. package/dist/providers/anthropic/client.js +17 -1
  84. package/dist/providers/anthropic/constants.d.ts +0 -1
  85. package/dist/providers/anthropic/constants.js +0 -2
  86. package/dist/providers/anthropic/index.d.ts +0 -1
  87. package/dist/providers/anthropic/index.js +0 -1
  88. package/dist/providers/googleAiStudio/index.d.ts +0 -1
  89. package/dist/providers/googleAiStudio/index.js +0 -1
  90. package/dist/providers/googleNativeGemini3/index.d.ts +0 -1
  91. package/dist/providers/googleNativeGemini3/index.js +0 -1
  92. package/dist/providers/googleVertex/client.d.ts +0 -51
  93. package/dist/providers/googleVertex/client.js +0 -678
  94. package/dist/providers/huggingFace/client.d.ts +1 -1
  95. package/dist/providers/huggingFace/client.js +1 -1
  96. package/dist/providers/huggingFace/index.d.ts +0 -1
  97. package/dist/providers/huggingFace/index.js +0 -1
  98. package/dist/providers/jina.d.ts +0 -1
  99. package/dist/providers/jina.js +0 -1
  100. package/dist/providers/litellm/index.d.ts +0 -2
  101. package/dist/providers/litellm/index.js +0 -2
  102. package/dist/providers/llamaCpp.d.ts +1 -0
  103. package/dist/providers/llamaCpp.js +3 -0
  104. package/dist/providers/lmStudio.js +3 -23
  105. package/dist/providers/nvidiaNim/index.d.ts +0 -1
  106. package/dist/providers/nvidiaNim/index.js +0 -1
  107. package/dist/providers/ollama/client.js +1 -23
  108. package/dist/providers/ollama/index.d.ts +0 -1
  109. package/dist/providers/ollama/index.js +0 -1
  110. package/dist/providers/openAI/index.d.ts +0 -2
  111. package/dist/providers/openAI/index.js +0 -2
  112. package/dist/providers/openRouter/utils.d.ts +0 -2
  113. package/dist/providers/openRouter/utils.js +0 -12
  114. package/dist/providers/openaiChatCompletionsBase.d.ts +9 -0
  115. package/dist/providers/openaiChatCompletionsBase.js +33 -0
  116. package/dist/providers/perplexity.d.ts +2 -1
  117. package/dist/providers/perplexity.js +2 -1
  118. package/dist/providers/replicate.d.ts +0 -1
  119. package/dist/providers/replicate.js +22 -9
  120. package/dist/providers/voyage.d.ts +0 -1
  121. package/dist/providers/voyage.js +0 -1
  122. package/dist/types/index.d.ts +0 -1
  123. package/dist/types/index.js +0 -1
  124. package/dist/types/providers.d.ts +2 -11
  125. package/dist/types/utilities.d.ts +29 -0
  126. package/dist/utils/json/coerce.d.ts +21 -1
  127. package/dist/utils/json/coerce.js +148 -11
  128. package/dist/utils/providerConfig.d.ts +0 -8
  129. package/dist/utils/providerConfig.js +0 -37
  130. package/dist/utils/providerUtils.js +1 -25
  131. package/package.json +8 -6
  132. package/dist/lib/providers/anthropic/utils.d.ts +0 -7
  133. package/dist/lib/providers/anthropic/utils.js +0 -150
  134. package/dist/lib/providers/googleAiStudio/utils.d.ts +0 -2
  135. package/dist/lib/providers/googleAiStudio/utils.js +0 -19
  136. package/dist/lib/providers/googleNativeGemini3/constants.d.ts +0 -1
  137. package/dist/lib/providers/googleNativeGemini3/constants.js +0 -2
  138. package/dist/lib/providers/huggingFace/utils.d.ts +0 -2
  139. package/dist/lib/providers/huggingFace/utils.js +0 -8
  140. package/dist/lib/providers/index.d.ts +0 -33
  141. package/dist/lib/providers/index.js +0 -34
  142. package/dist/lib/providers/litellm/constants.d.ts +0 -1
  143. package/dist/lib/providers/litellm/constants.js +0 -3
  144. package/dist/lib/providers/litellm/utils.d.ts +0 -5
  145. package/dist/lib/providers/litellm/utils.js +0 -11
  146. package/dist/lib/providers/nvidiaNim/utils.d.ts +0 -9
  147. package/dist/lib/providers/nvidiaNim/utils.js +0 -101
  148. package/dist/lib/providers/ollama/utils.d.ts +0 -8
  149. package/dist/lib/providers/ollama/utils.js +0 -43
  150. package/dist/lib/providers/openAI/constants.d.ts +0 -1
  151. package/dist/lib/providers/openAI/constants.js +0 -3
  152. package/dist/lib/providers/openAI/utils.d.ts +0 -2
  153. package/dist/lib/providers/openAI/utils.js +0 -8
  154. package/dist/lib/types/universalProviderOptions.d.ts +0 -89
  155. package/dist/lib/types/universalProviderOptions.js +0 -55
  156. package/dist/providers/anthropic/utils.d.ts +0 -7
  157. package/dist/providers/anthropic/utils.js +0 -149
  158. package/dist/providers/googleAiStudio/utils.d.ts +0 -2
  159. package/dist/providers/googleAiStudio/utils.js +0 -18
  160. package/dist/providers/googleNativeGemini3/constants.d.ts +0 -1
  161. package/dist/providers/googleNativeGemini3/constants.js +0 -1
  162. package/dist/providers/huggingFace/utils.d.ts +0 -2
  163. package/dist/providers/huggingFace/utils.js +0 -7
  164. package/dist/providers/index.d.ts +0 -33
  165. package/dist/providers/index.js +0 -33
  166. package/dist/providers/litellm/constants.d.ts +0 -1
  167. package/dist/providers/litellm/constants.js +0 -2
  168. package/dist/providers/litellm/utils.d.ts +0 -5
  169. package/dist/providers/litellm/utils.js +0 -10
  170. package/dist/providers/nvidiaNim/utils.d.ts +0 -9
  171. package/dist/providers/nvidiaNim/utils.js +0 -100
  172. package/dist/providers/ollama/utils.d.ts +0 -8
  173. package/dist/providers/ollama/utils.js +0 -42
  174. package/dist/providers/openAI/constants.d.ts +0 -1
  175. package/dist/providers/openAI/constants.js +0 -2
  176. package/dist/providers/openAI/utils.d.ts +0 -2
  177. package/dist/providers/openAI/utils.js +0 -7
  178. package/dist/types/universalProviderOptions.d.ts +0 -89
  179. package/dist/types/universalProviderOptions.js +0 -54
@@ -23,6 +23,48 @@ import { nextBalancedJsonSpan } from "./extract.js";
23
23
  function hasSafeParse(schema) {
24
24
  return typeof schema.safeParse === "function";
25
25
  }
26
+ /**
27
+ * Does `value` satisfy `schema`? A schema we cannot validate with (absent, or
28
+ * without a Zod-style `safeParse`) accepts everything — callers use this as a
29
+ * gate, not as proof, so an unknown schema must never block a value.
30
+ *
31
+ * Exists so the consumers of coerceJsonToSchema can refuse to publish a
32
+ * recovered value as `structuredData` when the caller's schema rejects it —
33
+ * e.g. a raw *string* under an object schema, which is the shape a truncated
34
+ * response degrades to.
35
+ */
36
+ export function schemaAccepts(schema, value) {
37
+ if (!schema || !hasSafeParse(schema)) {
38
+ return true;
39
+ }
40
+ return schema.safeParse(value).success;
41
+ }
42
+ /**
43
+ * Recover a JSON *scalar* root (string/number/boolean) from model text. The
44
+ * object/array case is handled by `coerceJsonToSchema`; this covers the
45
+ * residual scalar root after that path returns null. Encapsulates the JSON
46
+ * parsing, the empty-string normalization, and the `schemaAccepts` gate so the
47
+ * same policy cannot drift between consumers (`neurolink.recoverStructuredData`
48
+ * and `GenerationHandler.coerceTextMode`).
49
+ */
50
+ export function recoverScalarRoot(text, schema) {
51
+ try {
52
+ const scalar = JSON.parse(text);
53
+ if (scalar === "") {
54
+ return { kind: "empty" };
55
+ }
56
+ if (scalar === null || scalar === undefined) {
57
+ return { kind: "nullish" };
58
+ }
59
+ if (schemaAccepts(schema, scalar)) {
60
+ return { kind: "accepted", value: scalar };
61
+ }
62
+ return { kind: "rejected", value: scalar };
63
+ }
64
+ catch {
65
+ return { kind: "not-json" };
66
+ }
67
+ }
26
68
  /**
27
69
  * Parse `candidate` as JSON, repairing common escaping mistakes on failure.
28
70
  * Returns the parsed value plus whether jsonrepair had to alter the text.
@@ -51,6 +93,34 @@ function parseOrRepair(candidate) {
51
93
  }
52
94
  /** Bounds the recursive nested-string unwrap against pathological inputs. */
53
95
  const MAX_NESTED_UNWRAP_DEPTH = 6;
96
+ /** Bounds the structural back-off when salvaging a truncated root object. */
97
+ const MAX_SALVAGE_TRIMS = 8;
98
+ /**
99
+ * Last-resort recovery for output cut off mid-JSON: walk the unclosed root span
100
+ * backwards to successively earlier structural boundaries (`,` / `}` / `]`) and
101
+ * re-attempt parse+repair at each one. Truncation can land somewhere jsonrepair
102
+ * cannot close on its own (inside an escape, a half-written key, a dangling
103
+ * separator); dropping back to the last completed field gives it a repairable
104
+ * prefix. Returns a PARTIAL object — the fields the model did finish — so a
105
+ * truncated response degrades to an incomplete object rather than to raw text.
106
+ */
107
+ function salvageTruncatedRoot(span) {
108
+ let candidate = span;
109
+ for (let i = 0; i < MAX_SALVAGE_TRIMS && candidate.length > 1; i++) {
110
+ const cut = Math.max(candidate.lastIndexOf(","), candidate.lastIndexOf("}"), candidate.lastIndexOf("]"));
111
+ if (cut <= 0) {
112
+ break;
113
+ }
114
+ candidate = candidate.slice(0, cut);
115
+ const outcome = parseOrRepair(candidate);
116
+ if (outcome !== undefined &&
117
+ outcome.value !== null &&
118
+ typeof outcome.value === "object") {
119
+ return outcome.value;
120
+ }
121
+ }
122
+ return undefined;
123
+ }
54
124
  /**
55
125
  * Recursively replace any string-valued field whose content is itself a JSON
56
126
  * object/array with the parsed value. Models sometimes double-encode a NESTED
@@ -135,30 +205,54 @@ export function coerceJsonToSchema(text, schema) {
135
205
  // 3. first "{" or "[" to end of text (TRUNCATED output —
136
206
  // finishReason=length — where the closing bracket was cut off;
137
207
  // jsonrepair closes it)
138
- // `truncated` marks the first-open-to-end candidate: it is only reachable
139
- // when no balanced span and no first-to-last span matched, i.e. there was no
140
- // closing bracket at all the signature of token-truncated output.
208
+ // `truncated` marks a candidate that could only come from output cut short:
209
+ // the first-open-to-end span, and when the ROOT bracket never closes
210
+ // every other candidate too, since all of them are then partial views of an
211
+ // unfinished document.
212
+ //
213
+ // `rootAligned` marks candidates that start at the document's first opening
214
+ // bracket, i.e. at the real root. Candidates are ordered root-aligned FIRST.
215
+ // On truncated output the root brace never balances, so the balanced scan
216
+ // walks on and matches brackets that live INSIDE a string value (`[step 1]`
217
+ // in a shell script, say) — a syntactically fine but semantically bogus
218
+ // array. Preferring the root keeps the partial real object ahead of that.
141
219
  const candidates = [];
220
+ const openIndexes = [text.indexOf("{"), text.indexOf("[")].filter((i) => i >= 0);
221
+ const firstOpen = openIndexes.length > 0 ? Math.min(...openIndexes) : -1;
222
+ let rootClosed = false;
142
223
  let searchFrom = 0;
143
224
  for (;;) {
144
225
  const found = nextBalancedJsonSpan(text, searchFrom);
145
226
  if (!found) {
146
227
  break;
147
228
  }
148
- candidates.push({ text: found.span, truncated: false });
229
+ const start = found.end - found.span.length;
230
+ if (start === firstOpen) {
231
+ rootClosed = true;
232
+ }
233
+ candidates.push({
234
+ text: found.span,
235
+ truncated: false,
236
+ rootAligned: start === firstOpen,
237
+ });
149
238
  searchFrom = found.end;
150
239
  }
151
- const openIndexes = [text.indexOf("{"), text.indexOf("[")].filter((i) => i >= 0);
152
- const firstOpen = openIndexes.length > 0 ? Math.min(...openIndexes) : -1;
240
+ // No balanced span starts at the root bracket → the document is unclosed.
241
+ const rootUnclosed = firstOpen >= 0 && !rootClosed;
153
242
  const lastClose = Math.max(text.lastIndexOf("}"), text.lastIndexOf("]"));
154
243
  if (firstOpen >= 0 && lastClose > firstOpen) {
155
244
  candidates.push({
156
245
  text: text.slice(firstOpen, lastClose + 1),
157
- truncated: false,
246
+ truncated: rootUnclosed,
247
+ rootAligned: true,
158
248
  });
159
249
  }
160
250
  if (firstOpen >= 0) {
161
- candidates.push({ text: text.slice(firstOpen), truncated: true });
251
+ candidates.push({
252
+ text: text.slice(firstOpen),
253
+ truncated: true,
254
+ rootAligned: true,
255
+ });
162
256
  }
163
257
  // JSON-string-literal wrapper: some providers double-encode and return the
164
258
  // object as a JSON *string* (e.g. `"{\"k\":1}"`). Unwrap one layer and add
@@ -174,7 +268,11 @@ export function coerceJsonToSchema(text, schema) {
174
268
  if (!innerSpan) {
175
269
  break;
176
270
  }
177
- candidates.push({ text: innerSpan.span, truncated: false });
271
+ candidates.push({
272
+ text: innerSpan.span,
273
+ truncated: false,
274
+ rootAligned: false,
275
+ });
178
276
  innerFrom = innerSpan.end;
179
277
  }
180
278
  }
@@ -183,11 +281,20 @@ export function coerceJsonToSchema(text, schema) {
183
281
  // not a string literal — ignore
184
282
  }
185
283
  }
284
+ // Stable partition: root-aligned candidates first. Only the ORDER changes —
285
+ // no candidate is dropped — so the multi-object "prefer the most complete
286
+ // schema-valid candidate" selection below is unaffected (it is order
287
+ // independent), while the unschema'd first-parseable-wins path and the
288
+ // `firstValid` fallback now favour the document's real root.
289
+ const ordered = [
290
+ ...candidates.filter((c) => c.rootAligned),
291
+ ...candidates.filter((c) => !c.rootAligned),
292
+ ];
186
293
  let firstValid;
187
294
  const schemaValid = [];
188
295
  const hasSchema = !!(schema && hasSafeParse(schema));
189
296
  const seen = new Set();
190
- for (const candidate of candidates) {
297
+ for (const candidate of ordered) {
191
298
  if (seen.has(candidate.text)) {
192
299
  continue;
193
300
  }
@@ -202,6 +309,7 @@ export function coerceJsonToSchema(text, schema) {
202
309
  value: outcome.value,
203
310
  repaired: outcome.repaired,
204
311
  truncated: candidate.truncated,
312
+ rootAligned: candidate.rootAligned,
205
313
  };
206
314
  if (firstValid === undefined) {
207
315
  firstValid = record;
@@ -229,6 +337,7 @@ export function coerceJsonToSchema(text, schema) {
229
337
  value: unwrapped.value,
230
338
  repaired: true,
231
339
  truncated: candidate.truncated,
340
+ rootAligned: candidate.rootAligned,
232
341
  });
233
342
  }
234
343
  else if (
@@ -247,6 +356,7 @@ export function coerceJsonToSchema(text, schema) {
247
356
  value: outcome.value[0],
248
357
  repaired: true,
249
358
  truncated: candidate.truncated,
359
+ rootAligned: candidate.rootAligned,
250
360
  });
251
361
  }
252
362
  }
@@ -261,7 +371,34 @@ export function coerceJsonToSchema(text, schema) {
261
371
  ? cur
262
372
  : best)
263
373
  : undefined;
264
- const chosen = schemaMatch ?? firstValid;
374
+ let chosen = schemaMatch ?? firstValid;
375
+ // Salvage the root when the document is unclosed AND nothing trustworthy came
376
+ // out of it: either nothing parsed at all, or the only parseable candidate is
377
+ // a bracket pair scraped from INSIDE a string value (not root-aligned, not
378
+ // schema-valid) — the `["step 1"]`-from-a-shell-script case. Backing off to
379
+ // the last completed field yields a PARTIAL OBJECT flagged `truncated`,
380
+ // instead of a bogus value or raw text degrading to a string.
381
+ if (rootUnclosed &&
382
+ schemaMatch === undefined &&
383
+ (chosen === undefined || !chosen.rootAligned)) {
384
+ const salvaged = salvageTruncatedRoot(text.slice(firstOpen));
385
+ if (salvaged !== undefined) {
386
+ logger.debug("[coerceJsonToSchema] salvaged a partial truncated object", {
387
+ textLength: text.length,
388
+ });
389
+ chosen = {
390
+ value: salvaged,
391
+ repaired: true,
392
+ truncated: true,
393
+ rootAligned: true,
394
+ };
395
+ }
396
+ else if (chosen !== undefined) {
397
+ // Nothing salvageable, but we still know the output was truncated —
398
+ // never report a candidate scraped from a truncated document as complete.
399
+ chosen = { ...chosen, truncated: true };
400
+ }
401
+ }
265
402
  if (chosen === undefined) {
266
403
  return null;
267
404
  }
@@ -115,14 +115,6 @@ export declare function createDeepSeekConfig(): ProviderConfigOptions;
115
115
  * Creates NVIDIA NIM provider configuration
116
116
  */
117
117
  export declare function createNvidiaNimConfig(): ProviderConfigOptions;
118
- /**
119
- * Creates LM Studio provider configuration (local server)
120
- */
121
- export declare function createLmStudioConfig(): ProviderConfigOptions;
122
- /**
123
- * Creates llama.cpp provider configuration (local server)
124
- */
125
- export declare function createLlamaCppConfig(): ProviderConfigOptions;
126
118
  /**
127
119
  * Creates xAI Grok provider configuration.
128
120
  */
@@ -411,43 +411,6 @@ export function createNvidiaNimConfig() {
411
411
  ],
412
412
  };
413
413
  }
414
- /**
415
- * Creates LM Studio provider configuration (local server)
416
- */
417
- export function createLmStudioConfig() {
418
- return {
419
- providerName: "LM Studio",
420
- envVarName: "LM_STUDIO_BASE_URL",
421
- setupUrl: "https://lmstudio.ai/",
422
- description: "LM Studio server URL",
423
- instructions: [
424
- "1. Install LM Studio: https://lmstudio.ai/",
425
- "2. Open LM Studio and download a model (e.g. Llama 3.2 3B Instruct)",
426
- '3. Click "Local Server" → Start Server',
427
- "4. Default URL is http://localhost:1234/v1 (override via LM_STUDIO_BASE_URL)",
428
- ],
429
- // Base URL is optional — defaults to http://localhost:1234/v1 if unset.
430
- optional: true,
431
- };
432
- }
433
- /**
434
- * Creates llama.cpp provider configuration (local server)
435
- */
436
- export function createLlamaCppConfig() {
437
- return {
438
- providerName: "llama.cpp",
439
- envVarName: "LLAMACPP_BASE_URL",
440
- setupUrl: "https://github.com/ggerganov/llama.cpp",
441
- description: "llama.cpp server URL",
442
- instructions: [
443
- "1. Build llama.cpp: https://github.com/ggerganov/llama.cpp#build",
444
- "2. Run: ./llama-server -m model.gguf --port 8080",
445
- "3. Default URL is http://localhost:8080/v1 (override via LLAMACPP_BASE_URL)",
446
- ],
447
- // Base URL is optional — defaults to http://localhost:8080/v1 if unset.
448
- optional: true,
449
- };
450
- }
451
414
  /**
452
415
  * Creates xAI Grok provider configuration.
453
416
  */
@@ -20,19 +20,6 @@ export async function getBestProvider(requestedProvider) {
20
20
  // For explicit provider requests, ALWAYS honor the request
21
21
  // Never override explicit provider selection with health-based fallbacks
22
22
  logger.debug(`[getBestProvider] Using explicitly requested provider: ${requestedProvider}`);
23
- // Optional health check for logging purposes only
24
- try {
25
- const health = await ProviderHealthChecker.checkProviderHealth(requestedProvider, { includeConnectivityTest: false, cacheResults: true });
26
- if (health.isHealthy) {
27
- logger.debug(`[getBestProvider] Explicitly requested provider ${requestedProvider} is healthy`);
28
- }
29
- else {
30
- logger.warn(`[getBestProvider] Explicitly requested provider ${requestedProvider} may have issues, but using anyway`, { error: health.error });
31
- }
32
- }
33
- catch (error) {
34
- logger.warn(`[getBestProvider] Health check failed for explicitly requested provider ${requestedProvider}, using anyway`, { error: error instanceof Error ? error.message : String(error) });
35
- }
36
23
  // ALWAYS return the explicitly requested provider
37
24
  return requestedProvider;
38
25
  }
@@ -402,18 +389,7 @@ export function hasProviderEnvVars(provider) {
402
389
  * @returns Array of available provider names
403
390
  */
404
391
  export function getAvailableProviders() {
405
- return [
406
- "bedrock",
407
- "vertex",
408
- "openai",
409
- "anthropic",
410
- "azure",
411
- "google-ai",
412
- "litellm",
413
- "huggingface",
414
- "ollama",
415
- "mistral",
416
- ];
392
+ return Object.values(AIProviderName).filter((name) => name !== AIProviderName.AUTO);
417
393
  }
418
394
  /**
419
395
  * Validate provider name
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "10.12.8",
3
+ "version": "11.0.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
6
6
  "author": {
@@ -103,8 +103,10 @@
103
103
  "test:mcp:limits": "npx tsx test/continuous-test-suite-mcp-output-limits.ts",
104
104
  "test:mcp:infra": "npx tsx test/continuous-test-suite-mcp-infra.ts",
105
105
  "test:providers-mocked": "npx tsx test/continuous-test-suite-providers-mocked.ts",
106
+ "test:provider-structure": "npx tsx test/continuous-test-suite-provider-structure.ts",
106
107
  "test:provider-fallback": "npx tsx test/continuous-test-suite-provider-fallback.ts",
107
108
  "test:provider-fallback-latency": "npx tsx test/continuous-test-suite-provider-fallback-latency.ts",
109
+ "test:provider-wiring": "npx tsx test/continuous-test-suite-provider-wiring.ts",
108
110
  "test:rag": "npx tsx test/continuous-test-suite-rag.ts",
109
111
  "test:vector-pinecone": "npx tsx test/continuous-test-suite-vector-pinecone.ts",
110
112
  "test:vector-pgvector": "npx tsx test/continuous-test-suite-vector-pgvector.ts",
@@ -158,16 +160,16 @@
158
160
  "test:retry-after:vitest": "pnpm exec vitest run test/retryAfter.test.ts",
159
161
  "test:websearch-grounding": "npx tsx test/continuous-test-suite-websearch-grounding.ts",
160
162
  "test:ci": "pnpm run test && pnpm run test:client && pnpm run test:hitl",
161
- "// CI tier — fast, no live AI calls, safe for every commit": "",
163
+ "// CI tier — fast, no live AI calls, safe for every commit (test:unit; also see the separate provider-safety-net CI job, which runs build + test:providers-mocked + test:provider-structure on every PR)": "",
162
164
  "test:tool-routing": "npx tsx test/continuous-test-suite-tool-routing.ts",
163
165
  "test:classifier-router": "npx tsx test/continuous-test-suite-classifier-router.ts",
164
166
  "test:system-messages": "npx tsx test/continuous-test-suite-system-messages.ts",
165
167
  "test:test-stubs": "npx tsx test/continuous-test-suite-test-stubs.ts",
166
168
  "test:tool-routing-semantic": "npx tsx test/continuous-test-suite-tool-routing-semantic.ts",
167
- "test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && pnpm run test:file-detector-extension && pnpm run test:file-detector-magic-bytes && pnpm run test:mcp:infra && pnpm run test:mcp:bash && pnpm run test:mcp:limits && pnpm run test:mcp:spans && pnpm run test:autoresearch:redis && pnpm run test:tool-routing && pnpm run test:tool-routing-cli && pnpm run test:tool-dedup && pnpm run test:model-pool && pnpm run test:litellm-context && pnpm run test:dedup-execute-map && pnpm run test:step-budget-guard && pnpm run test:agent-plumbing && pnpm run test:tool-execution-recorder && pnpm run test:proxy-terminal-errors && pnpm run test:proxy-usage-refresh && pnpm run test:system-messages && pnpm run test:tool-routing-semantic && pnpm run test:anthropic-tools-policy && pnpm run test:anthropic-structured && pnpm run test:sagemaker-tools && pnpm run test:anthropic-multimodal && pnpm run test:unified-files-prompt-sync && pnpm run test:excel-interop && pnpm run test:model-capabilities && pnpm run test:agent-runtime:vitest && pnpm run test:agent-delegation && pnpm run test:retry-after:vitest && pnpm run test:sampling-params && pnpm run test:structured-recovery && pnpm run test:prompt-redaction && pnpm run test:mcp-result-cache && pnpm run test:test-stubs && pnpm run test:model-not-found-retryable && pnpm run test:websearch-grounding && pnpm run test:archive:security && pnpm run test:office:security && pnpm run test:proxy:vitest",
168
- "// CI tier — live providers, runs only when API keys are present (test:credentials and test:dynamic make real provider calls when keys are set, so they live here, not in test:unit)": "",
169
+ "test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && pnpm run test:file-detector-extension && pnpm run test:file-detector-magic-bytes && pnpm run test:mcp:infra && pnpm run test:mcp:bash && pnpm run test:mcp:limits && pnpm run test:mcp:spans && pnpm run test:autoresearch:redis && pnpm run test:tool-routing && pnpm run test:tool-routing-cli && pnpm run test:tool-dedup && pnpm run test:model-pool && pnpm run test:litellm-context && pnpm run test:dedup-execute-map && pnpm run test:step-budget-guard && pnpm run test:agent-plumbing && pnpm run test:tool-execution-recorder && pnpm run test:proxy-terminal-errors && pnpm run test:proxy-usage-refresh && pnpm run test:system-messages && pnpm run test:tool-routing-semantic && pnpm run test:anthropic-tools-policy && pnpm run test:anthropic-structured && pnpm run test:sagemaker-tools && pnpm run test:anthropic-multimodal && pnpm run test:unified-files-prompt-sync && pnpm run test:excel-interop && pnpm run test:model-capabilities && pnpm run test:agent-runtime:vitest && pnpm run test:agent-delegation && pnpm run test:retry-after:vitest && pnpm run test:sampling-params && pnpm run test:structured-recovery && pnpm run test:prompt-redaction && pnpm run test:mcp-result-cache && pnpm run test:test-stubs && pnpm run test:model-not-found-retryable && pnpm run test:websearch-grounding && pnpm run test:archive:security && pnpm run test:office:security && pnpm run test:proxy:vitest && pnpm run test:provider-wiring",
170
+ "// CI tier — live providers, runs only when API keys are present (test:credentials and test:dynamic make real provider calls when keys are set, so they live here, not in test:unit; test:matrix, a different suite covering the full provider capability matrix, runs nightly via .github/workflows/live-matrix.yml — test:providers itself is still only wired into test:live, not any GitHub Actions workflow)": "",
169
171
  "test:live": "pnpm run test:providers && pnpm run test:mcp:http && pnpm run test:mcp:sdk && pnpm run test:mcp:cli && pnpm run test:observability && pnpm run test:context && pnpm run test:memory && pnpm run test:tool-reliability && pnpm run test:evaluation && pnpm run test:autoresearch && pnpm run test:credentials && pnpm run test:dynamic",
170
- "// CI tier — product output (image/video/TTS/PPT) — costs $$ per run": "",
172
+ "// CI tier — product output (image/video/TTS/PPT) — costs $$ per run (not wired into any GitHub Actions workflow as of this comment; run manually or add to live-matrix.yml if nightly coverage is needed)": "",
171
173
  "test:product": "pnpm run test:media && pnpm run test:tts && pnpm run test:ppt && pnpm run test:proxy",
172
174
  "test:performance": "tsx tools/testing/performanceMonitor.ts",
173
175
  "// Content Generation (Cross-platform JS)": "",
@@ -224,7 +226,7 @@
224
226
  "quality:metrics": "tsx scripts/quality-metrics.ts",
225
227
  "quality:report": "pnpm run quality:metrics && echo 'Quality metrics saved to quality-metrics.json'",
226
228
  "pre-commit": "lint-staged",
227
- "pre-push": "pnpm run validate:commit && pnpm run validate:env && pnpm run validate && pnpm run test:ci",
229
+ "pre-push": "pnpm run build && pnpm run test:providers-mocked && pnpm run test:provider-structure",
228
230
  "check:all": "pnpm run lint && pnpm run format --check && pnpm run validate && pnpm run validate:commit",
229
231
  "test:litellm-context": "npx tsx test/continuous-test-suite-litellm-context-windows.ts",
230
232
  "test:dedup-execute-map": "npx tsx test/continuous-test-suite-dedup-execute-map.ts",
@@ -1,7 +0,0 @@
1
- import type { AnthropicAuthMethod, AnthropicRateLimitInfo, ClaudeSubscriptionTier, OAuthToken } from "../../types/index.js";
2
- export declare const getAnthropicApiKey: () => string;
3
- export declare const getDefaultAnthropicModel: () => string;
4
- export declare const getOAuthToken: () => OAuthToken | null;
5
- export declare const detectSubscriptionTier: (oauthToken: OAuthToken | null) => ClaudeSubscriptionTier;
6
- export declare const detectAuthMethod: (oauthToken: OAuthToken | null) => AnthropicAuthMethod;
7
- export declare const parseRateLimitHeaders: (headers: Headers | Record<string, string>) => AnthropicRateLimitInfo;
@@ -1,150 +0,0 @@
1
- import { existsSync, readFileSync } from "fs";
2
- import { homedir } from "os";
3
- import { join } from "path";
4
- import { AnthropicModels } from "../../constants/enums.js";
5
- import { logger } from "../../utils/logger.js";
6
- import { createAnthropicConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
7
- export const getAnthropicApiKey = () => {
8
- return validateApiKey(createAnthropicConfig());
9
- };
10
- export const getDefaultAnthropicModel = () => {
11
- return getProviderModel("ANTHROPIC_MODEL", AnthropicModels.CLAUDE_SONNET_4_6);
12
- };
13
- export const getOAuthToken = () => {
14
- // First, check stored credentials file (highest priority)
15
- try {
16
- const credentialsPath = join(homedir(), ".neurolink", "anthropic-credentials.json");
17
- if (existsSync(credentialsPath)) {
18
- const credentialsContent = readFileSync(credentialsPath, "utf-8");
19
- const credentials = JSON.parse(credentialsContent);
20
- if (credentials.type === "oauth" && credentials.oauth?.accessToken) {
21
- logger.debug("[AnthropicProvider] Using OAuth token from stored credentials file");
22
- return credentials.oauth;
23
- }
24
- }
25
- }
26
- catch (error) {
27
- logger.debug("[AnthropicProvider] Failed to read stored credentials:", error);
28
- }
29
- // Fallback to environment variables
30
- const tokenString = process.env.ANTHROPIC_OAUTH_TOKEN || process.env.CLAUDE_OAUTH_TOKEN;
31
- if (!tokenString) {
32
- return null;
33
- }
34
- // Try to parse as JSON (for full token object with refresh token and expiry)
35
- try {
36
- const parsed = JSON.parse(tokenString);
37
- if (typeof parsed === "object" && parsed.accessToken) {
38
- return parsed;
39
- }
40
- // If it's a simple string in JSON, use it as access token
41
- if (typeof parsed === "string") {
42
- return { accessToken: parsed };
43
- }
44
- }
45
- catch {
46
- // Not JSON, treat as plain access token string
47
- }
48
- // Treat as plain access token string
49
- return { accessToken: tokenString };
50
- };
51
- export const detectSubscriptionTier = (oauthToken) => {
52
- // Check explicit environment variable first
53
- const envTier = process.env.ANTHROPIC_SUBSCRIPTION_TIER?.toLowerCase();
54
- if (envTier) {
55
- const validTiers = [
56
- "free",
57
- "pro",
58
- "max",
59
- "max_5",
60
- "max_20",
61
- "api",
62
- ];
63
- if (validTiers.includes(envTier)) {
64
- logger.debug("[detectSubscriptionTier] Using environment override", {
65
- tier: envTier,
66
- });
67
- return envTier;
68
- }
69
- logger.warn("[detectSubscriptionTier] Invalid ANTHROPIC_SUBSCRIPTION_TIER", {
70
- value: envTier,
71
- validTiers,
72
- });
73
- }
74
- // If using OAuth, default to 'pro' (most common subscription tier)
75
- if (oauthToken) {
76
- // Check if token scopes indicate tier (future-proofing)
77
- const scopes = oauthToken.scopes ?? [];
78
- let detectedTier = "pro";
79
- if (scopes.includes("max_20")) {
80
- detectedTier = "max_20";
81
- }
82
- else if (scopes.includes("max_5")) {
83
- detectedTier = "max_5";
84
- }
85
- else if (scopes.includes("max")) {
86
- detectedTier = "max";
87
- }
88
- logger.debug("[detectSubscriptionTier] Detected from OAuth token", {
89
- tier: detectedTier,
90
- scopes,
91
- });
92
- return detectedTier;
93
- }
94
- // Default to 'api' for API key authentication
95
- logger.debug("[detectSubscriptionTier] No OAuth token, defaulting to API tier");
96
- return "api";
97
- };
98
- export const detectAuthMethod = (oauthToken) => {
99
- // Explicit env var takes highest precedence — allows forcing api_key mode
100
- // even when OAuth credentials exist (e.g., when using a proxy that handles auth)
101
- const explicit = process.env.ANTHROPIC_AUTH_METHOD?.toLowerCase();
102
- if (explicit === "api_key" || explicit === "apikey") {
103
- logger.debug("[detectAuthMethod] Forced to api_key by ANTHROPIC_AUTH_METHOD env var");
104
- return "api_key";
105
- }
106
- if (explicit === "oauth") {
107
- if (oauthToken) {
108
- logger.debug("[detectAuthMethod] Forced to oauth by ANTHROPIC_AUTH_METHOD env var");
109
- return "oauth";
110
- }
111
- logger.warn("[detectAuthMethod] ANTHROPIC_AUTH_METHOD=oauth but no OAuth token found; falling through to auto-detection");
112
- }
113
- else if (explicit) {
114
- logger.warn("[detectAuthMethod] Unrecognized ANTHROPIC_AUTH_METHOD value; falling through to auto-detection", {
115
- value: explicit,
116
- });
117
- }
118
- // Auto-detect: OAuth takes precedence if available
119
- const method = oauthToken ? "oauth" : "api_key";
120
- logger.debug("[detectAuthMethod] Auth method resolved", {
121
- method,
122
- hasOAuthToken: !!oauthToken,
123
- });
124
- return method;
125
- };
126
- export const parseRateLimitHeaders = (headers) => {
127
- const getHeader = (name) => {
128
- if (headers instanceof Headers) {
129
- return headers.get(name);
130
- }
131
- return headers[name] || headers[name.toLowerCase()] || null;
132
- };
133
- const parseNumber = (value) => {
134
- if (!value) {
135
- return undefined;
136
- }
137
- const num = parseInt(value, 10);
138
- return isNaN(num) ? undefined : num;
139
- };
140
- return {
141
- requestsLimit: parseNumber(getHeader("anthropic-ratelimit-requests-limit")),
142
- requestsRemaining: parseNumber(getHeader("anthropic-ratelimit-requests-remaining")),
143
- requestsReset: getHeader("anthropic-ratelimit-requests-reset") || undefined,
144
- tokensLimit: parseNumber(getHeader("anthropic-ratelimit-tokens-limit")),
145
- tokensRemaining: parseNumber(getHeader("anthropic-ratelimit-tokens-remaining")),
146
- tokensReset: getHeader("anthropic-ratelimit-tokens-reset") || undefined,
147
- retryAfter: parseNumber(getHeader("retry-after")),
148
- };
149
- };
150
- //# sourceMappingURL=utils.js.map
@@ -1,2 +0,0 @@
1
- import type { GenAIClient } from "../../types/index.js";
2
- export declare function createGoogleGenAIClient(apiKey: string): Promise<GenAIClient>;
@@ -1,19 +0,0 @@
1
- import { ErrorCategory, ErrorSeverity } from "../../constants/enums.js";
2
- import { ERROR_CODES, NeuroLinkError } from "../../utils/errorHandling.js";
3
- export async function createGoogleGenAIClient(apiKey) {
4
- const mod = await import("@google/genai");
5
- const ctor = mod.GoogleGenAI;
6
- if (!ctor) {
7
- throw new NeuroLinkError({
8
- code: ERROR_CODES.INVALID_CONFIGURATION,
9
- message: "@google/genai does not export GoogleGenAI",
10
- category: ErrorCategory.CONFIGURATION,
11
- severity: ErrorSeverity.CRITICAL,
12
- retriable: false,
13
- context: { module: "@google/genai", expectedExport: "GoogleGenAI" },
14
- });
15
- }
16
- const Ctor = ctor;
17
- return new Ctor({ apiKey });
18
- }
19
- //# sourceMappingURL=utils.js.map
@@ -1 +0,0 @@
1
- export declare const GEMINI3_NATIVE_MAX_STEPS = 100;
@@ -1,2 +0,0 @@
1
- export const GEMINI3_NATIVE_MAX_STEPS = 100;
2
- //# sourceMappingURL=constants.js.map
@@ -1,2 +0,0 @@
1
- export declare const getHuggingFaceApiKey: () => string;
2
- export declare const getDefaultHuggingFaceModel: () => string;
@@ -1,8 +0,0 @@
1
- import { createHuggingFaceConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
2
- export const getHuggingFaceApiKey = () => {
3
- return validateApiKey(createHuggingFaceConfig());
4
- };
5
- export const getDefaultHuggingFaceModel = () => {
6
- return getProviderModel("HUGGINGFACE_MODEL", "microsoft/DialoGPT-medium");
7
- };
8
- //# sourceMappingURL=utils.js.map
@@ -1,33 +0,0 @@
1
- /**
2
- * Provider exports for Vercel AI SDK integration
3
- * This file centralizes all AI provider classes for easy import and usage
4
- */
5
- export { GoogleVertexProvider as GoogleVertexAI } from "./googleVertex/index.js";
6
- export { AmazonBedrockProvider as AmazonBedrock } from "./amazonBedrock/index.js";
7
- export { AmazonSageMakerProvider as AmazonSageMaker } from "./amazonSagemaker.js";
8
- export { OpenAIProvider as OpenAI } from "./openAI/index.js";
9
- export { OpenAICompatibleProvider as OpenAICompatible } from "./openaiCompatible/index.js";
10
- export { AnthropicProvider as AnthropicProvider } from "./anthropic/index.js";
11
- export { AzureOpenAIProvider } from "./azureOpenai.js";
12
- export { GoogleAIStudioProvider as GoogleAIStudio } from "./googleAiStudio/index.js";
13
- export { HuggingFaceProvider as HuggingFace } from "./huggingFace/index.js";
14
- export { OllamaProvider as Ollama } from "./ollama/index.js";
15
- export { MistralProvider as MistralAI } from "./mistral.js";
16
- export { LiteLLMProvider as LiteLLM } from "./litellm/index.js";
17
- export { DeepSeekProvider as DeepSeek } from "./deepseek.js";
18
- export { NvidiaNimProvider as NvidiaNim } from "./nvidiaNim/index.js";
19
- export { LMStudioProvider as LMStudio } from "./lmStudio.js";
20
- export { LlamaCppProvider as LlamaCpp } from "./llamaCpp.js";
21
- export { XaiProvider as Xai } from "./xai.js";
22
- export { GroqProvider as Groq } from "./groq.js";
23
- export { CohereProvider as Cohere } from "./cohere.js";
24
- export { TogetherAIProvider as TogetherAI } from "./togetherAi.js";
25
- export { FireworksProvider as Fireworks } from "./fireworks.js";
26
- export { PerplexityProvider as Perplexity } from "./perplexity.js";
27
- export { CloudflareProvider as Cloudflare } from "./cloudflare.js";
28
- export { VoyageProvider as Voyage } from "./voyage.js";
29
- export { JinaProvider as Jina } from "./jina.js";
30
- export { StabilityProvider as Stability } from "./stability.js";
31
- export { IdeogramProvider as Ideogram } from "./ideogram.js";
32
- export { RecraftProvider as Recraft } from "./recraft.js";
33
- export { ReplicateProvider as Replicate } from "./replicate.js";