From ed275cbd25f5dc69a9ef952862c3cc10cc17fdac Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Sun, 12 Jul 2026 11:36:04 +0000 Subject: [PATCH] cu130onlytorch2110: pin the trio to +cu130 like the cu126/cu128 extras A bare torch==2.11.0 range lets a configured CUDA-12 extra index win the resolve: PEP 440 ranks the local build 2.11.0+cu126 above the unlabelled 2.11.0, so the cu130 extra could silently install cu126 wheels. An ===2.11.0 arbitrary-equality pin has the opposite problem: it rejects the +cu130 local builds that an install from the official cu130 index produces. Pinning ==2.11.0+cu130 matches the cu126/cu128 sibling extras exactly: it accepts only the cu130 builds (all three wheels exist on download.pytorch.org/whl/cu130) and fails the resolve loudly when no cu130 index is configured instead of installing the wrong flavor. _auto_install now appends the cu130 extra index for CUDA 13.0 the same way it does for 12.6/12.8, and the extras test parametrizes cu130 alongside its siblings. --- pyproject.toml | 21 ++++++++++----------- tests/test_torch2110_cuda_extras.py | 24 ++++-------------------- unsloth/_auto_install.py | 10 ++++++---- 3 files changed, 20 insertions(+), 35 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6f5cb83d13..c6a8ff6b99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -363,17 +363,16 @@ cu128onlytorch2110 = [ "xformers @ https://download.pytorch.org/whl/cu128/xformers-0.0.35-py39-none-win_amd64.whl ; (sys_platform == 'win32')", ] cu130onlytorch2110 = [ - # No +cu130 local pin needed: torch 2.11's DEFAULT PyPI wheel is already CUDA - # 13.0, so a bare resolve lands on a cu130 torch that matches the cu130 - # xformers below even without a dedicated index. Arbitrary equality (===) - # rather than a range: PEP 440 ranks a local build (2.11.0+cu126/+cu128) - # ABOVE the unlabelled 2.11.0, so with any CUDA-12 extra index configured a - # range would silently pair a CUDA-12 torch trio with the cu130 xformers - # (pip verified: torch>=2.11,<2.12 + --extra-index-url .../cu126 installs - # 2.11.0+cu126; torch===2.11.0 installs the unlabelled cu130 default). - "torch===2.11.0", - "torchvision===0.26.0", - "torchaudio===2.11.0", + # Pinned to the +cu130 local build like the cu126/cu128 extras: a bare range + # lets a configured CUDA-12 extra index win (PEP 440 ranks 2.11.0+cu126 above + # the unlabelled 2.11.0), and a ===2.11.0 pin would force-replace the trio on + # machines installed from the official cu130 index (whose wheels carry the + # +cu130 local tag). The exact +cu130 pins accept the official cu130-index + # install as-is and fail loudly anywhere the cu130 index is missing; + # _auto_install.py appends the matching index for every torch2110 CUDA extra. + "torch==2.11.0+cu130", + "torchvision==0.26.0+cu130", + "torchaudio==2.11.0+cu130", "xformers @ https://download.pytorch.org/whl/cu130/xformers-0.0.35-py39-none-manylinux_2_28_x86_64.whl ; ('linux' in sys_platform)", "xformers @ https://download.pytorch.org/whl/cu130/xformers-0.0.35-py39-none-win_amd64.whl ; (sys_platform == 'win32')", ] diff --git a/tests/test_torch2110_cuda_extras.py b/tests/test_torch2110_cuda_extras.py index cc76f82704..4865db39c4 100644 --- a/tests/test_torch2110_cuda_extras.py +++ b/tests/test_torch2110_cuda_extras.py @@ -3,8 +3,9 @@ torch 2.11's DEFAULT PyPI wheel is CUDA 13.0 (torch 2.10 defaulted to CUDA 12.x). So the CUDA-12 `cuXXXonlytorch2110` extras must pin the torch trio to the matching `+cuXXX` local build; a bare `torch>=2.11` there would resolve a cu130 torch from -PyPI alongside the cu126/cu128 xformers wheel and fail at import. The cu130 extra -needs no local pin because the bare default already lands on cu130. +PyPI alongside the cu126/cu128 xformers wheel and fail at import. The cu130 extra is +pinned to +cu130 too: a bare or ===-pinned spec either lets a foreign CUDA index +outrank the intended wheel or force-replaces official cu130-index installs. Hermetic: only parses pyproject.toml, no network or install. """ @@ -39,7 +40,7 @@ def _reqs(specs: list[str]) -> dict[str, Requirement]: return out -@pytest.mark.parametrize("cuda", ["cu126", "cu128"]) +@pytest.mark.parametrize("cuda", ["cu126", "cu128", "cu130"]) def test_cuda12_torch2110_pins_matching_local_build(cuda: str): # Each of torch/torchvision/torchaudio must pin the exact +cuXXX local build # so it can only resolve from the matching PyTorch CUDA index, never the @@ -54,20 +55,3 @@ def test_cuda12_torch2110_pins_matching_local_build(cuda: str): xf = reqs["xformers"] assert xf.url and f"/whl/{cuda}/" in xf.url, f"xformers not on the {cuda} index: {xf.url}" - -def test_cu130_torch2110_uses_arbitrary_equality(): - # cu130 matches torch 2.11's PyPI default, so no +cu130 local pin is needed - # (it could fail to resolve from PyPI's unlabelled default wheel). But a - # RANGE is not enough either: PEP 440 ranks a local build (2.11.0+cu126) - # above the unlabelled 2.11.0, so with any CUDA-12 extra index configured a - # range silently pairs a CUDA-12 trio with the cu130 xformers. Arbitrary - # equality (===) matches only the unlabelled release, excluding every - # +cuXXX local candidate. - expected = {"torch": "2.11.0", "torchvision": "0.26.0", "torchaudio": "2.11.0"} - reqs = _reqs(_extra("cu130onlytorch2110")) - for pkg in _TORCH_TRIO: - spec = str(reqs[pkg].specifier) - assert spec == f"==={expected[pkg]}", ( - f"cu130onlytorch2110: {pkg} must pin ==={expected[pkg]} " - f"(excludes +cuXXX local builds), got '{spec}'" - ) diff --git a/unsloth/_auto_install.py b/unsloth/_auto_install.py index 396fa34d12..0518d75a74 100644 --- a/unsloth/_auto_install.py +++ b/unsloth/_auto_install.py @@ -41,8 +41,10 @@ else: raise RuntimeError(f"Torch = {v} too new!") if v > V('2.6.9') and cuda not in ("11.8", "12.6", "12.8", "13.0"): raise RuntimeError(f"CUDA = {cuda} not supported!") if v >= V('2.10.0') and cuda not in ("12.6", "12.8", "13.0"): raise RuntimeError(f"Torch 2.10 requires CUDA 12.6, 12.8, or 13.0! Got CUDA = {cuda}") x = x.format(cuda.replace(".", ""), "-ampere" if False else "") # is_ampere is broken due to flash-attn -# The cu126/cu128 torch2110 extras pin torch==2.11.0+cuNNN, a local build that only -# resolves from the matching PyTorch CUDA index (torch 2.11's default PyPI wheel is -# CUDA 13.0), so add that index for exactly those CUDA-12 torch 2.11 environments. -extra_index = f' --extra-index-url https://download.pytorch.org/whl/cu{cuda.replace(".", "")}' if (x.endswith('-torch2110') and cuda in ("12.6", "12.8")) else '' +# The CUDA torch2110 extras pin the torch trio to the exact +cuNNN local build, +# which only resolves from the matching PyTorch CUDA index (a bare spec would let +# a configured foreign CUDA index outrank the intended wheel, since PEP 440 ranks +# any local build above the unlabelled release), so add that index for every +# CUDA torch 2.11 environment. +extra_index = f' --extra-index-url https://download.pytorch.org/whl/cu{cuda.replace(".", "")}' if (x.endswith('-torch2110') and cuda in ("12.6", "12.8", "13.0")) else '' print(f'pip install --upgrade pip setuptools wheel && pip install --no-deps git+https://github.com/unslothai/unsloth-zoo.git && pip install "unsloth[{x}] @ git+https://github.com/unslothai/unsloth.git" --no-build-isolation{extra_index}') \ No newline at end of file