Capture real token counts from backends and use for compaction#33
Merged
Conversation
LlamafileClient now records TokenUsage (prompt, completion, total) from every llama-server response. ContextManager uses these real counts instead of chars/4 heuristic when available, so compaction and context warnings fire based on actual KV cache pressure — critical for reasoning models where think tokens consume context invisibly. Closes #32 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Same pattern as LlamafileClient — captures prompt_eval_count and eval_count from Ollama responses (both streaming and non-streaming) into last_usage dict. ContextManager picks these up via the existing _sync_token_count path in run_inference. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
antoinezambelli
force-pushed
the
az/token-llama
branch
from
March 27, 2026 04:54
f49a83e to
bc260c9
Compare
isgallagher
pushed a commit
to isgallagher/forge-guardrails
that referenced
this pull request
May 29, 2026
…nezambelli#33) * Capture real token counts from llama-server and use for compaction LlamafileClient now records TokenUsage (prompt, completion, total) from every llama-server response. ContextManager uses these real counts instead of chars/4 heuristic when available, so compaction and context warnings fire based on actual KV cache pressure — critical for reasoning models where think tokens consume context invisibly. Closes antoinezambelli#32 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add token usage capture to OllamaClient Same pattern as LlamafileClient — captures prompt_eval_count and eval_count from Ollama responses (both streaming and non-streaming) into last_usage dict. ContextManager picks these up via the existing _sync_token_count path in run_inference. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.
Capture real token counts from backends and use for compaction
Summary
TokenUsagedataclass) from every LLM response into alast_usagedict keyed by slot IDupdate_token_count()) instead of the chars/4 heuristic when availablerun_inference()syncs token counts from the client to the context manager after every LLM callstream_options: {include_usage: true}and breaks on[DONE]instead offinish_reasonto capture the usage chunk that arrives between the twoWhy
The chars/4 heuristic significantly underestimates actual token usage — especially for reasoning models where
<think>tokens consume KV cache but aren't persisted in message content. In forge-code eval runs, 14B Q8 reasoning models StreamError at 6-9K estimated tokens against a 12K budget because compaction never fires — the heuristic thinks there's headroom when KV cache is actually full.With real counts, compaction and context warnings trigger based on actual server-reported usage.
Changes
clients/base.pyTokenUsagefrozen dataclassclients/llamafile.pylast_usagedict,_record_usage(),stream_options, break on[DONE]clients/ollama.pylast_usagedict,_record_usage()(mapsprompt_eval_count/eval_count)context/manager.pyupdate_token_count(),estimate_tokens()returns real count when availablecore/inference.py_sync_token_count()called after every send__init__.pyTokenUsageDesign decisions
last_usageis adict[int, TokenUsage]keyed by slot ID — supports future sub-agent work (issues Sub-agent support: synchronous swap scheduling #27-Sub-agent support: slot pool #29) where concurrent agents share a client with multiple slots. Default slot 0 for single-slot use.LLMResponseunion type._sync_token_countusesgetattrso clients withoutlast_usage(e.g. AnthropicClient) are silently skipped.[DONE]instead offinish_reasonto capture the usage chunk. Old llamafile binaries that don't send[DONE]are no longer supported.Test plan
Closes #32