Repository navigation
compaction-basic: add a prompt-message budget — long sessions die on gateways that cap message count #10474
Replies: 1 comment
Follow-up: what shipped locally, and where it still bitesTwo deployment shapes here, and they diverge — worth recording since the workaround is not equivalent to the fix: Source checkout / Packaged desktop build (Windows, Electron +
So the configuration workaround is really "bend the token budget until it accidentally approximates a message budget". A first-class Happy to iterate on the patch shape if a different seam is preferred — e.g. a generic "request budget" concept alongside the token one, rather than a message-specific field. |
Uh oh!
There was an error while loading. Please reload this page.
Summary
A long-lived session became permanently unsendable: every model call returned HTTP 400 from the provider, and nothing in DSH noticed. The token meter reported pressure below the compaction threshold, so
compaction-basicnever ran, and each retry rebuilt the same oversized prompt.The root cause is that some OpenAI-compatible gateways cap the prompt message count per request independently of the model's context window.
compaction-basiconly reacts to token pressure, so a tool-heavy session can reach that cap while still "fitting" the declared window.Observed failure
Session: 180 goal rounds, 2057 projected prompt messages, ~790k tokens, model declaring
contextWindow: 1000000. From one step onwards every request failed:The message comes from the gateway's internal request-rewrite layer and never clears on retry, because the history is already over the cap.
Root cause (bisected against the live endpoint)
a single request cannot evaluate more than 8192 path candidatesThe cap is message count, not bytes or content:
reasoning_contentstripped → still 400DSH side:
resolveCompactSpecderives pressure purely from tokens —min(contextWindow * thresholdRatio, contextWindow - reservedCompletionTokens - headroomTokens). WiththresholdRatio: 0.8,headroomTokens: 65536, a 32k output reservation and a 1M window that is 800,000 tokens, while 2048 messages of a tool-heavy session sat at ~790k. Compaction therefore never triggered, and the session could never be recovered.Suggested fix
Add a second, independent pressure trigger — a prompt-message budget — instead of relying on token scaling to approximate one:
CompactionPolicyConfig.maxMessages?: number(default1600;0disables), validated as a non-negative integer and inherited by exact-targetmodelPolicieslike the other policy fields.ResolvedPolicyFields→resolveTargetPolicy→resolveCompactSpec.compactIfNeeded(thepressurebranch), treatmeasurement.nodes.length >= spec.maxMessagesas pressure, and re-check the same predicate after the pruner pass and after each compaction attempt.TokenMeasurement.nodesalready gives the projected message count, so no new service or projection is needed. A per-route override is worth exposing too, since a gateway may cap lower than 2048.Sketch of the predicate change:
Patch
Since the repo does not take external PRs, the change lives on a branch of my fork (4 files, +37/-4,
packages/compaction/compaction-basiconly):layen-king/deepseek-harness@master...fix/compaction-max-messages
src/types.ts— newmaxMessagespolicy fieldsrc/config.ts— validation, default, inheritance, andresolveCompactSpecplumbingsrc/index.ts— the dual trigger and its re-checkstests/compaction-basic.spec.ts— defaults assertion updated (all 152 tests in the package pass)Verified locally: the session that always returned 400 now compacts at 1600 messages and continues (400 → 200 on the same request).
Environment
dsh0.2.1-rc.2,webprofileapi: openai-completions), single model route,reasoning_effortin useWorkaround applied meanwhile
Lowering the declared
contextWindowfor that provider's models (1M → 500k) so token pressure lands before the message cap. It works, but it is a guess at the gateway's real limit and costs usable context, which is why a first-class message budget seems preferable.All reactions