Repository navigation
fix(chat): keep every message a turn appends after the reply - #237
Merged
hijkzzz merged 1 commit intoOct 22, 2026
Merged
Conversation
A continuation turn is the previous request, the model's reply, then the new messages, but _run_turn sliced its delta as if exactly one message followed the reply (prefix chat[:-1], history chat[:-2]). With two or more new messages (parallel tool results, an observation plus a reminder) only the last one reached the generation context and the trained tokens, and the template's close of the reply was lost; with none, the reply was appended again as feedback. Index the slices by the previous request's length n instead: messages[n] is the reply, the prefix is chat[:n + 1], the history chat[:n], and the new images come from every message after the reply. A request that does not echo the reply rewrites the history the segment holds, so it starts a fresh segment like a compaction. A one-message turn renders as before. Signed-off-by: Max Freedom Pollard <[email protected]> Co-Authored-By: Claude Opus 5.5 <[email protected]>
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.
Problem
A chat continuation turn is the previous request, then the model's reply, then the new messages.
_run_turnsliced that turn's delta as if exactly one message followed the reply: it renderedchat[:-1]as the prefix andchat[:-2]as the history the reply was generated after. Any other count goes wrong silently, because the request still succeeds:\nafter<|im_end|>in ChatML).Minimal reproduction: a ChatML template, a reply that makes two tool calls, and the two tool responses sent back as two user messages (the
<tool_response>conventionchat_geo3k.pyuses). This is the context turn 2 generates over onmain:result anever reaches the model, and the trajectory trains on this context.Fix
Index the slices by the previous request's length
n.messages[n]is the reply, so the prefix ischat[:n + 1], the history ischat[:n], and the delta is everything after the reply. New images are read from all ofmessages[n + 1:], not only the last message.A request that extends the previous one without echoing the reply (
messages[n]is not an assistant message) rewrites the history the segment holds. It now starts a fresh segment, like a compaction, instead of being sliced into the old one.A turn that appends exactly one message after the reply, which is what
chat_geo3k.pysends, renders exactly as before.Tests
In
tests/unit/test_chat_server.py, using the existing drifty template and engine fakes:test_continuation_delta_covers_every_message_after_the_reply, parametrized over two tool results and the reply alone. Turn 2's generation context and the segment must equal the template's render, with each new message exactly once. Onmain,<tool>OBS1</tool>is missing in the first case, and the second case has<assistant>A1</assistant>duplicated.test_turn_without_the_reply_starts_fresh_segment. Onmain, the request is sliced into the old segment.Validation
pytest tests/unit/test_chat_server.py: 33 passed. The 3 new tests fail onmain.mainplus the 3 new tests.test_refit_lora_merge.pyneedsnemo_automodel, and three modules importvllm, so these four do not collect here. With a localvllmstub,test_prompts_dataset.pyandtest_collect_rollout_rewards.pypass (17 tests).test_gather_group_lineage.pyneeds a local Ray cluster, which this sandbox cannot start.python -m compileall -q molt examples/python testsandgit diff --check. On the changed files:ruff check,ruff format --check(0.9.9),isort8.0.1 andautoflake2.3.3, at the versions pinned in.pre-commit-config.yaml.result a,result band the\nclose are all present).🤖 Generated with Claude Code