Fix chat template prompt/completion formatting and SFT masking for Gemma 4 reasoning - #5013
Open
csgoogle wants to merge 1 commit into
Open
Fix chat template prompt/completion formatting and SFT masking for Gemma 4 reasoning#5013csgoogle wants to merge 1 commit into
csgoogle wants to merge 1 commit into
Conversation
csgoogle
requested review from
A9isha,
NuojCheng,
RissyRan,
SurbhiJainUSC,
abhinavclemson,
aireenmei,
bvandermoon,
darisoy,
dipannita08,
gagika,
gobbleturk,
hengtaoguo,
huytransformer,
igorts-git,
jiangjy1982,
khatwanimohit,
richjames0,
shralex,
shuningjin,
vipannalla and
xibinliu
as code owners
August 26, 2026 11:13
There was a problem hiding this comment.
Code Review
This pull request refactors the chat template formatting in input_pipeline_utils.py to determine prompt and completion boundaries using token-level common prefix matching, which correctly handles Gemma 4 thinking/reasoning channels. It also adds comprehensive unit tests to verify SFT prompt masking and thinking channel boundaries. The reviewer noted that directly loading the gated google/gemma-4-31b-it model from Hugging Face in unit tests will fail in unauthenticated CI/CD environments, and suggested using a local path or environment variable fallback.
csgoogle
force-pushed
the
fix-gemma4-reasoning-chat-template
branch
from
August 26, 2026 11:38
0b654ad to
6461e7a
Compare
…mma 4 reasoning When formatting conversational rounds for SFT training, the prompt segment was previously derived before observing the assistant message using add_generation_prompt=True. For reasoning models like Gemma 4, this appended an empty thought channel (<|channel>thought\n<channel|>) to the prompt, causing duplicate/premature channel closers when reasoning was present and injecting phantom thought channel tags when reasoning was absent. This change formats both prompt and completion segments directly from the full round tokens at the assistant turn boundary using token prefix matching via _split_turn_into_prompt_and_completion. This guarantees prompt + completion exactly reproduces the chat template output across reasoning and non-reasoning turns for all tokenizer models. Tests use ensure_tokenizer_downloaded with local MAXTEXT_ASSETS_ROOT tokenizer paths to prevent CI/CD failures on unauthenticated environments.
csgoogle
force-pushed
the
fix-gemma4-reasoning-chat-template
branch
from
August 26, 2026 11:47
6461e7a to
3877c07
Compare
| common_len = 0 | ||
| for full_id, prompt_id in zip(prompt_completion_ids, prompt_ids): | ||
| if full_id == prompt_id: | ||
| for fid, pid in zip(full_tokens, prompt_tokens): |
Collaborator
There was a problem hiding this comment.
nit: let's keep it as full_id and prompt_id
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.
Description
Fixes conversational round formatting in
apply_chat_templateand SFT prompt masking for reasoning models (such as Gemma 4).Problem & Evidence on Unmodified Code
Previously in
apply_chat_template, the prompt segment was generated eagerly when iterating over the user message:For Gemma 4,
add_generation_prompt=Trueemits<bos><|turn>user\n...<turn|>\n<|turn>model\n<|channel>thought\n<channel|>.When running unit tests against the unmodified code, 5 out of 8 tests failed:
Assistant turn WITH reasoning (
test_single_turn_with_thinking- FAIL on unmodified code):The completion returned by
_get_completion_in_chat_templatestarted after<|channel>thought\nwith{reasoning}\n<channel|>{content}<turn|>\n. Concatenating prompt + completion produced:The thought channel was prematurely closed by
<channel|>before the reasoning trace ever began, followed by duplicate closing tags.Assistant turn WITHOUT reasoning (
test_single_turn_without_thinking- FAIL on unmodified code):Because the prompt was generated before inspecting the assistant message, the empty thought channel remained in the prompt:
A phantom
<|channel>thought\n<channel|>was injected into normal conversational turns where ground truth had no thought channel.Solution
We extracted turn splitting into a clean, reusable helper:
_split_turn_into_prompt_and_completion(tokenizer_model, round_msgs):And simplified
apply_chat_template:Guarantees
full_tokensintofull_tokens[:common_len]andfull_tokens[common_len:]guaranteesprompt_str + completion_str == tokenizer.apply_chat_template(round_msgs, add_generation_prompt=False, tokenize=False).Tests
python3 -m unittest -v tests/unit/chat_template_sft_test.py(8/8 tests pass)python3 -m unittest -v tests.post_training.unit.sft_data_processing_test.SFTChatTemplateLogicTest(4/4 tests pass)