/** * Bringing your own relevance model, including an ONNX one. * * WHY THERE IS AN EXTENSION POINT RATHER THAN A BUNDLED MODEL. The default * ranker is BM25, and that is a competitive position rather than a shortcut: * it needs no Python, no model weights and no RAM floor, it runs in a * restricted sandbox, and it is deterministic -- which is what lets the cached * prefix stay byte-stable and a benchmark number mean anything. HeadRoom's * ModernBERT buys semantic recall and pays for it with all four of those. * * But BM25 is lexical, and lexical has a real ceiling: a question about "the * database ran out of handles" does not match a log line saying "connection * pool exhausted", however well it is tokenised. Somebody with a model and a * reason to run it should be able to, and the shape of that is the same shape * the engine registry already uses -- register an implementation, get the * default when you do not. * * RANKING IS SYNCHRONOUS, AND THAT IS NOT THE BARRIER IT LOOKED LIKE. The * engines are pure synchronous functions of (text, context) -- what keeps them * free of the per-request state HeadRoom's #3486 is about -- and * `onnxruntime-node`'s `session.run()` is async, so there is no `await` at the * point a ranker is called. * * An earlier version of this comment concluded from that that a model could not * be bundled, on the reasoning that the units to embed only exist after an * engine has parsed a block. THAT WAS WRONG, and the correction is worth * keeping visible: parsing is cheap and pure, so a request-level pre-pass can * collect candidate units, embed them all in one batch, and hand the sync * engines a cache to read. See `embedding.ts`, which does exactly that, and * `onnx.ts`, which is the forty lines of glue the barrier was hiding. * * So there are two ways in. `registerRanker` installs any synchronous Ranker, * and a warmed `EmbeddingCache` on the context takes precedence over it -- * the cache is the more specific answer, because someone embedded THIS * request. With neither, everything is BM25. */ import { type Ranker } from './relevance.js'; import { type EmbeddingCache } from './embedding.js'; /** * Builds a ranker for one question. * * The same signature as `relevance.ranker`, because that IS the default * implementation and a replacement has to be substitutable for it. */ export type RankerFactory = (query: string | undefined) => Ranker; /** * Installs a ranker, replacing the lexical default. * * Module-level and deliberately not a class, for the reason the engine * registry gives: this is configuration, not per-request state, so two * concurrent requests cannot observe each other's. */ export declare function registerRanker(next: RankerFactory): void; /** Restores the built-in lexical ranker. Mainly for tests. */ export declare function resetRanker(): void; /** True when something other than the default is installed. */ export declare function rankerIsCustom(): boolean; /** * The ranker in force, behind the same boundary the engines get. * * A REGISTERED RANKER THAT THROWS FALLS BACK RATHER THAN FAILING THE REQUEST. * A model is a great deal more likely to throw than a word count is -- a * missing file, an out-of-memory, a shape mismatch on an input it did not * expect -- and the whole design fails open. Losing semantic ranking costs * some retention quality; losing the request costs the turn. */ export declare function activeRanker(query: string | undefined, embeddings?: EmbeddingCache): Ranker; //# sourceMappingURL=ranking.d.ts.map