Features
Iterable / streaming datasets in GRPO and RLOO
Long-standing request finally landed. GRPO and RLOO rely on RepeatSampler to repeat each prompt num_generations times and group them across processes — but samplers can't attach to iterable datasets (no length, no indexing), and the old behavior silently bypassed the sampler, quietly corrupting advantage computation.
A new repeat_iterable_dataset generator reproduces RepeatSampler's exact ordering by transforming the stream itself instead of reordering indices. Trainers wrap iterable datasets via IterableDataset.from_generator, force dispatch_batches=False, and shard the stream per-process to preserve prompt groups after cross-process gathering.
from datasets import load_dataset
from trl import GRPOConfig, GRPOTrainer
dataset = load_dataset("trl-lib/DeepMath-103K", split="train", streaming=True)
trainer = GRPOTrainer(
model="Qwen/Qwen3-4B",
args=GRPOConfig(max_steps=1000), # required for iterable datasets
train_dataset=dataset,
reward_funcs=accuracy_reward,
)max_steps is required for iterable datasets (no length). A clear error also fires if dispatch_batches=True is combined with an iterable dataset.
by @albertvillanova in #6351
Environment-owned datasets
When an environment_factory is provided, the environment can now own the data. train_dataset becomes optional: reset() returns the prompt. No more fabricating a dummy dataset just to drive the loop.
class WordleEnv:
def reset(self, **kwargs) -> str: # returns the prompt
self._target = sample(words)
return f"Guess a {len(self._target)}-letter word."
def get_reward(self) -> float:
return 1.0 if self._solved else 0.0
def guess(self, word: str) -> str: # exposed tool
self._solved = word == self._target
return _feedback(word, self._target)
trainer = GRPOTrainer(
model="Qwen/Qwen3-4B",
args=GRPOConfig(max_steps=1000),
environment_factory=WordleEnv, # no train_dataset needed
)Applies to both GRPOTrainer and AsyncGRPOTrainer. A provided train_dataset still works exactly as before; multi-environment routing datasets can now be environment-only (no prompt column required).
by @qgallouedec in #6349
Message-level rollouts in AsyncGRPO
An opt-in way to build training rows from a multi-turn conversation. Message mode keeps the conversation as messages and re-tokenizes the whole thing each turn, then checks whether the fresh tokens still start with the tokens held so far. If yes → append the new part (same as token mode). If no → a rewrite happened → close the row and open a new one that matches what the model actually read.
AsyncGRPOConfig(
rollout_protocol="message", # "token" (default) | "message"
fork_threshold_tokens=1024, # message-mode only
)Per-turn drift is classified as CLEAN (append), REALIGN (last-answer tail wobble → overwrite same row), or FORK (real rewrite → new row). One advantage per conversation, stamped on every row it produced; under token-mean loss a fork is invisible. Sets up the plumbing for future tree-trajectory support — scoring is already branch-agnostic (groups by rollout_id).
by @AmineDiro in #6250
AsyncGRPO: VLLMClient and decoupled weight sync
A follow-up to v1.8's rollout worker split. A small VLLMClient becomes the single place that speaks HTTP to the vLLM server (named methods for wait_for_server_ready, get_max_model_len, pause/resume, weight-update endpoints — stateless, so picklable). Generation deliberately stays on its own async aiohttp session in the rollout child.
Weight sync is now built from config independent of rollout_worker (previously a custom rollout worker force-set it to None), and is injectable via a new WeightTransferProtocol. token_budget now waits for /health before defaulting to get_max_model_len(), so a slow-starting vLLM waits instead of failing the run.
by @AmineDiro in #6269
DistillationTrainer refactor
A ~13-PR sweep that reshapes the experimental DistillationTrainer for future stability:
- Extract
ServerDistillationTrainerfor the teacher-server path (#6454) - Move IW-OPD to a dedicated experimental trainer (#6453)
- Remove the local sparse top-1 loss path from the base trainer (#6455)
- Remove top-k support from the base generalized JSD loss (#6456)
- Validate the teacher by vocab size instead of tokenizer identity (#6457)
- Deprecate & remove
lmbdaon/off-policy mixing and the off-policy training branch (#6458, #6460); GKD paper reproduction now points atGKDTrainer(#6459) - Accept a
promptcolumn alongsidemessages(#6461); deprecate messages-format datasets (#6474) ⚠️ Fixnum_items_in_batchto count generated completion tokens — the loss denominator was wrong for on-policy training (counted dataset completions, not generated ones) and NaN'd on prompt-only datasets. This changes loss values for anyone on the old path (#6478)- Preparatory pins on the objective and on/off-policy behavior end to end (#6450, #6451, #6452)
All by @qgallouedec.
New IW-OPD distillation objective
Importance-Weighted On-Policy Distillation lands as an optional objective on the experimental DistillationTrainer — detached IW-OPD advantages built from sampled-token teacher and rollout logprobs (with cached vLLM rollout logprobs when use_vllm=True).
DistillationConfig(
distillation_objective="iw_opd",
iw_opd_gamma=1.0,
iw_opd_epsilon=0.2,
lmbda=1.0, reverse_kl_top_1_mode="sampled", # required
)Now split into its own experimental trainer as part of the refactor above.
log_multimodal for GRPO / RLOO
New config toggle to control whether images from multimodal completions are logged to trackers. Also applied to experimental DPPO and GRPO-with-replay-buffer.
by @apardyl in #5408 and by @albertvillanova in #6352
vLLM version sweep
- Support vLLM 0.24.0 by @qgallouedec in #6355
- Support vLLM 0.25.0 and 0.25.1 by @qgallouedec in #6406
- Drop vLLM 0.16 by @qgallouedec in #6404
Other
- Simplify KTO KL completion construction into a single batched map by @albertvillanova in #6335
- Add security policy to the repo by @albertvillanova in #6362
- Removed
generate_rollout_completionsby @sergiopaniego in #5870 - Set explicit neutral
top_pin GKD on-policy generation by @sergiopaniego in #6337 - Support
DatasetDictandIterableDatasetDictaseval_datasetinevaluateby @albertvillanova in #6326 - AsyncGRPO fork-independent epoch counting by @AmineDiro in #6385
- Align
AsyncGRPOTrainerargs handling with stable trainers by @qgallouedec in #6377 - Reject
auto_find_batch_sizein GRPO and RLOO by @qgallouedec in #6411 - Drop fully-masked examples after truncation in SFT by @qgallouedec in #6320
- Drop examples with no completion left after truncation in DPO and KTO by @qgallouedec in #6321
- Add
GMPOTrainerto the telemetry allowlist by @qgallouedec in #6410 [GRPO]Treat explicitmax_tool_calling_iterations=0as zero, not unlimited by @Strongich in #6423- Add a tip to the Paper Index on reading papers with the
hfCLI by @davanstrien in #6422
Deprecations and removals
- Remove the experimental DPPO trainer by @qgallouedec in #6402
- Deprecate
messages-format datasets inDistillationTrainer— usepromptcolumn instead — by @qgallouedec in #6474 - Deprecate
lmbdaon/off-policy mixing inDistillationTrainer— off-policy path fully removed by @qgallouedec in #6458 and #6460
Fixes
- Fix biased cross-rank aggregation of token-weighted metrics in GRPO / RLOO — mean-of-means was biased when per-rank token counts differ; now reduces numerator + count jointly for a true global mean. Also fixes
clip_ratio/low_min/high_maxto be per-completion instead of per-rank-batch-mean (DP-layout-independent). Logging-only, no loss change. By @qgallouedec in #6380 - Fix DPO/KTO
use_liger_kernelunder DeepSpeed ZeRO-3 by @qgallouedec in #6372 - Force
use_reentrant=Truefor PEFT + ZeRO-3 + gradient checkpointing across all trainers by @qgallouedec in #6356 - Fix
precompute_ref_log_probs=Truewith DeepSpeed in DPO and KTO by @qgallouedec in #6403 - Fix
cast_lm_head_to_fp32under FSDP2 / DeepSpeed ZeRO-3 by @qgallouedec in #6330 - Fix corrupted reasoning blocks in Qwen3-family training chat templates — the both-tags guard broke replay of self-generated thinking traces (SFT / distillation on model outputs); reverted to the original
</think>-only check. By @qgallouedec in #6363 - Fix EOS being masked when
pad_token_id == eos_token_idin GKD and GOLD by @roycho96 in #6357 - Fix GRPO truncated-completion loss normalization by @Functionhx in #6439
- Fix
apply_chat_templatedropping leading tokens fromchosenwhen the prompt common prefix shrinks againstrejectedby @sohumt123 in #6433 - Fix crash when reward function is a
functools.partialor callable instance by @qgallouedec in #6313 - Fix QLoRA + FSDP1 mixed-dtype crash in
prepare_peft_modelby @sergiopaniego in #6343 - Truncate GOLD on-policy prompts before generation, keeping the prompt end by @roycho96 in #6359
- Fix
FileNotFoundErrorin DPO/KTOprecompute_ref_log_probsby @Functionhx in #6348 - Fix
GeometricMixtureWrapper.generate()crash after transformers' MoE decode optimization change by @albertvillanova in #6413 - Fix GKD
vocab_sizecheck for multimodal model configs by @sergiopaniego in #6340 - Fix OpenEnv docker-image example bootstrap by @sergiopaniego in #6329
- Fix error type for
IterableDatasetin GRPO and RLOO by @albertvillanova in #6324 - Fix KTO evaluate crash on streaming datasets with unpaired-preference data by @albertvillanova in #6325
- Fix DPO/KTO evaluate crash with a dict
eval_datasetandprecompute_ref_log_probsby @albertvillanova in #6370 - Fix DPO/KTO double-preprocessing
eval_datasetdict passed toevaluate()by @albertvillanova in #6434 - Raise when DPO/KTO cannot precompute correct reference log-probs at eval time by @albertvillanova in #6443
- Avoid in-place mutation of input prompts in GRPO and DPPO environment loops by @albertvillanova in #6364 and #6366
- Align KTO with DPO: drop
hasattrguards onlm_head.biasin the Liger path by @qgallouedec in #6425 - Align KTO with DPO: fix
logits/chosenandlogits/rejectedmetrics by @qgallouedec in #6424 - Fix missing
eos_token_idin non-vLLM on-policy GOLD test stubs by @kashif in #6394 - Fix
BEMACallbackbias_powerdocstring by @DaoyuanLi2816 in #6390
Documentation and Examples
- Document
max_stepsrequirement for iterable datasets in GRPO and RLOO by @albertvillanova in #6432 - Align
AsyncGRPOConfig.max_completion_lengthdescription withGRPOConfigby @qgallouedec in #6374 - Align
AsyncGRPOTrainer.processing_classdocstring withGRPOTrainerby @qgallouedec in #6375 - Align
AsyncGRPOTrainerclip-ratio comment withGRPOTrainerby @qgallouedec in #6376 - Fix formatting of error message for
loss_typeby @qgallouedec in #6373 - Fix misleading name of
BEMACallbackbias_power=0.0test by @albertvillanova in #6464
CI
- Destroy vLLM colocate mode process group after each CI test by @albertvillanova in #6353
- Test KTO training on iterable datasets with the KL loss by @albertvillanova in #6338
- Test all supported
eval_datasetinput types in core trainerinit/evaluate()tests by @albertvillanova in #6336 and #6371 - Add
eval_datasetinit tests for experimental GRPO / Online-DPO / SFT & distillation / preference / BCO+CPO+ORPO+PRM trainers by @albertvillanova in #6395, #6396, #6397, #6398 and #6399 - Silence experimental
environment_factorywarning in GRPO tests by @albertvillanova in #6445 - Silence PEFT
ensure_weight_tyingwarning on newer PEFT in GRPO tests / DPO+KTO liger tests by @albertvillanova in #6446 and #6463 - Ignore benign
bitsandbytes_check_is_sizeFutureWarningin CI by @albertvillanova in #6448 - Ignore benign
IndexPutBackward0warning from Liger SAPO loss by @albertvillanova in #6436 - Filter reworded
torch.jit.script_methodwarning on Python 3.14 by @albertvillanova in #6465 - Filter triton
AnnAssignDeprecationWarningon Python 3.14 by @albertvillanova in #6467 - Fix
create_reference_modeldeprecation warning by @albertvillanova in #6418 - Fix uncaptured cross-architecture VLM warning in GOLD trainer CI tests by @albertvillanova in #6419
- Re-record the
dpoinvariant reference after #6321 by @qgallouedec in #6421 - Remove redundant
torch.tensorre-wrapping of batch labels in KTO by @albertvillanova in #6431 - Bump the actions group across 1 directory with 10 updates by @dependabot[bot] in #6442
New Contributors
- @Functionhx made their first contribution in #6348
- @sohumt123 made their first contribution in #6433
What's Changed
- ⬆️ Bump dev version by @qgallouedec in #6347
- Fix FileNotFoundError in DPO/KTO precompute_ref_log_probs by @Functionhx in #6348
- Add
log_multimodalparam to GRPOConfig and RLOOConfig to control image logging by @apardyl in #5408 - Set explicit neutral top_p in GKD on-policy generation by @sergiopaniego in #6337
- Fix GKD vocab_size check for multimodal model configs by @sergiopaniego in #6340
- Fix OpenEnv docker-image example bootstrap by @sergiopaniego in #6329
- Align log_multimodal in the DPPO and GRPO-with-replay-buffer experimental trainers by @albertvillanova in #6352
- Fix QLoRA + FSDP1 mixed-dtype crash in prepare_peft_model by @sergiopaniego in #6343
- Destroy vLLM colocate mode process group after each CI test by @albertvillanova in #6353
- Fix error type for IterableDataset in GRPO and RLOO by @albertvillanova in #6324
- Fix KTO evaluate crash on streaming datasets with unpaired-preference data by @albertvillanova in #6325
- Support DatasetDict and IterableDatasetDict as eval_dataset in evaluate by @albertvillanova in #6326
- Simplify KTO KL completion construction into a single batched map by @albertvillanova in #6335
- implement message level rollout with linear trajectories by @AmineDiro in #6250
- Add security policy to the repo by @albertvillanova in #6362
- Removed
generate_rollout_completionsby @sergiopaniego in #5870 - Fix crash when reward function is a
functools.partialor callable instance by @qgallouedec in #6313 - Drop fully-masked examples after truncation in SFT by @qgallouedec in #6320
- Fix
cast_lm_head_to_fp32under FSDP2 / DeepSpeed ZeRO-3 by @qgallouedec in #6330 - Add support for vLLM 0.24.0 by @qgallouedec in #6355
- Fix DPO/KTO
use_liger_kernelunder DeepSpeed ZeRO-3 by @qgallouedec in #6372 - Test KTO training on iterable datasets with the KL loss by @albertvillanova in #6338
- Avoid in-place mutation of input prompts in GRPO environments loop by @albertvillanova in #6364
- Avoid in-place mutation of input prompts in DPPO environments loop by @albertvillanova in #6366
- Fix DPO/KTO evaluate crash with a dict eval_dataset and precompute_ref_log_probs by @albertvillanova in #6370
- Test all supported eval_dataset input types in core trainer init tests by @albertvillanova in #6336
- Test all supported eval_dataset input types in core trainer evaluate() tests by @albertvillanova in #6371
- Drop examples with no completion left after truncation in DPO and KTO by @qgallouedec in #6321
- Fix EOS being masked when pad_token_id equals eos_token_id in GKD and GOLD by @roycho96 in #6357
- Truncate GOLD on-policy prompts before generation, keeping the prompt end by @roycho96 in #6359
- Fix missing eos_token_id in non-vLLM on-policy GOLD test stubs by @kashif in #6394
- add iw-opd distillation by @kashif in #6191
- Force
use_reentrant=Truefor PEFT + ZeRO-3 + gradient checkpointing in all trainers by @qgallouedec in #6356 - Fix GeometricMixtureWrapper.generate() crash after transformers' MoE decode optimization change by @albertvillanova in #6413
- Add eval_dataset init tests for experimental GRPO-family trainers by @albertvillanova in #6395
- Add eval_dataset init tests for experimental Online-DPO-family trainers by @albertvillanova in #6396
- Add eval_dataset init tests for experimental SFT and distillation trainers by @albertvillanova in #6397
- Add eval_dataset init tests for experimental preference trainers by @albertvillanova in #6398
- Add eval_dataset init tests for experimental BCO, CPO, ORPO and PRM trainers by @albertvillanova in #6399
- Fix corrupted reasoning blocks in Qwen3-family training chat templates by @qgallouedec in #6363
- Fix uncaptured cross-architecture VLM warning in GOLD trainer CI tests by @albertvillanova in #6419
- Fix create_reference_model deprecation warning by @albertvillanova in #6418
- Add
GMPOTrainerto the telemetry allowlist by @qgallouedec in #6410 - Async grpo vllm client by @AmineDiro in #6269
- Re-record the
dpoinvariant reference after #6321 by @qgallouedec in #6421 - Add a tip to the Paper Index on reading papers with the hf CLI by @davanstrien in #6422
- [GRPO] Treat explicit max_tool_calling_iterations=0 as zero, not unlimited by @Strongich in #6423
- Align KTO with DPO: Drop
hasattrguards onlm_head.biasin the Liger path by @qgallouedec in #6425 - Align KTO with DPO: Fix
logits/chosenandlogits/rejectedmetrics by @qgallouedec in #6424 - Support iterable datasets in GRPO and RLOO by @albertvillanova in #6351
- Align
AsyncGRPOTrainerclip-ratio comment withGRPOTrainerby @qgallouedec in #6376 - Ignore benign IndexPutBackward0 warning from Liger SAPO loss by @albertvillanova in #6436
- Align
AsyncGRPOConfig.max_completion_lengthdescription withGRPOConfigby @qgallouedec in #6374 - Fix formatting of error message for loss_type by @qgallouedec in #6373
- Align
AsyncGRPOTrainer.processing_classdocstring withGRPOTrainerby @qgallouedec in #6375 - Remove redundant torch.tensor re-wrapping of batch labels in KTO by @albertvillanova in #6431
- Document max_steps requirement for iterable datasets in GRPO and RLOO by @albertvillanova in #6432
- Fix DPO/KTO double-preprocessing eval_dataset dict passed to evaluate() by @albertvillanova in #6434
- Bump the actions group across 1 directory with 10 updates by @dependabot[bot] in #6442
- Fix GRPO truncated-completion loss normalization by @Functionhx in #6439
- Environment-owned dataset: make
train_datasetoptional by @qgallouedec in #6349 - Align
AsyncGRPOTrainerargs handling with stable trainers by @qgallouedec in #6377 - Fix biased cross-rank aggregation of token-weighted metrics (GRPO, RLOO) by @qgallouedec in #6380
- Silence experimental environment_factory warning in GRPO tests by @albertvillanova in #6445
- Ignore benign bitsandbytes _check_is_size FutureWarning in CI by @albertvillanova in #6448
- Silence PEFT ensure_weight_tying warning on newer PEFT in GRPO tests by @albertvillanova in #6446
- Fix BEMACallback bias_power docstring, which described the opposite of the actual behavior by @DaoyuanLi2816 in #6390
- AsyncGRPO fork-independent epoch counting by @AmineDiro in #6385
- Add support for vLLM 0.25.0 and 0.25.1 by @qgallouedec in #6406
- Drop vLLM 0.16 support by @qgallouedec in #6404
- Remove the experimental DPPO trainer by @qgallouedec in #6402
- Reject
auto_find_batch_sizein GRPO and RLOO by @qgallouedec in #6411 - Fix
precompute_ref_log_probs=Truewith DeepSpeed in DPO and KTO by @qgallouedec in #6403 - [DistillationTrainer refactor] Pin the distillation objective before refactoring by @qgallouedec in #6450
- [DistillationTrainer refactor] Pin on-policy and off-policy training end to end by @qgallouedec in #6451
- [DistillationTrainer refactor] Document the num_items_in_batch denominator bug by @qgallouedec in #6452
- Fix
apply_chat_templatedropping leading tokens fromchosenwhen the prompt common prefix shrinks againstrejectedby @sohumt123 in #6433 - [DistillationTrainer refactor] Move IW-OPD to a dedicated experimental trainer by @qgallouedec in #6453
- [DistillationTrainer refactor] Extract the teacher-server path into ServerDistillationTrainer by @qgallouedec in #6454
- [DistillationTrainer refactor] Remove the local sparse top-1 loss path from the base trainer by @qgallouedec in #6455
- [DistillationTrainer refactor] Remove top-k support from the base generalized JSD loss by @qgallouedec in #6456
- [DistillationTrainer refactor] Validate the teacher by vocab size instead of tokenizer identity by @qgallouedec in #6457
- [DistillationTrainer refactor] Deprecate
lmbdaon/off-policy mixing by @qgallouedec in #6458 - [DistillationTrainer refactor] Point the GKD paper reproduction at
GKDTrainerby @qgallouedec in #6459 - [DistillationTrainer refactor] Remove
lmbdaand the off-policy training branch by @qgallouedec in #6460 - Raise when DPO/KTO cannot precompute correct reference log-probs at eval time by @albertvillanova in #6443
- Silence PEFT ensure_weight_tying warning in DPO and KTO liger tests by @albertvillanova in #6463
- Fix misleading name of BEMACallback bias_power=0.0 test by @albertvillanova in #6464
- Filter reworded torch.jit.script_method warning on Python 3.14 by @albertvillanova in #6465
- Filter triton AnnAssign DeprecationWarning on Python 3.14 by @albertvillanova in #6467
- [DistillationTrainer refactor] Accept a
promptcolumn alongsidemessagesby @qgallouedec in #6461 - [DistillationTrainer refactor] Deprecate
messages-format datasets by @qgallouedec in #6474 - [DistillationTrainer refactor] Fix
num_items_in_batchto count generated completion tokens⚠️ changes loss values by @qgallouedec in #6478 - Release: v1.9 by @qgallouedec in #6495
Full Changelog: v1.8.0...v1.9.0