fix(compression): CJK-aware token estimates and stop re-copying stale head summaries - #69302
Merged
Conversation
…st path
The salvaged estimator ran a per-character Python loop on every
estimate_tokens_rough() call — a ~28,000,000x slowdown vs (len+3)//4 on a
1MB ASCII tool output (measured ~3.0s per call). Gate it:
- str.isascii() O(1) fast path keeps pure-ASCII text bit-identical to the
classic (len+3)//4 rule at ~1.3x baseline cost (0.23us vs 0.17us per
1MB call).
- Non-ASCII text counts dense CJK chars via a compiled character-class
regex in C (len(text) - len(re.sub(''))): ~352ms/1MB hangul vs ~2.1s
for the per-char loop.
- Non-ASCII-but-non-CJK text (accents, Cyrillic, emoji) keeps the classic
rule.
Also: parity tests against the per-char reference implementation, and
updated two stale expectations that encoded the old behavior (CJK now
counted ~1 token/char; short string content now ceil-divided instead of
floored to 0). The continuity test now detects merged-into-tail summaries
via _is_context_summary_content.
Contributor
૮ >ﻌ< ა ci reviewrunning on 42534d6 CI timingsCI timings · View jobWall time 7m23s vs 7m46s (-4.9%). 7 job(s) slower, 10 faster, 3 unchanged.
|
teknium1
added a commit
that referenced
this pull request
Jul 22, 2026
The summary_idx head-copy skip (from #69302) dropped the entire merged handoff message, deleting the genuine prior-tail user content that #47274's _strip_context_summary_handoff_message correctly unwraps. Strip handles both shapes: standalone handoffs drop, merged handoffs keep their real content. Caught by test_recompression_of_current_merged_handoff_preserves_prior_tail_once when both PRs landed together.
teknium1
added a commit
that referenced
this pull request
Jul 22, 2026
The summary_idx head-copy skip (from #69302) dropped the entire merged handoff message, deleting the genuine prior-tail user content that #47274's _strip_context_summary_handoff_message correctly unwraps. Strip handles both shapes: standalone handoffs drop, merged handoffs keep their real content. Caught by test_recompression_of_current_merged_handoff_preserves_prior_tail_once when both PRs landed together.
This was referenced Jul 22, 2026
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
The summary_idx head-copy skip (from NousResearch#69302) dropped the entire merged handoff message, deleting the genuine prior-tail user content that NousResearch#47274's _strip_context_summary_handoff_message correctly unwraps. Strip handles both shapes: standalone handoffs drop, merged handoffs keep their real content. Caught by test_recompression_of_current_merged_handoff_preserves_prior_tail_once when both PRs landed together.
33hodl
pushed a commit
to 33hodl/hermes-agent
that referenced
this pull request
Aug 12, 2026
The summary_idx head-copy skip (from NousResearch#69302) dropped the entire merged handoff message, deleting the genuine prior-tail user content that NousResearch#47274's _strip_context_summary_handoff_message correctly unwraps. Strip handles both shapes: standalone handoffs drop, merged handoffs keep their real content. Caught by test_recompression_of_current_merged_handoff_preserves_prior_tail_once when both PRs landed together.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Context compression now budgets CJK/Hangul/Kana text at its real token density and stops re-copying a stale handoff summary into the protected head on every re-compaction. Root causes:
estimate_tokens_roughapplied the English-centric(len+3)//4rule to CJK text (undercounting ~4x, inflating the protected tail), and the Phase-4 head-copy loop carried an old protected-head handoff forward verbatim while also inserting its updated replacement, so summaries accumulated across compactions and the live prompt never actually shrank.Changes
agent/model_metadata.py:estimate_tokens_roughcounts CJK-dense codepoints (Hangul Jamo/Syllables, CJK ideographs, fullwidth forms/halfwidth kana) as ~1 token each;estimate_messages_tokens_rough/estimate_request_tokens_roughroute through it via a new image-stripping token shadow.agent/model_metadata.py(salvage addition): perf gate —str.isascii()O(1) fast path keeps pure-ASCII text bit-identical to the classic(len+3)//4rule, and the CJK count runs as a single compiled-regex C pass instead of the PR's per-character Python loop.agent/context_compressor.py: Phase-4 head assembly skips the old handoff atsummary_idx(_is_context_summary_contentguarded) so re-compaction replaces it instead of duplicating it;last_head_rolenow reads from the assembled head._estimate_msg_budget_tokensuses the CJK-aware estimator for string content and tool-call envelopes while preserving the Tail-protection token estimate ignores codex_reasoning_items, so Codex/Responses sessions compact too late #55572_REPLAY_BUDGET_KEYSreplay-field accounting.tests/agent/test_cjk_token_estimation.py(+ salvage parity/fast-path tests against the per-char reference), continuity regression test for the stale-head-summary fix, and two stale expectations updated to the new intended behavior.Dropped from the original PR: the
compression.hygiene_thresholdgateway knob (scope creep — split out).Perf evidence (timeit, 1MB strings)
Validation
estimate_tokens_rough("가"*400)Targeted tests:
bash scripts/run_tests.sh tests/agent/ -q -k 'estimate or cjk or continuity or head'→ 163 passed, 0 failed; full runs of the 4 touched test files → 144 passed, 0 failed.Credit
Salvaged from #52517 by @plainOldCode (gateway hygiene knob split out; estimator perf-gated during salvage).
Infographic