unsloth/tests/python
benj 6a58ea0f0e
Add Intel Arc GPU detection and XPU PyTorch install to Windows installer (#7706)
* Add Intel Arc GPU detection and XPU PyTorch install to Windows installer

The installer's GPU detection chain (NVIDIA -> AMD ROCm -> else)
has no Intel Arc/SYCL/XPU branch, so Intel Arc GPUs fall into the
"none (chat-only / GGUF)" branch and get CPU PyTorch despite
PyTorch publishing XPU wheels at download.pytorch.org/whl/xpu.

This adds:
- WMI-based Intel GPU detection (Arc, Iris, UHD, HD Graphics)
- Torch XPU availability check for migrated/upgraded environments
- An XPU PyTorch install path with the whl/xpu index
- CPU fallback with a pointer to the Intel oneAPI docs when XPU
  isn't available
- Updated messaging from "NVIDIA or AMD ROCm" to include Intel Arc

The XPU wheels ship their own oneAPI runtime (intel-sycl-rt et al.)
so no Intel oneAPI Base Toolkit is required for GPU training.

Tested on: Windows 11, Intel Arc 140V GPU (8GB), PyTorch 2.9.0+xpu

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>

* Fix Intel XPU detection and install path for PR #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

* Keep install.sh diagnostics in parity with the install.ps1 xpu family

install.ps1 now classifies an /xpu index leaf as family xpu / branch xpu, so
mirror the same two cases in _tauri_torch_index_family and _tauri_gpu_branch.
These feed the [TAURI:DIAG] line only, and a Linux user can already reach the
xpu index via UNSLOTH_TORCH_INDEX_FAMILY, where it previously reported
auto/unknown. Linux Intel auto-detection is not added here.

* Tighten the Intel XPU comments in install.ps1

Comment and whitespace only, no code change.

* Address Codex review on the Intel XPU path

- Run the Intel scan before the GPU report chain instead of inside its final
  else. A WMI-named-only AMD adapter set ROCmGpuLabel and took that chain, so a
  discrete Arc card next to an AMD CPU's integrated Radeon was never detected.
  The scan is gated on no usable NVIDIA or AMD, and the Intel branch ranks above
  the two AMD-present-but-unusable branches, so a usable AMD host is unaffected.
- Let a migrated env's torch veto the hardware match only when it is itself an
  XPU build. A CPU build reports torch.xpu.is_available() False for lacking XPU
  support, not for unsuitable hardware, and was blocking the CPU to XPU upgrade.
- Detect Intel in studio/setup.ps1 too. It only knew NVIDIA and AMD, so every
  successful Intel install printed none (chat-only / GGUF) right after
  install.ps1 reported a usable Arc GPU. Self-contained so studio update works.

* Address the second Codex round on the Intel XPU path

- Reset $script:IsIntelXpu at the start of each invocation. Under the documented
  irm | iex path $script: is the caller's session scope, so a second run in the
  same session inherited a stale true, skipped the scan on a now-NVIDIA host and
  still rerouted to the xpu index. Reproduced in pwsh before fixing.
- Gate the Intel scan on whether AMD actually gets a wheel, not on whether an AMD
  arch was seen. An arch missing from the family map has no ROCm wheels and lands
  on CPU torch, so it must not outrank a usable Arc card. The map is hoisted above
  the scan and consumed unchanged by the AMD reroute.
- Select the XPU index in studio/setup.ps1, not just report it. Previously setup
  printed Intel GPU detected and then installed CPU torch, so studio update never
  migrated an Arc box off CPU. Adds a bounded XPU install with a CPU fallback,
  teaches the stale-venv check about +xpu, and mirrors the wheel-aware AMD gate so
  the two files agree instead of wiping the venv on every update.

* Address the third Codex round on the Intel XPU path

- Force the dependency pass on an Arc host whose torch is not XPU-capable, the
  Intel counterpart of the existing AMD escape. Without it the fast up-to-date
  path skipped the install block, so the xpu index selection was never reached
  and a CPU venv never migrated.
- Confirm a working XPU runtime before treating an xpu venv as stale. If CIM is
  unavailable or returns an Intel name outside the Arc match, the expected tag
  fell through to cpu and a valid XPU environment was rebuilt and lost.
- Force-reinstall the XPU trio only when the installed wheel is not already
  +xpu, or the pin changed. It was unconditional, so a fresh install re-fetched
  multiple GB immediately and again on every update.
- Warn when torch.xpu.is_available() is false after installing XPU torch, naming
  the Intel driver floor. Otherwise the installer promised GPU training while
  unsloth raised NotImplementedError at import on a stale driver.
- Stop the detection probe vetoing the hardware match. Its cpu fallback could not
  displace the installed +xpu wheel, so it only mislabelled a capable GPU as
  unusable; the driver warning covers that case honestly, and setup.ps1 agrees.

* Bound the XPU probes, repair xpu pins in install.sh, and floor bitsandbytes on the Intel path

install.sh: teach _torch_flavor_tag, _expected_torch_flavor_tag and
_torch_index_repairable about the xpu leaf. The diagnostic already reported
gpu_branch=xpu, but an xpu pin fell to the custom arm so a migrated env kept
its CPU wheel. The +xpu flavor arm is required alongside, otherwise a correct
2.10.0+xpu wheel reads as cpu and gets force-reinstalled every run.

install.ps1 / studio/setup.ps1: route every torch probe through a new bounded
Invoke-BoundedPythonProbe (ProcessStartInfo, both streams drained async,
WaitForExit, kill on timeout). A hanging Intel driver init is exactly what
these probes detect, and an unbounded one would hang the installer instead of
reaching the warning. Timeouts read as not-available. Get-InstalledTorchTag
now shares the helper rather than carrying a second copy of the pattern.

install.ps1: install bitsandbytes>=0.50.0 on the XPU path. unsloth's floor is
>=0.45.5, so a migrated venv keeps a pre-0.49 wheel with no XPU library and
4-bit QLoRA silently turns off. Same floor the AMD paths use, since <=0.49.2
NaNs at 4-bit decode and an Arc card can sit next to a Radeon.

* Floor bitsandbytes on the Studio XPU migration and on an explicit xpu pin

studio/setup.ps1: `unsloth studio update` migrating a CPU venv to XPU replaced
only the torch trio. install_python_stack.py then upgrades unsloth and
unsloth-zoo alone, so an installed bitsandbytes 0.45.x kept satisfying the base
floor while carrying no Windows XPU kernels, and 4-bit QLoRA silently turned
off. Adds the same bitsandbytes>=0.50.0 --no-deps pass install.ps1 got, placed
after the stack so it is the last word, gated on $XpuIndexUrl (the CPU fallback
clears it, no-torch never sets it) and still inside the -not $SkipPythonDeps
block so the up-to-date escape does not reach it.

install.ps1: key the bitsandbytes pass off the index leaf instead of
$script:IsIntelXpu. An explicit UNSLOTH_TORCH_INDEX_FAMILY=xpu pin on a
non-Intel host skips the XPU branch but still installs the trio from the xpu
index, so torch is +xpu and needs the same floor. The CPU fallback rewrites
$TorchIndexUrl, so a failed XPU install reads as cpu and stays quiet.

* Tighten the Intel XPU comments across the three installers

Comment-only pass now that the review has settled: several blocks grew over
successive rounds and were restating the code or narrating the review. Net 36
lines removed, with the load-bearing facts kept -- why ProcessStartInfo rather
than the call operator, why both probe streams drain async, why the helper is
defined above the Intel scan, the 0.50.0 bitsandbytes floor and why not the
curated extra, and why PEP 440 means a migrated env can confirm but never veto
the Intel match.

Also records why the Studio bitsandbytes pass must stay above the
ErrorActionPreference restore: Fast-Install needs EAP=Continue or PS 5.1 turns
pip stderr into a terminating error.

No code tokens changed; verified with a PowerShell token-stream diff of
install.ps1 and setup.ps1, and by hand for install.sh.

* Bound the Intel WMI scan, bound the stale flavor probe, and stop CUDA Triton shadowing XPU

studio/setup.ps1: the stale-venv flavor probe read StandardOutput.ReadToEnd()
before WaitForExit, so the timeout was unreachable and a wedged import torch
hung studio setup forever; stderr was never drained either. Routed through
Invoke-BoundedPythonProbe, which already drains both streams and kills on
timeout. A timeout now reads as unreadable flavor, so the venv rebuilds.

install.ps1 / studio/setup.ps1: bound the Win32_VideoController query and add a
registry fallback. -ErrorAction suppresses errors but bounds nothing, and
-OperationTimeoutSec is not enforced for the local COM session this uses, so a
degraded WMI repository blocks forever. install_llama_prebuilt.py already runs
this query out of process for the same reason and documents an Arc A770 being
misrouted by it. The registry class key answers in-process; it is the fallback
rather than the fast path because a stale driver config can outlive the
hardware, and here a false positive would install XPU torch on a host with no
Arc.

studio/setup.ps1: replace triton-windows with torch's own XPU triton after the
stack. Both distributions own the top-level triton package, sharing 151 paths
including __init__.py and _C/libtriton.pyd, so an in-place cu-to-xpu repair
leaves the CUDA build shadowing the XPU one. Removing it alone would delete the
shared files the XPU wheel overwrote, and unsloth declares triton-windows as a
win32 dependency so an earlier removal is reinstalled by the stack: uninstall
and reinstall, after the stack, only while triton-windows is present. The spec
is read from the installed torch, since the name changed from
pytorch-triton-xpu to triton-xpu in torch 2.10.

* Tighten the comments added with the bounded scan and Triton replacement

Comment-only pass over the previous commit's additions, which had not been
through one: 15 lines removed across the two bounded-scan headers, the two
registry-fallback headers and the Triton block.

Kept the facts that cost measurement: -OperationTimeoutSec not being enforced
for a local COM session, Ok being false on an empty answer because a Windows
host always has an adapter, the registry class key being fallback rather than
fast path here, the 151 shared Triton paths, and why the uninstall has to be
paired with a reinstall after the stack.

No code tokens changed; verified with a PowerShell token-stream diff of both
files, which also confirms the two helper copies stay identical.

* Stage the Triton replacement behind a download so the uninstall cannot strand the venv

The replacement uninstalled triton-windows and then installed the XPU triton
from the index. A failure between the two left the venv with a partially
deleted triton, since the uninstall drops the paths shared with the XPU
distribution, and the warning made that look like a skipped optional repair.

The uninstall cannot go last, because it removes the paths in triton-windows'
own record and those are the shared ones. So fetch first: pip download the
wheel, confirm one is actually on disk (exit 0 alone is not enough, an
sdist-only mirror satisfies that), and only then uninstall and install the
local file. A local wheel installs with the network refused, so nothing after
the destructive step depends on the index. A failed fetch leaves
triton-windows in place, which is the pre-existing shadowing rather than a
broken venv, and says so.

Past that point only disk or permissions can fail, so restore triton-windows
if the local install does, leaving a triton that imports. If both fail the
message is loud and carries the repair command, with the index URL redacted
since a mirror pin can carry a token.

pip only: uv has no pip download (astral-sh/uv#3163).

* Windows: harden the Intel registry fallback and declare the XPU install state up front

Get-IntelRegistryAdapterNames wrapped the whole enumeration in a single try, so one
unreadable subkey discarded every adapter found before it. windows_intel_gpu_in_registry(),
the in-process Python probe over the same class key, skips per subkey and continues; the
PowerShell copy now does too. It also matched on the PCI vendor id but returned DriverDesc,
which the callers re-filter on "Intel", so a localized or OEM-branded Arc was found here and
dropped there. Both installers carry the same copy and a test asserts they stay identical.

setup.ps1 read $installedTorchTag and $XpuIndexUrl from outside the blocks that assign them.
Unset and $null are both falsy so behaviour is unchanged, but a caller running with
Set-StrictMode -Version Latest turned those reads into terminating errors, and install.ps1
is documented as irm | iex into the caller's own session.

Two comment corrections: 0.48.2, not 0.49.0, is the first win_amd64 bitsandbytes wheel
carrying libbitsandbytes_xpu.dll, and the triton package overlap is version-dependent
rather than a fixed 151 paths.

The new test drives the shipped helper with the registry cmdlets mocked rather than reading
a hive, so it runs on Linux and macOS as well as Windows.

* Studio: show the Intel XPU runtime row in the About tab

hardware.py has always emitted versions["xpu"], but HardwareInfo only ever declared cuda and
rocm. On an Arc host both of those are null, so the runtime row disappeared entirely while
the GPU name and VRAM rows still rendered, leaving a host that looks half detected. That was
unreachable on Windows until the installer learned to select XPU wheels, which is what makes
it worth fixing here.

The three-way choice is lifted into a helper at module scope: inlining it pushes AboutTab
past the cognitive-complexity ceiling. The label is a proper noun, so every locale carries
the same literal.

* Windows: reach Intel XPU through a localized name, a stale fast path and an old wheel

Four holes in the XPU paths, all found by driving the shipped code rather than reading it.

The registry fallback only ran when the CIM scan failed. When it succeeds and returns a
localized adapter name, which on non-English Windows carries no ASCII "Intel", the filter
dropped the adapter and the host went to CPU torch. The registry now re-labels an adapter
WMI already reported, matched by name so an entry naming nothing WMI listed stays ignored:
a driver record outliving its card still cannot promote a host WMI answered for.

The XPU trio accepted torch 2.4 and 2.5, which unsloth/models/_utils.py rejects at import
for an XPU device. An xpu mirror carrying only an older wheel produced an install that
reported success and then failed on the first import, and an existing 2.5+xpu venv was kept
because it satisfied the range. The floor is 2.6 on the XPU paths only; the CPU fallback
keeps 2.4.

The "package is up to date" fast path escaped for an Arc host on CPU torch, but not for one
already on XPU torch whose bitsandbytes predates the XPU kernels or whose triton-windows
still shadows the XPU Triton. Those two live in the dependency pass, so a venv that reached
+xpu without them, an explicit pin or an update whose first pass ran the pre-XPU setup.ps1,
never got them on any later update either. An unreadable version reads as stale.

install_python_stack.py writes its completion manifest immediately before returning, so an
interrupt between the triton-windows uninstall and the XPU wheel install left a venv with no
triton that the next update read as complete. The manifest is now held aside across the swap
and restored only once a triton is importable again.

* Windows: move the install manifest across the Triton swap instead of rewriting it

Two problems with the hold added in 2603fc809, both on the restore side.

Reading and rewriting the file cannot survive a manifest carrying a non-ASCII path. Windows
PowerShell 5.1 writes Set-Content in the ANSI code page by default, and its -Encoding utf8
emits a BOM that install_manifest.read_manifest's json.load rejects outright
("Unexpected UTF-8 BOM"); Get-Content is ANSI on a BOM-less file too, so the read lost bytes
before the write got a chance to. The manifest is now MOVED into the wheel's temp directory
and moved back, so no encoding is involved at either end. That directory is already removed
in the finally, which is what keeps an unrestored manifest gone.

A manifest that would not move left the old valid one in place for the whole destructive
window, since the failure only cleared the saved copy and carried on into the uninstall.
That is the case the hold exists for, so it now skips the swap entirely and says so:
triton-windows keeps shadowing the XPU Triton, which costs torch.compile on the GPU and is
repairable on the next run, rather than risking a venv with no Triton that reads as complete.

* Windows: confirm the install manifest actually moved before the Triton swap

Move-Item across volumes is a copy followed by a delete, and it reports success when only
the delete fails, leaving the original exactly where it was. So the guard added in af928dd88
could believe it had set the manifest aside while a valid one sat there for the whole
destructive window, which is the case that guard exists to prevent.

Found by modelling the manifest in the setup.ps1 scenario matrix, which this had no coverage
for: with the parent directory read-only the swap still ran, and the locked scenario passed
for the wrong reason. The move is now confirmed by testing the source path afterwards, and a
manifest still standing aborts the swap like any other failure to move it.

Four new scenarios cover it: the swap keeping a byte-identical manifest, a swap where neither
Triton reinstalls correctly leaving it gone, a failed fetch never touching it, and a manifest
that cannot move aborting the swap.

* Windows: key the XPU fast-path remediation off the installed wheel, not just the GPU scan

$HasNvidiaSmi suppresses the Intel scan, so on a mixed NVIDIA + Intel box under an explicit
xpu pin $script:IsIntelXpu stays false while the pin still lands the venv on a +xpu wheel.
The staleness check added in 2603fc809 was gated on that flag alone, so those hosts kept
taking the fast path and never reached the bitsandbytes floor or the Triton replacement.

This is the same gating mistake the bitsandbytes pass had in round 4, where the fix was to
key off the index leaf rather than the scan. The leaf is not resolved yet at the fast path,
but the installed flavor tag is, and whatever put the venv on a +xpu wheel the two
remediations still apply. The runtime probe above stays on the scan: reinstalling XPU torch
is only right where an Intel GPU was actually found.

A pure NVIDIA host on a cu wheel never runs the probe, which the matrix asserts alongside the
two new mixed-host rows.

* Windows: reconcile Intel names for hybrid GPUs, and stop the XPU escapes firing where XPU is unreachable

Five fixes from a review of the XPU work so far.

The registry reconciliation was gated on "no ASCII Intel name present", so a hybrid laptop
reporting its Intel UHD alongside a localized Arc stopped at the UHD and left the Arc
unrecognised. It is now gated on the absence of an XPU match, and the regex behind both that
gate and the classification is defined once so they cannot drift.

The two fast-path escapes cleared $SkipPythonDeps for any Intel host, but the XPU install and
its two remediations are all gated on $XpuIndexUrl, which an explicit cpu / rocm / custom-leaf
pin never sets, and no-torch mode has no torch pass at all. Those hosts ran the whole
dependency pass, installed nothing new, and re-fired the identical condition on every later
update. Both escapes now require XPU to be reachable.

The manifest path was learned by a subprocess whose output parsing could not work: `& python`
returns one array element per line, interpolating that joins on $OFS, a SPACE, so splitting
on newlines yields a single element and a banner ahead of the answer arrives glued to the
path. Any such failure then skipped the hold silently and swapped anyway, which is the
window the hold exists to close. manifest_path() is venv_root()/MANIFEST_NAME and venv_root()
is sys.prefix, which is $VenvDir here, so it is assembled like Get-PersistedNoTorch already
does. A test asserts the literal still matches MANIFEST_NAME.

The uninstall's exit code was discarded. A triton-windows that will not uninstall, which on
Windows means Studio is running and holding libtriton.pyd open, still shadows the XPU Triton,
so installing over it achieved nothing and restored the manifest onto a venv this pass was
supposed to have changed.

The restore had no verification and an empty catch, while the finally deletes the held copy
either way, so a failed restore lost the manifest with nothing on screen.

* Windows: keep the WMI adapter list an array so the Intel re-label appends instead of concatenating

`$_gpuNames = if (...) { @(...) } else { @(...) }` wraps each branch, and a one-element array
unrolls on its way out of the if, so on any single-adapter host $_gpuNames was a String. The
`+=` that re-labels a localized adapter then concatenated two strings rather than appending a
name, and the GPU reported to the user came out doubled:

    Intel(R) UHD Graphics 620Intel Intel(R) UHD Graphics 620

No install decision changes. The re-label only appends a registry name that already contains
the WMI name, so the concatenation matches the Arc / Data Center regex exactly when the
registry name alone would, and every scenario in the matrix records the same verdict either
way. It is the displayed adapter name that was wrong.

Widened by the previous commit: gating on the absence of an XPU match rather than of any Intel
name brought ordinary single Intel iGPU hosts into the re-label for the first time.

@() now wraps the whole if in both installers, with a test asserting it stays that way.

* Windows: give pin-only XPU installs the 2.6 floor, and treat an unreadable dependency probe as stale

The XPU install branch required $script:IsIntelXpu as well as an xpu index leaf, so an
explicit FAMILY=xpu or URL pin on a host whose Intel scan never ran -- a mixed NVIDIA box,
where $HasNvidiaSmi suppresses it -- fell through to the generic branch and its torch>=2.4.
Against a mirror carrying an older +xpu wheel that installs a torch unsloth rejects at import.
Keyed off the leaf alone now, which is what the bitsandbytes gate below it already does and
says in its own comment. install.sh had the same gap from the other direction: its xpu leaf is
reachable only by an explicit pin and kept the generic floor, so it gets the same 2.6 trio.

The fast-path dependency probe treated "did not answer" as "nothing to do". A timeout, or a
malformed .dist-info making distributions() raise, then left the fast path intact and an XPU
migration never reached the bitsandbytes floor or the Triton replacement on any later update
either. It now clears the fast path, the same direction an unparseable version already took.

Two install.ps1 rows move, both FAMILY=xpu pins on non-Intel hosts, both onto the XPU branch.
The CPU fallback after a failed XPU install keeps its 2.4 floor.

* Windows XPU: probe the preserved venv, drop torchaudio on ARM64, and give POSIX XPU the bitsandbytes floor

Three fixes to the Intel XPU paths.

install.ps1, migrated-runtime probe: a rerun over an existing install moves the old
venv to $script:StudioVenvRollbackDir and creates an empty one in its place, both
before this probe runs, so it always asked an interpreter with no torch and answered
"no XPU". Ask the preserved environment when there is one, which is the migrated
runtime the fallback exists for.

install.ps1, Windows on ARM: no win_arm64 torchaudio wheel exists on any index.
Keying the XPU branch off the index leaf alone routes an arm64 interpreter into a
branch that hardcoded the trio, so the install aborted. Ask the interpreter for its
platform tag, as the generic path already does, and drop that one pin on arm64.
The CPU fallback below it gets the same treatment.

install.sh, XPU pins: bitsandbytes ships XPU kernels (libbitsandbytes_xpu2025.so and
_xpu2026.so) from 0.50.0 on manylinux, and nothing on the POSIX side raised the floor
for them, so a migrated environment kept a pre-XPU build and lost 4-bit QLoRA on a
torch that otherwise works. Matches what the Windows XPU pass already installs.

* Studio: stop the xpu label test from forbidding a partial locale

check-parity.ts states the contract plainly: "Locale files may be partial; missing keys
must fall back to English." The new test required every overlay to carry the xpu label,
which contradicts that and breaks on the next locale anyone adds. It already did: it.ts
landed on main after this branch, so the merged tree fails on all three runners even
though nothing about the label is wrong there. The label is a proper noun, so the
English fallback is byte-identical to a translation and the requirement bought nothing.

Assert what actually renders wrong instead: en.ts must carry the key, because it is the
fallback every locale resolves to, and no overlay may define a value that disagrees with
it. Both halves were checked against a merged working tree, and both still fail when the
condition they guard is broken.

* Linux XPU: hoist the bitsandbytes pass out of the fresh-install arm

It sat inside `elif [ -n "$TORCH_INDEX_URL" ]`, which a migrated environment never
enters because the `_MIGRATED` arm above it wins, so the one environment the pass
existed for was the one that skipped it. The AMD passes handle this by existing twice,
once per arm; this gate needs nothing branch-specific, so it moves past the chain
instead and both arms reach a single copy.

tests/sh/test_xpu_bitsandbytes_reachable.sh guards both halves: the block must be
placed where every arm reaches it, and it must still fire only on the xpu leaf.
25 checks over [migrated, fresh] x [xpu, mirrored xpu, cuda, rocm, cpu, none] x
[torch, no-torch], run against the block and the leaf parser extracted from install.sh.
Moving the block back inside an arm fails it.

* Report the XPU runtime before the hardware summary, and show every runtime in About

setup.ps1: the hardware report runs ~1300 lines before the torch.xpu.is_available()
check that keeps an XPU environment, so a host the WMI scan and the registry fallback
both miss (wedged CIM service, an Intel part outside the Arc|Data Center regex) was told
"none (chat-only / GGUF)" and then watched setup keep the XPU venv. Ask the same question
before printing, so the report and the decision cannot disagree.

A free disk read gates the interpreter launch: torch/version.py carries the local label,
so a CPU-only host never pays for an `import torch` on every `studio update` just to be
told it has no Intel GPU. The dist-info name cannot be used for this -- pip normalises the
local label out of it (torch-2.9.1.dist-info for a +cu128 wheel). The promotion carries its
own try: it must still run when the scan threw, which is the case it exists for, and a junk
UNSLOTH_STUDIO_HOME would otherwise abort setup from Join-Path.

about-tab.tsx: hardware.py reads versions["cuda"] off torch.version.cuda and sets
versions["xpu"] from an independent torch.xpu.is_available() probe, and UNSLOTH_FORCE_XPU=1
is a supported configuration where CUDA is present but XPU is selected. Both are non-null
there, so returning the first match hid the XPU row on exactly the host it was added for.
Collect every reported runtime instead.

tests/studio/test_setup_xpu_runtime_prereport.ps1 covers the two new helpers with the
filesystem mocked, so it runs on all three runners: override precedence, ~ expansion, the
four wheel flavours, a missing or unreadable version.py, and wiring assertions that the
promotion precedes the report and that the cheap read gates the probe. The About-tab test
gains a case that fails if the picker returns early again.

* POSIX: recognise a working XPU runtime, and raise the bitsandbytes floor on the update path

The hardware summary tested NVIDIA, AMD and Apple Silicon and then fell through to
"none (chat-only / GGUF)", so a Linux host running the +xpu wheel install.sh had just
installed was told training needs an NVIDIA or AMD GPU. Added an arm ranked below both,
matching setup.ps1.

The bitsandbytes floor was also unreachable on the route an existing XPU user actually
takes. `unsloth studio update` runs this file, never install.sh (see the note at the top
of setup.sh), and neither this file nor install_python_stack.py had an XPU floor, while
unsloth's own dep floor is 0.45.5 -- which a pre-XPU wheel satisfies indefinitely. So
4-bit QLoRA stayed unavailable on a torch that otherwise works.

One detection serves both, but they read different signals on purpose. The floor keys on
the WHEEL (+xpu, read off torch/version.py) and the summary keys on the RUNTIME
(torch.xpu.is_available()): a +xpu wheel installs fine on a host whose driver never
initialises, and that host should still get the kernels while no GPU is claimed for it.
The disk read gates the interpreter launch, so a CPU-only host pays nothing per update.

tests/sh/test_setup_xpu_posix_summary.sh builds real venv trees, version.py files and
stub interpreters rather than mocking, so the disk read and the runtime probe genuinely
execute: 13 checks over the four wheel flavours, working/dead/missing runtime, no venv,
and the arm's rank. Removing the arm fails four of them.

* POSIX XPU: make the bitsandbytes step nonfatal, bound the probe, and act on an XPU pin

Three defects in the POSIX XPU code from the previous commit.

run_quiet routes failure to setup_fail and exits, so the best-effort bitsandbytes
upgrade could abort an otherwise fine `studio update` over a transient download, and
the warning after it was unreachable. run_quiet_no_exit is the nonfatal wrapper.

The runtime probe had no timeout. A stalled Intel driver wedges inside `import torch`,
which is exactly the host this probe classifies, so it could hang every update forever.
Bounded at 60s rather than the 10s the smi probes use: a cold `import torch` takes
seconds by itself and a short bound would read a healthy host as having no GPU. Systems
without coreutils timeout keep the previous behaviour rather than losing detection.

An explicit XPU pin was protected but never acted on. An xpu leaf names no family the
cuda/rocm repair helpers know, so _explicit_unknown_family_torch_index_url makes both
skip it, and `unsloth studio update` never runs install.sh -- so switching a CPU install
to UNSLOTH_TORCH_INDEX_FAMILY=xpu left the CPU wheel in place indefinitely. The fix goes
in install_python_stack.py, which already parses the pin, rather than setup.sh, which has
no pin awareness at all: _ensure_xpu_torch mirrors the existing _ensure_cpu_torch, the
xpu leaf is classified so the backend is no longer unknown, and the ROCm helper skips an
xpu backend so it cannot treat the pin as an AMD host. Windows is excluded because
setup.ps1 owns torch there and installs the trio itself.

That put the XPU trio in a third file, so tests/sh/test_xpu_torch_spec_parity.sh asserts
the floors match across install.sh, install_python_stack.py and install.ps1 plus the
wiring. Each of its four structural guards was mutation-tested: a drifted floor, a lost
classification, wiring at one call site instead of two, and the ROCm skip removed all
fail it. The POSIX summary suite gains checks for the nonfatal wrapper and the bound.

* Linux XPU: swap generic Triton, gate the pin repair on the version, and escape the fast path

Three defects in the XPU code from the previous commit.

_ensure_xpu_torch returned on the +xpu tag alone, so a migrated 2.5+xpu venv was left
in place even though unsloth/models/_utils.py raises at import for an XPU device below
2.6. It now returns only when the flavour and the supported range both match.

That repair was also unreachable on the route it was written for. setup.sh skips
install_python_stack entirely when the package version is current, and that pass is the
only thing that acts on an XPU pin, so a CPU install switched to the xpu family stayed
CPU. Added a third fast-path escape beside the anyio and incomplete-manifest ones.

Generic triton and torch's pytorch-triton-xpu / triton-xpu both own the top-level triton
package, and resolving unsloth against a pinned +xpu torch pulls both -- uv reports
pytorch-triton-xpu 3.5.0 alongside triton 3.7.1 -- so the CUDA-oriented build lands last
and torch.compile loads the wrong library on an Intel GPU. This is the POSIX half of the
Windows swap: the spec is read from torch's own metadata, so the pytorch-triton-xpu to
triton-xpu rename at torch 2.10 needs no hardcoding, and the fetch happens before the
uninstall because the uninstall drops the shared paths from generic triton's own record.

test_torch_installs_do_not_use_deprecated_index_url forbade --index-url on
"$TORCH_INDEX_URL" anywhere in install.sh. That rule is about uv, which deprecated the
flag in favour of --default-index; pip never had --default-index, so the pre-fetch
legitimately uses it. The assertion is now per occurrence and exempts pip download only,
and it joins backslash continuations first, since the flag and its command are routinely
on different physical lines. Both a same-line and a continuation-line uv offender were
mutation-tested and are still caught.

tests/sh/test_xpu_triton_swap_posix.sh asserts the swap by execution -- ordering, the
rename, no generic triton, torch wanting CUDA triton, non-xpu index, no-torch, empty
index, and a dead mirror that must warn without removing anything.

* XPU: move the Triton swap where both routes reach it, and bootstrap pip for it

Five defects in the XPU code from the previous commits.

The Triton pre-fetch could never have run. `uv venv` is created without --seed, so a
fresh venv has no pip and `python -m pip download` fails with "No module named pip"
every time, leaving the swap a no-op that only ever warns. My shell test missed it
because its stub interpreter answered pip commands. install.sh already bootstraps pip
this way before its pre-release bitsandbytes wheel.

The swap also never ran on `unsloth studio update`, which runs setup.sh and never
install.sh. Both fixes fall out of moving it: install.sh runs setup.sh, which runs
install_python_stack.py, so that module is the one place both routes pass through. The
install.sh copy is deleted rather than duplicated, and the shell test is replaced by
tests/studio/test_xpu_triton_swap.py, which covers the no-pip case and asserts install.sh
carries no second copy.

The fast-path pin match missed authenticated and fragmented mirrors
(https://mirror/whl/xpu?token=...), which read as "no XPU pin" and skipped the repair;
query and fragment are now stripped before the leaf test.

That escape also launched an interpreter, which a wedged Intel driver hangs inside. It
now reads the local label out of torch/version.py instead: nothing to bound, and a
CPU-only host pays nothing per update.

setup.ps1's fast path asked only whether XPU was available. A 2.5+xpu build answers yes
and is still rejected by unsloth/models/_utils.py at import, so it now checks the
supported range too, via Test-TorchXpuVersionSupported.

The POSIX suite is up to 22 checks; the three new guards were mutation-tested by removing
the query strip, the fragment strip, and by making the escape launch an interpreter.

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

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

* XPU: make a failed Triton swap unsurvivable, and widen the fast-path escapes

Five defects in the XPU code from the last two commits.

The Triton uninstall ignored its return code. A read-only or locked venv leaves generic
triton registered, so installing over it lets a later upgrade of that distribution delete
the shared files again, and every dependency pass repeats the swap. A failed uninstall now
changes nothing at all.

Past the uninstall the venv has no triton, because the uninstall takes the shared
top-level files with it, so a warning there let the caller write a completion manifest
over a venv whose torch.compile is broken -- and the next update fast-paths straight past
it, since no generic distribution is left to trigger on. That install is now fatal.

_ensure_xpu_torch returned when the probe timed out. On this path a wedged `import torch`
is evidence rather than noise: the usual cause is a stalled Intel driver under an
unsupported +xpu wheel, which the resolver keeps because it satisfies the base range. An
authoritative pin now repairs on an inconclusive probe. This deliberately differs from the
CPU counterpart, where a wedge has no such likely cause.

The fast-path pin match stripped one trailing slash, so a ".../whl/xpu//" pin still read
as no pin. It now strips them all, like the shared leaf parsers.

Moving the Triton swap into the Python stack left the fast path with no reason to run it:
a migrated environment with supported +xpu torch and a leftover generic triton kept the
CUDA-oriented build forever. A stale generic triton now forces the dependency pass too,
detected from the dist-info name so no interpreter is launched.

The POSIX suite is up to 26 checks and the Triton tests to 16. Two of the guards were
rebuilt after their own negative controls found them vacuous: the stale-triton check
matched the detection loop rather than the branch that acts on it, and a fixed line window
had drifted off the code it was meant to cover, so it is now anchored on the block.

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

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

* Do not require the XPU pin again after install

Three Intel paths still assumed the pin was still in the environment, or that
every XPU host looks like x64 Linux.

install_python_stack.py: the generic-Triton swap returned unless
UNSLOTH_TORCH_INDEX_URL / _FAMILY was set. That pin is one-shot -- a user who
ran UNSLOTH_TORCH_INDEX_FAMILY=xpu ./install.sh has nothing left in the
environment by the next plain `unsloth studio update`, yet that update's
dependency pass can pull generic triton back in and shadow torch's XPU build
again. The installed +xpu wheel is the durable signal (setup.sh already raises
the bitsandbytes floor off it), so fall back to it and to the default xpu
index. The label is read off disk: importlib.metadata drops the local version
label, and `import torch` loads the SYCL runtime, which can wedge.

install.ps1: the flavor repair built its own XPU trio including torchaudio,
which has no win_arm64 wheel on any index. A migrated ARM64 venv skips the
fresh XPU branch and takes this path, so the repair failed outright before
setup.ps1 could reach its ARM-aware fallback. One builder now serves both
sites, since the two copies drifted the moment only one learned about ARM.

install.sh: adding the xpu tag made the final flavor guard reachable on an
Intel pin, and it probes with an unbounded `import torch`. On a host whose
driver initialization wedges that hangs the installer, with no timeout
anywhere before setup.sh's bounded probes. The xpu path reads torch/version.py
off disk instead; every other family keeps the interpreter read unchanged.

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

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

* Key the setup XPU paths on the installed wheel, not the pin

Follow-up to d07c179f: install_python_stack now treats the installed +xpu
wheel as the durable signal, but the two callers upstream of it did not.

setup.sh fast path: the escape only ran under `case $_setup_pin in *xpu`, so
after a one-shot UNSLOTH_TORCH_INDEX_FAMILY=xpu install every later `studio
update` saw no pin, kept _SKIP_PYTHON_DEPS=true and never reached the Triton
swap at all -- generic triton kept shadowing the XPU build forever. The disk
read now happens unconditionally and the swap escape keys on the wheel. The
pin leaf is also compared exactly, like the shared index parsers: a custom
mirror ending in -xpu was classified as the curated family, which cleared the
skip flag on every up-to-date run while _ensure_xpu_torch declined to act.

setup.ps1: bounding the flavour probe turned a timeout into "rebuild", and the
host most likely to time out inside `import torch` is an Arc box whose compute
driver stalled -- where torch/version.py still names a good +xpu wheel. With no
currently exported pin the stale path then deleted the venv. It now falls back
to the same disk check and warns about the driver. Other families still
rebuild on an unreadable flavour.

setup.sh summary: a +xpu wheel whose runtime will not initialise fell through
to "none (chat-only / GGUF)", telling an Arc owner their hardware is
unsupported and hiding the driver update that fixes it. It gets its own arm.

* Stop the XPU paths from stranding or wiping a venv

Four ways the Intel paths could still leave a user worse off than before they
ran anything.

setup.ps1 stale check: on a hybrid NVIDIA + Arc host the XPU promotion is
gated on -not $HasNvidiaSmi, so a pinless `unsloth studio update` expects a
cu* tag, calls the working Arc venv stale and DELETES it -- then exits,
because only install.ps1 creates venvs. A direct update now keeps any +xpu
venv and says to re-run install.ps1, which rebuilds with a rollback copy.

setup.ps1 Triton swap: when the staged XPU wheel failed to install after
triton-windows was removed AND the generic restore also failed, the branch
only printed. $stackExit stayed 0, so setup reported success and install.ps1
committed a venv with no importable triton over its rollback. It now carries
the real failure code into the existing handler.

install_python_stack: the `pip download` that stages the XPU Triton wheel
inherited the user's pip index environment. PIP_NO_INDEX makes pip ignore
--index-url outright, and PIP_EXTRA_INDEX_URL / PIP_FIND_LINKS are consulted
in addition to it, so the fetch could fail (leaving generic Triton shadowing
the XPU build) or serve the wheel from an index the pin never named. It now
takes the same _install_env_for_cmd scrub every other pinned install gets.

setup.sh runtime probe: the arm taken when coreutils `timeout` is absent ran
the probe with no deadline, on exactly the stalled-driver host the bounding
exists for. The deadline now lives inside the probe as signal.alarm, which
terminates the process even while the driver blocks in C.

* Keep a preserved XPU venv on the XPU index

Follow-up to 10ba6e31c, which stopped a direct update wiping a +xpu venv on a
hybrid NVIDIA + Arc host but left the rest of the pass believing the host was
CUDA. The index chain prefers NVIDIA over Intel, and the CUDA arm does not
--reinstall-package torch, so uv left the +xpu wheel in place as satisfied
while installing triton-windows over torch's XPU triton -- and with
$XpuIndexUrl null nothing swapped it back. A half-converted venv is worse than
either end state, so the preserved case now selects the xpu leaf, ahead of the
NVIDIA arm and behind an explicit pin. The hardware report is untouched: there
really is an NVIDIA GPU in the machine.

install_python_stack: an inconclusive XPU probe was always read as a flavour
mismatch, but on a stalled Intel driver under a SUPPORTED wheel that is two
90-second hangs and two force-reinstalls of the whole trio on every update,
repairing nothing. The disk answers what the probe cannot, so a supported
wheel now yields the driver warning and an unsupported or missing one still
repairs.

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

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

* Lowercase the setup.sh pin leaf like every other index parser

install.sh's _torch_index_url_leaf, setup.ps1's Get-TorchIndexLeaf and
install_python_stack's _torch_index_leaf all lowercase before classifying. This copy did
not, so UNSLOTH_TORCH_INDEX_FAMILY=XPU (or a URL ending in /XPU) left the leaf uppercase,
the equality test against "xpu" failed, and the fast path stayed on. Those same classifiers
call that pin XPU once they are reached, so the wheel was never migrated and the update
silently repaired nothing. The comment above the line already claimed to match the shared
parsers; now it does.

Three cases added to tests/sh/test_setup_xpu_fastpath_escape.sh (FAMILY=XPU, FAMILY=Xpu,
a URL ending /XPU), plus one that lowercasing must not widen the match: a custom leaf like
PRIVATE-XPU stays an unknown family. All three fail against the previous line and pass now.

* Do not promise CPU training when the XPU runtime will not start

The unavailable-runtime arm said training and GPU inference run on CPU until the driver is
fixed. They do not: with neither CUDA nor XPU available, get_device_type() in
unsloth/device_type.py raises NotImplementedError, so importing unsloth fails outright
rather than falling back. llama.cpp is unaffected, which is what chat and GGUF actually run
on, so say that instead.

The drift guard added with it needed two passes to be worth anything. Anchoring the arm on
the flag name alone matched the bitsandbytes block instead, whose own "4-bit QLoRA may be
unavailable" warning made both assertions pass on any wording; and the arm's explanatory
comment quotes the phrase it must not use, so comment lines have to go before the grep.
Restoring the old message now fails both checks.

* Let an explicit non-XPU pin migrate off an XPU wheel

Two halves of the same gap: asking for CUDA/ROCm/CPU on a host already running +xpu did
nothing.

setup.sh: the fast-path escape fired only when the pin itself was xpu, or when a stale
generic triton shadowed the build. With an up-to-date install, a +xpu wheel and the pin
switched to another family, neither arm matched, install_python_stack never ran, and the
authoritative pin was ignored. Added an arm for that case, digit-gated like the shared
classifiers so a custom verbatim leaf (rocm-current, cu-private) stays UNKNOWN and does not
force a pass that repairs nothing.

install_python_stack: _ensure_cpu_torch classifies the installed build and returns early on
"already a CPU build". Its probe tested hip, rocm, cuda and +cu<digits>; an XPU wheel sets
neither torch.version.cuda nor .hip, so it read as CPU and an explicit CPU pin over it did
nothing at all. Keyed on the +xpu local label, since torch.version.xpu is None on some
builds. Additive: +cu128 and +rocm still read gpu, +cpu and untagged still read cpu.

The escape suite's extractor stopped after the second _SKIP_PYTHON_DEPS assignment, so
adding a third arm truncated the block and the new cases failed while the old ones passed.
It now stops at the next outer arm and asserts exactly three arms extract, so a future arm
fails loudly instead of disappearing.

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

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

* Stop a wedged Intel driver from blocking the paths that repair it

Both of these are fallout from making the CPU repair XPU-aware: it now has to classify an
XPU wheel, and every route to that classification went through `import torch`, which loads
the SYCL runtime and blocks on the exact host these paths exist to rescue.

install_python_stack: the classifier probe times out after 90s and the except branch
returned, so an explicit CPU pin over a wedged +xpu venv stayed a no-op. Classify off disk
on timeout via _installed_torch_label_on_disk (find_spec, no interpreter) and fall through
to the repair. Gated on a GPU label so a slow but healthy CPU-only host does not
force-reinstall torch every update.

install.sh: the rollback preservation probe read torch.__version__ through the interpreter
at venv-replacement time, ahead of every bounded probe in setup.sh, so a hang there took the
whole installer with it. It now reads torch/version.py, the same source
_installed_torch_version_for_tag already uses for this reason. The interpreter stays as the
fallback for a layout without one, where torch is absent and the import fails fast.

The install.sh test executes the block against a fake venv whose stub interpreter records
being called, so "read off disk" is proven by the interpreter never running rather than by
reading the source.

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

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

* Match torch index families exactly, and judge the XPU fast path on the wheel

studio/setup.sh classified a pin as a known non-XPU family with prefix globs
(cu[0-9]*, rocm[0-9]*), so cu128-private, cu128rc1, cu128.1, rocm7.2-private,
rocm7. and rocm7.2.1 all read as known while install_python_stack calls every
one of them UNKNOWN and runs no repair: the fast path was cleared and the
dependency pass that followed applied nothing, every update. It now matches
exact families like install.sh _is_pip_rocm_family_leaf and
install_python_stack _is_cuda_family_leaf: cpu, cu<digits>,
rocm<digits>[.<digits>], gfx<digit>... (gfx stays a prefix on all three sides,
since gfx120x-all is a real Radeon index leaf).

studio/setup.ps1 keyed the same escape on torch.xpu.is_available(), which is
also false for a supported +xpu wheel on an old or wedged compute driver. No
dependency pass can repair a driver, and the pass force-reinstalls nothing when
the flavour already matches, so each studio update repeated the bounded probes
and a full resolution just to reach the warning Assert-XpuRuntimeReady already
prints. The escape now asks Test-VenvTorchIsXpuSupported, which reads
torch/version.py off disk and applies the same 2.6 <= v < 2.11 window, matching
what setup.sh does on POSIX and removing the last import torch from a path an
Arc host with a stalled driver is most likely to hit. Its only caller gone,
Test-TorchXpuVersionSupported is removed.

Tests: the escape test now also asks install_python_stack itself about a
28-leaf corpus and asserts the shell predicate agrees leaf for leaf, so the two
cannot drift again (75 checks; 11 fail against the previous globs). The
pre-report test covers the new helper and asserts the fast-path escape names no
readiness probe and launches no interpreter.

* Trim comments across the Intel XPU detection changes

* Normalise setup.ps1 line endings before the wiring regexes

A Windows checkout returns CRLF, so the fast-path escape pattern, which is
anchored on a literal \n, matched nothing on windows-latest: the region came
back empty, "the escape was found" failed, and the two -not checks inside it
reported PASS with nothing to look at. Cross-platform parity caught it on
windows-latest with 3 failures.

$setupText is now normalised to LF once at the read, which covers both literal
newline patterns in the file, and a new check asserts the raw CRLF form does
NOT match the same pattern, so it is the normalisation rather than luck that
makes this work. Verified against a CRLF copy of setup.ps1: the previous test
fails there with exactly those 3 checks and the new one passes.

* Run the Triton swap after every torch migration, not between two of them

_ensure_xpu_triton keys off the installed +xpu label when no explicit XPU pin
is set, and it ran ahead of _ensure_cpu_torch. So an existing +xpu venv updated
with an explicit CPU pin had generic triton removed and XPU triton installed,
and only then did _ensure_cpu_torch replace torch with the CPU build: a CPU
environment whose top-level triton package is the XPU implementation, with the
generic triton its own dependency set declares now gone.

The CUDA and ROCm repairs already ran ahead of the swap, so their pins left the
label correct by the time it read it; CPU was the one migration that did not.
Moving the swap to the end of both repair blocks fixes it for every family at
once and removes the ordering assumption entirely.

The new test asserts the order on the AST at both call sites, so a reflow
cannot fake it; against the previous order it fails on the first assertion.

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

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

* Bound the wedged-driver probe without GNU timeout

macOS ships no GNU timeout (Homebrew coreutils installs it as gtimeout), so on
the macOS parity leg the `timeout 30 python3 ...` line exited 127 the instant it
was called. The test reads only the exit code, and 127 is non-zero with an
elapsed time of 0, so both assertions passed without python ever starting: the
alarm behaviour they exist to prove was never exercised on macOS.

Replaced with the script's own background watchdog, which behaves the same on
every platform, and added a lower bound on the elapsed time. The alarm is 2s, so
a run that returns instantly did not execute the probe, which is precisely how
the missing-timeout case looked.

Verified by shimming `timeout` to exit 127: the previous test still reports 37
passed, and by shimming python3 to return instantly: the previous test still
reports 37 passed while this one fails on the deadline check.

* Trim comments in the Intel XPU detection changes

---------

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Co-authored-by: danielhanchen <danielhanchen@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: danielhanchen <unslothshared@gmail.com>
2026-08-04 03:03:10 -07:00
..
__init__.py Consolidate dual venvs and separate install from update (#4530) 2026-03-25 05:24:21 -07:00
conftest.py Formatting: ruff line-length 100, kwarg-spacing passes, drop blank after short local imports (#6079) 2026-06-08 04:24:13 -07:00
test_bitsandbytes_kernel_readiness.py Anchor the bnb bind assertion on the symbol, not the module alias (#7590) 2026-07-28 21:35:04 -07:00
test_change_system_message.py Escape the system message spliced into predefined chat templates (#7746) 2026-08-02 07:18:09 -07:00
test_conftest_bitsandbytes_preimport.py Tests: import bitsandbytes before the GPU-free harness spoofs CUDA (#7582) 2026-07-28 18:52:25 -07:00
test_construct_chat_template_validation.py Escape caller template text spliced into Jinja string literals (#7731) 2026-08-02 05:32:25 -07:00
test_cpo_processor_text_tokenizer.py tests: read checked-in files as UTF-8 instead of the platform default (#7438) 2026-07-26 23:31:56 -07:00
test_cross_platform_parity.py Windows: unblock the consumer install on clean and no-winget machines (#7549) 2026-07-28 22:24:40 -07:00
test_dpo_vision_processor_passthrough.py tests: read checked-in files as UTF-8 instead of the platform default (#7438) 2026-07-26 23:31:56 -07:00
test_e2e_no_torch_sandbox.py Fix Windows no-torch setup (#7511) 2026-07-28 05:54:25 -07:00
test_fast_language_model_text_only.py tests: read checked-in files as UTF-8 instead of the platform default (#7438) 2026-07-26 23:31:56 -07:00
test_fast_model_config_passthrough.py tests: read checked-in files as UTF-8 instead of the platform default (#7438) 2026-07-26 23:31:56 -07:00
test_fast_sentence_transformer_embedding_parity.py Fix FastSentenceTransformer Qwen embedding preprocessing (#6939) 2026-07-09 01:46:22 -07:00
test_fast_sentence_transformer_redirect_lifecycle.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_flash_attn_install_python_stack.py Studio: fix flash-attn and torchao install on Blackwell (sm_100+) GPUs (Closes #6961) (#6970) 2026-07-08 06:38:10 -07:00
test_get_chat_template_escaping.py Escape the system message spliced into predefined chat templates (#7746) 2026-08-02 07:18:09 -07:00
test_get_lora_parameters_bias_fp8_block_size.py Propagate fp8 block_size before the early return in get_lora_parameters_bias (#7189) 2026-07-17 16:30:45 -07:00
test_get_lora_parameters_fp8_block_size.py Propagate fp8 block_size before the early return in get_lora_parameters_bias (#7189) 2026-07-17 16:30:45 -07:00
test_gpu_init_ldconfig_guard.py tests: read checked-in files as UTF-8 instead of the platform default (#7438) 2026-07-26 23:31:56 -07:00
test_grpo_ddp_model_config.py tests: read checked-in files as UTF-8 instead of the platform default (#7438) 2026-07-26 23:31:56 -07:00
test_import_without_bitsandbytes.py Keep import unsloth working when bitsandbytes is absent (#7502) 2026-07-28 08:03:54 -07:00
test_install_python_stack.py install: let UNSLOTH_TORCH_INDEX_FAMILY / _URL override CUDA wheel detection (#6692) 2026-07-20 00:58:52 -07:00
test_install_uv_override_space.py Installer: make UV_OVERRIDE space-safe on Apple Silicon (#6503) (#6639) 2026-06-24 17:34:18 -07:00
test_mlx_public_trainer_api.py feat(mlx): route trainer callbacks (#6929) 2026-07-08 03:25:50 -07:00
test_no_torch_filtering.py Fix Windows no-torch setup (#7511) 2026-07-28 05:54:25 -07:00
test_orpo_processor_text_tokenizer.py tests: read checked-in files as UTF-8 instead of the platform default (#7438) 2026-07-26 23:31:56 -07:00
test_pad_token_fix.py tests: read checked-in files as UTF-8 instead of the platform default (#7438) 2026-07-26 23:31:56 -07:00
test_patch_trl_rl_trainers_defensive.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_remove_special_tokens_no_bos.py fix: guard remove_special_tokens against tokenizers without a BOS token (#7048) 2026-07-10 14:55:11 -03:00
test_revision_forwarding.py Forward revision to the config, weight and tokenizer loads (#4222) 2026-08-02 22:35:19 -07:00
test_studio_import_no_torch.py tests: read checked-in files as UTF-8 instead of the platform default (#7438) 2026-07-26 23:31:56 -07:00
test_to_sharegpt_optional_none.py Fix to_sharegpt optional block rendering "None" for missing extra columns (#6827) 2026-07-08 17:57:41 -03:00
test_tokenizers_and_torch_constraint.py Add Intel Arc GPU detection and XPU PyTorch install to Windows installer (#7706) 2026-08-04 03:03:10 -07:00
test_torchcodec_torch_compat.py Gate the torchcodec audio extras to platforms that have a wheel (#7587) 2026-07-28 20:56:11 -07:00
test_unsloth_run_tool_policy_resolver.py Keep server-side tools enabled under --secure (#6403) 2026-06-18 05:52:40 -07:00
test_v100_fullft_precision.py Export ACCELERATE_MIXED_PRECISION when bf16/fp16 is set explicitly (fixes #4891) (#7534) 2026-07-29 06:39:18 -07:00
test_vision_lora_targeting.py tests: read checked-in files as UTF-8 instead of the platform default (#7438) 2026-07-26 23:31:56 -07:00
test_windows_arm64_python_choice.py Windows: unblock the consumer install on clean and no-winget machines (#7549) 2026-07-28 22:24:40 -07:00
test_windows_git_gate.py Windows: do not abort setup on an unreadable llama.cpp install (#7735) 2026-08-02 07:25:39 -07:00
test_windows_no_torch_setup.py Fix Windows no-torch setup (#7511) 2026-07-28 05:54:25 -07:00
test_windows_python_venv_hardening.py Windows: validate managed Python before package installation (#7763) 2026-08-03 05:08:47 -07:00
test_windows_vcredist_download_tls.py Windows: unblock the consumer install on clean and no-winget machines (#7549) 2026-07-28 22:24:40 -07:00