feat: tokenBudgetMax — absolute, window-independent injection ceiling - #358
Conversation
…ling Memory injection is sized as tokenBudgetFraction × the model's context window, so a large-window model (e.g. 1M) balloons per-turn injection proportionally — on a 1M-window model a fresh session showed ~170-200k of context coming purely from memory injection, rewritten into the cache every turn. tokenBudgetMax caps injection in absolute tokens, independent of the window: - Stage 1 (base cap): the budget handed to the daemon's assemble is capped to min(window, tokenBudgetMax / tokenBudgetFraction) so the daemon pre-trims its sub-channels. - Stage 2 (enforcer): after all five injection paths land (main assemble, continuity, exact-recall, predictive_context, beforeTurn), the combined systemPromptAddition is truncated to tokenBudgetMax. This is the real ceiling because several paths size against the real window, not the capped budget. Only injection is bounded — the cap is never applied to enforceTokenBudgetInvariant or compaction, so the usable conversation window is untouched. Unset preserves current behavior (no-op). Adds the config type, JSON schema entry, and unit tests for both stages plus the uncapped pass-through. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 25 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
compoodment
left a comment
There was a problem hiding this comment.
Vale Review — PR #358
Quality: Q3/5 — promising
Head: 02291dc
Findings:
- None
Proof gaps: Local targeted test compile was blocked by a stale/mismatched contracts dependency in the available dependency tree; PR author reports type/unit coverage, but I could not independently verify the new tests in this run.
Verdict: comment-only — the two-stage cap is well-scoped and covered by focused tests on paper; no proven blocker from the diff review.
– Vale
Summary
Memory injection is sized as
tokenBudgetFraction × model_context_window, so injection scales with the window. On a large-window model (e.g. a 1M-token model) a fresh session can show ~170–200k of context coming purely from memory injection — and because the recalled prefix changes per turn, it's rewritten into the prompt cache every turn.tokenBudgetMaxadds an absolute ceiling on injection, independent of the window:min(window, tokenBudgetMax / tokenBudgetFraction), so the daemon pre-trims its sub-channels.assembleContextInternalcallpredictive_context, beforeTurn), the combinedsystemPromptAdditionis truncated totokenBudgetMax.Stage 2 is the real enforcer: several paths (e.g.
predictive_context) size against the real window viaresolveEffectiveAssembleBudget, not the capped budget, so a base cap alone leaks.Only injection is bounded. The cap is never applied to
enforceTokenBudgetInvariantor compaction — those operate against the real window — so the usable conversation window is untouched. Unset preserves current behavior (no-op).Why an absolute cap (not just a smaller fraction)
A fraction can't express "never inject more than N tokens regardless of model." Operators running the same plugin across a 200k model and a 1M model want a stable injection footprint (and a stable per-turn cache cost), not one that 5×'s when they switch models.
Testing
tsc --noEmitclean;tsc -p tsconfig.build.json+ bundle clean.tokenBudgetMax: 1000,tokenBudgetFraction: 0.2and a 1M window, the daemon receivestokenBudget = 5000(stage 1) and the injectedsystemPromptAdditionis truncated to ≤ the cap (stage 2:injectionBefore=9000 → injectionAfter=1000).mainwith tests. Happy to validate any requested changes there.Notes
Backward compatible and fully opt-in. Schema entry added (
additionalProperties: false, so the key must be declared). A trim, when it fires, logs atinfo:LibraVDB tokenBudgetMax trim ... injectionBefore=... injectionAfter=... cap=....Separately, the observed real-token cost of injection runs ~2× the plugin's estimate for dense/structured/CJK memory content (the
APPROX_CHARS_PER_TOKEN = 4assumption vs ~1.7 actual). That's a distinct token-accounting issue; this PR is just the ceiling mechanism. Happy to file the accounting observation separately if useful.