Lets `import unsloth.trainer` succeed on hosts without a CUDA/XPU/HIP
accelerator (typical of zoo's source-inspection test matrix). The env
var is read exactly once per process via @functools.cache on
`get_device_type()`, so production hosts pay no runtime cost.
Three edits beyond the device_type fallback:
* `_gpu_init.py:212/247` -- the bf16 + libcuda/bnb setup blocks call
`torch.cuda.get_device_capability()` and `libcuda_dirs()`/`bnb.functional.lib.*`
unconditionally when DEVICE_TYPE == "cuda". Guard with
`and torch.cuda.is_available()` so the new CPU-CI sentinel doesn't
fault those.
* `_gpu_init.py:353` -- gate `_patch_trl_trainer()` (the
`_backwards_compatible_trainer.__init__` wrapper). Under
UNSLOTH_ALLOW_CPU we want pristine upstream TRL classes for
downstream `inspect.getsource(SFTTrainer)` drift detectors.
* `models/_utils.py:1196` -- same `and torch.cuda.is_available()` guard
for `get_device_capability()` at import time.
* `models/rl.py:PatchFastRL` -- early-return under UNSLOTH_ALLOW_CPU=1
so the heavier `patch_trl_rl_trainers()` (which replaces
`trl.SFTTrainer` with the compiled `UnslothSFTTrainer` class)
doesn't fire either. Without this gate the drift detectors that
do `inspect.getsource(SFTTrainer)` see the wrapper source and
spurious fail.
Local sanity: `UNSLOTH_ALLOW_CPU=1 python -c "import unsloth.trainer"`
succeeds on a CPU-only venv, `trl.SFTTrainer.__init__.__qualname__`
stays `SFTTrainer.__init__` (not `UnslothSFTTrainer.__init__`), and
`inspect.getsource(SFTTrainer)` still contains `self._signature_columns`.
Without the env var on a CUDA host, TRL is still patched normally
(verified `UnslothSFTTrainer.__init__`).