From ce2ab9a3934425a221460353eeba25a6cb77cd05 Mon Sep 17 00:00:00 2001 From: Hamza Shah <87142882+Hamjaster@users.noreply.github.com> Date: Fri, 7 Aug 2026 14:34:24 +0500 Subject: [PATCH 1/3] core: export MIN_RESPONSE_TOKENS from countTokens.ts --- core/llm/countTokens.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/core/llm/countTokens.ts b/core/llm/countTokens.ts index b742d70b0f0..a99a2a99ce4 100644 --- a/core/llm/countTokens.ts +++ b/core/llm/countTokens.ts @@ -562,6 +562,7 @@ export { countTokens, countTokensAsync, extractToolSequence, + MIN_RESPONSE_TOKENS, pruneLinesFromBottom, pruneLinesFromTop, pruneRawPromptFromTop, From 86f4a17968250841eb6a020352531eb8721c53a4 Mon Sep 17 00:00:00 2001 From: Hamza Shah <87142882+Hamjaster@users.noreply.github.com> Date: Fri, 7 Aug 2026 14:34:25 +0500 Subject: [PATCH 2/3] fix(autocomplete): cap reserved output tokens in pruneLength When a model's contextLength (e.g. Ollama's num_ctx) is smaller than the default maxTokens (4096), pruneLength computed a negative prompt budget, causing every autocomplete request to prune the prefix/suffix to nothing, silently, with no error. compileChatMessages already caps its output reservation at MIN_RESPONSE_TOKENS for the same reason; pruneLength now does the same. Fixes #13038 --- core/autocomplete/templating/index.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/autocomplete/templating/index.ts b/core/autocomplete/templating/index.ts index 3ebf29b71be..6de5b8df209 100644 --- a/core/autocomplete/templating/index.ts +++ b/core/autocomplete/templating/index.ts @@ -9,6 +9,7 @@ import { DEFAULT_MAX_TOKENS } from "../../llm/constants.js"; import { countTokens, getTokenCountingBufferSafety, + MIN_RESPONSE_TOKENS, pruneLinesFromBottom, pruneLinesFromTop, } from "../../llm/countTokens"; @@ -204,7 +205,12 @@ function pruneLength(llm: ILLM, prompt: string): number { const contextLength = llm.contextLength; const reservedTokens = llm.completionOptions.maxTokens ?? DEFAULT_MAX_TOKENS; const safetyBuffer = getTokenCountingBufferSafety(contextLength); - const maxAllowedPromptTokens = contextLength - reservedTokens - safetyBuffer; + // Reserve at most MIN_RESPONSE_TOKENS for the completion, same as compileChatMessages. + // Otherwise a large maxTokens (the default is 4096) can consume the entire context + // window on small-context models, leaving no room for the prompt and silently + // pruning it to nothing on every request. + const minOutputTokens = Math.min(MIN_RESPONSE_TOKENS, reservedTokens); + const maxAllowedPromptTokens = contextLength - minOutputTokens - safetyBuffer; const promptTokenCount = countTokens(prompt, llm.model); return promptTokenCount - maxAllowedPromptTokens; } From d9821cb7951b01f65ada96e95e23f8f84f8cc2f0 Mon Sep 17 00:00:00 2001 From: Hamza Shah <87142882+Hamjaster@users.noreply.github.com> Date: Fri, 7 Aug 2026 14:34:27 +0500 Subject: [PATCH 3/3] test(autocomplete): pin pruneLength's MIN_RESPONSE_TOKENS cap --- .../__tests__/renderPrompt.vitest.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/core/autocomplete/templating/__tests__/renderPrompt.vitest.ts b/core/autocomplete/templating/__tests__/renderPrompt.vitest.ts index 039dde6cabe..33ef14e2204 100644 --- a/core/autocomplete/templating/__tests__/renderPrompt.vitest.ts +++ b/core/autocomplete/templating/__tests__/renderPrompt.vitest.ts @@ -26,12 +26,14 @@ vi.mock("../../../llm/countTokens", () => { const pruneLinesFromBottom = (str: string, allowed: number) => str.slice(0, allowed); const getTokenCountingBufferSafety = () => 0; + const MIN_RESPONSE_TOKENS = 1000; return { countTokens, pruneLinesFromTop, pruneLinesFromBottom, getTokenCountingBufferSafety, + MIN_RESPONSE_TOKENS, }; }); @@ -254,6 +256,31 @@ describe("renderPromptWithTokenLimit parity & pruning", () => { expect(compiledPrefix.length).toBeLessThan(120); }); + + it("does not wipe a small prompt when default maxTokens exceeds a small context length", () => { + // Regression test: a model whose contextLength (e.g. Ollama's num_ctx) is smaller + // than the default maxTokens reservation (4096) used to compute a negative prompt + // budget, which caused every prefix/suffix to be pruned to nothing regardless of + // how small the actual prompt was. + const shortPrefix = "A".repeat(50); + + const helper = makeHelper({ prunedPrefix: shortPrefix }); + + const llmStub = { + contextLength: 2048, + completionOptions: {}, + model: "test-model", + } as any; + + const { prefix: compiledPrefix } = renderPromptWithTokenLimit({ + snippetPayload: emptySnippetPayload, + workspaceDirs: ["file:///workspace"], + helper, + llm: llmStub, + }); + + expect(compiledPrefix.includes(shortPrefix)).toBe(true); + }); }); describe("stop-token merging", () => {