@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,88 @@
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.76.0",
3
+ "version": "9.78.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applications with 21+ providers: OpenAI, Anthropic, Google AI Studio, Google Vertex, AWS Bedrock, Azure OpenAI, Mistral, LiteLLM, SageMaker, Hugging Face, Ollama, OpenAI-compatible, OpenRouter, DeepSeek, NVIDIA NIM, LM Studio, llama.cpp, plus voice (OpenAI TTS, ElevenLabs, Deepgram, Azure Speech).",
6
6
  "author": {
@@ -122,11 +122,15 @@
122
122
  "test:tool-routing-cli": "pnpm run test:tool-routing-cli:vitest && npx tsx test/continuous-test-suite-tool-routing-cli.ts",
123
123
  "test:tool-dedup:vitest": "pnpm exec vitest run test/toolDedup.test.ts",
124
124
  "test:tool-dedup": "pnpm run test:tool-dedup:vitest && npx tsx test/continuous-test-suite-tool-dedup.ts",
125
+ "test:model-pool:vitest": "pnpm exec vitest run test/modelPool.test.ts",
126
+ "test:model-pool": "pnpm run test:model-pool:vitest && npx tsx test/continuous-test-suite-model-pool.ts",
125
127
  "test:ci": "pnpm run test && pnpm run test:client && pnpm run test:hitl",
126
128
  "// CI tier — fast, no live AI calls, safe for every commit": "",
127
129
  "test:unit:vitest": "pnpm exec vitest run test/toolRouting.test.ts",
128
130
  "test:tool-routing": "pnpm run test:unit:vitest && npx tsx test/continuous-test-suite-tool-routing.ts",
129
- "test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && 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:unit:vitest && pnpm run test:tool-routing-cli:vitest && pnpm run test:tool-dedup:vitest",
131
+ "test:tool-routing-semantic:vitest": "pnpm exec vitest run test/toolRoutingSemantic.test.ts",
132
+ "test:tool-routing-semantic": "pnpm run test:tool-routing-semantic:vitest && npx tsx test/continuous-test-suite-tool-routing-semantic.ts",
133
+ "test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && 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:unit:vitest && pnpm run test:tool-routing-cli:vitest && pnpm run test:tool-dedup:vitest && pnpm run test:model-pool:vitest && pnpm run test:tool-routing-semantic:vitest",
130
134
  "// 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)": "",
131
135
  "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",
132
136
  "// CI tier — product output (image/video/TTS/PPT) — costs $$ per run": "",