/** * How much of a reply is text the model was already shown. * * TIER 3 OF THE OUTPUT LEDGER, AND NOT A SAVING. The other two tiers are * differences between two populations: what we emitted against what comparable * untreated requests emitted. This one has no counterfactual at all. It is a * property of a single response -- the share of its word n-grams that already * appear verbatim in the context we sent -- and it answers a question the other * two cannot: whether there is output waste here worth attacking in the first * place. A high ratio is an opportunity; it is never added to a saving, and the * renderer must never let it be read as one. * * JUDGED AGAINST WHAT WE SENT, NOT AGAINST THE ORIGINAL. The model can only * echo what reached it, so the n-grams come from the FORWARDED body. Measuring * against the pre-compression conversation would count a passage we had already * removed as something the model copied, which is the opposite of true. * * COUNTS ONLY LEAVE THIS FILE. The scanner holds hashes of the context and a * handful of words of carry while a response streams, and emits one number * between 0 and 1. No text, no n-gram and no hash is recorded, logged or * transmitted, and the scanner is discarded when the response ends. * * OPT-IN, DEFAULT OFF, because it is the one instrument here that costs real * memory while a request is in flight -- a long context is a few hundred * thousand hashes -- and an operator who has not asked for an output-waste * figure should not pay for one. * * REFUSES RATHER THAN UNDERSTATES. Every bound in this file (the n-gram cap, * the pending-buffer cap) makes the scanner return `null` when it is hit, never * a ratio computed from the part it managed to see. An understated waste figure * would make a future output optimizer look better than it is, which is exactly * the direction a measurement must not be allowed to fail in. */ /** The env var that turns the scanner on. Absent or falsey means no scan. */ export declare const ECHO_ENV = "TOKEN_OPTIMIZER_OUTPUT_ECHO"; /** * Words per n-gram. Eight is long enough that ordinary prose does not match by * chance -- a shared four-word phrase is unremarkable English -- and short * enough to catch a restated sentence rather than only a copied paragraph. */ export declare const ECHO_WINDOW = 8; /** * The context n-gram cap, past which the scanner refuses. * * 200,000 distinct hashes is roughly a 200k-word context, well past the largest * window any current model offers, and about 1.6 MB of `Set` while one * request is in flight. */ export declare const ECHO_MAX_NGRAMS = 200000; /** * The cap on response bytes held while waiting for a JSON string to close. * * A complete `"text":"..."` value is consumed the moment its closing quote * arrives, so this only ever holds one partial value. A body that pushes past * it is malformed or is not the shape we parse, and either way the scan is * abandoned rather than finished from a fragment. */ export declare const ECHO_MAX_PENDING = 262144; export declare function echoEnabled(env?: NodeJS.ProcessEnv): boolean; /** * WHY HASHES AND NOT THE N-GRAMS THEMSELVES. A `Set` of 200,000 * eight-word phrases is tens of megabytes per in-flight request. A * `Set` of 32-bit hashes is about 1.6 MB, at the cost of collisions: * at 200,000 entries the chance that a given non-echoed n-gram collides with * some context n-gram is about 0.005%, which moves a ratio in the fourth * decimal place. * * AND THE ERROR RUNS THE RIGHT WAY ROUND ONLY HERE. A collision can only * OVERSTATE echo, which overstates the waste left to attack -- the direction * that makes our own future work look worse, not better. A Bloom filter sized * for the same memory would do the same thing with a worse constant; a lossy * cache that dropped entries would understate, and must not be used. */ export declare function contextNgrams(text: string, size?: number): Set | null; export declare function splitWords(text: string): string[]; /** A complete text value pulled out of a buffer, and what is left to re-scan. */ export interface Extraction { readonly values: readonly string[]; readonly rest: string; } /** * Pulls every COMPLETE text value out of a buffer, leaving the rest for later. * * INCREMENTAL BECAUSE A STREAM DOES NOT RESPECT TOKENS. An SSE chunk can split * `"text":"hello"` anywhere -- inside the key, before the colon, inside the * value -- so a scan that discarded what it could not finish would silently * drop exactly the long passages a copied paragraph arrives in. Anything not * consumed comes back as `rest` and is re-scanned with the next chunk. */ export declare function extractTextValues(buffer: string): Extraction; /** Watches a response stream and reports how much of it was already in view. */ export interface EchoScanner { /** Feeds one decoded response chunk. Never throws. */ push(chunk: string): void; /** The echo ratio in [0, 1], or null when the scan cannot stand behind one. */ ratio(): number | null; } /** * A scanner over one response, or null when no ratio is possible for it. * * NULL IS A FIRST-CLASS ANSWER HERE. A context shorter than one window, or one * so large its n-grams pass the cap, gives a ratio that would be a different * statistic from the one this reports. The caller records nothing in that case, * and the ledger's absent field says "not scanned" rather than "no echo found". */ export declare function createEchoScanner(contextText: string, size?: number): EchoScanner | null; //# sourceMappingURL=echo.d.ts.map