install: complete pinned-index handling for ROCm/Windows edge cases

Follow-ups to the override work flagged in review:

- install.ps1: a pinned gfx*/rocm>=7.2 index previously skipped the AMD reroute
  that sets the torch>=2.11 floor, so the generic install used torch>=2.4,<2.11
  and could resolve the known-bad _grouped_mm wheel. Route a pinned ROCm index
  through the ROCm install path with the 2.11 floor + companions, and guard the
  companion-spec lookup so a skipped reroute block cannot null-deref.
- studio/setup.ps1: the stale-venv check compared the installed flavor (cuXXX/cpu,
  with +rocm misread as cpu) against the raw pinned leaf (gfx1151 / rocm6.4), so a
  correct pinned ROCm venv was always marked stale. Classify +rocm wheels as the
  generic 'rocm' flavor and normalize a pinned rocm*/gfx* leaf to 'rocm' before
  comparing (cu* stays specific so cu126-vs-cu128 still rebuilds).
- install_python_stack.py: _ensure_cuda_torch now also reinstalls from a pinned
  CUDA index when the venv carries a CPU wheel (headless CPU-venv-to-CUDA
  cross-install via 'studio update'), not only when it finds a ROCm build.
- tests: parity assertions already cover all four installers honoring the override.
This commit is contained in:
Daniel Han 2026-06-26 09:16:19 +00:00
commit d2d5f90175
3 changed files with 66 additions and 10 deletions

View file

@ -1073,16 +1073,31 @@ def _ensure_cuda_torch() -> None:
_marker_lines = [
line.strip() for line in probe.stdout.decode(errors = "replace").splitlines() if line.strip()
]
if not _marker_lines or _marker_lines[-1] != "hip":
if not _marker_lines:
return
_marker = _marker_lines[-1]
# Reinstall CUDA torch when the venv carries a ROCm build on an NVIDIA host
# (the poisoning signature), OR when an explicit CUDA index is pinned but the
# venv still has a CPU wheel. The latter is the headless CPU-venv-to-CUDA
# cross-install (`studio update` with UNSLOTH_TORCH_INDEX_FAMILY=cu128): the
# update path preserves torch rather than preinstalling it from install.sh, so
# without this the explicit CUDA pin stays ineffective. A healthy CUDA torch,
# or a CPU wheel with no CUDA pin, is deliberate and left alone.
_pin = _explicit_torch_index_url()
_pinned_cuda = bool(_pin) and _pin.rstrip("/").rsplit("/", 1)[-1].lower().startswith("cu")
if _marker == "hip":
_why = "torch is a ROCm build on an NVIDIA host"
elif _marker == "cpu" and _pinned_cuda:
_why = "torch is a CPU build but an explicit CUDA index is pinned"
else:
return # healthy CUDA torch, or a deliberate CPU wheel -- leave as-is
index_url = _detect_cuda_torch_index_url()
_torch_pkg, _vision_pkg, _audio_pkg = _CUDA_TORCH_PKG_SPEC
print(
f" torch is a ROCm build on an NVIDIA host -- reinstalling "
f"CUDA torch from {index_url}\n"
f" (set UNSLOTH_TORCH_BACKEND=rocm to keep a deliberate ROCm torch "
f"on a mixed AMD+NVIDIA host)"
f" {_why} -- reinstalling CUDA torch from {index_url}\n"
f" (set UNSLOTH_TORCH_BACKEND=rocm or cpu to keep a deliberate "
f"non-CUDA torch)"
)
pip_install(
"CUDA torch repair",