@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
80 lines (79 loc) • 4.57 kB
TypeScript
/**
* The model catalogue — turning the registry into a routable pool.
*
* `ClassifierRouter` has always taken a host-declared `pool`, because the set
* of models a host is *willing to be billed for* is not something NeuroLink
* can invent. But the registry already knows 64 models across 7 providers
* (132 aliases), each carrying a context window, in/out pricing, speed and
* quality buckets, capability flags and seven 1–10 use-case scores — and
* routing read none of it beyond cost and quality. `maxContextTokens` in
* particular was never read anywhere.
*
* The registry covers 7 of the registered providers, so the catalogue is
* an addition to a declared pool, never a replacement: a host routing over
* LiteLLM, OpenRouter or a self-hosted model still declares those by hand,
* and `enrichCandidate` ranks declared and catalogue members on one scale.
*
* This module closes that gap for hosts that opt in (`catalog.enabled`): it
* builds pool members from the registry, intersected with the credentials
* actually present, and renders each as one line for a decision model to rank.
*
* Two constraints shape everything here:
*
* 1. **The rendered catalogue is a question, not state.** A decision model's
* binding limit is `state + the single longest question`, and the model
* choice question's `criteria` map is that question. Rendering has to stay
* terse and the count has to stay capped.
* 2. **A deterministic ranker must exist first.** Every other decision
* consumer falls back to what the code did before, but there was no
* "pick from the whole registry" behaviour to fall back to.
* {@link rankCatalogue}
* is that fallback, and it runs whenever no decision provider is
* configured, the call fails, or the verdict is not confident enough.
*
* @module routing/modelCatalog
*/
import type { ClassifierCandidate, ClassifierCatalogConfig, ClassifierDifficulty, ClassifierRouterInput, ClassifierRouterPoolMember, ModelInfo } from "../types/index.js";
/** Provider names this host has credentials for AND that generate text. */
export declare function reachableTextProviders(): Set<string>;
/**
* Rank candidates deterministically for a difficulty. The fallback that makes
* the catalogue safe to enable: with no decision provider configured this is
* the whole selection, and it never consults the network.
*
* `requiredCapabilities` filters leniently — a candidate with no recorded
* capabilities is kept rather than starving the pool on missing metadata,
* matching `ClassifierRouter.filterByCapabilities`.
*/
export declare function rankCatalogue(candidates: readonly ClassifierCandidate[], difficulty: ClassifierDifficulty, input?: Pick<ClassifierRouterInput, "estimatedInputTokens">, requiredCapabilities?: readonly string[]): ClassifierCandidate[];
/**
* Build catalogue pool members from the registry.
*
* Returns `[]` when the catalogue is disabled or nothing is reachable, which
* leaves the declared pool as the only source — the pre-catalogue behaviour.
*/
export declare function buildModelCatalog(config?: ClassifierCatalogConfig): ClassifierRouterPoolMember[];
/**
* Enrich a pool member into a classifier candidate, pulling registry metadata
* the member did not declare.
*
* Works for any pool, catalogue-derived or hand-declared: a host that listed
* `{ provider: "openai", model: "gpt-4o" }` gets the same context window and
* pricing as a catalogue entry, because both resolve through the registry.
* Members the registry does not know (LiteLLM, self-hosted) keep exactly what
* the host declared and are still rankable — `tierScore` falls back to
* neutral values rather than dropping them.
*/
export declare function enrichCandidate(id: string, member: ClassifierRouterPoolMember, difficulty: ClassifierDifficulty, registry?: Map<string, ModelInfo>): ClassifierCandidate;
/** Index the registry by model id once per build, for `enrichCandidate`. */
export declare function buildRegistryIndex(): Map<string, ModelInfo>;
/**
* Render one candidate as a single criteria line for the decision model.
*
* Terse on purpose: this text is multiplied by the candidate count inside a
* single question, and that question competes with the request for the
* ~33K ceiling. Numbers are rendered in units a reader can compare at a
* glance (K tokens, ¢ per 1K) because a decision model reads digits as text
* and does better with "128K" than with "131072".
*/
export declare function renderCandidate(candidate: ClassifierCandidate): string;