Skip to content

feat(compaction): reconcile token estimator with provider-reported usage#3449

Merged
Sayt-0 merged 1 commit into
mainfrom
feat/compaction-usage-reconciled-estimator
Jul 3, 2026
Merged

feat(compaction): reconcile token estimator with provider-reported usage#3449
Sayt-0 merged 1 commit into
mainfrom
feat/compaction-usage-reconciled-estimator

Conversation

@Sayt-0

@Sayt-0 Sayt-0 commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

Closes #3437. compaction.EstimateMessageTokens was a fixed len/4 + 5 heuristic. It now reconciles with provider-reported usage: exact counts for messages that carry usage, plus a session-calibrated correction for unreported content, with a conservative bias so compaction fires slightly early rather than overflowing.

Issue expectations vs implementation

Issue expectation Implementation
Reconcile the heuristic against provider-reported usage per message Messages carrying chat.Message.Usage weigh their provider-counted size (OutputTokens, minus reasoning tokens that are never resent as input)
Usage available via MessageUsage The underlying per-message data (msg.Usage) is read directly; a new compaction.Estimator additionally calibrates on prompt-token deltas between consecutive usage-bearing assistant turns
Conservative bias (compact slightly early rather than overflow) Base heuristic at 3.5 chars/token (tracks code/JSON, mildly overestimates prose), calibration ratio clamped to [0.75, 2.0], invalid delta windows discarded
Starting points pkg/compaction, pkg/runtime/compactor SplitIndexForKeep / FirstIndexInBudget self-calibrate on the slice they receive (signatures unchanged, the compactor benefits transitively); compactIfNeeded calibrates the fresh tool-result estimate against session history
Extend estimator tests 11 EstimateMessageTokens cases, 8 NewEstimator sub-tests, calibration test for SplitIndexForKeep, runtime test proving calibration flips the compactIfNeeded decision

Calibration mechanism

prompt(j) - (prompt(i) + output(i)) = provider-exact size of the messages between assistant turns i and j
ratio = sum(deltas) / sum(heuristic estimates of those messages)
      = multiplicative correction applied to heuristic estimates only

Guard rails:

  • non-positive deltas discarded (a compaction between the anchors rebuilt the prompt)
  • anchor pairs spanning a model switch discarded (mixed tokenizers)
  • exactly sized in-between content subtracted from the delta, keeping the ratio a pure heuristic-vs-provider comparison
  • ratio trusted only past 512 heuristic tokens of sample mass, then clamped to [0.75, 2.0]
  • provider-reported counts are never scaled

Estimator accuracy fixes

Content Before After
Assistant turns with usage len/4 guess exact provider count
Code/JSON tool results len/4 (undercounts 15-25%) len/3.5, then usage-calibrated
Inline document text not counted counted as text
Binary attachments (images, documents) not counted flat 1500 tokens per part
Hidden reasoning (OpenAI o-series) counted as if resent excluded when no reasoning content is stored

Interaction with #3448 (configurable threshold)

Built on top of the merged threshold work: ShouldCompact(..., threshold), sessionCompactionEnabled(a) and a.CompactionThreshold() are untouched; this PR only changes how addedTokens and split boundaries are estimated.

Validation

Check Result
go build, go vet, golangci-lint clean, 0 issues
pkg/compaction, pkg/runtime (+ compactor, toolexec, wire), pkg/agent, pkg/session, pkg/tui green
Full go test ./... green except pre-existing network-dependent failures (pkg/config, pkg/httpclient, pkg/teamloader), reproduced identically on a clean main checkout

Residual design note

The 0.75 calibration floor limits downward correction: if provider reports prove the heuristic overestimates by more than 25%, compaction still fires early. That is the conservative trade-off the issue asks for; the floor can be lowered if accuracy is preferred over the early-compaction bias.

Assistant messages now weigh their exact provider-reported token count
instead of the chars-per-token heuristic, and a new compaction.Estimator
calibrates heuristic estimates against prompt-token deltas observed
between consecutive usage-bearing turns. The base heuristic moves to
3.5 chars/token and binary attachments get a flat conservative charge,
keeping the estimator biased toward compacting slightly early rather
than overflowing.

Closes #3437
@Sayt-0
Sayt-0 requested a review from a team as a code owner July 3, 2026 13:34
@Sayt-0
Sayt-0 merged commit 2addd15 into main Jul 3, 2026
13 checks passed
@Sayt-0
Sayt-0 deleted the feat/compaction-usage-reconciled-estimator branch July 3, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(compaction): reconcile token estimator with provider-reported usage

2 participants