mirror of
https://github.com/unslothai/unsloth.git
synced 2026-08-20 22:34:00 +00:00
Installer: select CUDA wheels that cover the host's GPUs (#7814)
* Installer: select CUDA wheels that cover the host's GPUs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix Windows venv wipe and warning dedupe for PR #7814 - Windows pins torch<2.11, whose cu128 still ships sm_70, so capping a Volta to cu126 there rewrote a working family. The stale-venv check then read that as drift and deleted the venv on a direct "unsloth studio update", which cannot recreate it. Make the pre-Turing floor per-family (70 for cu128). - Repair an unpinned cu* -> cu* move in place instead of rebuilding the venv. - Decide the cu126 advice before deduping the uncovered-host warning: the host facts are release invariant but the artifact list is not, so the release walk-back let an unhelpful release swallow the remedy. - Gate the new coverage repair and the cu126 advice on x86_64, matching the cap. - Add tests/studio/test_pre_turing_cap.ps1: the parity test only greps for the call spelling, so neither PowerShell copy had behavioural coverage. * Tighten comments for PR #7814 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: danielhanchen <unslothshared@gmail.com>
This commit is contained in:
parent
f26727faec
commit
f8730f4339
10 changed files with 1049 additions and 42 deletions
|
|
@ -68,6 +68,53 @@ class TestInstallShHasGpuDetection:
|
|||
), "install.sh should assign TORCH_INDEX_URL from get_torch_index_url()"
|
||||
|
||||
|
||||
class TestPreTuringCapParity:
|
||||
"""Every wheel-selection site caps cu128/cu130 on a pre-Turing host (issue #7765).
|
||||
|
||||
PyTorch 2.11 builds those families for sm_75 and newer, so a Maxwell/Pascal/Volta
|
||||
box needs cu126 -- both for torch itself and for the CUDA 12 runtime that gets it
|
||||
a llama.cpp GGUF bundle. Four scripts pick the family; none may be left behind.
|
||||
"""
|
||||
|
||||
# (file, call spelling, selection function that must invoke it, its end marker). The
|
||||
# spelling carries the first argument, so a prose mention cannot satisfy the assertion.
|
||||
_SITES = (
|
||||
(INSTALL_SH, '_cap_cuda_family_for_pre_turing "', "get_torch_index_url() {", "\n}"),
|
||||
(
|
||||
INSTALL_PS1,
|
||||
"Get-CudaFamilyCappedForPreTuring $",
|
||||
"function Get-TorchIndexUrl",
|
||||
"\n }",
|
||||
),
|
||||
(SETUP_PS1, "Get-CudaFamilyCappedForPreTuring $", "function Get-PytorchCudaTag", "\n}"),
|
||||
(
|
||||
STACK_PY,
|
||||
"_cap_cuda_family_for_pre_turing(",
|
||||
"def _detect_cuda_torch_index_url",
|
||||
"\ndef ",
|
||||
),
|
||||
)
|
||||
|
||||
def test_cu126_span_agrees_across_the_python_modules(self):
|
||||
# Neither module imports the other (the installer runs before dependencies
|
||||
# exist), so assert the shared span here rather than let it drift silently.
|
||||
span = "_CU126_SM_RANGE = (50, 90)"
|
||||
for path in (STACK_PY, REPO_ROOT / "studio" / "install_llama_prebuilt.py"):
|
||||
assert span in path.read_text(encoding = "utf-8"), f"{path.name} lost {span}"
|
||||
|
||||
@pytest.mark.parametrize("path,call,start,end", _SITES)
|
||||
def test_selection_function_applies_the_cap(self, path, call, start, end):
|
||||
text = path.read_text(encoding = "utf-8")
|
||||
assert start in text, f"{path.name} no longer defines {start!r}"
|
||||
body = text.split(start, 1)[1].split(end, 1)[0]
|
||||
assert call in body, f"{path.name}'s selection function never applies {call!r}"
|
||||
|
||||
|
||||
# A ladder rung names its family either as an index-URL suffix ("$base/cu128") or as a
|
||||
# variable a later step can still cap ("_cuda_tag=cu128"), so accept both spellings.
|
||||
_CUDA_LEAF_RE = r"""[/=]\s*["']?(cu\d+|cpu)"""
|
||||
|
||||
|
||||
class TestCudaMappingParity:
|
||||
"""CUDA version thresholds must match between install.sh and install.ps1."""
|
||||
|
||||
|
|
@ -84,7 +131,7 @@ class TestCudaMappingParity:
|
|||
if in_func and line.startswith("}"):
|
||||
break
|
||||
if in_func and ("_major" in line or "_minor" in line):
|
||||
m = re.search(r"/(cu\d+|cpu)", line)
|
||||
m = re.search(_CUDA_LEAF_RE, line)
|
||||
if m:
|
||||
results.append(m.group(1))
|
||||
return results
|
||||
|
|
@ -106,7 +153,7 @@ class TestCudaMappingParity:
|
|||
break
|
||||
# Only match the if-chain lines that compare $major/$minor
|
||||
if "$major" in line or "$minor" in line:
|
||||
m = re.search(r"/(cu\d+|cpu)", line)
|
||||
m = re.search(_CUDA_LEAF_RE, line)
|
||||
if m:
|
||||
results.append(m.group(1))
|
||||
return results
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue