From 7ef8cde3c22a3bc2240353353caa21051a42ea90 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Tue, 21 Apr 2026 22:39:35 -0700 Subject: [PATCH] Coerce TRL's tuple-cached _*_available flags to bool (#5129) transformers >= 4.48's `_is_package_available(name)` returns a tuple `(bool, version_or_None)`. TRL's `trl.import_utils` caches that tuple directly in `_vllm_ascend_available`, `_llm_blender_available`, `_deepspeed_available`, `_joblib_available`, etc. and the matching `is_*_available()` accessors return the tuple unchanged. A non-empty tuple is always truthy, so `if is_vllm_ascend_available():` (in `trl/extras/vllm_client.py`) fires unconditionally and triggers `from vllm_ascend.distributed.device_communicators.pyhccl import ...`, which fails outside Huawei Ascend hosts and blocks `from trl import GRPOConfig, GRPOTrainer`. The same shape blocks `is_llm_blender_available()` -> `import llm_blender` in `trl/trainer/judges.py`. Add `fix_trl_vllm_ascend()` to `import_fixes.py` and call it from `unsloth/__init__.py` before any `from .trainer import *` that would eagerly `import trl`. The fix walks `trl.import_utils` once and coerces every `_*_available` tuple to a bool; the existing accessors that just return the cached value then naturally yield a bool and the `if` checks behave. --- unsloth/__init__.py | 3 +++ unsloth/import_fixes.py | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/unsloth/__init__.py b/unsloth/__init__.py index 73a9ce8fef..52114bb544 100644 --- a/unsloth/__init__.py +++ b/unsloth/__init__.py @@ -137,6 +137,7 @@ from .import_fixes import ( fix_vllm_aimv2_issue, check_vllm_torch_sm100_compatibility, fix_vllm_guided_decoding_params, + fix_trl_vllm_ascend, fix_vllm_pdl_blackwell, fix_triton_compiled_kernel_missing_attrs, patch_trunc_normal_precision_issue, @@ -159,6 +160,7 @@ fix_vllm_aimv2_issue() # Check vLLM + torch < 2.9.0 + SM100 compatibility BEFORE importing vLLM check_vllm_torch_sm100_compatibility() fix_vllm_guided_decoding_params() +fix_trl_vllm_ascend() fix_vllm_pdl_blackwell() fix_triton_compiled_kernel_missing_attrs() patch_trunc_normal_precision_issue() @@ -179,6 +181,7 @@ del fix_xformers_performance_issue del fix_vllm_aimv2_issue del check_vllm_torch_sm100_compatibility del fix_vllm_guided_decoding_params +del fix_trl_vllm_ascend del fix_vllm_pdl_blackwell del fix_triton_compiled_kernel_missing_attrs del patch_trunc_normal_precision_issue diff --git a/unsloth/import_fixes.py b/unsloth/import_fixes.py index ca44a0ce7e..6cebad7939 100644 --- a/unsloth/import_fixes.py +++ b/unsloth/import_fixes.py @@ -489,6 +489,32 @@ def fix_vllm_guided_decoding_params(): ) +def fix_trl_vllm_ascend(): + # transformers >= 4.48's `_is_package_available(name)` returns a tuple + # (bool, version_or_None). TRL caches that tuple in module-level + # `_*_available` flags and the matching `is_*_available()` accessors + # return the tuple directly. A non-empty tuple is always truthy, so + # `if is_X_available():` fires even when X is absent, triggering an + # unconditional `import X` that fails. The surfaced case is + # `vllm_ascend` (blocks `from trl import GRPOConfig, GRPOTrainer` + # outside Huawei Ascend hosts); `llm_blender`, `deepspeed`, `joblib` + # share the same shape. Coerce every tuple-cached flag in + # trl.import_utils to bool; the existing accessors that just return + # the cached value then naturally yield a bool. + if importlib.util.find_spec("trl") is None: + return + try: + import trl.import_utils as tiu + except Exception: + return + for attr in list(vars(tiu)): + if not (attr.startswith("_") and attr.endswith("_available")): + continue + cached = getattr(tiu, attr) + if isinstance(cached, tuple): + setattr(tiu, attr, bool(cached and cached[0])) + + def ignore_logger_messages(): # Ignore Environment variable `HF_TOKEN` is set try: