install: honor pinned torch index in CUDA/ROCm repair paths
Follow-up to the override work in this PR: the get_torch_index_url / install.sh reroute already respect a pinned UNSLOTH_TORCH_INDEX_URL / _FAMILY, but the Python repair helpers in install_python_stack.py still re-probed the GPU and could overwrite the pinned family. Make the pin authoritative there too: - _ensure_cuda_torch: an explicit cu* pin commits to CUDA wheels, so repair a ROCm-poisoned venv even when no NVIDIA GPU is visible here (headless / container / CI cross-install), instead of bailing on the GPU-presence gate. - _ensure_rocm_torch: skip the AMD per-gfx (Strix) reroute when a ROCm index is pinned, and in the generic reinstall path install from the pinned URL verbatim rather than re-detecting the host ROCm version. gfx*/rocm7.2 indexes serve torch 2.11+, so select the 2.11 package specs for a gfx leaf. - install.sh: raise the torch constraint to 2.11 for */gfx* indexes too, matching rocm7.2, so a pinned full-URL/family override that returns early keeps a valid constraint. Add _explicit_torch_index_url / _explicit_rocm_torch_index_url helpers and tests covering the no-GPU CUDA pin repair and the explicit gfx index honored verbatim.
This commit is contained in:
parent
b02a609af5
commit
997155d193
4 changed files with 111 additions and 19 deletions
|
|
@ -981,6 +981,30 @@ def _detect_cuda_torch_index_url() -> str:
|
|||
return f"{_PYTORCH_WHL_BASE}/{tag}"
|
||||
|
||||
|
||||
def _explicit_torch_index_url() -> "str | None":
|
||||
"""The wheel index URL pinned via UNSLOTH_TORCH_INDEX_URL / _FAMILY, else None.
|
||||
|
||||
Lets the CUDA/ROCm repair helpers honour the exact pinned family/URL instead
|
||||
of re-probing the GPU. Mirrors install.sh::get_torch_index_url's override.
|
||||
"""
|
||||
url = os.environ.get("UNSLOTH_TORCH_INDEX_URL", "").strip()
|
||||
if url:
|
||||
return url.rstrip("/")
|
||||
family = os.environ.get("UNSLOTH_TORCH_INDEX_FAMILY", "").strip()
|
||||
if family:
|
||||
return f"{_PYTORCH_WHL_BASE}/{family.strip('/')}"
|
||||
return None
|
||||
|
||||
|
||||
def _explicit_rocm_torch_index_url() -> "str | None":
|
||||
"""The pinned wheel index URL when it names a ROCm family (rocm*/gfx*), else None."""
|
||||
url = _explicit_torch_index_url()
|
||||
if url is None:
|
||||
return None
|
||||
leaf = url.rstrip("/").rsplit("/", 1)[-1].lower()
|
||||
return url if leaf.startswith(("rocm", "gfx")) else None
|
||||
|
||||
|
||||
def _ensure_cuda_torch() -> None:
|
||||
"""Repair a venv whose torch is a ROCm build on an NVIDIA host.
|
||||
|
||||
|
|
@ -1013,7 +1037,10 @@ def _ensure_cuda_torch() -> None:
|
|||
return
|
||||
# Only NVIDIA hosts should carry CUDA torch. _has_usable_nvidia_gpu()
|
||||
# covers the /proc/driver/nvidia/gpus fallback when nvidia-smi is absent.
|
||||
if not _has_usable_nvidia_gpu():
|
||||
# An explicit CUDA wheel-index pin (headless / container / CI cross-install)
|
||||
# commits to CUDA wheels regardless of whether a GPU is visible here, so it
|
||||
# overrides the GPU-presence gate.
|
||||
if not _has_usable_nvidia_gpu() and _explicit_torch_index_url() is None:
|
||||
return
|
||||
|
||||
# Classify the installed torch: "hip" (ROCm build -- the poisoning
|
||||
|
|
@ -1256,7 +1283,9 @@ def _ensure_rocm_torch() -> None:
|
|||
# an incompatible wheel. Use HIP_VISIBLE_DEVICES for the runtime target.
|
||||
_strix_override_url: "str | None" = None
|
||||
_strix_override_pkgs: "tuple[str, str, str] | None" = None
|
||||
if ver < (7, 2):
|
||||
# An explicit ROCm wheel-index pin is authoritative: never auto-reroute it to
|
||||
# the AMD per-gfx index (the caller already chose the family/URL).
|
||||
if ver < (7, 2) and _explicit_rocm_torch_index_url() is None:
|
||||
gfx_codes = _detect_amd_gfx_codes()
|
||||
_strix_gfx = {"gfx1151", "gfx1150"}
|
||||
_detected_strix = _strix_gfx.intersection(gfx_codes)
|
||||
|
|
@ -1319,23 +1348,35 @@ def _ensure_rocm_torch() -> None:
|
|||
)
|
||||
rocm_torch_ready = True
|
||||
elif not has_hip_torch:
|
||||
# Select best matching wheel tag (newest ROCm version <= installed)
|
||||
tag = next(
|
||||
(
|
||||
t
|
||||
for (maj, mn), t in sorted(_ROCM_TORCH_INDEX.items(), reverse = True)
|
||||
if ver >= (maj, mn)
|
||||
),
|
||||
None,
|
||||
)
|
||||
# Honour an explicit ROCm wheel-index pin verbatim instead of re-detecting
|
||||
# the host ROCm version; otherwise select the best wheel tag (newest ROCm
|
||||
# version <= installed). gfx*/rocm7.2 indexes serve torch 2.11+, so match
|
||||
# the constraints to the index leaf when overridden.
|
||||
_override_idx = _explicit_rocm_torch_index_url()
|
||||
if _override_idx is not None:
|
||||
index_url = _override_idx
|
||||
tag = index_url.rstrip("/").rsplit("/", 1)[-1].lower()
|
||||
else:
|
||||
tag = next(
|
||||
(
|
||||
t
|
||||
for (maj, mn), t in sorted(_ROCM_TORCH_INDEX.items(), reverse = True)
|
||||
if ver >= (maj, mn)
|
||||
),
|
||||
None,
|
||||
)
|
||||
if tag is None:
|
||||
print(f" No PyTorch wheel for ROCm {ver[0]}.{ver[1]} -- " f"skipping torch reinstall")
|
||||
else:
|
||||
index_url = f"{_PYTORCH_WHL_BASE}/{tag}"
|
||||
print(f" ROCm {ver[0]}.{ver[1]} -- installing torch from {index_url}")
|
||||
_torch_pkg, _vision_pkg, _audio_pkg = _ROCM_TORCH_PKG_SPECS.get(
|
||||
tag, _ROCM_TORCH_PKG_SPECS["_default"]
|
||||
)
|
||||
if _override_idx is None:
|
||||
index_url = f"{_PYTORCH_WHL_BASE}/{tag}"
|
||||
print(f" ROCm torch -- installing from {index_url}")
|
||||
if tag.startswith("gfx"):
|
||||
_torch_pkg, _vision_pkg, _audio_pkg = _ROCM_TORCH_PKG_SPECS["rocm7.2"]
|
||||
else:
|
||||
_torch_pkg, _vision_pkg, _audio_pkg = _ROCM_TORCH_PKG_SPECS.get(
|
||||
tag, _ROCM_TORCH_PKG_SPECS["_default"]
|
||||
)
|
||||
pip_install(
|
||||
f"ROCm torch ({tag})",
|
||||
"--force-reinstall",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue