From 93d533d8bd985c27bfaeec4d8842665bfe78ce84 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Thu, 7 May 2026 06:14:45 +0000 Subject: [PATCH 1/3] fix: import fast_lora_forward inside patch_fast_lora patch_fast_lora has referenced an unbound `fast_lora_forward` since ddf118a8f (2024-11-21). The function is defined at unsloth/kernels/fast_lora.py:652 and re-exported through unsloth/kernels/__init__.py:45, but it was never imported into unsloth/models/_utils.py, so calling patch_fast_lora() raises NameError: name 'fast_lora_forward' is not defined. The bug went unnoticed because no production code path calls patch_fast_lora() unconditionally. Surfaced by a new CPU-CI check that invokes every zero-arg patch_* helper across unsloth + unsloth_zoo (consolidated-tests-ci.yml on PR #5312). Importing inside the function (rather than at module top) keeps the import surface narrow and avoids a circular-import risk if unsloth.kernels.fast_lora ever needs to import from unsloth.models._utils. --- unsloth/models/_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/unsloth/models/_utils.py b/unsloth/models/_utils.py index 34fec53504..d3eee03325 100644 --- a/unsloth/models/_utils.py +++ b/unsloth/models/_utils.py @@ -2483,6 +2483,7 @@ def patch_tokenizer(model, tokenizer): def patch_fast_lora(): import peft.tuners.lora.bnb + from ..kernels.fast_lora import fast_lora_forward peft.tuners.lora.bnb.Linear4bit.forward = fast_lora_forward From 29277560593096e5717feb1310bbfcef0a2f9ef1 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Thu, 7 May 2026 06:15:50 +0000 Subject: [PATCH 2/3] fix: inject typing imports into patch_sft_trainer_tokenizer's exec namespace patch_sft_trainer_tokenizer rewrites the source of TRL's SFTTrainer methods (_prepare_non_packed_dataloader, _prepare_dataset) and re-execs them. With TRL 1.x, those methods carry `Union[...]` type hints in their signatures. The current rewrite only injects identifiers found by `dir(trl.trainer.sft_trainer)` into the exec namespace, which does not include `Union`, so exec(function, ...) raises NameError: name 'Union' is not defined. Fix: import Union, Optional, List, Any, Callable, Tuple, Dict, Iterator inside the function. exec receives `locals()` as its globals dict, so those names are visible to the executed source body. Same pattern as unsloth/models/_utils.py:patch_linear_scaling, which already injects `from typing import Union, Optional, List, Any, Callable, Tuple` into its own exec_code. Surfaced by the consolidated CPU-CI runtime patch_* check on PR #5312 in the matrix cell `transformers>=5,<6 + trl>=1,<2`. --- unsloth/tokenizer_utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/unsloth/tokenizer_utils.py b/unsloth/tokenizer_utils.py index 130894e385..67edc41d52 100644 --- a/unsloth/tokenizer_utils.py +++ b/unsloth/tokenizer_utils.py @@ -1580,6 +1580,12 @@ def patch_sft_trainer_tokenizer(): except: return all_imports = dir(trl.trainer.sft_trainer) + # Make typing names available to the exec'd source bodies. TRL >= 1.x + # type-hints _prepare_dataset / _prepare_non_packed_dataloader with + # `Union[...]` and friends; without these imports in the exec namespace + # those become NameErrors at exec time. Mirrors the pattern used in + # unsloth/models/_utils.py:patch_linear_scaling. + from typing import Union, Optional, List, Any, Callable, Tuple, Dict, Iterator # noqa: F401 for ( function_name, From 6cc92a54e91b4b3f2fac786309d53193c4263663 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Thu, 7 May 2026 06:16:30 +0000 Subject: [PATCH 3/3] fix: guard openenv_vllm_reload_weights against OSError from inspect.getsource TRL 0.29.1 and the 1.x line ship some openenv helpers as compiled bytecode without accessible source on disk. inspect.getsource(patch_target) raises OSError("could not get source code") in that case, which surfaces as a hard failure in patch_trl_openenv() and aborts the rest of the RL_ADDITIONAL_FUNCTIONS["openenv"] iteration. Wrap the getsource call in a try/except OSError and log a warning instead. The wake_up(tags=...) rewrite is the only thing skipped; the core weight-reload patch path stays functional. Surfaced by the consolidated CPU-CI runtime patch_* check on PR #5312 in matrix cells running TRL 0.29.1 (latest <1.0.0) and TRL 1.3.0 (latest 1.x). The pyproject pin (TRL 0.18.2-0.24.0) still gets source for this function so the original code path runs unchanged there. --- unsloth/models/rl_replacements.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/unsloth/models/rl_replacements.py b/unsloth/models/rl_replacements.py index 0f10847282..c2be1bf74a 100755 --- a/unsloth/models/rl_replacements.py +++ b/unsloth/models/rl_replacements.py @@ -1780,7 +1780,20 @@ def openenv_vllm_reload_weights(): patch_target_name = "generate_rollout_completions" patch_target = getattr(openenv_utils, patch_target_name) - src = inspect.getsource(patch_target) + # TRL 0.29.1+ ships some openenv helpers as compiled bytecode without + # accessible source on disk; inspect.getsource raises OSError("could + # not get source code") in that case. Skip the source-rewrite patch + # rather than crashing -- the core unsloth weight-reload path stays + # functional, only the wake_up tag rewrite is skipped. + try: + src = inspect.getsource(patch_target) + except OSError as e: + logger.warning( + f"Unsloth: Could not retrieve source for trl openenv " + f"{patch_target_name} ({e}); skipping rewrite. " + f"Weight reload still functional." + ) + return src = textwrap.dedent(src) original_src = src