Add GRPO resume vLLM cleanup guard (#4411)

* Add GRPO resume vLLM cleanup guard

* Guard GRPO resume sleep on vLLM sleep mode

* Harden GRPO resume vLLM cleanup guard

- Wrap llm.sleep(1) in try/except so a failed sleep does not block
  training resume (best-effort cleanup)
- Also check kwargs["model_path"] which transformers.Trainer.train()
  still accepts and normalizes to resume_from_checkpoint internally

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
TR-3B 2026-03-25 10:07:45 +05:30 committed by GitHub
commit 8c94b461fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -93,6 +93,58 @@ def vLLMSamplingParams(**kwargs):
return sampling_params
def _maybe_prepare_vllm_for_resume(trainer):
if not torch.cuda.is_available():
return
llm = getattr(trainer, "llm", None)
if llm is None:
llm = getattr(getattr(trainer, "model", None), "vllm_engine", None)
if llm is None:
return
model_config = getattr(
getattr(getattr(llm, "llm_engine", None), "vllm_config", None),
"model_config",
None,
)
if not getattr(model_config, "enable_sleep_mode", False):
return
try:
llm.sleep(1)
except Exception:
pass
import gc
for _ in range(3):
gc.collect()
torch.cuda.empty_cache()
def _patch_resume_from_checkpoint_memory(trainer_class):
original_train = getattr(trainer_class, "train", None)
if original_train is None:
return
if getattr(original_train, "_unsloth_resume_guard", False):
return
def _unsloth_train_with_resume_guard(self, *args, **kwargs):
resume_from_checkpoint = kwargs.get("resume_from_checkpoint", None)
if resume_from_checkpoint is None:
resume_from_checkpoint = kwargs.get("model_path", None)
if resume_from_checkpoint is None and len(args) != 0:
resume_from_checkpoint = args[0]
if resume_from_checkpoint:
_maybe_prepare_vllm_for_resume(self)
return original_train(self, *args, **kwargs)
_unsloth_train_with_resume_guard._unsloth_resume_guard = True
trainer_class.train = _unsloth_train_with_resume_guard
def PatchRL(FastLanguageModel):
try:
from trl.models.utils import unwrap_model_for_generation
@ -686,8 +738,8 @@ def _patch_trl_rl_trainers(trainer_file = "grpo_trainer"):
else:
continue
call_args.append(f"{k} = {k}")
arguments = f"\n{' '*8}" + f",\n{' '*8}".join(arguments)
call_args = f"\n{' '*12}" + f",\n{' '*12}".join(call_args)
arguments = f"\n{' ' * 8}" + f",\n{' ' * 8}".join(arguments)
call_args = f"\n{' ' * 12}" + f",\n{' ' * 12}".join(call_args)
processed.append(
(
arguments,
@ -701,7 +753,7 @@ def _patch_trl_rl_trainers(trainer_file = "grpo_trainer"):
# Add tokenizer if not seen
if "tokenizer" not in parameters and "processing_class" in parameters:
arguments += f",\n{' '*8}tokenizer = None"
arguments += f",\n{' ' * 8}tokenizer = None"
call_args = call_args.replace(
"processing_class = processing_class",
"processing_class = tokenizer if tokenizer is not None else processing_class",
@ -1490,6 +1542,9 @@ def _patch_trl_rl_trainers(trainer_file = "grpo_trainer"):
imports,
overwrite = False,
)
patched_trainer = getattr(created_module, f"Unsloth{RLTrainer_name}")
if trainer_file == "grpo_trainer":
_patch_resume_from_checkpoint_memory(patched_trainer)
# Patch Trainer
exec(
@ -1706,8 +1761,8 @@ def patch_functions(RLTrainer, trainer_file, RLTrainer_name, all_imports, import
sampling_params = re.sub(r"[\,][\s]{0,}\,", ",", sampling_params)
new_vllm_part = (
f"\n{' '*8}if {args}.use_vllm:\n{sampling_params}"
f"\n{' '*8}else:\n"
f"\n{' ' * 8}if {args}.use_vllm:\n{sampling_params}"
f"\n{' ' * 8}else:\n"
)
if trl_version >= Version("0.18.0"):