Pull requests / #1899
#1899 serve: the thinking budget's continuation keeps the tokens emitted after the STOP, so it is not read again
open · @Vokturz · 0 コメント · GitHub で見る
BenchmarksServer & APINVIDIA / CUDAModels & quantsWindowsLinux
本文
## Title Issue: none open for this (searched); the closest are #1620 (fixed in 0.1.42: a cancel before the restored conversation is read past) and #615 (why the Monitor's prefill column looks low for such requests). This one is a cancel after the generation: the thinking budget's STOP. ## Summary When the thinking reaches `reasoning_budget_tokens`, the server stops the engine and continues with the prompt + the generated tokens + the wrap-up. The engine emits a verify window's tokens at once and finishes the window in flight after the STOP, so it can emit a few tokens past the server's cut. Those were drained and dropped, but the engine's session holds them (every emitted token but the last). With two or more past the cut, the session was no longer a prefix of the continuation: the engine fell back to the prompt's checkpoint and read all of the thinking again. Now the tokens read while draining to DONE are kept and join the thinking before the wrap-up, so the continuation holds the whole session and reuses it. ## What changed - `serve/server.py` - `StrataEngine.generate` keeps the `T` lines it reads while draining to DONE after a STOP in `self.after_stop` (reset at every request, before the batch branch, which does not fill it). - `Service._run`, at the budget's wrap-up: the `after_stop` tokens are fed like the rest of the thinking (emitted to the client, counted, appended to `seg`) before `REASONING_WRAP_UP`, when there is room and they are plain thinking. - `after_stop_continues(tok, ids, stop_ids)`: no end-of-turn token among them, no `<` (it may start `</think>` or a call, which the parser treats on its own), no character split at their end. Otherwise the continuation is built as before. - `serve/test_server.py`: `ThinkingBudgetAfterStop` - a mock engine that emits 3 tokens past the stop (they join the thinking and the second prompt is the first + all of them + the wrap-up), a tag past the stop is left out as before, and the helper. ## Extra Notes **Answers can differ:** where the engine had run past the cut, the thinking now keeps those few tokens (at most what one verify window emits) before the wrap-up, where it used to drop them, and the answer is written after them. A request without a budget, or whose budget is not reached, is unchanged. **Measured** on engine 0.1.42 (local CUDA 13.3 build), RTX 3090, WSL2, Swift 1.5 IQ3_XXS, `--spec 4 --max-context 204800 --kv-resident 32768`, the same ~720-token coding prompt with a random nonce, one request at a time: | `reasoning_budget_tokens` 300, 20 requests | continuations that reused the session | read the thinking again | |---|---|---| | before | 12 | 8 (40%), 4.9 s in all | | after | 20 | 0 | After, the thinking kept 0-5 tokens past the budget (`thinking budget reached (300..305 tokens)`: 7 x 300, 8 x 301, 2 x 302, 2 x 304, 1 x 305). With a 12,000-token budget the same race costs a re-read of the whole thinking, here 12,023 tokens in 4.4 s: ``` prompt 710 tokens = 0 reused + 710 read in 1629 ms (435.8 tok/s), 12004 generated in 92457 ms ... prompt 12728 tokens = 705 reused + 12023 read in 4418 ms (2721.5 tok/s), 5780 generated ... ``` After the change, 3 of 3 such continuations reused 12,710-12,711 tokens and read 19. **Tests:** `python -m unittest serve.test_server serve.test_request_accounting`: 301 tests pass. **Not changed here:** the batch slots' path; the forced tool call that opens after the thinking (`tool_choice`), the other STOP-and-continue path, which has the same pattern; `reasoning_close_retry`; the engine. **Not tested:** native Linux or Windows, other models. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
関連リンク
インストール・モデル・リリースへの站内リンク。