@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
81 lines (80 loc) • 3.29 kB
JavaScript
/**
* Default heuristic request router.
*
* `createDefaultRequestRouter` produces a `RequestRouter` function that maps
* lightweight request characteristics to a provider/model/region override.
* It is entirely optional — hosts may supply their own `RequestRouter` function
* instead, or disable routing entirely by not passing either.
*
* This PR adds `requestRouter` (and `modelPool`) as NeuroLink constructor
* options. The router is skipped only when BOTH options.provider AND
* options.model are explicitly set by the caller — a partial pin (e.g. only
* provider) still allows the router to run.
*
* Decision logic (checked in order):
* 1. Vision request → visionTier
* 2. Large input or tools → largeTier
* 3. Default → smallTier (only when config.smallTier is provided)
*
* When none of the configured tiers matches (or when the tier is not
* configured), the router returns `{}` which is a no-op — the caller keeps
* whatever provider/model was set by the user.
*/
/** Token threshold above which we treat a request as "large". */
const DEFAULT_LARGE_TOKEN_THRESHOLD = 32_000;
/**
* Built-in defaults for each tier.
* These are intentionally conservative — hosts should override them to match
* their provider credentials and preferred models.
*/
const DEFAULT_VISION_TIER = {
provider: "vertex",
model: "gemini-2.5-flash",
};
const DEFAULT_LARGE_TIER = {
provider: "anthropic",
model: "claude-sonnet-4-5",
};
const DEFAULT_SMALL_TIER = {
provider: "anthropic",
model: "claude-haiku-3-5",
};
function tierToDecision(tier, reason) {
return {
provider: tier.provider,
model: tier.model,
region: tier.region,
reason,
};
}
/**
* Creates a heuristic `RequestRouter` from a `DefaultRequestRouterConfig`.
*
* @param config — optional tier overrides; built-in defaults apply when omitted.
* @returns a synchronous `RequestRouter` function (satisfies the async signature).
*/
export function createDefaultRequestRouter(config) {
const largeTokenThreshold = config?.largeInputTokenThreshold ?? DEFAULT_LARGE_TOKEN_THRESHOLD;
const visionTier = config?.visionTier ?? DEFAULT_VISION_TIER;
const largeTier = config?.largeTier ?? DEFAULT_LARGE_TIER;
const smallTier = config?.smallTier ?? DEFAULT_SMALL_TIER;
return function defaultRequestRouter(ctx) {
// 1. Vision
if (ctx.requiresVision) {
return tierToDecision(visionTier, "vision request detected");
}
// 2. Large input or tool-heavy
const tokenCount = ctx.estimatedInputTokens ?? 0;
if (tokenCount >= largeTokenThreshold || ctx.hasTools) {
return tierToDecision(largeTier, ctx.hasTools ? "tool-enabled request" : "large input detected");
}
// 3. Default small/fast tier — only when config.smallTier is explicitly
// provided. When not configured, return {} (no-op) so callers that did
// not opt into a small tier see no surprise provider override.
if (config?.smallTier !== undefined) {
return tierToDecision(smallTier, "default small tier");
}
// No override — let the caller keep its own provider/model.
return {};
};
}