test(e2e): let the cache-rebuild gate run on reasoning checkpoints - #145
Open
rakhimovv wants to merge 2 commits into
Open
test(e2e): let the cache-rebuild gate run on reasoning checkpoints#145rakhimovv wants to merge 2 commits into
rakhimovv wants to merge 2 commits into
Conversation
_generate() asks for 32 tokens and asserts a non-empty message["content"]. enable_thinking is a Qwen chat-template knob, so on a checkpoint that reasons by its own protocol the flag is a no-op: the budget goes to the reasoning trace, the text lands in reasoning_content, and content comes back empty with finish_reason "length". The gate then fails on the baseline generation, before any rebuild is attempted, so the path it exists to protect is never exercised. Against openai/gpt-oss-20b this reproduced on three consecutive runs. Raise the budget to 256 tokens and accept either channel, which matches the helper's own docstring: the point is that the engine still runs, not what it says. enable_thinking is left in place, still correct for the Qwen-family checkpoints the gate was written against.
Reworks the previous commit after review. Asserting on content-or-reasoning_content still ties the gate to which reasoning parser --reasoning-parser auto happened to pick and to how that parser splits channels, none of which is the thing under test. usage.completion_tokens is accumulated from the scheduler's own acks, upstream of every parser, so it says decode ran without depending on any of it -- and tests/README asks for expectations checked against something independent. That also makes the max_tokens bump unnecessary: the observed failure was content being empty, not the budget being short, and 32 tokens is enough to show decode ran. Reverting it keeps the gate cheap, which matters because _generate is called four times and the offload/cpu backends this project exists for decode at 1-20 tok/s, where 256 tokens per call can cross _post's 180 s timeout and cascade into the rebuild's if_idle refusal. Corrects the docstring too: enable_thinking is read by the qwen/glm/gemma/ dsv4 templates, not Qwen alone, and gpt-oss is inert to it because its Harmony channels are not gated by that flag.
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.
Small test-only fix found while running the pre-PR gates from
tests/README.mdon a fresh box. Thanks for documenting them — they were easy to find and run.What breaks
tests/README.mdcallstests/e2e/test_cache_rebuild.pythe primary gate for the rebuildpath and asks contributors to run it before opening a PR, pointing
FREETOKEN_REBUILD_TEST_MODELat "a SMALL local model dir". Onopenai/gpt-oss-20b— thesmallest checkpoint on the known-good list in
docs/models.md— the gate cannot get past itsfirst assertion.
_generate()posts to/v1/chat/completionsand returnsmessage["content"]; line 145 thenasserts that string is non-empty:
gpt-oss's Harmony channels are not gated by
enable_thinking, so it emits an analysis traceregardless, that text lands in
reasoning_content, andcontentcan come back empty on aperfectly healthy engine.
Observed
With
FREETOKEN_REBUILD_TEST_MODELpointed atopenai/gpt-oss-20b, three consecutive runsfailed identically:
The failure is on the baseline generation — the one taken before anything is torn down —
so no rebuild is ever attempted. The path the gate exists to protect is not exercised at all;
the gate just reports red.
It is a boundary case rather than a structural impossibility: the same request against a
separately booted server returned
content: 'ready'withcompletion_tokens: 31of the 32allowed plus 83 characters of
reasoning_content. One token of headroom, and which side ofthe line you land on depends on the prompt and the engine's configuration.
The gate was not masking an engine bug
Worth confirming, since a red gate that starts passing after a test edit deserves suspicion.
Against a live
ft serveon gpt-oss-20b the rebuild path itself is healthy:The pool table then showed
moe 512 slots (lru, 66.7%) 6.3 GiB, down from768 slots ... 9.5 GiB, withlast rebuild ok, andft ctl generate "2+2=" --max-tokens 12still produced text afterwards.The fix
Return
usage.completion_tokensinstead of the reply text, and assert it is non-zero.Which field the text lands in depends on the checkpoint's reasoning protocol and on which
parser
--reasoning-parser autopicked, neither of which is what this gate is about. Readingeither channel keeps that coupling; counting tokens drops it.
completion_tokensisaccumulated from the scheduler's own acks, upstream of every reasoning parser, so it says
decode ran without depending on any of it — which is the kind of independent expectation
tests/README.mdasks for.max_tokensstays at 32. The observed failure wascontentbeing empty, not the budgetbeing short, and 32 tokens is plenty to show decode ran. That matters because
_generateiscalled four times and the offload/cpu backends this project exists for decode at 1-20 tok/s,
where 256 tokens per call can cross
_post's 180 s timeout — and an aborted request keepsdecoding server-side, which the rebuild's
if_idlemode refuses rather than waits for. Thegate is advertised as cheap; this keeps it that way.
enable_thinking: Falseis left in place. It is read by the qwen/glm/gemma/dsv4 templatesand is simply inert for gpt-oss; Jinja ignores undeclared variables, so it cannot harm a
checkpoint that does not read it.
Before / after
Before, three runs in a row, all
AssertionError: assert ''attests/e2e/test_cache_rebuild.py:145. After, same command and same checkpoint:Command:
The test fails before this change and passes after it, on the same checkpoint and the same
command.
Tested on
0.1.2, at commitbd372b6openai/gpt-oss-20b, revision6cee5e81ee83917806bbde320786a8fb61efebeeuv pip install -e ".[accel,dev]"What was wrong in the first version
enable_thinkingis a Qwen chat-template knob." It is read by the qwen/glm/gemma/dsv4templates —
tokenizer/effort.pybroadcasts it for all four. gpt-oss is the family it isinert for, which is the opposite of what I wrote.
contentcomes back empty"contradicted my own datum three paragraphs later (
content: 'ready'at 31/32 tokens).Restated above as the boundary case it is.
max_tokensbump was unevidenced — with the assertion fixed, 32 tokens passes.has a single commit in its history and names no checkpoint family.