Skip to content

Commit b88db27

Browse files
committed
Fix Intel XPU detection and install path for PR unslothai#7706
The XPU index selected during GPU detection was overwritten by Get-TorchIndexUrl before the install branch read it, so Intel hosts still got CPU PyTorch while being told XPU wheels were being installed. - Move the XPU reroute after Get-TorchIndexUrl, and let an explicit pin win - Detect via Get-CimInstance (Get-WmiObject is absent in PowerShell 7) - Match only Arc / Data Center GPU, so UHD / HD / Iris Xe are not promised XPU - Split Intel GPU present from XPU-capable so the CPU fallback hint works - Bound the XPU torch trio like every other index (bare names resolved torch 2.13.0 + torchaudio 2.11.0 and pulled unsloth back to an old release) - Clear the XPU state after a CPU fallback, mirroring the ROCm path - Teach the index family, GPU branch and torch flavor helpers about xpu
1 parent 3e7d93e commit b88db27

1 file changed

Lines changed: 58 additions & 33 deletions

File tree

install.ps1

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ function Install-UnslothStudio {
8383
if ([string]::IsNullOrWhiteSpace($TorchIndexUrl)) { return "none" }
8484
# Drop query/fragment first so a token-authenticated pin classifies by family.
8585
$leaf = (($TorchIndexUrl -split '[?#]', 2)[0].TrimEnd('/') -split '/')[-1].ToLowerInvariant()
86-
if (@("cpu", "cu118", "cu124", "cu126", "cu128", "cu130") -contains $leaf) { return $leaf }
86+
if (@("cpu", "xpu", "cu118", "cu124", "cu126", "cu128", "cu130") -contains $leaf) { return $leaf }
8787
if ($leaf -match '^rocm[0-9]+\.[0-9]+$') { return $leaf }
8888
return "auto"
8989
}
@@ -94,6 +94,7 @@ function Install-UnslothStudio {
9494
# Require a digit after "cu" so /current or /custom isn't branded CUDA (parity ^cu[0-9]).
9595
if ($TorchIndexFamily -match '^cu[0-9]') { return "cuda" }
9696
if ($TorchIndexFamily -like "rocm*") { return "rocm" }
97+
if ($TorchIndexFamily -eq "xpu") { return "xpu" }
9798
if ($TorchIndexFamily -eq "cpu") { return "cpu" }
9899
return "unknown"
99100
}
@@ -2192,49 +2193,51 @@ exit 0
21922193
substep "Could not determine the GPU arch -- install the HIP SDK or set" "Yellow"
21932194
substep "UNSLOTH_ROCM_GFX_ARCH to enable GPU ROCm PyTorch:" "Yellow"
21942195
substep "https://rocm.docs.amd.com/en/latest/deploy/windows/index.html" "Yellow"
2195-
} elseif (-not $HasNvidiaSmi -and -not $HasROCm) {
2196-
# ── Intel GPU detection (Arc / Data Center GPU Max / Ultra AIPC iGPU) ──
2197-
# PyTorch publishes XPU (SYCL) wheels at download.pytorch.org/whl/xpu,
2198-
# which ship their own oneAPI runtime — no Intel oneAPI Base Toolkit needed.
2196+
} else {
2197+
# ── Intel GPU detection (Arc / Data Center GPU Max / Flex) ──
2198+
# PyTorch publishes XPU (SYCL) wheels at download.pytorch.org/whl/xpu that ship
2199+
# their own oneAPI runtime. Windows also needs the Intel GPU driver (and Unsloth
2200+
# additionally documents oneAPI + Level Zero), so an Intel adapter alone is not
2201+
# proof of a usable XPU: $HasIntelGpu is "an Intel GPU is present",
2202+
# $script:IsIntelXpu is "XPU wheels are appropriate for it".
2203+
# Get-CimInstance, not Get-WmiObject: the latter does not exist in PowerShell 7.
2204+
# Only Arc / Data Center parts are XPU-capable; UHD / HD / Iris Xe are not.
21992205
$HasIntelGpu = $false
22002206
$IntelGpuLabel = $null
22012207
try {
2202-
$wmiIntel = Get-WmiObject Win32_VideoController -ErrorAction SilentlyContinue |
2203-
Where-Object { $_.Name -match "(?i)(Intel.*Arc|Intel.*Graphics|Intel.*Iris|Intel.*UHD|Intel.*HD Graphics)" } |
2204-
Select-Object -First 1
2205-
if ($wmiIntel) {
2208+
$intelGpus = @(Get-CimInstance Win32_VideoController -ErrorAction SilentlyContinue |
2209+
Where-Object { $_.Name -match "(?i)Intel" })
2210+
if ($intelGpus.Count -gt 0) {
22062211
$HasIntelGpu = $true
2207-
$IntelGpuLabel = $wmiIntel.Name
2212+
$xpuGpu = $intelGpus | Where-Object { $_.Name -match "(?i)Intel.*(Arc|Data Center GPU)" } | Select-Object -First 1
2213+
$IntelGpuLabel = if ($xpuGpu) { $xpuGpu.Name } else { $intelGpus[0].Name }
2214+
if ($xpuGpu) { $script:IsIntelXpu = $true }
22082215
}
22092216
} catch {}
2210-
# Double-check: torch.xpu.is_available() is the authoritative test when
2211-
# PyTorch is already installed (migrated env). This catches the case where
2212-
# a WMI Intel GPU entry exists but no XPU driver/runtime is loaded.
2213-
if (-not $HasIntelGpu -and (Test-Path -LiteralPath $VenvPython)) {
2217+
# torch.xpu.is_available() is authoritative when torch is already installed
2218+
# (migrated env), so let it both confirm and veto the name match above.
2219+
if (Test-Path -LiteralPath $VenvPython) {
22142220
try {
22152221
$xpuCheck = & $VenvPython -c "import torch; print(torch.xpu.is_available())" 2>$null | Out-String
22162222
if ($xpuCheck.Trim() -eq 'True') {
22172223
$HasIntelGpu = $true
2218-
$IntelGpuLabel = "Intel GPU (detected by PyTorch XPU)"
2224+
$script:IsIntelXpu = $true
2225+
if (-not $IntelGpuLabel) { $IntelGpuLabel = "Intel GPU (detected by PyTorch XPU)" }
2226+
} elseif ($xpuCheck.Trim() -eq 'False') {
2227+
$script:IsIntelXpu = $false
22192228
}
22202229
} catch {}
22212230
}
2222-
if ($HasIntelGpu) {
2231+
if ($script:IsIntelXpu) {
22232232
step "gpu" "Intel GPU detected" "Green"
22242233
substep "$IntelGpuLabel"
2225-
substep "PyTorch XPU (SYCL) wheels will be installed from https://download.pytorch.org/whl/xpu"
2226-
# Override the TorchIndexUrl to the XPU index so the install section below
2227-
# pulls torch from the Intel wheel repository instead of CPU/CUDA.
2228-
$XpuBaseUrl = if ($env:UNSLOTH_PYTORCH_MIRROR) { $env:UNSLOTH_PYTORCH_MIRROR.TrimEnd('/') } else { "https://download.pytorch.org/whl" }
2229-
$TorchIndexUrl = "$XpuBaseUrl/xpu"
2230-
$script:IsIntelXpu = $true
2234+
# The wheel-index message lives with the reroute below: only that point
2235+
# knows whether a pin or --no-torch overrode XPU, and the real mirror URL.
22312236
} else {
22322237
step "gpu" "none (chat-only / GGUF)" "Yellow"
2238+
if ($HasIntelGpu) { substep "Detected: $IntelGpuLabel (not XPU-capable)" "Yellow" }
22332239
substep "Training and GPU inference require an NVIDIA, AMD ROCm, or Intel Arc GPU." "Yellow"
22342240
}
2235-
} else {
2236-
step "gpu" "none (chat-only / GGUF)" "Yellow"
2237-
substep "Training and GPU inference require an NVIDIA, AMD ROCm, or Intel Arc GPU." "Yellow"
22382241
}
22392242
# On an AMD GPU (no NVIDIA), surface the optional WSL-ROCm driver hint.
22402243
if (-not $HasNvidiaSmi -and ($ROCmGfxArch -or $ROCmGpuLabel)) { Show-AmdWslDriverHint }
@@ -2251,6 +2254,14 @@ exit 0
22512254
return $value.Substring(0, $idx).TrimEnd('/') + $value.Substring($idx)
22522255
}
22532256

2257+
# Index leaf (cpu / cu128 / xpu / gfx1201), query and fragment stripped so a
2258+
# token-authenticated mirror still classifies by family. Shared by the callers below.
2259+
function Get-TorchIndexLeafName {
2260+
param([string]$Url)
2261+
if ([string]::IsNullOrWhiteSpace($Url)) { return "" }
2262+
return ((($Url -split '[?#]', 2)[0].TrimEnd('/') -split '/')[-1]).ToLowerInvariant()
2263+
}
2264+
22542265
# ── Choose the correct PyTorch index URL based on driver CUDA version ──
22552266
# Mirrors Get-PytorchCudaTag in setup.ps1.
22562267
function Get-TorchIndexUrl {
@@ -2313,6 +2324,7 @@ exit 0
23132324
if (-not $TorchVersion) { return $null }
23142325
if ($TorchVersion -match '\+(cu\d+)') { return $Matches[1] }
23152326
if ($TorchVersion -match '\+rocm') { return 'rocm' }
2327+
if ($TorchVersion -match '\+xpu') { return 'xpu' }
23162328
if ($TorchVersion -match '\+cpu') { return 'cpu' }
23172329
return 'cpu'
23182330
}
@@ -2327,6 +2339,7 @@ exit 0
23272339
$leaf = (($TorchIndexUrl -split '[?#]', 2)[0].TrimEnd('/') -split '/')[-1].ToLowerInvariant()
23282340
if ($leaf -match '^cu\d+$') { return $leaf }
23292341
if ($leaf -eq 'cpu') { return 'cpu' }
2342+
if ($leaf -eq 'xpu') { return 'xpu' }
23302343
if ($leaf -match '^rocm') { return 'rocm' }
23312344
# gfx must be followed by a digit (an architecture leaf); gfx-private is custom.
23322345
if ($leaf -match '^gfx[0-9]') { return 'rocm' }
@@ -2370,6 +2383,14 @@ exit 0
23702383
(-not [string]::IsNullOrWhiteSpace($env:UNSLOTH_TORCH_INDEX_FAMILY))
23712384
$TorchIndexUrl = Get-TorchIndexUrl
23722385

2386+
# Intel XPU reroute. Must run AFTER Get-TorchIndexUrl or it would be overwritten;
2387+
# an explicit pin still wins, exactly like the AMD ROCm reroute below.
2388+
if ($script:IsIntelXpu -and -not $TorchIndexPinned -and -not $SkipTorch) {
2389+
$XpuBaseUrl = if ($env:UNSLOTH_PYTORCH_MIRROR) { $env:UNSLOTH_PYTORCH_MIRROR.TrimEnd('/') } else { "https://download.pytorch.org/whl" }
2390+
$TorchIndexUrl = "$XpuBaseUrl/xpu"
2391+
substep "PyTorch XPU (SYCL) wheels will be installed from $(Remove-IndexUrlCredentials $TorchIndexUrl)"
2392+
}
2393+
23732394
# ── GPU arch → newest compatible Windows ROCm wheel release ──
23742395
# Wheels bundle their own ROCm runtime; the installed HIP SDK version does
23752396
# not constrain which release to use. Always picks the newest release that
@@ -2488,9 +2509,9 @@ exit 0
24882509
substep "Installing CPU-only PyTorch (AMD GPU arch unknown -- install the HIP SDK" "Yellow"
24892510
substep "or set UNSLOTH_ROCM_GFX_ARCH to enable GPU ROCm)." "Yellow"
24902511
} elseif ($HasIntelGpu -and -not $script:IsIntelXpu) {
2491-
substep "Intel GPU detected but XPU not available. Installing CPU-only PyTorch." "Yellow"
2492-
substep "If you want GPU training, install Intel oneAPI Base Toolkit 2025.2.1" "Yellow"
2493-
substep "and re-run this installer. See: https://unsloth.ai/docs/get-started/install/intel" "Yellow"
2512+
substep "Intel GPU detected but not XPU-capable. Installing CPU-only PyTorch." "Yellow"
2513+
substep "PyTorch XPU needs Intel Arc or Data Center GPU plus a current driver." "Yellow"
2514+
substep "See: https://unsloth.ai/docs/get-started/install/intel" "Yellow"
24942515
} else {
24952516
substep "No NVIDIA GPU detected." "Yellow"
24962517
}
@@ -2605,16 +2626,16 @@ exit 0
26052626
$ROCmIndexUrl = $null
26062627
$ROCmTorchFloor = $null
26072628
}
2608-
} elseif ($script:IsIntelXpu -and $TorchIndexUrl -like "*/xpu") {
2629+
} elseif ($script:IsIntelXpu -and (Get-TorchIndexLeafName $TorchIndexUrl) -eq "xpu") {
26092630
# ── Intel Arc / XPU PyTorch install ──
26102631
# XPU wheels ship their own oneAPI runtime (intel-sycl-rt et al.) and
26112632
# are published at https://download.pytorch.org/whl/xpu under PEP 503.
26122633
Write-TauriLog "STEP" "Installing PyTorch (Intel XPU)"
26132634
substep "installing PyTorch from $(Remove-IndexUrlCredentials $TorchIndexUrl)..."
2614-
# Let pip resolve the correct version triple for the host — the XPU index
2615-
# publishes torch, torchvision, torchaudio, triton-xpu, pytorch-triton-xpu
2616-
# and all oneAPI runtime deps as wheels. No floor/ceiling pins needed.
2617-
$torchInstallExit = Invoke-InstallCommandRetry -Label "install PyTorch (Intel XPU)" { uv pip install --python $VenvPython --force-reinstall --default-index $TorchIndexUrl torch torchvision torchaudio }
2635+
# Bound the trio exactly like every other index: the XPU index serves torch
2636+
# past Unsloth's ceiling (2.13.0), and torchaudio dropped its exact torch pin,
2637+
# so bare names resolve a mismatched pair and drag unsloth back to an old release.
2638+
$torchInstallExit = Invoke-InstallCommandRetry -Label "install PyTorch (Intel XPU)" { uv pip install --python $VenvPython --force-reinstall "torch>=2.4,<2.11.0" "torchvision>=0.19,<0.26.0" "torchaudio>=2.4,<2.11.0" --default-index $TorchIndexUrl }
26182639
if ($torchInstallExit -ne 0) {
26192640
# Transient XPU-index failure: fall back to CPU base.
26202641
$CpuFallbackIndexUrl = if ($env:UNSLOTH_PYTORCH_MIRROR) { "$($env:UNSLOTH_PYTORCH_MIRROR.TrimEnd('/'))/cpu" } else { "https://download.pytorch.org/whl/cpu" }
@@ -2624,6 +2645,10 @@ exit 0
26242645
Write-Host "[ERROR] Failed to install PyTorch (XPU and CPU base both failed, exit code $torchInstallExit)" -ForegroundColor Red
26252646
return (Exit-InstallFailure "Failed to install PyTorch (exit code $torchInstallExit)" $torchInstallExit)
26262647
}
2648+
# CPU base is in; drop the XPU expectation so the flavor-repair block
2649+
# below won't retry the index that just failed (mirrors the ROCm path).
2650+
$script:IsIntelXpu = $false
2651+
$TorchIndexUrl = $CpuFallbackIndexUrl
26272652
}
26282653
} else {
26292654
Write-TauriLog "STEP" "Installing PyTorch"

0 commit comments

Comments
 (0)