From ba2d6ac8d8f90d9823eca6ecbac0066e426c4bfe Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Wed, 8 Jul 2026 11:13:44 +0000 Subject: [PATCH] Add torch 2.11 prebuilt-wheel readiness canary The CUDA torch pin stays < 2.11 because flash-attn, causal-conv1d, and mamba-ssm do not publish torch 2.11 wheels yet; bumping now would drop those prebuilt accelerators and force slow source builds. This adds a version-compat canary that queries the GitHub releases of all three and fails only once all of them ship torch 2.11 Linux wheels, which is when it is safe to raise the pin. Green while any is pending, skips on a network error, and wired into version-compat-ci (PR paths + daily cron). --- .github/workflows/version-compat-ci.yml | 24 +++++++++ tests/version_compat/_fetch.py | 18 +++++++ .../test_torch_211_prebuilt_readiness.py | 53 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 tests/version_compat/test_torch_211_prebuilt_readiness.py diff --git a/.github/workflows/version-compat-ci.yml b/.github/workflows/version-compat-ci.yml index 6becccc90a..6600de0803 100644 --- a/.github/workflows/version-compat-ci.yml +++ b/.github/workflows/version-compat-ci.yml @@ -202,6 +202,30 @@ jobs: tests/version_compat/test_transformers_pinned_symbols.py \ -v --tb=short + torch-211-readiness: + name: torch 2.11 prebuilt-wheel readiness canary + runs-on: ubuntu-latest + timeout-minutes: 6 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.12' + cache: 'pip' + - name: Install pytest only + run: | + python -m pip install --upgrade pip + pip install 'pytest>=8' + - name: Run torch 2.11 readiness canary + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PYTHONPATH=. python -m pytest \ + tests/version_compat/test_torch_211_prebuilt_readiness.py \ + -v --tb=short + # Optional second layer: actually `pip install` ONE representative # version of each package and verify unsloth + unsloth-zoo modules # import on it under the existing CUDA spoof. CPU-only, runs on diff --git a/tests/version_compat/_fetch.py b/tests/version_compat/_fetch.py index 706b26efad..8a4dfadc21 100644 --- a/tests/version_compat/_fetch.py +++ b/tests/version_compat/_fetch.py @@ -4,6 +4,7 @@ from __future__ import annotations +import json import os import re import urllib.error @@ -12,6 +13,23 @@ import urllib.request import pytest +def fetch_json(url: str): + """GET a GitHub API URL and parse JSON. None on 404; skips on transient network errors.""" + req = urllib.request.Request(url, headers = {"Accept": "application/vnd.github+json"}) + token = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN") + if token: + req.add_header("Authorization", f"Bearer {token}") + try: + with urllib.request.urlopen(req, timeout = 15) as r: + return json.loads(r.read().decode("utf-8", errors = "replace")) + except urllib.error.HTTPError as e: + if e.code == 404: + return None + pytest.skip(f"GitHub API failed ({e.code}) for {url}") + except (urllib.error.URLError, TimeoutError, json.JSONDecodeError) as e: + pytest.skip(f"GitHub API failed ({e}) for {url}") + + def fetch_text(repo: str, ref: str, path: str) -> str | None: """Fetch a file from GitHub raw. None on 404; skips on transient network errors.""" url = f"https://raw.githubusercontent.com/{repo}/{ref}/{path}" diff --git a/tests/version_compat/test_torch_211_prebuilt_readiness.py b/tests/version_compat/test_torch_211_prebuilt_readiness.py new file mode 100644 index 0000000000..1694336eee --- /dev/null +++ b/tests/version_compat/test_torch_211_prebuilt_readiness.py @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: AGPL-3.0-only +# Copyright 2026-present the Unsloth AI Inc. team. +"""Canary for raising the CUDA torch pin to 2.11. + +The Studio installer selects torch-version-tagged prebuilt CUDA wheels for three +accelerators: flash-attn (wheel_utils.flash_attn_package_version) and causal-conv1d +/ mamba-ssm (worker._install_package_wheel_first). None of them publish torch 2.11 +wheels yet, so the CUDA torch pin stays < 2.11 (_CUDA_TORCH_PKG_SPEC in +studio/install_python_stack.py); moving to 2.11 today would drop those wheels and +force slow source builds. This test stays green while any of the three is missing +torch 2.11 Linux wheels and fails once all three ship them, i.e. when it is finally +safe to bump the pin. +""" + +from __future__ import annotations + +import re + +from tests.version_compat._fetch import fetch_json + +# human name -> GitHub repo +_REPOS = { + "flash-attn": "Dao-AILab/flash-attention", + "causal-conv1d": "Dao-AILab/causal-conv1d", + "mamba-ssm": "state-spaces/mamba", +} +_TORCH211_LINUX = re.compile(r"torch2\.11.*linux", re.IGNORECASE) + + +def _has_torch211_linux_wheel(repo: str) -> bool: + """True if a recent release of ``repo`` publishes a torch 2.11 Linux wheel.""" + releases = fetch_json(f"https://api.github.com/repos/{repo}/releases?per_page=5") + if not releases: + return False + return any( + _TORCH211_LINUX.search(asset.get("name", "")) + for rel in releases + for asset in rel.get("assets", []) + ) + + +def test_torch_211_prebuilt_wheels_not_all_ready(): + status = {name: _has_torch211_linux_wheel(repo) for name, repo in _REPOS.items()} + ready = sorted(n for n, ok in status.items() if ok) + pending = sorted(n for n, ok in status.items() if not ok) + print(f"torch 2.11 wheel readiness -> ready: {ready or 'none'}; pending: {pending}") + assert pending, ( + f"torch 2.11 Linux wheels are now published for all of {ready}. It is time " + "to raise the CUDA torch pin: bump _CUDA_TORCH_PKG_SPEC to <2.12.0 (with the " + "matching torchvision/torchaudio bounds) in studio/install_python_stack.py, " + "add torch 2.11 to wheel_utils.flash_attn_package_version, and bump the " + "causal-conv1d / mamba release tags in studio/backend/core/training/worker.py." + )