Releases: huggingface/trl
Release list
v1.10.0
Features
🎓 DistillationTrainer is now a stable trainer
After a ~30-PR refactor that reshaped its data contract, loss path, generation stack, config surface, and tests, DistillationTrainer and DistillationConfig graduate from trl.experimental.distillation to the top-level trl package. Same import surface as SFT / DPO / GRPO / KTO. The old experimental path still works and emits a FutureWarning (removal in v2.0.0).
# Before
from trl.experimental.distillation import DistillationConfig, DistillationTrainer
# Now
from trl import DistillationConfig, DistillationTrainerAlso lands a trl distillation CLI and moves the tests to tests/. The full refactor arc: switched signature columns to prompt (deprecated messages-format), pinned the generation stack to GRPO's, wired the chunked JSD loss and deleted the full-logit path, cleaned the Liger path to share extraction with the chunked path, adopted GRPO's log() / training_step timing / _save_checkpoint, rebuilt the test suite to GRPO shape, and much more.
by @qgallouedec across ~30 PRs (#6479, #6480, #6481, #6482, #6484, #6487, #6497, #6508, #6509, #6510, #6511, #6512, #6513, #6521, #6522, #6523, #6524, #6525, #6526, #6530, #6537, #6604, #6605, #6606, #6607, #6609, #6610, #6611, #6612, #6613, #6614, #6629, #6630, #6632, #6633, #6634, #6639, #6640, #6641, #6642, #6643, #6644, #6645, #6647, #6653).
DistillationTrainer supports Vision Language Models
Alongside the graduation, VLMs work end-to-end in DistillationTrainer.
by @qgallouedec in #6650
AsyncGRPO: loop-owning agent training (OpenEnv harness)
New experimental loop-owning (black-box) path for AsyncGRPOTrainer — for training external agents like opencode that run their own tool loop, rather than TRL driving each turn as in the environment_factory white-box path.
The agent runs in an OpenEnv session in transparent_proxy mode; an in-sandbox proxy captures each turn's token ids and logprobs. On completion, TRL reads the trace, rebuilds per-turn training rows, and scores the workspace with the session's verify(). Ships with three caller-supplied policy hooks (rollout_reward_fn, train_turn_fn, agent_turn_fn) so you can drop framework aux calls (title generator, context summarizer) and reinforce only the turns you want.
Includes a self-contained examples/scripts/openenv/opencode.py (local subprocess sandbox + DeepCoder held-out stdin/stdout verifier), validated end-to-end on Qwen3-4B.
by @AmineDiro in #6420, plus HF-sandbox variant by @sergiopaniego in #6565
AsyncGRPO: observability + sampling controls
- Record rollout traces to trackio by @AmineDiro in #6491
- Step-time metric by @AmineDiro in #6490
- Fix queue wait time metric by @AmineDiro in #6489
top_p/top_k/min_p/repetition_penaltyinAsyncGRPOConfigby @AmineDiro in #6608- Fork-independent epoch counting — landed in v1.9 window
- Close in-flight harness sessions concurrently on shutdown by @sergiopaniego in #6564
DiffusionGemma block-diffusion SFT example
A new SFT example for google/diffusiongemma-26B-A4B-it implementing the reference recipe: one response block per step, uniform random token corruption with t ~ U(eps, 1), two-pass self-conditioning, flat cross-entropy over the whole canvas plus an autoregressive co-loss on the encoder. LoRA targets attention + dense MLP linears. Requires transformers >= 5.12.0.
Ships with diffusion_gemma.jinja / diffusion_gemma_training.jinja chat templates (with {% generation %} markers) so assistant_only_loss=True works out of the box.
More model & template support
- LFM2 and LFM2.5 — day-zero training support by @qgallouedec in #6428
- Gemma 4 response template by @qgallouedec in #6535
Default changes
Two default flips this cycle — pinning them explicitly is recommended if you want the old behavior.
SFTConfig.max_completion_length/GRPOConfig.max_completion_length: default bumped from256to512. By @dhruvnigam93 in #6264GRPOConfig.use_bias_correction_klnow defaults toTrue. By @gowtham-sai-yadav in #6503
Packing and padding-free on VLMs with text-only data
VLM configurations no longer reject packing / padding-free when the batch happens to be text-only.
by @cris96spa in #6547
Other
- Use a reference adapter with
target_parametersonpeft>=0.20.0by @albertvillanova in #6591 - Reject unsupported
train_datasettypes in core trainers by @albertvillanova in #6493 and #6494 - Reject unsupported
f_divergence_type/ loss type combinations in DPO by @qgallouedec in #6559 - Validate that reward functions return one reward per completion by @gowtham-sai-yadav in #6534
- Validate YAML config root type by @RerankerGuo in #6656
- Respect
TQDM_DISABLEin DPO/KTO/BCO reference log-prob loops by @qgallouedec in #6507 - Respect
use_cpuincreate_model_from_pathdevice_mapdefault by @Strongich in #6295 - Use
dtypeinstead oftorch_dtype(deprecated) by @qgallouedec in #6560 - Add missing logged metrics to the GRPO and RLOO docs by @PoojanTa in #6624
- Add
test_train_moe_peft_modelfor KTO by @albertvillanova in #6589
vLLM version sweep
- Add support for vLLM 0.26.0 by @qgallouedec in #6569
Fixes
- Fix DAPO / CISPO / VESPO loss normalization when
steps_per_generation != gradient_accumulation_steps— gradients were silently mis-scaled bygradient_accumulation_steps / steps_per_generation(e.g. 0.5× or 4× the intended token-mean in common configs). Default-equivalent configs are unaffected. By @0xadvait in #6024 - Fix Liger kernel crash on pre-Ampere GPUs by @albertvillanova in #6518
- Fix vLLM weight synchronization when
vllm_enable_sleep_mode=Trueby @muupan in #5313 - Fix vLLM server-mode communicator initialization to use the current accelerator device by @sywangyi in #6417
- Use accelerator device instead of hardcoded
cudain FSDP2 vLLM weight sync by @verma8076 in #6592 - Fix
prepare_deepspeedcrash with CPU offload optimizer by @roycho96 in #5916 fix(ppo): exclude padding tokens from entropy calculation by @mukund1985 in #6121fix: avoidCopySliceswhen scaling policy logits by @DaoyuanLi2816 in #6554- Fix multimodal field handling in GRPO tool-call loop by @HariomJangra in #6286
[GRPO]Apply the completion mask elementwise in the LuSPO loss aggregation by @YaseenBashaT in #6654[GRPO]Fix entropy bonus normalization inconsistency across loss types by @YaseenBashaT in #6648- Fix incorrect example script path in Online DPO docs by @latent-9 in #6598
- Point the opencode example at
huggingface/OpenEnvby @sergiopaniego in #6529 - Remove symlink support from skills installation by @albertvillanova in #6665
Documentation and Examples
- docs: complete
GRPOConfigloss_typehelp by @gowtham-sai-yadav in #6477 - Fix inaccuracies in the release instructions by @albertvillanova in #6566
- Fix patch release instructions: pull release branch instead of main by @albertvillanova in #6563
- Add
opencode_hf_sandbox.py: loop-owning training in remote HF sandboxes by @sergiopaniego in #6565
CI
test: don't hard-codebf16=Trueon devices that lack bf16 support by @behroozazarkhalili in #6036- Cleanup xpu cache memory after each test by @kaixuanliu in #5771
- Force
use_cpuin tests when no CUDA device is available by @albertvillanova in #6504 - Silence transformers
LOAD REPORTtables in test output by @albertvillanova in #6502 - Require vision for VLM response parsing tests by @albertvillanova in #6602
- Require
jmespathonly on transformers below 5.13.0 by @albertvillanova in #6597 - Add regression tests for evaluating init-time eval datasets after training with precomput...
v1.9.2
What's Changed
- Revert xfail for NemotronH GRPO/RLOO tests now that transformers#47569 fixed the kernels bug by @albertvillanova in #6558
- Fix test_peft_with_quantization tests for bitsandbytes 0.50.0 by @albertvillanova in #6546
Full Changelog: v1.9.1...v1.9.2
v1.9.1
What's Changed
- Fix vLLM server-mode communicator initialization to use the current A… by @sywangyi in #6417
- Fix : queue wait time metric AsyncGRPOTrainer by @AmineDiro in #6489
- Hotfix CI: Temporarily pin liger-kernel < 0.8.1 by @albertvillanova in #6517
- Fix crash when training with Liger kernel on pre-Ampere GPUs by @albertvillanova in #6518
- Fix prepare_deepspeed crash with cpu offload optimizer by @roycho96 in #5916
- Add Gemma4 response template by @qgallouedec in #6535
- fix(grpo): correct DAPO/CISPO/VESPO loss normalization when steps_per_generation != gradient_accumulation_steps by @0xadvait in #6024
- Hotfix CI: Temporarily pin bitsandbytes < 0.50.0 by @albertvillanova in #6544
- Hotfix CI: Xfail NemotronH GRPO/RLOO tests against a broken transformers dev build by @albertvillanova in #6542
Full Changelog: v1.9.0...v1.9.1
v1.9.0
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 s...
v1.8.0
Features
🎓 KTO is now a stable trainer
After many cycles of KTOTrainer ↔ DPOTrainer alignment work, KTO graduates from trl.experimental.kto to the top-level trl package. Same API as DPO/GRPO/SFT — imports move from experimental, tests move to the main test tree, docs no longer flag it as experimental. The experimental path still works and emits a FutureWarning (removal in v2.0.0).
# Before
from trl.experimental.kto import KTOConfig, KTOTrainer
# Now
from trl import KTOConfig, KTOTrainerPer our telemetry, KTO is the 4th most used trainer in TRL — this graduation was overdue.
by @albertvillanova in #6175, #6287 and #6345
Environment-owned rewards & multi-environment support
Three interrelated changes make agentic RL training with environments substantially more ergonomic.
Environment-owned reward. If your environment_factory env defines a reserved get_reward() method (no args → float), it's called once per completed rollout and treated as a reward source. reward_funcs becomes optional — no more leaking env state back out to trainer-owned reward funcs.
class WordleEnv:
def reset(self, **kwargs):
self._target = sample(words); self._solved = False
def get_reward(self) -> float: # optional, reserved (not a tool)
return 1.0 if self._solved else 0.0
def guess(self, word: str) -> str: # exposed as a tool
self._solved = word == self._target; ...
trainer = GRPOTrainer(
model=model,
train_dataset=dataset,
environment_factory=WordleEnv, # no reward_funcs needed
)Multi-environment support. environment_factory now accepts dict[str, factory] in addition to a single callable. Each dataset row selects its environment via an environment column, and only that env's tools are exposed in that row's prompt — so a coding task and a game can train together in one run without leaking each other's tool schemas. Single-callable usage is unchanged.
Same wiring lands in GRPO, AsyncGRPO, DPPO, and GRPO-with-replay-buffer.
Env-owned reward by @qgallouedec in #6238; multi-env in #6001 and #6002
Entropy regularization for GRPO
GRPOConfig now supports both static and adaptive entropy regularization (Skywork-OR1). The bonus encourages exploration and helps prevent premature policy collapse.
GRPOConfig(
entropy_coef=0.01, # static
# or:
use_adaptive_entropy=True, # adjust to target entropy
entropy_target=1.5,
entropy_coef_delta=0.01,
entropy_coef_min=0.0,
entropy_coef_max=0.1,
)Adaptive mode adjusts the coefficient once per optimizer step from window-aggregated entropy (gradient accumulation-aware) and persists entropy_coef in the checkpoint for resume. Not compatible with the Liger kernel.
by @albertvillanova in #6140
quantization_config trainer argument (streamlined QLoRA)
QLoRA no longer requires reaching into model_init_kwargs or pre-loading the model manually.
SFTTrainer(
model="meta-llama/Llama-2-7b-hf",
quantization_config=BitsAndBytesConfig(load_in_4bit=True),
peft_config=LoraConfig(),
train_dataset=dataset,
)Added to SFTTrainer, DPOTrainer, GRPOTrainer, RLOOTrainer, RewardTrainer, and KTOTrainer. Sits next to peft_config (the other non-serializable QLoRA ingredient), flows into from_pretrained, and raises if also set in args.model_init_kwargs. Also drops the redundant get_kbit_device_map() line — QLoRA trains identically without it across all tested configurations.
by @qgallouedec in #6157 and #6276
MoE aux loss extends to DPO and KTO
v1.7 added the router load-balancing auxiliary loss to GRPOTrainer / RLOOTrainer / AsyncGRPOTrainer. It's now on DPOTrainer and KTOTrainer too, so post-training MoE models with preference data keeps experts balanced.
by @qgallouedec in #6208 and #6275
Neuron-friendly chunked_nll via static-shape token packing
The chunked NLL path had data-dependent indexing that broke XLA/Neuron compilation. This PR reworks it to pack valid tokens to the front via a stable argsort on the validity mask, iterate over ceil(n_valid / chunk_size) * chunk_size whole chunks (a tensor, no Python-int sync), and use ignore_index=-100 inside each chunk. GPU behavior is unchanged; Neuron now works too — same tiny memory footprint (~1.33 GiB vs the naive 5.94 GiB alternative) with no per-mask recompilation.
by @michaelbenayoun in #6314
Packing-aware dynamic batching in AsyncGRPO
AsyncGRPO micro-batching becomes packing-aware and token-bounded on top of the padding-free path from v1.7:
- Σ Lᵢ² row balancing — a greedy longest-first assignment gives every DP row the same Σ Lᵢ² instead of the same token count (attention is O(L²), FFN is O(L), so equal token counts don't equalize wall-clock). Cross-rank stragglers vanish; +19% MFU at 4B in the benchmark.
- Token-budget packing (opt-in) — cap each row at
token_budgettokens with a variable sample count, decoupling peak memory fromper_device_train_batch_size. Useful in memory-bound regimes (no gradient checkpointing, long context, very large models).
Both ride HF Trainer's existing gradient accumulation — no training-loop surgery, FSDP/EP collectives stay in lockstep.
by @AmineDiro in #6092
VLM support in GOLDTrainer
GOLDTrainer now supports vision-language models end-to-end.
by @Strongich in #5969
Support tool calling in KTO
by @qgallouedec in #6259
Support PEFT + Liger in DPO and KTO
by @albertvillanova in #6159 and #6277
Per-dataset fraction in dataset mixtures
You can now weight a DatasetMixtureConfig by fraction instead of only by explicit counts.
by @qgallouedec in #6199
SFT: truncate during dataset preparation
Continuing the label-refactor from v1.7 (#6037), truncation moves out of the collator and into dataset preparation. SFTTrainer and DPOTrainer now truncate consistently at the same phase.
by @qgallouedec in #6155
Simplify tokenization [1-5/N]
A big refactor pass merging DPO / SFT / Reward / KTO tokenization into one shared implementation:
- Remove redundant
is_vlmparameter — #6298 - Make
_tokenizea module-level function — #6301 - Factor
_tokenizeinto a single shared function — #6302 - Bundle
apply_chat_templatekwargs — #6305 - Pass
chat_templateasapply_chat_template_kwargs— #6315
All by @albertvillanova. Related: align collators across DPO / SFT / Reward / KTO by @qgallouedec in #6178.
Other
- Support
DatasetDictandIterableDatasetDictaseval_datasetin trainers by @albertvillanova in #6322 - Auto-load
processing_classin CPO/ORPO trainers when omitted by @DaoyuanLi2816 in #6087 - Add
trust_remote_codetoGRPOConfigby @muupan in #4186 - Integrate the new response parsing API by @qgallouedec in #5791
- Raise a clear error when the ChatML collator truncates the whole prompt by @qgallouedec in #6310
- Raise a clear error when GKD student and teacher vocab sizes differ by @sergiopaniego in #6252
- Add prompt-learning guard for PEFT with Liger in GRPO by @albertvillanova in #6186
[async GRPO]Per-generationreset()observation by @qgallouedec in #6072- Default AsyncGRPO
token_budgetto vLLMmax_model_lenby @AmineDiro in #6218 - Drop vLLM 0.14 support by @qgallouedec in #6209
- Drop vLLM 0.15 support by @qgallouedec in #6239
- Log
entropyandnum_tokensmetrics in KTO by @qgallouedec in #6257 and #6256 - Standardize
TrainerCallbackimport to the public top-level path by @qgallouedec in #6249 - Remove redundant
get_kbit_device_map()by @qgallouedec in #6158 - Factorize
get_dataset_column_namesfrom core and experimental trainers by @albertvillanova in #6272 and #6273
Deprecations and removals
- Remove
GFPOTrainerby @qgallouedec in #6309 — no known usage, and its behavior can be reproduced by adjustingreward_funcs. - Remove
PAPOTrainerby @qgallouedec in #6235 - Remove post-training-toolkit integration by @qgallouedec in...
v1.7.1
What's Changed
- Fix GRPO + vLLM colocate + PEFT hang on non-NVLink hardware by @albertvillanova in #6139
- Fix dataset fingerprinting in DPO/SFT tokenization by @qgallouedec in #6206
- Pass GPU device_ids to barrier fix in GRPO + vLLM colocate + PEFT by @albertvillanova in #6187
- Integrate the new response parsing API by @qgallouedec in #5791
- Fix
add_response_schematests for the newparse_responseprefix requirement by @qgallouedec in #6236 - Add prompt-learning guard for PEFT with Liger in GRPO by @albertvillanova in #6186
- Fix activation offload storage dedupe reuse by @winglian in #6241
Full Changelog: v1.7.0...v1.7.1
v1.7.0
Features
SFT default loss_type is now "chunked_nll"
The flip announced in v1.6 has landed. Setting loss_type is optional, and the default now resolves to "chunked_nll" — giving every SFTTrainer run ~30% less peak VRAM on average (up to ~50% on large-vocab models) with wall-clock time neutral or slightly faster. No action needed.
The auto-resolve falls back to "nll" when use_liger_kernel=True (the two paths are incompatible). If you want the old behavior — e.g. for custom heads — pin it explicitly:
SFTConfig(loss_type="nll")by @qgallouedec in #5846
MoE auxiliary loss in GRPO / RLOO / AsyncGRPO
Post-training MoE models now correctly include the router load-balancing auxiliary loss, matching the model's own reference forward and SFTTrainer. Enable via model_init_kwargs:
GRPOConfig(
...,
model_init_kwargs={"output_router_logits": True, "router_aux_loss_coef": 0.001},
)Plumbed through _get_per_token_logps_and_entropies (now returns a 3-tuple including aux_loss), folded into the policy loss with grad-accum scaling matched per trainer, and logged as aux_loss. AsyncGRPO recomputes it via load_balancing_loss_func in the chunked LM-head path (same as SFT's chunked path).
by @AmineDiro in #6083, plus router_aux_loss_coef config wiring by @qgallouedec in #6085
New experimental GMPO trainer
Geometric-Mean Policy Optimization lands as an experimental trainer. Replaces GRPO's per-token arithmetic mean of importance ratios with a sequence-level geometric mean (mean of clipped log-ratios, then exp); clipping is one-sided by advantage sign and applied in log space. Default epsilon=0.4 per the paper.
from trl.experimental.gmpo import GMPOConfig, GMPOTrainer
trainer = GMPOTrainer(
model="Qwen/Qwen3-4B",
args=GMPOConfig(epsilon=0.4),
reward_funcs=accuracy_reward,
train_dataset=dataset,
)by @raghulchandramouli in #6078
Transformers continuous batching in GRPO / RLOO
use_transformers_paged was deprecated in v1.4; it's now replaced with proper transformers continuous batching. The old branch silently bypassed importance-sampling correction (logprobs = None); the new path captures logprobs from output.logprobs and exposes a ContinuousBatchingConfig for KV-cache tuning.
GRPOConfig(
...,
use_transformers_continuous_batching=True,
transformers_continuous_batching_config={
"use_cuda_graph": False,
"max_memory_percent": 0.4, # leave headroom for training
},
)Benchmark (Llama-3.2-1B-Instruct, A100 80GB, GSM8K): 1.25× faster at N=64 generations with -16 GB peak VRAM vs default generate(). Use when N ≥ 32 with variable completion lengths.
use_transformers_paged=True still works and forwards to the new flag with a FutureWarning. Requires transformers>=5.8.0.
by @sergiopaniego in #5765
AsyncGRPO: native weight sync with vLLM ≥ 0.22.0
WeightTransferClient now drives vLLM's native 4-phase RL weight-transfer API instead of the older 2-call flow: pause(mode="keep") → start_weight_update → threaded update_weights + NCCL broadcast → finish_weight_update → resume. Validated end-to-end on H100 across single-node, FSDP2×4 + TP=4, and 2-node FSDP2×4 + DP=2×TP=4 (weight-sync time ≈ 0.18-0.8 s).
by @AmineDiro in #5892
Padding-free training in AsyncGRPO
AsyncGRPO now supports the same padding-free path SFT already had. Flattens the batch and uses position_ids-based document boundaries instead of right-padding to the longest sequence — meaningful speedup and memory savings on heterogeneous-length workloads.
by @qgallouedec in #5854
Experimental Harbor integration
A new trl.experimental.harbor adapter plugs Harbor agentic task suites into GRPOTrainer via environment_factory. Same pattern as the OpenReward integration — one spec wires all three trainer slots:
from trl import GRPOConfig, GRPOTrainer
from trl.experimental.harbor import HarborSpec
spec = HarborSpec("AdithyaSK/data_agent_rl_environment_train", agent="bash", num_tasks=64)
trainer = GRPOTrainer(
model="Qwen/Qwen3-4B",
args=GRPOConfig(num_generations=8, max_steps=50, max_tool_calling_iterations=25),
train_dataset=spec.train_dataset,
environment_factory=spec.environment_factory,
reward_funcs=spec.reward_funcs,
)Built-in bash harness, plus jupyter and terminal_notes example harnesses. Gated by the new trl[harbor] extra.
by @adithya-s-k in #6018
trust_remote_code in trainer configs
A single trust_remote_code: bool = False field on the trainer configs now covers the whole load surface — model, processor / tokenizer, reference model, reward model, reward tokenizer, teacher — instead of forcing users to thread it through several independent kwarg dicts.
SFTConfig(trust_remote_code=True)ModelConfig.trust_remote_code is removed to avoid duplicate --trust_remote_code when combining dataclasses; CLI behavior is unchanged.
by @qgallouedec in #5802
KTO ↔ DPO alignment: tests, evaluate, sync_ref_model
The last alignment cycle before graduation: KTO now has parity with DPO on pad_to_multiple_of, sync_ref_model, evaluate(), method order/signature, metric placement (all moved into _compute_loss), and a real text + VLM (including multi-image) test suite.
PRs all by @albertvillanova: #6029, #6030, #6033, #6034, #6035, #6080, #6093, #6148, #6149, #6150, #6152, #6160, #6163.
LFM2-VL multimodal inputs in GRPO / RLOO
GRPO and RLOO now support LFM2-VL multimodal inputs end-to-end.
by @zwischenraum in #6114
New built-in reward helpers
get_repetition_penalty_rewardby @qgallouedec in #6058get_cosine_scaled_rewardby @qgallouedec in #6066
SFT refactor: build labels during dataset preparation
Label construction moves out of the collator and into dataset preparation, so "what's trainable" is defined in exactly one place. A single batched map produces a labels column where each token keeps its ID when every applicable mask is 1, else -100. Plain LM stays storage-neutral; pre-tokenized datasets with mask columns now go through the same path. Step 1 toward fixing #3927.
Idefics3 chat template
{% generation %}-marker training template for Idefics3, enabling assistant_only_loss=True.
vLLM version sweep
- Support vLLM 0.19.1 by @qgallouedec in #6107
- Support vLLM 0.20.0 by @qgallouedec in #6108
- Support vLLM 0.22.1 by @qgallouedec in #6119
- Support vLLM 0.23.0 by @qgallouedec in #6153
- Drop vLLM 0.12 by @qgallouedec in #6109
- Drop vLLM 0.13 by @qgallouedec in #6154
Other
- Normalize JSD distillation loss by
num_items_in_batchfor gradient accumulation by @behroozazarkhalili in #6006 [AsyncGRPO]Rollout worker: set aiohttp limit tomax(100, max_inflight_tasks)by @ggcr in #5861- Align SDPO with GRPO/RLOO: drop NaN values when averaging logged metrics by @anshulkulhari7 in #6055
- Make
evaluate()accept the same dataset types as the trainer by @qgallouedec in #6116 - Keep extra columns in
unpair_preference_datasetby @albertvillanova in #6161 - Warn when sequence-level importance sampling is combined with a token-summed loss type by @discobot in #6042
fix(profiling): logProfilingContextmetrics to Trackio backend by @Anai-Guo in #5979- Move experimental example scripts out of the packaged tree by @sergiopaniego in #6141
- Remove redundant
.contiguous()calls by @qgallouedec in #6045 and #6046 - Harmonize logger imports by @qgallouedec in #6142
Fixes
- Share frozen layers with reference model instead of duplicating in memory —
create_reference_modelwithnum_shared_layerswas double-allocating the "shared" frozen layers because the loop never assigned_ref_paramback. Now it does, so shared layers are held once. By @behroozazarkhalili in #6053 - Fix
chunked_nllmixed Tensor/DTensor error under FSDP2 + PEFT by @albertvillanova in #6065 - Fix per-chunk
lm_head.weightall-gathers under FSDP2 +chunked_nllby @albertvillanova in #6077 - **Fix ZeRO-3 + PEF...
v1.6.0
Features
AsyncGRPO rollout worker now runs in a separate process
AsyncRolloutWorker is no longer a thread — it's a spawned child process with its own GIL. The trainer's autograd engine no longer competes with recursive_parse / accuracy_reward for the GIL, which was causing 1-5s stalls in real Qwen3-30B-A3B @ 16k runs and ultimately NCCL watchdog timeouts on other ranks.
Architectural changes:
AsyncRolloutWorker(parent) owns the child process + sharedmp.Queue/mp.Value/mp.Event._AsyncRolloutLoop(child-only) handles tokenization, dataset iteration, reward funcs, and asyncio loops.- A new
WeightTransferClientowns the NCCL group with vLLM (/pause,/resume,/init_weight_transfer_engine,/update_weights); the rollout child only talks to/v1/completions.
Two correctness fixes shipped alongside (they would have conflicted otherwise): broader aiohttp retry (now catches ClientPayloadError) with bounded exponential backoff, and all-NaN reward columns are now preserved — np.nansum was silently returning 0, giving unscorable completions a real advantage signal and pushing the policy away from correct answers (~30% of DeepMath / OpenR1-Math rows).
Note
reward_funcs / tools / environment_factory must now be picklable, and the child runs CPU-only (CUDA_VISIBLE_DEVICES="").
by @AmineDiro in #5749
New experimental A2PO trainer (Optimal Advantage Regression)
A new A2POTrainer implements A*-PO from "Accelerating RL for LLM Reasoning with Optimal Advantage Regression". Two stages: an offline V* estimation pass from reference policy samples (with optional filter_all_incorrect to drop prompts where every reference completion fails), then on-policy training with one generation per prompt and a plain least-squares loss on β₂·log(π/π_ref) vs r − V*. No group, no critic, no clipping, no reward normalization.
from trl.experimental.a2po import A2POConfig, A2POTrainer
trainer = A2POTrainer(
model="Qwen/Qwen3-4B",
args=A2POConfig(num_value_samples=8, filter_all_incorrect=True),
train_dataset=dataset,
reward_funcs=accuracy_reward,
)
trainer.train()Designed for binary verifiable rewards (math/code), not open-ended problems.
by @raghulchandramouli in #5940
KTO now supports VLMs + big alignment push
The biggest KTO ↔ DPO alignment cycle yet — KTOTrainer now supports vision-language models, plus a deep restructuring of compute_loss, KL dataset generation, ref-logp precomputation, activation offloading, sampler strategy, metrics, and more. KTO graduation is very close.
from trl.experimental.kto import KTOConfig, KTOTrainer
trainer = KTOTrainer(
model="Qwen/Qwen2.5-VL-3B-Instruct",
args=KTOConfig(...),
train_dataset=vision_kto_dataset,
)VLM support: by @albertvillanova in #5939. Plus ~20 alignment PRs all by @albertvillanova: #5820, #5849, #5852, #5850, #5866, #5864, #5856, #5872, #5875, #5900, #5901, #5899, #5906, #5909, #5914, #5982, #5936, #5996, #5998, #5999.
Cross-tokenizer alignment in GOLD via byte offsets
The GOLD distillation trainer used to align student/teacher tokens by extending two decoded strings and flushing on equality. It silently broke on any byte-level disagreement — including the common case of one tokenizer prepending BOS while the other doesn't (Llama-3 ↔ Qwen-3). The X-Token paper called this out by name.
Each side now carries (start_byte, end_byte) spans derived once from the fast tokenizer's char offsets, and the walker syncs on cumulative byte boundaries. On the on-policy path, spans come from piece_byte_len over the sampled token ids (not from re-encoding the decoded completion — BPE makes that round-trip non-injective).
Two related fixes shipped: long rows no longer lose the completion (now keeping the last max_length tokens), and the vLLM on-policy original_prompt_text is now decoded from the truncated ids the student actually consumed.
SDFT / SDPO: live teacher logprobs from the vLLM server
When teacher_model_kind="live" and vllm_mode="server", the vLLM generation server already holds the current student weights (synced every step for rollouts). The new use_teacher_server=True flag scores the teacher's log-probs on that same server instead of running a separate local teacher forward — removing the teacher from the training step entirely.
Supported modes: sampled_token (reverse KL on the realized token) and topk_logits. When buffered batches reuse steps (num_iterations > 1), weights are re-synced before scoring so the teacher never scores stale.
Bidirectional masked importance sampling (MIS) for IcePop
vLLM importance sampling in GRPO now uses a two-sided band [C_min, C_max] instead of a single upper cap, aligning TIS/MIS with IcePop's bidirectional handling of train–inference ratio outliers.
from trl import GRPOConfig
config = GRPOConfig(
vllm_importance_sampling_clip_min=0.5,
vllm_importance_sampling_clip_max=2.0,
vllm_importance_sampling_correction="mask", # or "truncate"
)The old vllm_importance_sampling_cap is deprecated and maps to clip_max.
NemotronH and Nemotron 3 Ultra support
Day-zero training support for NVIDIA's new model families.
- NemotronH integration by @qgallouedec in #5938
- Nemotron 3 Ultra support by @qgallouedec in #5942
- Enable gradient checkpointing in Nemotron 3 SFT example by @sergiopaniego in #5944
Even more training chat templates
Three more model families with {% generation %} markers (assistant-only loss out of the box):
Distributed backend boilerplate, hidden
A new trl/distributed.py introduces a single DistributedBackend class that detects ZeRO stage and FSDP version once, then exposes two context managers (gather_params, summon_full_params) used everywhere. Replaces the scattered getattr(state, "fsdp_plugin", None) / gather_if_zero3 / summon_full_params if ... else nullcontext() boilerplate spread across vllm_generation.py, models/utils.py, and the main trainers. Future deprecations land in one place.
by @albertvillanova in #6000
Decoupled self-distillation trainers
A two-PR refactor that disentangles SDPO, SDFT, and other self-distillation trainers from their shared base, making each one self-contained and consistent with the rest of the codebase.
by @LeonEricsson in #5862 and #5883
Heads-up: SFT default loss_type will change in 1.7
Setting SFTConfig.loss_type is now optional, and leaving it unset emits a FutureWarning: in TRL 1.7 the default will switch from "nll" to "chunked_nll". No action needed — you'll just get the new default automatically on upgrade — unless you want to pin the current behavior (e.g. for custom models) with loss_type="nll".
by @qgallouedec in #5997
Other
- Support
'None'as CLI value forOptional[T]fields by @qgallouedec in #5843 - Support non-
lm_headoutput projections in chunked SFT loss (GPTNeoX) by @qgallouedec in #5857 SFTTrainer: merge entropy and accuracy computation to eliminate redundant logits copy by @flutist in #5897- Remove redundant
.contiguous()calls inDPOTrainerto reduce peak memory by @flutist in #5926 - Remove unnecessary explicit
.contiguous()beforeentropy_from_logitsby @qgallouedec in #5930 - Exclude
Nonereward completions from GRPO/RLOO advantage baseline by @AmineDiro in #5902 - Support multimodal config in PPO ValueHead by @albertvillanova in #5907
- Support vision datasets for Liger in DPO by @albertvillanova in #5943
- Raise if
precompute_ref_log_probswith vision datasets in DPO by @albertvillanova in #5867 - 🔒 Gate trainer telemetry on an explicit class-name allowlist by @qgallouedec in #5851
- Update vLLM version support to 0.19.0 by @sergiopaniego in #5879
- Improve error message when image tokens are truncated by
max_lengthby @lxk8998 in #5927 - Padding-free invariance test by @qgallouedec in #5842
- Per-field invariance tolerances, calibrated by @qgallouedec in #5844
Fixes
- Fix
loss_type="chunked_nll"under DeepSpeed ZeRO-3 by @qgallouedec in #5873 - Fix GRPO
use_liger_kernelunder DeepSpeed ZeRO-3 by @kashif in #5891 async_grpo: don't return onqueue.Emptyby @AmineDiro in https://github.c...
v1.5.1
What's Changed
- 🔒 Gate trainer telemetry on an explicit class-name allowlist by @qgallouedec in #5851
Full Changelog: v1.5.0...v1.5.1
v1.5.0
Features
Even more training chat templates
Three more model families gain training-compatible templates with {% generation %} markers (so assistant_only_loss=True just works):
- Phi-3.5 by @DagaBhai in #5746
- Qwen3-VL by @aazizyan in #5764
- Qwen3.5 Think / NoThink by @aazizyan in #5824
Final logits softcapping for async GRPO
The chunked LM-head path used by AsyncGRPOTrainer now supports models that use final_logit_softcapping (notably Gemma 2). _ChunkedLogProbFunction applies logit_scale, optional tanh-based softcapping, and temperature consistently in both forward and backward — softcapped models are no longer rejected.
by @mlarnouhet in #5691
KTO ↔ DPO alignment continues
Two more cycles closer to KTO graduation:
- Align
compute_lossflow by @albertvillanova in #5810 - Align
_compute_loss_ligerflow by @albertvillanova in #5816
Trainer telemetry (opt-out)
_BaseTrainer.__init__ now emits a single anonymous huggingface_hub.send_telemetry ping per trainer instantiation, so we can finally see which trainers / model families / distributed backends are actually being used in practice and prioritize accordingly.
The payload is intentionally minimal — TRL version, trainer class name, model architecture, PEFT yes/no, distributed backend (deepspeed/fsdp/ddp/none), bucketed world size, device type, GPU model when available. No user data, no dataset names, no model paths, no hyperparameter values, never sent in CI / offline / HF_HUB_DISABLE_TELEMETRY mode.
See usage_stats.md for what's collected and how to opt out.
by @qgallouedec in #5758
Other
OpenRewardSpec: fix omitting task-scoped tools during rollout binding (fixes #5727) by @rycerzes in #5729- Add OpenReward example to the list of examples by @sergiopaniego in #5752
- Add DDP-2 members to invariant test suite by @qgallouedec in #5736
- Align and simplify the stable training scripts by @qgallouedec in #5812
- Replace uv installation script with setup action by @qgallouedec in #5735
Fixes
- Fix exponential backtracking in qwen3 / qwen3_5 / glm4moe response parsing —
GRPOTrainerwas hanging indefinitely on truncated<tool_call>blocks (a degenerate case that happens naturally when generation hitsmax_completion_lengthmid-tool-call). Rewrote the regex to be non-backtracking — worst case goes from O(2ⁿ) to O(n). By @xodn348 in #5798 - CUDA memory leak: release BNB dequantization buffers & stale state in
OffloadActivations— follow-up to v1.4's activation-offloading leak fix. By @butterwecksolutions in #5730 - Invalidate ZeRO-3 param coordinator trace in
add_hooksby @roycho96 in #4693 - Fix nested
vocab_sizeforDistillationTrainerandGOLDTrainerby @Beichen-Ma in #5592 - Fix MPS support in experimental
empty_cache()by @jamie-peterson-ml in #5799 - Fix
metric_for_best_modelfor trainer-specific eval metrics by @qgallouedec in #5811 - Fix
generate_batch: inference tensors blocking inplace ops in background thread by @albertvillanova in #5818 - Replace deprecated
torch_dtypewithdtypeacross examples, docs, notebooks, tests, and experimentaldistillation/goldtrainers by @qgallouedec in #5717
Documentation and Examples
CI
- Migrate tests to Qwen3.5 Think/NoThink fixtures + tiny-model generation scripts by @aazizyan in #5819 and #5821
- Align tiny
Glm4MoeForCausalLM/Cohere/Cohere2/Qwen2.5-VLconfigs with their reference models by @qgallouedec in #5638, #5706, #5707 and #5739 - Fix tiny Qwen3-VL
deepstack_visual_indexesand drop the test skip by @qgallouedec in #5779 - Fix tiny Qwen2.5-VL
fullatt_block_indexesout of range for depth=2 by @albertvillanova in #5805 - Remove non-existent params from tiny Qwen2-VL model by @albertvillanova in #5795
- Fix vision config
num_headskey in Qwen VL tiny model scripts by @matdou in #5792 - Drop unjustified
model.visual.skip in GRPO/RLOO Qwen2.5-VL tests by @qgallouedec in #5780 - Make the LLaVA / LLaVA-Next test guard explicit by @qgallouedec in #5778
- Remove obsolete Gemma 3 vision-head guard from VLM training tests by @qgallouedec in #5772
- Fix OOM in CI: reduce batch size in VLM SFT / GRPO/RLOO VLM / toolcall tests by @albertvillanova in #5687, #5767, #5801
- Fix OOM in CI by clearing chained exception tracebacks by @albertvillanova in #5776
- Fix OOM in CI by reducing intermediate_size and image token budget for tiny Gemma 4 by @albertvillanova in #5760
- Fix CI errors in response parsing for gpt-oss/llama with transformers v5 by @albertvillanova in #5755
- Fix CI
AttributeError: 'GptOssConfig' object has no attribute 'num_experts'by @albertvillanova in #5756 - Fix CI
apply_model_revisionsby removing_commit_hashkwarg by @albertvillanova in #5762 - Fix CI test to avoid skipping
model.visualparams by @albertvillanova in #5806 - Fix transformers min version for tiny gemma 4 as 5.5.0 by @albertvillanova in #5763
- Hotfix CI: pin
torch < 2.12.0(later reverted) by @albertvillanova in #5769 - Fix catch-all empty string in Makefile
pytest --only-rerunby @albertvillanova in #5784 - chore: update
tests_latest.ymlby @hf-security-analysis[bot] in #5733
New Contributors
- @hf-security-analysis[bot] made their first contribution in #5733
- @Beichen-Ma made their first contribution in #5592
- @DagaBhai made their first contribution in #5746
- @xodn348 made their first contribution in #5740
- @mlarnouhet made their first contribution in #5691
- @matdou made their first contribution in #5792
- @jamie-peterson-ml made their first contribution in #5799
- @rycerzes made their first contribution in #5729
What's Changed
- ⬆️ Bump dev version by @qgallouedec in #5734
- chore: update tests_latest.yml by @hf-security-analysis[bot] in #5733
- fix: CUDA memory leak / release BNB dequantization buffers & stale state in OffloadActivations by @butterwecksolutions in #5730
- fix: invalidate ZeRO-3 param coordinator trace in add_hooks by @roycho96 in #4693
- Fix nested vocab_size for DistillationTrainer and GOLDTrainer by @Beichen-Ma in #5592
- feat: add Phi-3.5 training chat templates with generation markers by @DagaBhai in #5746
- docs(grpo): align model to Qwen2.5 and add GRPO OOM tab in quickstart by @xodn348 in #5740
torch_dtype->dtypeby @qgallouedec in #5717- Add OpenReward example to the list of examples by @sergiopaniego in #5752
- Fix CI errors in response parsing for gptoss/llama with transformers v5 by @albertvillanova in #5755
- Add DDP-2 members to invariant test suite by @qgallouedec in #5736
- Hotfix CI param not updated AssertionError: Pin torch < 2.12.0 by @albertvillanova in #5769
- Align tiny-Glm4MoeForCausalLM with GLM-4.5 reference config by @qgallouedec in #5638
- Align tiny Cohere config with aya-expanse-8b by @qgallouedec in #5706
- Align tiny Cohere2 config with tiny-aya-earth by @qgallouedec in #5707
- Fix OOM in CI by reducing intermediate_size and image token budget for tiny Gemma4 by @albertvilla...