/** * How much context a model will actually accept, and what to do when that is exceeded. * * Compaction used to fire at a fixed 80,000 tokens whatever the model was. qwen2.5-coder through * Ollama accepts 32,768, so the threshold was never reached before the model overflowed — and * Ollama does not refuse an over-long prompt, it silently drops the front of it. The agent then * carries on with the beginning of its own conversation missing, which from outside looks exactly * like a model that has forgotten what it was doing. It was never given the chance to remember. * * Two mechanisms, because neither alone is enough. A table of known limits is a guess that is * usually right and occasionally wrong; an overflow error is the truth but arrives too late to * prevent one. So the table drives early compaction, and the error teaches the real number for * the rest of the session. */ /** * What to assume when the model is not recognised. * * Deliberately not generous. Compacting a little early costs one summarisation; compacting too * late costs the front of the conversation without saying so, which is the failure this exists * to prevent. A hosted model that turns out to be larger corrects itself the first time real * usage figures come back under the limit. */ export declare const DEFAULT_CONTEXT_LIMIT = 32768; /** * What to assume for an unrecognised model on somebody else's hardware. * * The conservative default above is right for a runtime on this machine: Ollama really does serve * 4,096 unless told otherwise, and a local build of a model is usually the small one. It is wrong * for a hosted API, where a model published in 2026 rarely offers less than this — and being * wrong in that direction is not symmetric. * * Too generous costs one rejected request: the engine recognises an overflow, compacts, and * retries, and the session learns the real number for good. Too conservative costs nothing * visible and everything real — compaction fires at three quarters of the assumed window, so the * prompt never grows past it, so the assumption is never tested and never corrected. The session * simply summarises itself every few turns forever, throwing away what it just learned and going * back to re-read it. * * That is what was reported: a repo audit that kept running the same reconnaissance over and * over, on a hosted Qwen assumed to be a 32k local Ollama build because the name matched. */ export declare const DEFAULT_HOSTED_CONTEXT_LIMIT = 128000; /** Where a window figure came from, so an assumption can be shown as one. */ export type LimitSource = 'configured' | 'known' | 'assumed'; /** The window for a model, by name, and how confident that is. */ export declare function contextLimitWithSource(model: string, configured?: number, local?: boolean): { limit: number; source: LimitSource; }; /** The window for a model, by name. */ export declare function contextLimitFor(model: string, configured?: number, local?: boolean): number; /** * The share of the window to stay under. * * The prompt is not the whole story: the reply needs room too, and the next turn's tool results * arrive before compaction can run again. Three quarters leaves space for both without * summarising away a conversation that had plenty of room left. */ export declare const COMPACT_AT = 0.75; export declare function shouldCompact(promptTokens: number, limit: number): boolean; /** Whether a provider error is "your prompt is too long" rather than anything else. */ export declare function looksLikeContextOverflow(err: unknown): boolean; /** * The real limit, when the provider names it in the complaint. * * Worth reading rather than guessing again: "maximum context length is 32768 tokens" is the * number, and taking it means the rest of the session compacts at the right point instead of * repeating the same failure. */ export declare function parseContextLimit(err: unknown): number | null; /** * The context window as the provider itself reports it. * * A table of model names is a guess, and a gateway makes it a bad one: `auto/best-coding` is an * alias that resolves to whatever is best today, has a 1,048,576-token window, and matches * nothing in any table. Assuming 32,768 for it compacted a conversation with 97% of its room * left — sixty-nine messages summarised away for nothing, on a real run. * * An OpenAI-compatible /models listing usually carries the real number, and asking costs one * request per session. A provider that does not answer, or answers without it, leaves the table * in charge. */ export declare function fetchModelContextLimit(baseURL: string, apiKey: string | undefined, model: string, timeoutMs?: number): Promise; /** * Ollama's default serving window, which is not the length its models are trained for. * * qwen3-coder is trained for 32,768 and `/api/show` says so, but Ollama serves 4,096 unless told * otherwise — and truncates a longer prompt silently rather than refusing it. So a run compacting * at three quarters of 32,768 overflowed at 4,096 with nothing said, and what came back was * nothing at all. * * The trained length is the wrong number to plan around; this is the one that decides what fits. */ export declare const OLLAMA_DEFAULT_CONTEXT = 4096; /** * The window to plan around for a locally served model. * * OLLAMA_CONTEXT_LENGTH is Ollama's own way of raising the default, so it is believed when set. * Absent that, the default is assumed — being wrong in this direction costs one summarisation, * and being wrong in the other costs the whole prompt without a word. */ export declare function ollamaServingLimit(env?: NodeJS.ProcessEnv): number; /** Whether a prompt of this size leaves usable room in the window. */ export declare function windowIsTooSmall(promptTokens: number, limit: number): boolean; /** * What every request costs before the user has typed anything. * * Measured rather than assumed, because the number turned out to matter more than anything else * about running a local model: the system prompt is about 1.5k tokens and the tool schemas about * 2.3k, so a request starts at roughly 3.8k. Against Ollama's default 4,096 that leaves a few * hundred tokens for the conversation, the reply and every tool result — which is exactly what a * model looping, emitting tool calls as prose, and inventing its own origin looks like from * outside. * * Characters over 3.6 is a rough conversion and deliberately not presented as exact. It only has * to be right enough to tell a comfortable fit from a hopeless one. */ export declare function fixedRequestTokens(systemPrompt: string, toolSchemas: string): number; /** Whether the fixed cost leaves so little room that the session cannot work. */ export declare function overheadIsCrowded(fixedTokens: number, limit: number): boolean; /** * What to tell somebody whose window cannot hold the prompt. * * `remote` matters and was wrong in the advice given before this: OLLAMA_CONTEXT_LENGTH is read by * the Ollama server, so for an endpoint on another machine it has to be set on that machine. Told * to set it locally, someone would restart the wrong Ollama and see no change. */ export declare function crowdedWindowAdvice(fixedTokens: number, limit: number, opts: { model: string; endpoint: string; remote: boolean; platform?: NodeJS.Platform; runtime?: ServedRuntime | null; }): string; /** * The window a locally served model actually has, asked of the server. * * KONECK assumed 4,096 for anything served locally, because that is Ollama's default. An * assumption is not a measurement, and the objection was fair: if the server is running with a * larger window, KONECK was compacting and trimming for a constraint that did not exist. Nothing * should be limited that does not have to be. * * `/api/ps` reports the models Ollama currently has loaded, and each entry carries the context * length it was loaded with — the serving value, which is the one that matters. `/api/show` is * deliberately not used as a substitute: it reports the length the model was *trained* for, which * is the number that caused this whole class of trouble in the first place, being several times * larger than what the server will accept. * * Returns null when the server does not say, and the caller falls back to the default while * labelling it as an assumption rather than a fact. */ export declare function probeOllamaContext(baseURL: string, model: string, fetchImpl?: typeof fetch, timeoutMs?: number): Promise<{ limit: number; source: string; } | null>; /** * What to do with a window the server reported, given what the session was already told. * * Pure, and shared by the two places that ask. The rule used to live inline at startup only, which * is how it came to be applied exactly once — at the moment it was least likely to get an answer. */ export type ServedWindowDecision = { kind: 'within'; limit: number; source: string; } | { kind: 'conflict'; limit: number; source: string; asked: number; } | { kind: 'adopt'; limit: number; source: string; } | { kind: 'keep'; }; export declare function decideServedWindow(asked: number, probed: { limit: number; source: string; } | null): ServedWindowDecision; /** * The runtime behind an endpoint, as far as the window probe is concerned. * * Not the provider name: a declared provider called "omni" pointing at an Ollama box is Ollama, and * judging by the name got that setup neither the right window nor a word of warning. The name is * used as a hint about which probe to try first, and nothing more — every probe is tried before * giving up, because being wrong about the name should cost a request, not the measurement. */ export type ServedRuntime = 'ollama' | 'llamacpp' | 'lmstudio' | 'vllm'; export interface ServedWindow { limit: number; runtime: ServedRuntime; source: string; } /** * What window the server has actually loaded this model with. * * Tried in order, the hinted runtime first, stopping at the first endpoint that answers with a * plausible number. Every request is bounded and failure is silent: an endpoint that is none of * these simply returns null, which is what happened for every runtime but Ollama until now. */ export declare function probeServedContext(baseURL: string, model: string, opts?: { hint?: string; fetchImpl?: typeof fetch; timeoutMs?: number; }): Promise; export interface PromptSample { /** Characters of conversation sent, which is a fair measure of growth even if not of size. */ sentChars: number; /** What the server said it counted. */ reported: number; } export declare function detectWindowPlateau(samples: readonly PromptSample[]): { window: number; } | null; /** * A configured window the server will not honour. * * The setting exists for the case where KONECK cannot tell — a gateway that reports nothing, a * runtime it has no way to ask. It cannot make a server accept a longer prompt, and the failure when * somebody assumes it can is silent: Ollama truncates and says nothing, so the symptom is empty * turns and a model insisting it has no tools, several steps away from the setting that caused it. */ export declare function windowSettingIgnored(asked: number, serving: number): string; /** * How to raise the window, for whichever runtime is actually serving. * * Measuring a llama.cpp window and then saying "set OLLAMA_CONTEXT_LENGTH" would be worse than * saying nothing: it is confident, specific, and about a different program. With the runtime known * the answer is exact; without it the answer says so and covers the ones worth covering, which is * still better than naming the wrong one. */ export declare function windowFixFor(runtime: ServedRuntime | null, opts: { endpoint?: string; remote: boolean; platform?: NodeJS.Platform; tokens?: number; }): string; export declare function runsOnThisMachine(endpoint: string): boolean; export declare function ollamaWindowFix(opts: { endpoint?: string; remote: boolean; platform?: NodeJS.Platform; tokens?: number; }): string; export declare function truncationNotice(window: number, opts?: { floor?: number; endpoint?: string; remote?: boolean; platform?: NodeJS.Platform; runtime?: ServedRuntime | null; }): string; //# sourceMappingURL=context-limit.d.ts.map