installer: fix false "no GPU detected" on AMD hosts (dead KFD check) + clearer ROCm-less warning (#7314)

* installer: fix Linux AMD GPU detection + actionable ROCm-less warning

The rocminfo/amd-smi-less fallback in _has_amd_rocm_gpu keyed on a
/gpu_id/ line inside each KFD node's properties file, but gpu_id is a
separate sibling sysfs file and never appears in properties. The guard
never matched, so the fallback missed every AMD host without ROCm
tooling (e.g. a fresh CachyOS/Arch box) and reported 'no GPU detected'
despite vendor_id 4098 being present in the KFD topology.

Detect via vendor_id == 4098 directly: the KFD CPU node reports
vendor_id 0, so any 4098 node is an AMD GPU, while NVIDIA's KFD nodes
report 4318 and stay excluded.

Also rework the 'ROCm version could not be determined' warning into an
actionable message (install the ROCm/HIP SDK; Arch/CachyOS:
rocm-hip-sdk) so ROCm-less users know the concrete next step instead of
silently landing on CPU-only PyTorch.

* tests: replace the FNR==1 KFD invariant with the per-line vendor_id check

The FNR==1 reset guarded the old paired gpu_id+vendor_id awk against
cross-node state leakage. The new detection is a single atomic
vendor_id==4098 line condition, so there is no per-node state to reset;
assert the new invariant instead (single-line vendor match, and no
/gpu_id/ pattern, which never matched inside properties).

tests/studio/install/test_rocm_support.py: 344 passed, 2 skipped.

* installer: mirror the KFD vendor_id fix in setup.sh + honest CPU-fallback summary

Codex P2 follow-ups:
- studio/setup.sh carried the same dead gpu_id-inside-properties awk, so a
  host install.sh now routes to ROCm still failed setup's independent AMD
  re-probe and got a CPU llama.cpp. Use the same per-line vendor_id 4098
  check.
- When the AMD GPU is detected but the torch index stays CPU, the summary
  printed the old false diagnosis (gpu none / "No GPU detected"). Gate both
  on _has_amd_rocm_gpu and say what actually happened: AMD GPU present, no
  usable ROCm, CPU fallback.
- Structure test asserting setup.sh's KFD awk stays in sync with install.sh.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Keep KFD-only AMD hosts on the CPU fallback (Codex P2s)

The KFD-topology fix makes _has_amd_rocm_gpu / _setup_amd_detected true on hosts that expose an AMD GPU to the kernel but ship no rocminfo/amd-smi. Detection alone does not mean ROCm is usable or that the gfx arch is known, and two downstream paths wrongly assumed it did:

- studio/setup.sh forwarded --has-rocm with no gfx, so install_llama_prebuilt found no per-gfx bundle and dropped to a HIP source build (slow, or a hard failure without build deps) instead of the CPU prebuilt these hosts used to get. Now --has-rocm is forwarded for a gfx-unknown host only when hipcc is present; otherwise it keeps the CPU prebuilt.

- install.sh get_torch_index_url selected a generic rocmX.Y index whenever the ROCm version was readable, but the Strix reroute only learns gfx from rocminfo/amd-smi, so a Strix KFD-only host landed on the broken _grouped_mm wheels. Now, when neither rocminfo nor amd-smi is present (gfx unknowable), it stays on CPU with a hint to install them.

Detection and the improved diagnostics are unchanged; only the routing for gfx-unknown KFD-only hosts is made safe. Adds tests for both gates.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Harden KFD-only fallback: probe gfx, accept versioned hipcc (Codex P2s)

Follow-up to the previous commit's two guards:

- install.sh: the KFD-only torch guard tested only 'command -v rocminfo/amd-smi', so a host where those binaries exist but do not enumerate the GPU (gfx unreadable) slipped through and, with hipconfig/rocm-core present, still got a generic rocm index -- breaking Strix. Now it actually reads the gfx (rocminfo, then amd-smi list / static --asic, the same probe the reroute uses) and falls back to CPU whenever the arch is unreadable, not just when the binaries are absent.

- studio/setup.sh: the hipcc gate missed a HIP toolchain installed only under a versioned prefix (/opt/rocm-*/bin/hipcc), which the source build at setup.sh:1663 does support, so such hosts were dropped to the CPU prebuilt unnecessarily. The gate now also accepts /opt/rocm-*/bin/hipcc.

Tests updated to assert the gfx-read (not binary-presence) gate and the versioned hipcc path; full test_rocm_support.py green (347 passed). Verified the gfx probe by execution: rocminfo-with-no-gfx now routes to CPU, amd-smi fallback still resolves gfx.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Honor UNSLOTH_ROCM_GFX_ARCH before the CPU fallback for PR #7314

Seed both the gfx-unknown guard in get_torch_index_url and the Strix reroute
from UNSLOTH_ROCM_GFX_ARCH before probing rocminfo/amd-smi, so a host that
names its arch reaches the correct rocm index instead of being forced to CPU
(or to the generic wheels) when the runtime probes can't enumerate the GPU.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Probe gfx with visibility masks cleared for PR #7314 (Codex P2)

rocminfo/amd-smi honor ROCR/HIP_VISIBLE_DEVICES, so a container that masks the
GPU (e.g. ROCR_VISIBLE_DEVICES=-1) would make the gfx probe read nothing and
force CPU torch, even though the KFD-based AMD detection is env-independent and
hipconfig can still supply the ROCm version. Clear the visibility masks for the
rocminfo/amd-smi arch probe only (the Strix reroute keeps them for per-GPU index
selection), so a masked/container host keeps its ROCm route.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Re-probe gfx unmasked in the Strix reroute when a mask hides all agents for PR #7314 (Codex P2)

* Remove leftover conflict marker from the test merge

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Report an explicit CPU pin instead of a ROCm misdiagnosis for PR #7314 (Codex P3)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Trigger the reroute re-probe on a set-but-empty visibility mask for PR #7314 (subagent review)

* Guard the ROCm version chain against set -e when no source exists for PR #7314 (simulation find)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Preserve the inferred-gfx reroute for KFD-only hosts (Codex P2)

The gfx-unknown CPU guard in get_torch_index_url fired before the
runtime-less reroute could run: with the KFD topology fix,
_has_amd_rocm_gpu is true on KFD-only hosts, so the reroute's
'! _has_amd_rocm_gpu' gate never let _infer_linux_amd_gfx_arch route
them to AMD per-arch wheels, regressing inferable boxes (PCI/cpuinfo/
lspci) from arch-specific PyTorch to CPU-only.

- Factor the override->rocminfo->amd-smi gfx probe (masks cleared)
  into _probe_amd_gfx_arch, shared by the guard and the reroute gate
  so the two can't disagree on what 'readable' means.
- Reroute gate now also fires when the GPU is detected but the probe
  is empty (KFD-only). Deliberate CPU fallbacks (old/unreadable ROCm
  version) all had a readable gfx and stay excluded.
- The guard defers to the reroute (no false 'installing CPU-only
  PyTorch' promise) only when inference yields a supported family;
  otherwise the actionable CPU warning is unchanged.

Executed tests: KFD-only host reroutes to repo.amd.com per-arch wheels
and exports UNSLOTH_ROCM_GFX_ARCH for setup.sh; readable-gfx CPU
fallback stays un-rerouted; undetected-GPU reroute unchanged; the
guard's three inference outcomes covered. Suite: 375 passed, bash -n
clean on both scripts.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix two false diagnostics on the KFD-only paths (Codex P3s)

1. get_torch_index_url: with UNSLOTH_ROCM_GFX_ARCH set on a KFD-only
   host that has no ROCm version sources, the no-version endpoint
   printed 'falling back to CPU-only PyTorch' even though the reroute
   (gated on the override) then installs the per-arch wheels. When the
   override maps to a wheel family, defer with an accurate message;
   an unmappable override keeps the CPU warning since the reroute
   can't route it either.

2. Runtime-less reroute: the KFD-only branch reached the warning
   'ROCm runtime not visible (/dev/kfd, rocminfo, amd-smi)' although
   /dev/kfd is exactly what detected the GPU. The diagnostic now
   distinguishes KFD-visible/tooling-blind hosts from truly
   runtime-invisible ones.

Executed tests: supported override defers without the false CPU
warning, unsupported override and readable-gfx no-version hosts keep
it; KFD-only reroute emits the KFD wording, undetected-GPU reroute
keeps the original. Version sources are shimmed so the tests hold on
dev boxes with a real hipconfig. Suite: 376 passed, bash -n clean.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
Leo Borcherding 2026-07-23 02:42:03 -05:00 committed by GitHub
commit 430ada617a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 704 additions and 35 deletions

View file

@ -2115,13 +2115,16 @@ _has_amd_rocm_gpu() {
amd-smi list 2>/dev/null | awk '/^GPU[[:space:]]*[:\[][[:space:]]*[0-9]/{ found=1 } END{ exit !found }'; then
return 0
elif [ -e /dev/kfd ] && \
awk 'FNR==1{ gpu=0; amd=0 } /gpu_id/{ gpu=($2+0>0) } /vendor_id/{ amd=($2==4098) } \
gpu && amd { found=1 } END{ exit !found }' \
awk '/vendor_id/ && $2 == 4098 { found = 1 } END { exit !found }' \
/sys/class/kfd/kfd/topology/nodes/*/properties 2>/dev/null; then
# vendor_id 4098 = 0x1002 (AMD). NVIDIA open kernel module (driver
# 560+) can register KFD topology nodes with non-zero gpu_id but
# vendor_id 4318 (0x10DE). Require AMD vendor to avoid misrouting
# NVIDIA-only hosts to the ROCm install path.
# vendor_id 4098 = 0x1002 (AMD) marks a GPU node: the KFD CPU node
# reports vendor_id 0, so any 4098 node is an AMD GPU. NVIDIA's open
# kernel module (driver 560+) registers KFD nodes as vendor_id 4318
# (0x10DE), so this never false-positives on NVIDIA-only hosts.
# The prior check also required a gpu_id line, but gpu_id is a SIBLING
# sysfs file, not a line in properties -- it never matched, so the
# fallback silently missed every ROCm-less AMD host (issue: fresh
# Arch/CachyOS boxes reporting "no GPU detected").
return 0
fi
return 1
@ -2230,6 +2233,30 @@ EOF
return 1
}
# Reads the AMD gfx arch for wheel-index decisions: a user-set
# UNSLOTH_ROCM_GFX_ARCH is authoritative (lowercased), else rocminfo, then
# amd-smi. rocminfo/amd-smi honor ROCR/HIP_VISIBLE_DEVICES, so a container mask
# (e.g. ROCR_VISIBLE_DEVICES=-1) would hide a GPU that the env-independent KFD
# detection still sees -- the tool probes run with the masks cleared. Prints the
# gfx token(s) or nothing when unreadable, and always returns 0 (a failing probe
# as the last command would trip set -e in callers' assignments). Shared by
# get_torch_index_url's gfx gate and the runtime-less reroute gate so the two
# can never disagree on what "readable" means.
_probe_amd_gfx_arch() {
_ensure_rocm_probe_env
_pg=$(printf '%s' "${UNSLOTH_ROCM_GFX_ARCH:-}" | tr '[:upper:]' '[:lower:]')
if [ -z "$_pg" ] && command -v rocminfo >/dev/null 2>&1; then
_pg=$( (unset ROCR_VISIBLE_DEVICES HIP_VISIBLE_DEVICES; rocminfo 2>/dev/null) | grep -oE 'gfx[1-9][0-9a-z]{2,3}' || true)
fi
if [ -z "$_pg" ] && command -v amd-smi >/dev/null 2>&1; then
_pg=$( (unset ROCR_VISIBLE_DEVICES HIP_VISIBLE_DEVICES; amd-smi list 2>/dev/null) | grep -oE 'gfx[1-9][0-9a-z]{2,3}' || true)
if [ -z "$_pg" ]; then
_pg=$( (unset ROCR_VISIBLE_DEVICES HIP_VISIBLE_DEVICES; amd-smi static --asic 2>/dev/null) | grep -oE 'gfx[1-9][0-9a-z]{2,3}' || true)
fi
fi
printf '%s\n' "$_pg"
}
# ── Detect GPU and choose PyTorch index URL ──
# Mirrors Get-TorchIndexUrl in install.ps1.
# On CPU-only machines this returns the cpu index, avoiding the solver
@ -2283,6 +2310,29 @@ get_torch_index_url() {
if ! _has_amd_rocm_gpu; then
echo "$_base/cpu"; return
fi
# A generic rocm index is only safe when the gfx arch is readable: the
# Strix reroute (gfx1150/1151 -> arch-specific index) learns gfx from
# rocminfo/amd-smi, so if those are missing OR do not enumerate the GPU, an
# unknown-arch box might be Strix and would get the broken _grouped_mm
# wheels. Probe via the shared helper (override first, then rocminfo/amd-smi
# with visibility masks cleared); if the arch is unreadable, never guess a
# rocm index. A KFD-only host whose arch is still inferable from hardware
# IDs (PCI/cpuinfo/lspci) returns the cpu index and lets the runtime-less
# reroute below upgrade it to AMD per-arch wheels -- the reroute gate uses
# this same probe, so the handoff can't misfire. Only when inference fails
# too is CPU final, with the actionable warning.
_amd_gfx_probe=$(_probe_amd_gfx_arch)
if [ -z "$_amd_gfx_probe" ]; then
if _amd_inferred_gfx=$(_infer_linux_amd_gfx_arch 2>/dev/null) && \
[ -n "$_amd_inferred_gfx" ] && \
_amd_arch_index_family_for_gfx "$_amd_inferred_gfx" >/dev/null 2>&1; then
echo "[WARN] AMD GPU detected but rocminfo/amd-smi can't read its gfx arch -- inferring $_amd_inferred_gfx from hardware IDs." >&2
echo "$_base/cpu"; return
fi
echo "[WARN] AMD GPU detected but its gfx arch can't be read (rocminfo/amd-smi missing or not enumerating the GPU) -- installing CPU-only PyTorch." >&2
echo "[WARN] For GPU PyTorch, install or repair rocminfo/amd-smi (e.g. sudo pacman -S rocm-hip-sdk) and re-run this installer." >&2
echo "$_base/cpu"; return
fi
# AMD GPU confirmed -- detect ROCm version
_rocm_tag=""
_rocm_tag=$({ command -v amd-smi >/dev/null 2>&1 && \
@ -2299,7 +2349,11 @@ get_torch_index_url() {
{ command -v rpm >/dev/null 2>&1 && \
ver="$(rpm -q --qf '%{VERSION}\n' rocm-core 2>/dev/null)" && \
[ -n "$ver" ] && \
printf '%s\n' "$ver" | awk -F'[.-]' '{print "rocm"$1"."$2; exit}'; }) 2>/dev/null
printf '%s\n' "$ver" | awk -F'[.-]' '{print "rocm"$1"."$2; exit}'; }) 2>/dev/null || _rocm_tag=""
# ^ || guard: when EVERY version source is missing (e.g. rocminfo present
# but rocm-core not installed, so dpkg-query/rpm exit 1), the whole ||
# chain fails and set -e would kill the installer BEFORE the actionable
# no-version WARN below -- exactly the fresh-install case it exists for.
# Validate _rocm_tag: must match "rocmX.Y" with major >= 1
case "$_rocm_tag" in
rocm[1-9]*.[0-9]*) : ;; # valid (major >= 1)
@ -2335,12 +2389,27 @@ get_torch_index_url() {
esac
return
fi
# AMD GPU confirmed by rocminfo/amd-smi but ROCm version could not be
# read from any source (amd-smi, /opt/rocm/.info/version, hipconfig,
# dpkg, rpm). Warn explicitly rather than silently installing CPU PyTorch.
echo "[WARN] AMD GPU detected but ROCm version could not be determined -- falling back to CPU-only PyTorch" >&2
echo "[WARN] Ensure one of the following is accessible: amd-smi, hipconfig, /opt/rocm/.info/version, rocm-core package" >&2
echo "[WARN] To install ROCm: https://rocm.docs.amd.com/en/latest/deploy/linux/index.html" >&2
# AMD GPU confirmed (rocminfo/amd-smi or the KFD topology fallback) but
# no ROCm/HIP install was found to read the version from (amd-smi,
# /opt/rocm/.info/version, hipconfig, dpkg, rpm). This is the common
# fresh-install case: the GPU is real, but with no ROCm userspace the
# correct PyTorch build can't be selected. Warn with an actionable fix
# rather than silently installing CPU PyTorch.
# A user-set UNSLOTH_ROCM_GFX_ARCH seeded the probe above, so rocminfo/
# amd-smi may still be unable to see the GPU; when the named arch maps to
# a wheel family, the runtime-less reroute (gated on the override) will
# install the AMD per-arch wheels -- a CPU-only warning here would be
# false for that path. Defer like the inferable-arch branch does.
if [ -n "${UNSLOTH_ROCM_GFX_ARCH:-}" ] && \
_amd_arch_index_family_for_gfx "$_amd_gfx_probe" >/dev/null 2>&1; then
echo "[WARN] AMD GPU detected with no readable ROCm version, but UNSLOTH_ROCM_GFX_ARCH=$_amd_gfx_probe is set -- routing to AMD per-arch wheels." >&2
echo "$_base/cpu"; return
fi
echo "[WARN] AMD GPU detected, but no ROCm/HIP install was found to select the matching GPU PyTorch build -- falling back to CPU-only PyTorch." >&2
echo "[WARN] Install the ROCm/HIP SDK, then re-run this installer:" >&2
echo "[WARN] Arch / CachyOS : sudo pacman -S rocm-hip-sdk" >&2
echo "[WARN] other distros : https://rocm.docs.amd.com/en/latest/deploy/linux/index.html" >&2
echo "[WARN] Minimum required for version detection: amd-smi, hipconfig, /opt/rocm/.info/version, or the rocm-core package." >&2
echo "$_base/cpu"; return
fi
# Parse CUDA version from nvidia-smi output (POSIX-safe, no grep -P).
@ -2841,14 +2910,20 @@ TORCH_INDEX_URL=$(get_torch_index_url)
# Linux: ROCm runtime missing but a supported AMD gfx arch is inferable (Strix Halo
# in /proc/cpuinfo, lspci marketing name, UNSLOTH_ROCM_GFX_ARCH). Route to AMD's
# per-arch wheels like install.ps1 does on Windows (unslothai#7301).
# Gated on _has_amd_rocm_gpu being FALSE: a */cpu index on a host whose GPU IS
# visible to the ROCm probes is a deliberate fallback (unsupported/unreadable
# ROCm version, after its own warning), not a missing runtime -- rerouting it
# would contradict that decision. An explicit UNSLOTH_ROCM_GFX_ARCH override
# stays authoritative either way.
# Gated on the runtime probes NOT naming a gfx: either no AMD GPU is detected at
# all (_has_amd_rocm_gpu false), or the GPU is visible only through the
# env-independent KFD topology while rocminfo/amd-smi can't read its arch
# (KFD-only host, unslothai#7314 -- before the KFD detection fix these hosts
# reached this reroute via the false branch, so the empty-probe condition
# preserves that routing). A */cpu index chosen WITH a readable gfx
# (unsupported/unreadable ROCm version, after its own warning) is a deliberate
# fallback -- rerouting it would contradict that decision, and stays excluded
# because the shared probe returns its gfx. An explicit UNSLOTH_ROCM_GFX_ARCH
# override stays authoritative either way.
if [ "$_torch_index_pinned" = false ] && [ "$SKIP_TORCH" = false ] && \
! _has_usable_nvidia_gpu && \
{ [ -n "${UNSLOTH_ROCM_GFX_ARCH:-}" ] || ! _has_amd_rocm_gpu; } && \
{ [ -n "${UNSLOTH_ROCM_GFX_ARCH:-}" ] || ! _has_amd_rocm_gpu || \
[ -z "$(_probe_amd_gfx_arch)" ]; } && \
case "$(uname -s)" in Linux) true ;; *) false ;; esac && \
case "$_ARCH" in x86_64|amd64) true ;; *) false ;; esac; then
# ROCm torch wheels are x86_64-only; get_torch_index_url returns CPU on other
@ -2880,7 +2955,13 @@ if [ "$_torch_index_pinned" = false ] && [ "$SKIP_TORCH" = false ] && \
;;
esac
echo "" >&2
echo " [WARN] ROCm runtime not visible (/dev/kfd, rocminfo, amd-smi) but $_linux_inferred_gfx inferred." >&2
# KFD-only hosts reach this reroute with /dev/kfd present
# (that's what detected them), so don't claim it's missing.
if _has_amd_rocm_gpu; then
echo " [WARN] AMD GPU visible via the kernel driver (KFD) but rocminfo/amd-smi can't read its gfx arch; using $_linux_inferred_gfx." >&2
else
echo " [WARN] ROCm runtime not visible (/dev/kfd, rocminfo, amd-smi) but $_linux_inferred_gfx inferred." >&2
fi
echo " [WARN] Routing to AMD arch-specific wheels ($(_strip_index_url_credentials "$TORCH_INDEX_URL"))." >&2
echo " [WARN] These wheels bundle their own ROCm runtime; install the kernel stack for native compute:" >&2
echo " [WARN] https://docs.unsloth.ai/get-started/install-and-update/amd" >&2
@ -3004,8 +3085,10 @@ case "$_torch_index_leaf" in
# || true on each probe: no gfx match makes grep exit 1, which under
# set -euo pipefail would abort the installer before the next fallback
# runs (now that the case matches every rocm* index, not just rocm7.1).
_gfx_all=""
if command -v rocminfo >/dev/null 2>&1; then
# A user-supplied UNSLOTH_ROCM_GFX_ARCH overrides probing (mirrors setup.sh
# and the display block), so a Strix override still reaches the arch index.
_gfx_all=$(printf '%s' "${UNSLOTH_ROCM_GFX_ARCH:-}" | tr '[:upper:]' '[:lower:]')
if [ -z "$_gfx_all" ] && command -v rocminfo >/dev/null 2>&1; then
_gfx_all=$(rocminfo 2>/dev/null | grep -oE 'gfx[1-9][0-9a-z]{2,3}' || true)
fi
if [ -z "$_gfx_all" ] && command -v amd-smi >/dev/null 2>&1; then
@ -3016,6 +3099,23 @@ case "$_torch_index_leaf" in
_gfx_all=$(amd-smi static --asic 2>/dev/null | grep -oE 'gfx[1-9][0-9a-z]{2,3}' || true)
fi
fi
# get_torch_index_url reads the arch with ROCR/HIP masks cleared, so a
# mask hiding every agent (e.g. ROCR_VISIBLE_DEVICES=-1) still lands
# here on a generic rocm index; re-probe unmasked or a masked-out Strix
# box keeps the broken generic wheels. Partial masks never get here
# (they enumerate at least one agent above) and keep their selection.
# ${VAR+x} (not :-): a SET-but-empty mask also hides every agent and
# must trigger the re-probe too.
if [ -z "$_gfx_all" ] && [ -n "${ROCR_VISIBLE_DEVICES+x}${HIP_VISIBLE_DEVICES+x}" ]; then
if command -v rocminfo >/dev/null 2>&1; then
_gfx_all=$( (unset ROCR_VISIBLE_DEVICES HIP_VISIBLE_DEVICES; rocminfo 2>/dev/null) | grep -oE 'gfx[1-9][0-9a-z]{2,3}' || true)
fi
if [ -z "$_gfx_all" ] && command -v amd-smi >/dev/null 2>&1; then
_gfx_all=$( (unset ROCR_VISIBLE_DEVICES HIP_VISIBLE_DEVICES; amd-smi list 2>/dev/null) | grep -oE 'gfx[1-9][0-9a-z]{2,3}' || true)
[ -z "$_gfx_all" ] && \
_gfx_all=$( (unset ROCR_VISIBLE_DEVICES HIP_VISIBLE_DEVICES; amd-smi static --asic 2>/dev/null) | grep -oE 'gfx[1-9][0-9a-z]{2,3}' || true)
fi
fi
_runtime_gfx=""
if [ -n "$_gfx_all" ]; then
_vis="${HIP_VISIBLE_DEVICES:-${ROCR_VISIBLE_DEVICES:-}}"
@ -3169,6 +3269,17 @@ elif case "$TORCH_INDEX_URL" in */rocm*|*/gfx*) true ;; *) false ;; esac; then
elif [ "$OS" = "macos" ] && [ "$_ARCH" = "arm64" ]; then
# Apple Silicon: PyTorch gets Metal (MPS) acceleration over unified memory, so not CPU-only.
step "gpu" "Apple Silicon (Metal, unified memory)"
elif _has_amd_rocm_gpu; then
if [ "$_torch_index_pinned" = true ]; then
# An explicit UNSLOTH_TORCH_INDEX_URL/_FAMILY pin skipped all probing;
# do not claim ROCm is unusable when a CPU/other index was requested.
step "gpu" "AMD GPU (torch index pinned: $_torch_index_leaf)" "$C_WARN"
else
# AMD GPU visible to the kernel but the torch index stayed CPU: no usable
# ROCm userspace to pick a wheel. "none" would repeat the false diagnosis
# this installer used to give.
step "gpu" "AMD GPU (no usable ROCm -- CPU fallback)" "$C_WARN"
fi
else
step "gpu" "none (CPU-only)" "$C_WARN"
fi
@ -3177,8 +3288,17 @@ fi
case "$TORCH_INDEX_URL" in
*/cpu)
if [ "$SKIP_TORCH" = false ] && [ "$OS" != "macos" ]; then
substep "No GPU detected -- installing CPU-only PyTorch." "$C_WARN"
if [ "$OS" = "wsl" ]; then
if [ "$_torch_index_pinned" = true ]; then
# An explicit CPU pin is a request, not a detection failure:
# skip the SDK guidance (ROCm may be perfectly healthy here).
substep "CPU-only PyTorch (index pinned via UNSLOTH_TORCH_INDEX_URL / _FAMILY)."
elif _has_amd_rocm_gpu; then
substep "AMD GPU detected, but no usable ROCm/HIP install -- installing CPU-only PyTorch." "$C_WARN"
substep "Install the ROCm/HIP SDK and re-run this installer for GPU PyTorch." "$C_WARN"
else
substep "No GPU detected -- installing CPU-only PyTorch." "$C_WARN"
fi
if [ "$OS" = "wsl" ] && [ "$_torch_index_pinned" = false ]; then
# WSL + no GPU detected (detection above found nothing). Common
# cause: an AMD GPU whose ROCm-on-WSL runtime isn't exposed yet --
# /dev/dxg present (graphics) but no ROCm runtime.