Count assistant tool_calls in count_message_tokens - #232
Merged
Merged
Conversation
count_message_tokens only encoded string values found directly on a message or
one level down inside list items. Assistant tool_calls look like
{"function": {"name": ..., "arguments": "..."}}, so the nested `function`
dict was skipped and tool-call names and arguments were never counted. In a
tool-heavy session (many long shell commands) those arguments can be most of
the conversation, so the estimate that drives compaction ran well under the
real size.
Recurse through lists and dicts instead (image_url blocks are still counted
flat and not encoded). The docstring now says what the estimate does not see:
the chat template's own markup and the tool schemas sent alongside the messages.
Tests: tests/test_token_counter.py. The two tool-call tests fail without this
change.
🤖 Godspeed Review
SecurityNo secrets detected in changed files. |
🤖 Godspeed Review
SecurityNo secrets detected in changed files. |
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.
Why
count_message_tokensdrives context compaction. It encoded only string values on a message or one level inside list items, so the nestedfunctiondict of assistanttool_calls(tool name andarguments) was never counted.Observed on a local llama-server with a hard 32K window (KAT-Coder REAP-50, Godspeed agent-in-loop runner, SWE-bench Lite dev): Godspeed logged compaction at
tokens=26999/27944/34473(its own estimate), yet the server hitn_tokens = 32767, truncated = 1five times in one run. Each time the reply was cut off in the middle of a tool call, llama-server answered HTTP 500Failed to parse tool call arguments as JSON ... missing closing quoteon every retry, and the session ended withexit_reason=llm_error. Those sessions emitted 80-170 tool calls.Changes
count_message_tokensnow recurses through lists and dicts (_collect_strings), so tool-call names and arguments count.image_urlblocks are still counted flat and not encoded.tests/test_token_counter.py: tool-call arguments counted, tool-call name counted, plain content unchanged, content blocks counted, image blocks flat-estimated without encoding their URL. The two tool-call tests fail without the change.Measured effect (real requests)
18 requests sampled from a Qwen3.8-27B agent-in-loop run (captured with a logging proxy, 8 SWE-bench tasks), true tokens = server
/apply-template(with tools) then/tokenize:So on main the estimate is 22-39% too low, and the default compaction threshold of 0.8 on a 32K window corresponds to roughly 0.8 x 1.29 x 32K = 33.8K true tokens, i.e. already past the window: compaction cannot prevent the overflow. With this PR the same threshold corresponds to about 29K true tokens. Tool schemas add a further median of 562 tokens in this runner (more in the full harness) and are still not estimated.
Verification
test_token_counter,test_multimodal,test_agent_loop,test_context/.ruff check .andruff format --check .(CI flags) clean.Not verified / limits
usage.prompt_tokenswould be a sensible follow-up; that is not part of this PR.