@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
package/dist/index.d.ts CHANGED
@@ -219,6 +219,7 @@ export { logger } from "./utils/logger.js";
219
219
  export { getPoolStats } from "./utils/redis.js";
220
220
  export { ToolRoutingCache } from "./core/toolRoutingCache.js";
221
221
  export { resolveToolRoutingExclusions, buildToolRoutingCatalog, buildRoutingQueryFromHistory, DEFAULT_ROUTER_PROMPT_PREFIX, } from "./core/toolRouting.js";
222
+ export { cosineSimilarity, ToolEmbeddingIndex, selectRelevantToolNames, } from "./core/toolRoutingEmbedding.js";
222
223
  export declare function initializeTelemetry(): Promise<boolean>;
223
224
  export declare function getTelemetryStatus(): Promise<{
224
225
  enabled: boolean;
@@ -337,6 +338,36 @@ export { BALANCED_ADAPTIVE_WORKFLOW, createAdaptiveWorkflow, QUALITY_MAX_WORKFLO
337
338
  export { CONSENSUS_3_FAST_WORKFLOW, CONSENSUS_3_WORKFLOW, createConsensus3WithPrompt, } from "./workflow/workflows/consensusWorkflow.js";
338
339
  export { AGGRESSIVE_FALLBACK_WORKFLOW, FAST_FALLBACK_WORKFLOW, } from "./workflow/workflows/fallbackWorkflow.js";
339
340
  export { createMultiJudgeWorkflow, MULTI_JUDGE_3_WORKFLOW, MULTI_JUDGE_5_WORKFLOW, } from "./workflow/workflows/multiJudgeWorkflow.js";
341
+ /**
342
+ * ModelPool and RequestRouter — opt-in multi-provider failover with
343
+ * error-class-aware cooldown, and a pluggable pre-call provider/model router.
344
+ *
345
+ * @example ModelPool
346
+ * ```typescript
347
+ * import { ModelPool, classifyProviderError } from '@juspay/neurolink';
348
+ *
349
+ * const pool = new ModelPool({
350
+ * members: [
351
+ * { provider: 'anthropic', model: 'claude-sonnet-4-5' },
352
+ * { provider: 'vertex', model: 'gemini-2.5-flash' },
353
+ * ],
354
+ * strategy: 'priority',
355
+ * cooldownMs: 30_000,
356
+ * });
357
+ * ```
358
+ *
359
+ * @example RequestRouter
360
+ * ```typescript
361
+ * import { createDefaultRequestRouter } from '@juspay/neurolink';
362
+ *
363
+ * const router = createDefaultRequestRouter({
364
+ * visionTier: { provider: 'vertex', model: 'gemini-2.5-pro' },
365
+ * largeTier: { provider: 'anthropic', model: 'claude-opus-4-5' },
366
+ * smallTier: { provider: 'anthropic', model: 'claude-haiku-3-5' },
367
+ * });
368
+ * ```
369
+ */
370
+ export { classifyProviderError, ModelPool, createDefaultRequestRouter, } from "./routing/index.js";
340
371
  /**
341
372
  * Server Adapters for exposing NeuroLink as HTTP APIs
342
373
  *
package/dist/index.js CHANGED
@@ -362,6 +362,8 @@ export { getPoolStats } from "./utils/redis.js";
362
362
  export { ToolRoutingCache } from "./core/toolRoutingCache.js";
363
363
  // Pre-call tool routing resolver — exported for host integrations and testing
364
364
  export { resolveToolRoutingExclusions, buildToolRoutingCatalog, buildRoutingQueryFromHistory, DEFAULT_ROUTER_PROMPT_PREFIX, } from "./core/toolRouting.js";
365
+ // Pre-call tool routing — embedding fast-path (ITEM B: L2 semantic retrieval)
366
+ export { cosineSimilarity, ToolEmbeddingIndex, selectRelevantToolNames, } from "./core/toolRoutingEmbedding.js";
365
367
  // ============================================================================
366
368
  // REAL-TIME SERVICES & TELEMETRY - Enterprise Platform Features
367
369
  // ============================================================================
@@ -541,6 +543,39 @@ export { AGGRESSIVE_FALLBACK_WORKFLOW, FAST_FALLBACK_WORKFLOW, } from "./workflo
541
543
  // Pre-built workflows - Multi-judge
542
544
  export { createMultiJudgeWorkflow, MULTI_JUDGE_3_WORKFLOW, MULTI_JUDGE_5_WORKFLOW, } from "./workflow/workflows/multiJudgeWorkflow.js";
543
545
  // ============================================================================
546
+ // MODEL POOL & REQUEST ROUTER - Pluggable pre-call routing + failover
547
+ // ============================================================================
548
+ /**
549
+ * ModelPool and RequestRouter — opt-in multi-provider failover with
550
+ * error-class-aware cooldown, and a pluggable pre-call provider/model router.
551
+ *
552
+ * @example ModelPool
553
+ * ```typescript
554
+ * import { ModelPool, classifyProviderError } from '@juspay/neurolink';
555
+ *
556
+ * const pool = new ModelPool({
557
+ * members: [
558
+ * { provider: 'anthropic', model: 'claude-sonnet-4-5' },
559
+ * { provider: 'vertex', model: 'gemini-2.5-flash' },
560
+ * ],
561
+ * strategy: 'priority',
562
+ * cooldownMs: 30_000,
563
+ * });
564
+ * ```
565
+ *
566
+ * @example RequestRouter
567
+ * ```typescript
568
+ * import { createDefaultRequestRouter } from '@juspay/neurolink';
569
+ *
570
+ * const router = createDefaultRequestRouter({
571
+ * visionTier: { provider: 'vertex', model: 'gemini-2.5-pro' },
572
+ * largeTier: { provider: 'anthropic', model: 'claude-opus-4-5' },
573
+ * smallTier: { provider: 'anthropic', model: 'claude-haiku-3-5' },
574
+ * });
575
+ * ```
576
+ */
577
+ export { classifyProviderError, ModelPool, createDefaultRequestRouter, } from "./routing/index.js";
578
+ // ============================================================================
544
579
  // SERVER ADAPTERS - HTTP API Framework Integration
545
580
  // ============================================================================
546
581
  // Server Types
@@ -16,6 +16,15 @@
16
16
  * Fail-open by design: missing query, <=1 routable server, validation
17
17
  * failure, empty/invalid pick, or any thrown error returns an EMPTY list
18
18
  * (exclude nothing -> all tools, identical to routing disabled). Never throws.
19
+ *
20
+ * L2 embedding fast-path (ITEM B): when `params.embedFn` is supplied and the
21
+ * catalog's total tool count exceeds `embeddingConfig.minToolsToActivate`, a
22
+ * hybrid cosine+BM25 retriever narrows the candidate set BEFORE the LLM router.
23
+ * Any embedding failure falls back to the standard LLM-router path.
24
+ *
25
+ * Tool granularity (ITEM D): when `params.granularity === "tool"`, individual
26
+ * unpicked tools are excluded rather than whole servers. Falls back to "server"
27
+ * if the embedding path is disabled or fails.
19
28
  */
20
29
  import type { ToolRoutingCatalogEntry, ToolRoutingResolutionParams, ToolRoutingServerDescriptor } from "../types/index.js";
21
30
  /**
@@ -16,13 +16,27 @@
16
16
  * Fail-open by design: missing query, <=1 routable server, validation
17
17
  * failure, empty/invalid pick, or any thrown error returns an EMPTY list
18
18
  * (exclude nothing -> all tools, identical to routing disabled). Never throws.
19
+ *
20
+ * L2 embedding fast-path (ITEM B): when `params.embedFn` is supplied and the
21
+ * catalog's total tool count exceeds `embeddingConfig.minToolsToActivate`, a
22
+ * hybrid cosine+BM25 retriever narrows the candidate set BEFORE the LLM router.
23
+ * Any embedding failure falls back to the standard LLM-router path.
24
+ *
25
+ * Tool granularity (ITEM D): when `params.granularity === "tool"`, individual
26
+ * unpicked tools are excluded rather than whole servers. Falls back to "server"
27
+ * if the embedding path is disabled or fails.
19
28
  */
20
29
  import { z } from "zod";
21
30
  import { logger } from "../utils/logger.js";
22
31
  import { withTimeout } from "../utils/async/index.js";
32
+ import { selectRelevantToolNames } from "./toolRoutingEmbedding.js";
23
33
  const routerOutputSchema = z.object({
24
34
  servers: z.array(z.string()),
25
35
  });
36
+ /** Default minimum catalog tool count that activates the embedding fast-path. */
37
+ const DEFAULT_EMBEDDING_MIN_TOOLS = 20;
38
+ /** Default number of top-ranked tool candidates the embedding retriever keeps. */
39
+ const DEFAULT_EMBEDDING_TOP_K = 20;
26
40
  /**
27
41
  * Upper bound on the user-query text interpolated into the router prompt.
28
42
  * The query is untrusted and can attempt prompt injection — the blast radius
@@ -169,12 +183,116 @@ function safeEmitDecision(emitDecision, decision) {
169
183
  // Intentionally swallowed — telemetry must never affect routing behaviour.
170
184
  }
171
185
  }
186
+ // ---------------------------------------------------------------------------
187
+ // L2 embedding fast-path helpers (ITEM B + D)
188
+ // ---------------------------------------------------------------------------
189
+ // ToolRoutingEmbeddingFastPathResult is imported from src/lib/types/toolRouting.ts
190
+ // (Critical Rule 2 — all type aliases live in src/lib/types/).
191
+ /**
192
+ * Runs the L2 embedding fast-path over `routableServers` for `userQuery`.
193
+ *
194
+ * Returns a result with `embeddingActivated: false` on any skip or error —
195
+ * the caller must treat this as a "fall back to LLM router" signal.
196
+ *
197
+ * This function is PURE with respect to provider access: the caller injects
198
+ * `embedFn` so no provider classes are imported here.
199
+ */
200
+ async function runEmbeddingFastPath(userQuery, routableServers, alwaysIncludeServerIds, embedFn, opts) {
201
+ const { granularity, vectorCache } = opts;
202
+ const noop = {
203
+ excludedToolNames: [],
204
+ embeddingActivated: false,
205
+ candidateToolCount: 0,
206
+ granularity,
207
+ };
208
+ // Count routable tools (always-include servers are already excluded from
209
+ // routableServers at call site, but be defensive).
210
+ const routableToolCount = routableServers.reduce((sum, server) => sum + server.toolNames.length, 0);
211
+ if (routableToolCount < opts.minToolsToActivate) {
212
+ logger.debug("[ToolRouting] L2 embedding skipped — catalog below threshold", {
213
+ toolCount: routableToolCount,
214
+ minToolsToActivate: opts.minToolsToActivate,
215
+ });
216
+ return noop;
217
+ }
218
+ // Build retrieval items: one per individual tool, with text =
219
+ // "<server description> — <tool name>" so both server context and the tool
220
+ // identifier itself inform the embedding.
221
+ const items = routableServers.flatMap((server) => server.toolNames.map((toolName) => ({
222
+ name: toolName,
223
+ text: `${server.description} — ${toolName}`,
224
+ })));
225
+ try {
226
+ const topK = Math.min(opts.topK, items.length);
227
+ const topToolNames = await selectRelevantToolNames(userQuery, items, {
228
+ topK,
229
+ weights: opts.weights,
230
+ embedFn,
231
+ vectorCache,
232
+ });
233
+ const topToolSet = new Set(topToolNames);
234
+ if (granularity === "tool") {
235
+ // Tool-granularity (ITEM D): exclude every catalog tool NOT in the top-K,
236
+ // regardless of server. Always-include server tools are not in
237
+ // routableServers so they are untouched.
238
+ const excludedToolNames = items
239
+ .map((item) => item.name)
240
+ .filter((name) => !topToolSet.has(name));
241
+ logger.debug("[ToolRouting] L2 embedding applied (tool granularity)", {
242
+ totalTools: items.length,
243
+ topK,
244
+ keptTools: topToolNames.length,
245
+ excludedTools: excludedToolNames.length,
246
+ });
247
+ return {
248
+ excludedToolNames,
249
+ embeddingActivated: true,
250
+ candidateToolCount: topToolNames.length,
251
+ granularity: "tool",
252
+ };
253
+ }
254
+ // Server-granularity (default): map surviving tools back to their servers.
255
+ // A server is "kept" if at least one of its tools survived the top-K cut.
256
+ // The tools of every server with ZERO surviving tools are excluded.
257
+ const keptServerIds = new Set(routableServers
258
+ .filter((server) => server.toolNames.some((toolName) => topToolSet.has(toolName)))
259
+ .map((server) => server.id));
260
+ // Never exclude always-include servers (defensive — they should not be in
261
+ // routableServers, but guard against caller mistakes).
262
+ const alwaysSet = new Set(alwaysIncludeServerIds);
263
+ const excludedServers = routableServers.filter((server) => !keptServerIds.has(server.id) && !alwaysSet.has(server.id));
264
+ const excludedToolNames = excludedServers.flatMap((server) => server.toolNames);
265
+ logger.debug("[ToolRouting] L2 embedding applied (server granularity)", {
266
+ totalTools: items.length,
267
+ topK,
268
+ keptServers: keptServerIds.size,
269
+ excludedServers: excludedServers.length,
270
+ excludedTools: excludedToolNames.length,
271
+ });
272
+ return {
273
+ excludedToolNames,
274
+ embeddingActivated: true,
275
+ candidateToolCount: topToolNames.length,
276
+ granularity: "server",
277
+ };
278
+ }
279
+ catch (embeddingError) {
280
+ logger.warn("[ToolRouting] L2 embedding fast-path failed, falling back to LLM router", {
281
+ error: embeddingError instanceof Error
282
+ ? embeddingError.message
283
+ : String(embeddingError),
284
+ });
285
+ return noop;
286
+ }
287
+ }
172
288
  /**
173
289
  * Resolves which registered tool names to EXCLUDE for a single stream() turn.
174
290
  * Returns an empty list on any skip/failure path — see module doc.
175
291
  */
176
292
  export async function resolveToolRoutingExclusions(params) {
177
- const { catalog, alwaysIncludeServerIds, userQuery, routerPromptPrefix, routerModel, timeoutMs, generateFn, emitDecision, } = params;
293
+ const { catalog, alwaysIncludeServerIds, userQuery, routerPromptPrefix, routerModel, timeoutMs, generateFn, emitDecision,
294
+ // L2 / ITEM D parameters (all optional — omitting reproduces today's behavior)
295
+ embedFn, embeddingConfig, granularity = "server", embeddingVectorCache, } = params;
178
296
  const routableServers = catalog.filter((server) => !alwaysIncludeServerIds.includes(server.id));
179
297
  const routingStartTime = Date.now();
180
298
  try {
@@ -198,6 +316,64 @@ export async function resolveToolRoutingExclusions(params) {
198
316
  });
199
317
  return [];
200
318
  }
319
+ // -------------------------------------------------------------------------
320
+ // L2 EMBEDDING FAST-PATH (ITEM B + D)
321
+ // -------------------------------------------------------------------------
322
+ // Attempt the embedding retriever when an embedFn is provided. On success,
323
+ // return the embedding-derived exclusion list immediately WITHOUT an LLM
324
+ // call. On any failure (embedFn throws, threshold not met, etc.), fall
325
+ // through to the existing LLM-router path below.
326
+ if (embedFn) {
327
+ const embResult = await runEmbeddingFastPath(userQuery, routableServers, alwaysIncludeServerIds, embedFn, {
328
+ topK: embeddingConfig?.topK ?? DEFAULT_EMBEDDING_TOP_K,
329
+ minToolsToActivate: embeddingConfig?.minToolsToActivate ?? DEFAULT_EMBEDDING_MIN_TOOLS,
330
+ weights: embeddingConfig?.weights,
331
+ granularity,
332
+ vectorCache: embeddingVectorCache,
333
+ });
334
+ if (embResult.embeddingActivated) {
335
+ logger.debug("[ToolRouting] L2 embedding fast-path succeeded", {
336
+ granularity: embResult.granularity,
337
+ candidateToolCount: embResult.candidateToolCount,
338
+ excludedToolCount: embResult.excludedToolNames.length,
339
+ routableServerCount: routableServers.length,
340
+ durationMs: Date.now() - routingStartTime,
341
+ });
342
+ // For server-granularity results, reconstruct selected/excluded server
343
+ // ids for the decision so telemetry fields are consistent.
344
+ let selectedServerIds = [];
345
+ let excludedServerIds = [];
346
+ if (embResult.granularity === "server") {
347
+ const excludedToolSet = new Set(embResult.excludedToolNames);
348
+ // A server is "excluded" if ALL its tools are excluded.
349
+ excludedServerIds = routableServers
350
+ .filter((server) => server.toolNames.every((name) => excludedToolSet.has(name)))
351
+ .map((server) => server.id);
352
+ selectedServerIds = routableServers
353
+ .filter((server) => !excludedServerIds.includes(server.id))
354
+ .map((server) => server.id);
355
+ }
356
+ safeEmitDecision(emitDecision, {
357
+ outcome: "applied",
358
+ selectedServerIds,
359
+ excludedServerIds,
360
+ hallucinatedIds: [],
361
+ excludedToolCount: embResult.excludedToolNames.length,
362
+ routableServerCount: routableServers.length,
363
+ cacheHit: false,
364
+ durationMs: Date.now() - routingStartTime,
365
+ embeddingActivated: true,
366
+ candidateToolCount: embResult.candidateToolCount,
367
+ granularity: embResult.granularity,
368
+ });
369
+ return embResult.excludedToolNames;
370
+ }
371
+ // embedFn was provided but threshold not met OR retriever failed open —
372
+ // fall through to the LLM-router path below.
373
+ }
374
+ // -------------------------------------------------------------------------
375
+ // EXISTING LLM ROUTER PATH (L3) — runs when embedding is off or fell back
376
+ // -------------------------------------------------------------------------
201
377
  const routerPrompt = buildRouterPrompt(userQuery, routableServers, routerPromptPrefix);
202
378
  // `timeout` lets the provider abort its own request (frees the socket);
203
379
  // withTimeout adds a hard wall-clock ceiling over the whole call so router
@@ -290,6 +466,7 @@ export async function resolveToolRoutingExclusions(params) {
290
466
  routableServerCount: routableServers.length,
291
467
  cacheHit: false,
292
468
  durationMs: Date.now() - routingStartTime,
469
+ granularity: "server",
293
470
  });
294
471
  return excludedToolNames;
295
472
  }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Embedding-retrieval fast-path for pre-call tool routing (ITEM B / L2).
3
+ *
4
+ * For large tool catalogs, ranking by semantic similarity is far cheaper than
5
+ * an LLM router call and benchmarks at sub-10 ms when embedding vectors are
6
+ * cached. This module is intentionally PURE with no provider imports: the
7
+ * caller injects an `embedFn` so the retriever stays testable and free of
8
+ * circular dependencies.
9
+ *
10
+ * Hybrid scoring formula:
11
+ * score = weights.cosine * cosineSimilarity(queryVec, toolVec)
12
+ * + weights.bm25 * bm25Score(query, toolText)
13
+ *
14
+ * Both components are independently normalized to [0, 1] before combination
15
+ * so neither scale dominates.
16
+ *
17
+ * Fail-safe contract:
18
+ * `ToolEmbeddingIndex.rank()` and `selectRelevantToolNames()` propagate any
19
+ * error thrown by the injected `embedFn`. The CALLER (tool-routing
20
+ * orchestration) is responsible for catching that error and degrading
21
+ * gracefully to the LLM-router / server-granularity path.
22
+ *
23
+ * Determinism:
24
+ * All public functions are deterministic given the same inputs and embedFn.
25
+ * No Math.random(), no argless Date, no global mutable state outside the
26
+ * instance-level vector cache (which is keyed by text content).
27
+ */
28
+ import type { ToolRetrievalItem, ToolRetrievalRankedResult, ToolRetrievalSelectOptions, ToolRetrievalWeights } from "../types/index.js";
29
+ /**
30
+ * Cosine similarity between two vectors.
31
+ *
32
+ * Returns 0 when either vector is zero-length, has zero magnitude, or the
33
+ * lengths differ — all of which indicate the comparison is meaningless.
34
+ */
35
+ export declare function cosineSimilarity(a: number[], b: number[]): number;
36
+ /**
37
+ * An in-process index that ranks tool catalog items by hybrid semantic +
38
+ * lexical relevance to a query.
39
+ *
40
+ * Embedding vectors for tool descriptions are computed lazily on the first
41
+ * `rank()` call and cached by description text, so subsequent turns that
42
+ * share the same catalog pay only the cost of embedding the query itself.
43
+ *
44
+ * ### Fail-safe
45
+ * Any error thrown by `embedFn` propagates out of `rank()`. The CALLER must
46
+ * catch it and degrade to the LLM-router path; `ToolEmbeddingIndex` itself
47
+ * never silently swallows embedFn errors.
48
+ *
49
+ * ### Thread-safety
50
+ * The internal cache is a plain `Map`. Node.js is single-threaded so there
51
+ * are no data races, but if the same index instance is used concurrently
52
+ * (e.g. two turns in parallel) both calls will race to populate the cache;
53
+ * the last writer wins (same content either way because `embedFn` is
54
+ * deterministic for a given text).
55
+ */
56
+ export declare class ToolEmbeddingIndex {
57
+ private readonly items;
58
+ private readonly embedFn;
59
+ private bm25CorpusCache?;
60
+ /**
61
+ * Cached embedding vectors keyed by the item's `text` field.
62
+ * Populated on first `rank()` call for texts not yet seen.
63
+ *
64
+ * Callers can share a single Map across multiple index instances (e.g.
65
+ * across turns in the same NeuroLink session) so tool vectors are computed
66
+ * once and reused. Pass the shared Map via the constructor's third argument.
67
+ */
68
+ private readonly vectorCache;
69
+ constructor(items: ToolRetrievalItem[], embedFn: (texts: string[]) => Promise<number[][]>,
70
+ /**
71
+ * Optional shared vector cache. When supplied, cached vectors from
72
+ * previous turns are reused and any newly-computed vectors are stored
73
+ * into this same Map, making it warm for the next call.
74
+ */
75
+ sharedVectorCache?: Map<string, number[]>);
76
+ /**
77
+ * Returns the top-K catalog items ranked by hybrid score descending.
78
+ *
79
+ * @throws If `embedFn` throws — propagated verbatim so the caller can fail
80
+ * open. No wrapping, no swallowing.
81
+ */
82
+ rank(query: string, opts: {
83
+ topK: number;
84
+ weights?: ToolRetrievalWeights;
85
+ timeoutMs?: number;
86
+ }): Promise<ToolRetrievalRankedResult[]>;
87
+ }
88
+ /**
89
+ * Selects the most relevant tool names from a catalog given a query.
90
+ *
91
+ * Creates a temporary `ToolEmbeddingIndex`, runs `rank()`, and returns just
92
+ * the tool names. Use `ToolEmbeddingIndex` directly when you want to reuse
93
+ * cached tool vectors across multiple queries (e.g. multiple turns with the
94
+ * same catalog).
95
+ *
96
+ * @throws If `opts.embedFn` throws — propagated so the caller can degrade to
97
+ * the LLM-router path.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * const tools = await selectRelevantToolNames(
102
+ * "show me yesterday's sales",
103
+ * catalog.flatMap(server => server.toolNames.map(name => ({
104
+ * name,
105
+ * text: `${server.description} — ${name}`,
106
+ * }))),
107
+ * { topK: 5, embedFn: myEmbedFn },
108
+ * );
109
+ * // => ["analytics_getSales", "analytics_getRevenue", ...]
110
+ * ```
111
+ */
112
+ export declare function selectRelevantToolNames(query: string, items: ToolRetrievalItem[], opts: ToolRetrievalSelectOptions): Promise<string[]>;