@@ -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