Repository navigation
bug(chat): context fill is measured as cumulative spend, so compaction fires early and the user is shown a wrong percentage #17802
Description
Problem
fill_percentage — the number that triggers compaction and the number the user is shown — is
computed from a cumulative counter, while the value added to it each turn is the size of the
entire prompt for that turn. The meter therefore grows roughly quadratically in turn count while
real context fill grows linearly.
Evidence
SessionTokenTracker.add_message_tokens increments, per turn:
autobot-backend/chat_history/context_overflow.py:108-118—hincrby(key, "total_tokens", prompt_tokens + completion_tokens)autobot-backend/chat_history/context_overflow.py:690-694—fill_percentage = total_tokens / context_limit
The value it is given is the provider's own per-response usage, whose prompt_tokens is the whole
prompt, not a delta:
autobot-backend/llm_shared/providers/ollama_provider.py:166—prompt_eval_countautobot-backend/llm_shared/providers/vllm.py:112—len(output.prompt_token_ids)autobot-backend/llm_shared/providers/anthropic.py:357—response.usage.input_tokensautobot-backend/llm_shared/providers/openai_compatible.py:227—usage.prompt_tokens
reaching the tracker unmodified:
autobot-backend/chat_history/overflow_integration.py:92-105—usage = llm_response.usage, passed straight throughautobot-backend/api/chat.py:836— the only production call site
Why this is a bug and not a convention
The correct convention is already implemented in the same function. After a compaction,
_create_summary resets the session and re-seeds it with estimate_fast(...) per retained
message — sum-of-message-sizes, which is the right basis for fill:
autobot-backend/chat_history/context_overflow.py:820-836
So one counter carries two incompatible meanings: conversation size immediately after a compaction,
cumulative spend from the next turn onward.
Consequences
- Compaction fires far earlier and far more often than the 90% threshold implies. Each firing is a
paid LLM call that collapses history. Worked example: a 2k-token conversation on an 8k model reads
as over 100% full after ~5 turns, at ~25% actual fill. - The 80% warning surfaced to the user is a wrong number:
autobot-backend/api/chat.py:858-868(context_warning.fill_percentage). - This is a plausible amplifier for bug(chat): a user instruction erodes across repeated compactions — verbatim protection does not compound #14393 (a user instruction erodes across repeated
compactions): erosion compounds per compaction, and this defect multiplies how many happen.
Fix shape
The Redis hash already holds four fields (total_tokens, prompt_tokens, completion_tokens,
message_count). Keep those as spend, and add an active_tokens field written with HSET from
the latest turn's usage; compute fill_percentage from active_tokens.
Acceptance criteria
-
fill_percentageis computed from the current turn's prompt size, not a running total - Cumulative spend remains available under its own field (no telemetry regression)
- A positive control asserts the meter moves for a growing conversation and does not grow
when the same-size prompt is sent repeatedly — the case today's code gets wrong - A test pins the worked example above (2k prompt, 8k limit, 5 turns → well under threshold)
Relationships
- Sibling operand of the same formula: fix(llm): context windows come from a static catalog with a 4096 fallback — no probe, no learning from the provider's own rejection #14029 — the denominator comes from a static catalog
with a 4096 fallback. This issue is the numerator. Neither defect is visible while the other is
broken. - Probable downstream symptom: bug(chat): a user instruction erodes across repeated compactions — verbatim protection does not compound #14393.
- Same file, so one PR and one owner: fix(chat): compaction clips tool results to 400 chars and keeps no way back to the full result #16624 (
_TOOL_RESULT_CLIP_CHARSat
context_overflow.py:51), bug(chat): a user instruction erodes across repeated compactions — verbatim protection does not compound #14393 (_PRESERVED_USER_MESSAGE_CAPatcontext_overflow.py:46).
Provenance
Found by a comparative study of an external agent platform, which states compaction pressure in
terms of active tokens and requires a candidate to reduce active tokens before activation. Nothing
external is proposed for import; the finding is entirely in our own code. Full write-up:
docs/research/ai-employee-workforce-graph-platform.md.
Activity
refactor(speech): one Whisper inference path, one MIME map, and that …