/** * neural-router.ts — Optional cost-optimal neural routing path (ADR-148). * * Wires `@metaharness/router` (pure-TS k-NN/KRR + optional FastGRNN via * `@ruvector/tiny-dancer`) into the model-routing path as a graceful, gated * addition. The shipped heuristic + Thompson bandit stays as the default; * this module only contributes a decision when: * * 1. `CLAUDE_FLOW_ROUTER_NEURAL=1` is set * 2. Either a trained artifact path resolves (`CLAUDE_FLOW_ROUTER_MODEL_PATH`) * OR the bundled seed corpus loads * 3. The dynamic `import('@metaharness/router')` succeeds * * Otherwise `tryCostOptimalRoute(...)` returns `null` and the caller falls * back to the bandit path with `routedBy: 'bandit-fallback'`. * * Observability — `routedBy` is returned on every result and must never be * inferred from "did the import resolve?" (ADR-074, ADR-086). It carries * exactly one of: * - 'metaharness-knn' pure-TS k-NN, no training (uses raw seed examples) * - 'metaharness-krr' pure-TS KRR with LOO-CV λ (TrainedRouter JSON) * - 'fastgrnn' native FastGRNN via tiny-dancer (NativeRouter) * * Performance — module-level caches resolve the backend, seed corpus and * router once per process. Hot path is a single `route(embedding)` call. * * @module neural-router */ import type { ClaudeModel } from './model-router.js'; /** Backend identifier carried on every result (never inferred). */ export type NeuralRoutedBy = 'metaharness-knn' | 'metaharness-krr' | 'fastgrnn'; /** Cost-optimal route decision. */ export interface NeuralRouteResult { /** Chosen Claude tier label (back-compat). Derived from `modelId`. */ model: ClaudeModel; /** * Concrete picked model id — ADR-149. May be an Anthropic SDK id or an * OpenRouter slug. Always a string; the closest tier label is in `model` * for back-compat with consumers that still expect ClaudeModel. */ modelId: string; /** Predicted quality the chosen candidate is expected to achieve (0..1). */ predictedQuality: number; /** Did the predicted quality clear the configured `qualityBar`? */ metBar: boolean; /** Per-candidate predicted qualities, ordered cheapest-first. */ alternatives: Array<{ model: ClaudeModel; modelId: string; predictedQuality: number; costPerMTok: number; }>; /** Backend that produced the decision. */ routedBy: NeuralRoutedBy; /** Inference latency in microseconds. */ inferenceTimeUs: number; /** * ADR-149 iter 45 — ensemble disagreement diagnostic. Absolute difference * in predicted quality for the PICKED model between the unified KRR and * the bucket specialist (iter 16). Set only when both are loaded AND a * complexityBucket was supplied. Operators tuning iter 44's * `CLAUDE_FLOW_ROUTER_ENSEMBLE_UNCERTAINTY_THRESHOLD` need to observe * realistic disagreement values to pick a sensible cutoff. */ ensembleDisagreement?: number; } /** Module-level configuration. Read once at first call from env. */ interface NeuralRouterConfig { enabled: boolean; modelPath?: string; /** Bundled fallback artifact (KRR JSON). Used when `modelPath` is unset. */ bundledKrrPath: string; qualityBar: number; seedCorpusPath: string; /** k for k-NN backend (default 5). */ k: number; /** * ADR-149 iter 12 — optional latency budget in ms. When > 0, candidates * whose measured p50 latency exceeds the budget are filtered OUT before * the cost-optimal selector runs. Default 0 (unbounded, cost-only). * For interactive flows that need sub-second responses, set 1000. */ latencyBudgetMs: number; /** * ADR-149 iter 22+24 — post-hoc isotonic calibration. When the bundled * calibrator JSON is present, KRR predict() outputs are piped through * IsotonicCalibrator.transform() before cost-optimal selection. * * DEFAULT ON (iter 24, ADR-149). Iter 23's out-of-sample LOO validation * showed ECE drops from 0.1604 (POORLY-CALIBRATED) to 0.0335 * (WELL-CALIBRATED) — a 79% reduction with only a 0.0144 train/test gap, * confirming the calibrator generalizes. Set * `CLAUDE_FLOW_ROUTER_CALIBRATE=0` to opt out and recover iter 0-21 raw * KRR behavior. */ calibrateEnabled: boolean; /** Path to the bundled calibrator JSON (iter 22). */ calibratorPath: string; /** * ADR-149 iter 29 — orthogonal selector mode: when > 0, filter candidates * by blended price ≤ ceiling, then pick the HIGHEST predicted quality * (not cheapest-above-bar). Lets ops with a hard budget cap ask "best * model under $X" instead of "cheapest above quality threshold". Default * 0 (disabled) preserves iter 0-28 cost-optimal-above-bar semantics. */ costCeilingPerMTok: number; /** * ADR-149 iter 44 — ensemble-uncertainty-aware fallback. When > 0, the * selector queries BOTH the unified KRR and the bucket specialist for * the same query, then computes |unified_q - specialist_q| for the * picked model. If the disagreement exceeds this threshold, returns null * so the caller falls back to the bandit — same path as a 429/5xx API * error today, but triggered by prediction uncertainty instead. * * Typical values: 0.10 (mild — only the most uncertain predictions * fall back), 0.20 (aggressive — any meaningful ensemble disagreement * triggers fallback). 0 disables (default — preserves iter 0-43 behavior). */ ensembleUncertaintyThreshold: number; } /** * Cost-optimal route via the optional neural backend. Returns `null` when the * neural path is disabled (gate closed), unavailable (deps missing), or * unconfigured (no corpus / artifact). Callers must fall back to the * heuristic+bandit path on null and tag the result `routedBy: 'bandit-fallback'`. * * @param embedding 384-dim (or matching corpus dim) query embedding * @returns NeuralRouteResult on success, or `null` when the path is inactive */ export declare function tryCostOptimalRoute(embedding: number[], opts?: { complexityBucket?: 'low' | 'med' | 'high'; }): Promise; /** * Batch counterpart to `tryCostOptimalRoute`. Routes a list of embeddings * in one go, sharing backend resolution + (for the pure-TS paths) * candidate-view setup across the batch. The native FastGRNN path still * dispatches per-call (xenova's worker doesn't support array inputs for * tiny-dancer's Router.route). * * Order of the output array matches the input order. Each slot is either * a NeuralRouteResult or null (gate closed, backend unavailable, etc. — * mirrors the single-call contract). * * For harness-style callers (batch evals, GAIA runs, parallel agent * dispatch) this amortizes backend init across N queries — first-call * cold-load (~10 ms) becomes a fixed cost regardless of batch size. */ export declare function tryCostOptimalRouteBatch(embeddings: number[][]): Promise>; /** * Diagnostic surface — returns the active backend without performing a route. * Used by the bench and by `claude-flow neural router status` (future CLI). */ export declare function neuralRouterStatus(): Promise<{ enabled: boolean; available: boolean; routedBy: NeuralRoutedBy | null; reason: string; config: NeuralRouterConfig; }>; /** * ADR-149 iter 7 — fallback selector for retry-on-failure. Returns the * cheapest candidate predicted to clear the quality bar (or the best- * predicted if none do) that is NOT in `excludeModelIds`. Used by * `executeAgentTask` to retry with a different model after a 429/5xx. * * Returns `null` when: * - the gate is closed (mirrors tryCostOptimalRoute) * - the embedding is missing or empty * - the backend isn't loadable * - every candidate is excluded (all retries exhausted) * * Selection is per-candidate via predictAll, then filter by exclude, * then cheapest-clearing-bar (falling back to best-predicted). */ export declare function nextCostOptimalAlternative(embedding: number[], excludeModelIds: Iterable): Promise; /** * Test seam — reset module-level caches so unit tests can simulate cold init. * Not exported from the package's barrel. */ export declare function __resetNeuralRouterForTests(): void; export {}; //# sourceMappingURL=neural-router.d.ts.map