@juspay/neurolink 9.76.0 → 9.78.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 (51) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/browser/neurolink.min.js +341 -341
  3. package/dist/core/toolRouting.d.ts +9 -0
  4. package/dist/core/toolRouting.js +178 -1
  5. package/dist/core/toolRoutingEmbedding.d.ts +112 -0
  6. package/dist/core/toolRoutingEmbedding.js +322 -0
  7. package/dist/index.d.ts +31 -0
  8. package/dist/index.js +35 -0
  9. package/dist/lib/core/toolRouting.d.ts +9 -0
  10. package/dist/lib/core/toolRouting.js +178 -1
  11. package/dist/lib/core/toolRoutingEmbedding.d.ts +112 -0
  12. package/dist/lib/core/toolRoutingEmbedding.js +323 -0
  13. package/dist/lib/index.d.ts +31 -0
  14. package/dist/lib/index.js +35 -0
  15. package/dist/lib/neurolink.d.ts +22 -0
  16. package/dist/lib/neurolink.js +544 -175
  17. package/dist/lib/routing/index.d.ts +7 -0
  18. package/dist/lib/routing/index.js +8 -0
  19. package/dist/lib/routing/modelPool.d.ts +83 -0
  20. package/dist/lib/routing/modelPool.js +243 -0
  21. package/dist/lib/routing/requestRouter.d.ts +30 -0
  22. package/dist/lib/routing/requestRouter.js +81 -0
  23. package/dist/lib/types/config.d.ts +18 -0
  24. package/dist/lib/types/index.d.ts +2 -0
  25. package/dist/lib/types/index.js +4 -0
  26. package/dist/lib/types/modelPool.d.ts +47 -0
  27. package/dist/lib/types/modelPool.js +11 -0
  28. package/dist/lib/types/requestRouter.d.ts +75 -0
  29. package/dist/lib/types/requestRouter.js +16 -0
  30. package/dist/lib/types/toolRouting.d.ts +171 -0
  31. package/dist/lib/utils/providerErrorClassification.d.ts +24 -0
  32. package/dist/lib/utils/providerErrorClassification.js +89 -0
  33. package/dist/neurolink.d.ts +22 -0
  34. package/dist/neurolink.js +544 -175
  35. package/dist/routing/index.d.ts +7 -0
  36. package/dist/routing/index.js +7 -0
  37. package/dist/routing/modelPool.d.ts +83 -0
  38. package/dist/routing/modelPool.js +242 -0
  39. package/dist/routing/requestRouter.d.ts +30 -0
  40. package/dist/routing/requestRouter.js +80 -0
  41. package/dist/types/config.d.ts +18 -0
  42. package/dist/types/index.d.ts +2 -0
  43. package/dist/types/index.js +4 -0
  44. package/dist/types/modelPool.d.ts +47 -0
  45. package/dist/types/modelPool.js +10 -0
  46. package/dist/types/requestRouter.d.ts +75 -0
  47. package/dist/types/requestRouter.js +15 -0
  48. package/dist/types/toolRouting.d.ts +171 -0
  49. package/dist/utils/providerErrorClassification.d.ts +24 -0
  50. package/dist/utils/providerErrorClassification.js +88 -0
  51. package/package.json +6 -2
@@ -38,6 +38,71 @@ export type ToolRoutingModelConfig = {
38
38
  /** Router sampling temperature. Default: 0. */
39
39
  temperature?: number;
40
40
  };
41
+ /**
42
+ * Weights for the hybrid scoring formula used by `ToolEmbeddingIndex.rank()`.
43
+ * Scores are computed as: `cosine * cosine + bm25 * bm25Score` then
44
+ * normalized before sorting.
45
+ * Default: `{ cosine: 0.8, bm25: 0.2 }`.
46
+ */
47
+ export type ToolRetrievalWeights = {
48
+ /** Weight applied to the cosine-similarity (dense) component. */
49
+ cosine: number;
50
+ /** Weight applied to the BM25 (sparse/lexical) component. */
51
+ bm25: number;
52
+ };
53
+ /**
54
+ * Configuration for the L2 embedding fast-path (ITEM B).
55
+ *
56
+ * When enabled and the catalog's total tool count reaches `minToolsToActivate`,
57
+ * a hybrid cosine + BM25 retriever ranks all tools by relevance to the query
58
+ * and takes the top-`topK` candidates. This is far cheaper than an LLM call
59
+ * (sub-10 ms warm) and fires BEFORE or INSTEAD of the LLM router.
60
+ *
61
+ * Fail-open: any embedding error (missing provider, network failure, wrong
62
+ * model) silently falls back to the existing LLM-router / server-granularity
63
+ * path — the turn is never broken.
64
+ */
65
+ export type ToolRoutingEmbeddingConfig = {
66
+ /**
67
+ * Activate the embedding fast-path. Default: false (backward-compatible).
68
+ * Setting this to true without supplying `provider`/`model` causes the SDK
69
+ * to try the stream call's configured provider; if that provider does not
70
+ * support embeddings the layer fails open.
71
+ */
72
+ enabled?: boolean;
73
+ /**
74
+ * Maximum number of top-ranked tool candidates passed to the post-embedding
75
+ * decision stage. Default: 20.
76
+ */
77
+ topK?: number;
78
+ /**
79
+ * Minimum total tool count in the catalog before the embedding path
80
+ * activates. Below this threshold the catalog is small enough that the LLM
81
+ * router alone is cheap and fast. Default: 20.
82
+ */
83
+ minToolsToActivate?: number;
84
+ /**
85
+ * Weights for the hybrid scoring formula:
86
+ * score = cosine * cosineSim + bm25 * bm25Score (both normalized to [0,1])
87
+ * Default: `{ cosine: 0.8, bm25: 0.2 }`.
88
+ */
89
+ weights?: ToolRetrievalWeights;
90
+ /**
91
+ * Provider name to use for the embedding call (e.g. "openai", "vertex").
92
+ * Defaults to the stream/generate call's configured provider. The provider
93
+ * must support `embedMany()`.
94
+ */
95
+ provider?: string;
96
+ /**
97
+ * Embedding model name (provider-specific). When omitted the provider's
98
+ * default embedding model is used (e.g. text-embedding-3-small for OpenAI).
99
+ */
100
+ model?: string;
101
+ /**
102
+ * Timeout for embedding calls in milliseconds. Default: 10000.
103
+ */
104
+ timeoutMs?: number;
105
+ };
41
106
  /** Constructor-level configuration for pre-call tool routing. */
42
107
  export type ToolRoutingConfig = {
43
108
  /** Master switch. Routing runs only when true AND the server catalog is non-empty. */
@@ -88,6 +153,24 @@ export type ToolRoutingConfig = {
88
153
  /** Number of turns for which a previously-selected server stays warm. Default: 3. */
89
154
  turns?: number;
90
155
  };
156
+ /**
157
+ * L2 embedding fast-path (ITEM B). When enabled the SDK ranks tools by
158
+ * semantic + lexical relevance using a hybrid cosine/BM25 score and narrows
159
+ * the candidate set BEFORE (or instead of) the LLM router. Disabled by
160
+ * default for backward compatibility.
161
+ */
162
+ embedding?: ToolRoutingEmbeddingConfig;
163
+ /**
164
+ * Routing granularity (ITEM D).
165
+ *
166
+ * - `"server"` (default) — routing excludes the tools of entire unpicked
167
+ * servers. This is the original behavior.
168
+ * - `"tool"` — routing excludes individual tools that are not in the
169
+ * embedding top-K candidate set, regardless of which server they belong
170
+ * to. Requires `embedding.enabled: true`; if the embedding fast-path is
171
+ * off (or fails) the granularity falls back to `"server"` automatically.
172
+ */
173
+ granularity?: "server" | "tool";
91
174
  };
92
175
  /** Catalog entry pairing a server descriptor with its registered tool names. */
93
176
  export type ToolRoutingCatalogEntry = {
@@ -152,6 +235,19 @@ export type ToolRoutingDecision = {
152
235
  cacheHit: boolean;
153
236
  /** Wall-clock time spent in the routing resolution in milliseconds. */
154
237
  durationMs: number;
238
+ /** True when the L2 embedding fast-path ran and produced candidate results. */
239
+ embeddingActivated?: boolean;
240
+ /**
241
+ * Number of tool candidates produced by the embedding retriever before the
242
+ * post-embedding server or tool filtering step.
243
+ */
244
+ candidateToolCount?: number;
245
+ /**
246
+ * Granularity at which exclusions were applied ("server" or "tool").
247
+ * Matches `ToolRoutingConfig.granularity`; present only when routing was
248
+ * applied (outcome === "applied").
249
+ */
250
+ granularity?: "server" | "tool";
155
251
  };
156
252
  /** Parameters for `resolveToolRoutingExclusions()`. */
157
253
  export type ToolRoutingResolutionParams = {
@@ -176,4 +272,79 @@ export type ToolRoutingResolutionParams = {
176
272
  * the resolver.
177
273
  */
178
274
  emitDecision?: (decision: ToolRoutingDecision) => void;
275
+ /**
276
+ * Injected async function that converts an array of texts into embedding
277
+ * vectors. Built by the caller (NeuroLink) from the configured embedding
278
+ * provider so the resolver stays pure and free of provider imports.
279
+ * When undefined the embedding fast-path is skipped entirely.
280
+ */
281
+ embedFn?: (texts: string[]) => Promise<number[][]>;
282
+ /**
283
+ * Embedding fast-path configuration forwarded from `ToolRoutingConfig`.
284
+ * Only consulted when `embedFn` is provided.
285
+ */
286
+ embeddingConfig?: ToolRoutingEmbeddingConfig;
287
+ /**
288
+ * Routing granularity forwarded from `ToolRoutingConfig`. Default: "server".
289
+ */
290
+ granularity?: "server" | "tool";
291
+ /**
292
+ * Optional shared vector cache for the L2 embedding fast-path. When
293
+ * supplied, tool embedding vectors computed on prior turns are reused rather
294
+ * than re-fetched from the embedding provider on every call.
295
+ *
296
+ * The NeuroLink instance manages the lifecycle: it creates the Map once and
297
+ * passes the same reference across turns. It clears the reference when the
298
+ * tool catalog changes (via `setToolRoutingServers`) so stale vectors are
299
+ * never used after a catalog update.
300
+ */
301
+ embeddingVectorCache?: Map<string, number[]>;
302
+ };
303
+ /**
304
+ * A single item in the tool retrieval catalog, pairing a tool name with the
305
+ * text (tool description + server context) used to build its embedding vector.
306
+ */
307
+ export type ToolRetrievalItem = {
308
+ /** Fully-qualified tool name (e.g. `${serverId}_${toolName}`). */
309
+ name: string;
310
+ /** Descriptive text used as the embedding document for this tool. */
311
+ text: string;
312
+ };
313
+ /**
314
+ * One ranked result from `ToolEmbeddingIndex.rank()` or
315
+ * `selectRelevantToolNames()`.
316
+ */
317
+ export type ToolRetrievalRankedResult = {
318
+ /** Tool name (mirrors `ToolRetrievalItem.name`). */
319
+ name: string;
320
+ /** Combined hybrid score (higher = more relevant). */
321
+ score: number;
322
+ };
323
+ /**
324
+ * Options passed to `selectRelevantToolNames()` — the high-level convenience
325
+ * wrapper around `ToolEmbeddingIndex`.
326
+ */
327
+ export type ToolRetrievalSelectOptions = {
328
+ /** Maximum number of tool names to return. */
329
+ topK: number;
330
+ /** Optional weight override (defaults to `{ cosine: 0.8, bm25: 0.2 }`). */
331
+ weights?: ToolRetrievalWeights;
332
+ /**
333
+ * Async function that converts an array of text strings into embedding
334
+ * vectors. Must return one vector per input text in the same order.
335
+ * Errors thrown here propagate to the caller (so it can fail open).
336
+ */
337
+ embedFn: (texts: string[]) => Promise<number[][]>;
338
+ /**
339
+ * Optional shared vector cache (keyed by text string). When supplied the
340
+ * underlying `ToolEmbeddingIndex` reads from and writes to this Map so that
341
+ * tool vectors computed on a prior call are reused on subsequent calls for
342
+ * the same item text. Callers that want warm-cache behavior across turns
343
+ * should pass the same Map instance each time.
344
+ */
345
+ vectorCache?: Map<string, number[]>;
346
+ /**
347
+ * Timeout for each embedding provider call in milliseconds. Default: 10000.
348
+ */
349
+ timeoutMs?: number;
179
350
  };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared provider-error classification utilities used by both neurolink.ts and
3
+ * the ModelPool routing subsystem.
4
+ *
5
+ * Extracted here to avoid duplication and to ensure that typed error classes
6
+ * (AuthenticationError, AuthorizationError, ModelAccessDeniedError) are
7
+ * checked consistently in both code paths.
8
+ */
9
+ /**
10
+ * Detects whether an error object looks like a model-access-denied condition.
11
+ * Matches LiteLLM "team not allowed" / "team can only access models=[...]"
12
+ * plus typed-error name/code markers when the full typed class is not present.
13
+ */
14
+ export declare function looksLikeModelAccessDenied(error: unknown): boolean;
15
+ /**
16
+ * Returns true when the error is definitively non-retryable: typed error
17
+ * classes (auth, access-denied, invalid-model), non-retryable HTTP status
18
+ * codes, or deterministic 400-class message patterns.
19
+ *
20
+ * NOTE: ContextBudgetExceededError is intentionally NOT non-retryable —
21
+ * each provider has its own context window, so a budget rejection on one
22
+ * provider does not preclude another provider accepting the same payload.
23
+ */
24
+ export declare function isNonRetryableProviderError(error: unknown): boolean;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Shared provider-error classification utilities used by both neurolink.ts and
3
+ * the ModelPool routing subsystem.
4
+ *
5
+ * Extracted here to avoid duplication and to ensure that typed error classes
6
+ * (AuthenticationError, AuthorizationError, ModelAccessDeniedError) are
7
+ * checked consistently in both code paths.
8
+ */
9
+ import { AuthenticationError, AuthorizationError, InvalidModelError, ModelAccessDeniedError, } from "../types/index.js";
10
+ import { NON_RETRYABLE_HTTP_STATUS_CODES, isDeterministicClientErrorMessage, } from "./retryability.js";
11
+ /**
12
+ * Detects whether an error object looks like a model-access-denied condition.
13
+ * Matches LiteLLM "team not allowed" / "team can only access models=[...]"
14
+ * plus typed-error name/code markers when the full typed class is not present.
15
+ */
16
+ export function looksLikeModelAccessDenied(error) {
17
+ if (!error) {
18
+ return false;
19
+ }
20
+ const e = error;
21
+ if (e.name === "ModelAccessDeniedError") {
22
+ return true;
23
+ }
24
+ if (e.code === "MODEL_ACCESS_DENIED") {
25
+ return true;
26
+ }
27
+ const msg = typeof e.message === "string"
28
+ ? e.message
29
+ : error instanceof Error
30
+ ? error.message
31
+ : String(error);
32
+ if (!msg) {
33
+ return false;
34
+ }
35
+ const lower = msg.toLowerCase();
36
+ return ((lower.includes("team") && lower.includes("not allowed")) ||
37
+ lower.includes("team can only access") ||
38
+ /not\s+allowed\s+to\s+access\s+(this\s+)?model/i.test(msg));
39
+ }
40
+ /**
41
+ * Returns true when the error is definitively non-retryable: typed error
42
+ * classes (auth, access-denied, invalid-model), non-retryable HTTP status
43
+ * codes, or deterministic 400-class message patterns.
44
+ *
45
+ * NOTE: ContextBudgetExceededError is intentionally NOT non-retryable —
46
+ * each provider has its own context window, so a budget rejection on one
47
+ * provider does not preclude another provider accepting the same payload.
48
+ */
49
+ export function isNonRetryableProviderError(error) {
50
+ if (error instanceof InvalidModelError) {
51
+ return true;
52
+ }
53
+ if (error instanceof AuthenticationError) {
54
+ return true;
55
+ }
56
+ if (error instanceof AuthorizationError) {
57
+ return true;
58
+ }
59
+ if (error instanceof ModelAccessDeniedError) {
60
+ return true;
61
+ }
62
+ if (error && typeof error === "object") {
63
+ const err = error;
64
+ const status = typeof err.status === "number"
65
+ ? err.status
66
+ : typeof err.statusCode === "number"
67
+ ? err.statusCode
68
+ : undefined;
69
+ if (status !== undefined &&
70
+ NON_RETRYABLE_HTTP_STATUS_CODES.includes(status)) {
71
+ return true;
72
+ }
73
+ }
74
+ if (error instanceof Error) {
75
+ const msg = error.message;
76
+ if (msg.includes("NOT_FOUND") ||
77
+ msg.includes("Model Not Found") ||
78
+ msg.includes("model not found") ||
79
+ msg.includes("PERMISSION_DENIED") ||
80
+ msg.includes("UNAUTHENTICATED")) {
81
+ return true;
82
+ }
83
+ if (isDeterministicClientErrorMessage(msg)) {
84
+ return true;
85
+ }
86
+ }
87
+ return false;
88
+ }
89
+ //# sourceMappingURL=providerErrorClassification.js.map
@@ -102,6 +102,7 @@ export declare class NeuroLink {
102
102
  private conversationMemoryConfig?;
103
103
  private toolRoutingConfig?;
104
104
  private toolRoutingCacheInstance?;
105
+ private toolRoutingVectorCache?;
105
106
  private toolDedupConfig?;
106
107
  private enableOrchestration;
107
108
  private authProvider?;
@@ -109,6 +110,8 @@ export declare class NeuroLink {
109
110
  private authInitPromise?;
110
111
  private credentials?;
111
112
  private readonly fallbackConfig;
113
+ private readonly modelPool;
114
+ private readonly requestRouter;
112
115
  /**
113
116
  * Merge instance-level credentials with per-call credentials.
114
117
  *
@@ -612,6 +615,25 @@ export declare class NeuroLink {
612
615
  private maybeHandleEarlyGenerateResult;
613
616
  private runStandardGenerateRequest;
614
617
  private maybeApplyGenerateOrchestration;
618
+ /**
619
+ * Applies the host-configured `requestRouter` to `options` in place.
620
+ *
621
+ * The router is skipped when:
622
+ * - no `requestRouter` is configured on this instance, or
623
+ * - the caller explicitly set both `options.provider` AND `options.model`
624
+ * (we only skip the router if BOTH are set; a caller setting only one
625
+ * still lets the router fill in the other field).
626
+ *
627
+ * Fails open: any router error is logged at WARN level and the call
628
+ * continues with the original options unmodified.
629
+ *
630
+ * @param options — the mutable options object (generate or stream).
631
+ * @param promptText — the text prompt used to build the RouterInputContext.
632
+ * @param hasTools — true when at least one tool is available for this call.
633
+ * @param requiresVision — true when the call includes image attachments.
634
+ * @param thinkingLevel — optional thinking level string from the call options.
635
+ */
636
+ private applyRequestRouter;
615
637
  private prepareGenerateAugmentations;
616
638
  private buildGenerateTextOptions;
617
639
  private finalizeGenerateRequestResult;