/** * LocalPrefilter — spend a local model's tokens so the expensive one never sees the noise. * * A broad `search_workspace` returns 200 matches across 60 files. Four of them matter. The * frontier model currently pays for all 200 — in the turn that produced them and in every * turn afterwards, because tool results stay in the transcript. A 3B model running on the * user's own machine can rank that list for free, in the local sense of free: no API cost, * no context cost, no per-token billing. * * This is the one place in Lemma where a model's judgement decides what the main model * sees, so the safety rules are stricter than anywhere else: * * 1. **It ranks; it does not delete.** The prefilter reorders candidates and the caller * shows a prefix of that order. Nothing is claimed to be irrelevant — the output says * how many were withheld and how to get them all back. * 2. **Fail open, always.** No Ollama, wrong model, timeout, malformed reply, an index out * of range — every one of those returns null and the caller sends the unfiltered list. * A prefilter that fails closed would silently hide code from the model. * 3. **Disclosed at the call site.** The caller must state that a local model did the * ranking. A subset presented as if it were the whole search is a lie by omission, * and the user cannot audit what they were not told happened. * * Opt-in: set `LEMMA_LOCAL_PREFILTER=1` (or `mcp.localPrefilter: true` in lemma.config.json). * Off by default, because a wrong ranking costs a correct answer and the user should choose * to accept that trade. */ export interface PrefilterConfig { enabled: boolean; url: string; model: string; } export declare function loadPrefilterConfig(cwd?: string): PrefilterConfig; export declare function isLocalModelAvailable(config: PrefilterConfig): Promise; export interface Candidate { /** Stable identifier the caller uses to map a ranking back to its own data. */ id: string; /** One short line describing the candidate — a path plus the matched text, typically. */ summary: string; } export interface RankResult { /** Candidate ids, most relevant first. Contains every input id: ranking is not filtering. */ order: string[]; model: string; } /** * Parse the model's reply into an ordering. * * Strict on purpose. A small model will sometimes emit prose, duplicate an index, or invent * one; any of those means we do not actually know what it decided, and guessing at its * intent is how the relevant file ends up ranked last. Anything not exactly parseable * returns null, and the caller sends everything. */ export declare function parseRanking(reply: string, candidates: Candidate[]): string[] | null; /** * Rank candidates with the local model. Returns null whenever ranking did not definitively * succeed — the caller must then use its own original order and withhold nothing. */ export declare function rankCandidates(query: string, candidates: Candidate[], config?: PrefilterConfig): Promise; /** Test seam — the availability probe is cached for the process lifetime. */ export declare function resetPrefilterProbe(): void; //# sourceMappingURL=LocalPrefilter.d.ts.map