@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
84 lines (83 loc) • 3.46 kB
TypeScript
/**
* ModelPool runtime — multi-provider failover with per-member cooldown.
*
* This module is PURE (no provider imports, no circular dependencies).
* It works exclusively with provider NAMES + (provider, model, region) tuples;
* the existing AIProviderFactory creates actual provider instances.
*
* Usage:
* const pool = new ModelPool({ members: [...], strategy: "round-robin" });
* const member = pool.selectNext();
* try {
* await callProvider(member);
* pool.recordSuccess(member);
* } catch (err) {
* pool.recordFailure(member, classifyProviderError(err));
* // try next member ...
* }
*/
import type { ModelPoolConfig, ModelPoolMember, ProviderErrorClass } from "../types/index.js";
/**
* Classify a provider error into a coarse `ProviderErrorClass`.
*
* Rules (checked in order):
* 1. HTTP 429 or message pattern → "rate_limit"
* 2. HTTP 401/403, access-denied pattern, or auth keywords → "auth"
* 3. Context-window / token-limit message → "context_window"
* 4. HTTP 5xx or server-error message → "server"
* 5. Network connectivity error → "network"
* 6. Everything else → "unknown"
*/
export declare function classifyProviderError(error: unknown): ProviderErrorClass;
/**
* Multi-provider pool with per-member cooldown and strategy-based selection.
*
* All state (cooldowns, cursor) is instance-local and resets on construction.
* Thread safety is not required — Node.js is single-threaded for async work.
*/
export declare class ModelPool {
private readonly config;
private readonly strategy;
private readonly cooldownMs;
private readonly state;
private cursor;
private readonly now;
constructor(config: ModelPoolConfig, injectors?: {
now?: () => number;
});
/**
* The maximum number of attempts per call (pool config value or member count).
* Used by callers that drive the retry loop externally.
*/
get maxAttempts(): number;
/**
* Returns a stable string key for a pool member.
* Format: `${provider}:${model ?? "*"}:${region ?? "*"}`
*/
memberKey(member: ModelPoolMember): string;
/** Returns members whose cooldown has expired (or were never cooled). */
availableMembers(): ModelPoolMember[];
/**
* Selects the next member to try according to the configured strategy.
*
* @param excludedKeys — keys of members already attempted this call.
* @returns the chosen member, or undefined when all members are exhausted.
*/
selectNext(excludedKeys?: Set<string>): ModelPoolMember | undefined;
/**
* Records a provider failure, setting a cooldown appropriate for the error class.
*
* - Retryable classes (rate_limit, server, network, unknown): timed cooldown
* for `cooldownMs` so the member can recover and be retried later.
* - Non-retryable classes (auth, context_window): permanent cooldown for the
* lifetime of this ModelPool instance, because these errors are structural
* and will not resolve between calls (wrong credentials, model not available
* in team whitelist, payload exceeds the model's context window).
*/
recordFailure(member: ModelPoolMember, errorClass: ProviderErrorClass): void;
/**
* Records a successful response, clearing any existing cooldown for this member
* so it remains fully available.
*/
recordSuccess(member: ModelPoolMember): void;
}