5,682 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
cc1a724efc |
Source llama.cpp prebuilts from unslothai/llama.cpp (CUDA, ROCm, macOS) (#5963)
* Studio: route arm64 Linux CUDA hosts to linux-arm64-cuda prebuilts
* Studio: SM-aware selection for windows-cuda app bundles
* Studio: select published ROCm bundles by gfx target (linux + windows)
* Studio: route macOS installs to the fork's prebuilt bundles
* Studio: fix windows cuda13 driver-13.0 gate and ROCm gfx prefix overreach
* Fix Blackwell Windows pin shadowing native app-bundle (b9360 over b9457)
* Match Windows cuda12 driver floor to Linux (12.x minor-version compat)
* Fix Windows app-bundle dropped when runtime DLLs come from torch/lib
* Fold the manifest resolver into the simple-path resolver (one entry, no dormant full path)
* Remove unused UNSLOTH_LLAMA_PUBLISHED_REPO override
* Route Windows GPU hosts to the fork prebuilts in setup.ps1
* Document sm_103 path divergence and mark --simple-policy as a no-op
* Note sm_103 coverage now comes from the producer manifest
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Remove the now-vestigial --simple-policy flag (one resolver handles all hosts)
* Unify the fork onto the manifest path; drop the linux-x64 filename path and hardcoded coverage tables
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Strip whitespace from manifest gfx_target/mapped_targets when parsing
* Windows CUDA: sort coverage-unknown bundles last so they can't outrank targeted ones
* Share the SM-coverage sort key between the linux and windows selectors via _sm_range
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Studio: reject approved releases with an exact source archive but no source repo to clone from
* Studio: accept the fork's windows-rocm kind in the Windows reinstall check
* Studio: accept a manifest-bundle source repo in the exact-source release check
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Studio: route Linux hosts to the fork only when a usable GPU is present
* Fix Windows AMD lemonade tag resolution for PR #5963
The fork release scan passes each scanned release's upstream tag
(b9518, ...) to the lemonade lookup, but lemonade publishes its own tag
series (b1292, ...) that never contains upstream tag numbers. On a
Windows AMD host every scanned release therefore 404s the lemonade
fetch twice, the upstream HIP zip is dropped by the approved-hash gate,
and the scan walks the whole release history until it dies on the
unauthenticated GitHub rate limit or falls to a HIP source build. The
Linux path already passes the requested tag ("latest") and works.
Thread the requested tag through resolve_release_asset_choice ->
resolve_asset_choice -> resolve_upstream_asset_choice as lemonade_tag,
used only by the lemonade lookups. Upstream asset names keep the
concrete per-release tag and all new parameters default to the old
behavior.
Verified on a gfx1151 box: before, the native Windows install scanned
b9518..b8811 and aborted on rate limit; after, it selects
llama-b1292-windows-rocm-gfx1151-x64.zip (lemonade) from fork release
b9518, passes staged validation, and the installed llama-server
enumerates ROCm0. WSL keeps selecting the matching ubuntu bundle.
Adds a regression test pinning that the Windows fork path resolves
lemonade via /releases/latest, never /releases/tags/<fork-tag>.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Plan lemonade for Linux ROCm hosts on the ggml-org direct path for PR #5963
Audit follow-up to
|
||
|
|
8bca7bcfc9 |
Studio: accept audio files through Add photos & files and fix the audio gate for Gemma 4 models (#6064)
* Studio: sync detected model capabilities into models[] after load The chat composer gates audio upload on activeModel.hasAudioInput, but /api/models/list omits audio fields for default and active-GGUF entries and the single chat load path never wrote the load response's capability flags back into the store. Audio-capable models such as the Gemma 4 GGUFs therefore never unlocked audio input in the main chat, while the compare composer (which does sync) worked. Add syncModelCapabilities and call it after a successful load and after the status fetch in refresh, so the flags also survive F5 and are not clobbered by stale catalog data. * Studio: merge audio upload into the Add photos & files picker Remove the separate Upload audio row from the composer plus menu and register an AudioAttachmentAdapter in the shared attachment pipeline, so the standard picker and drag-drop accept wav, mp3, m4a, ogg, flac and webm directly. Gating matches images: the picker always lists audio and models without audio input get a toast at add() time. The 50MB limit is kept and the file shows as a normal attachment chip. On send the adapter emits an audio content part on the attachment and findLatestUserAudioBase64 now also scans attachment content, so the request still carries audio_base64 exactly as before. * Studio: extract AudioAttachmentAdapter into its own module Move the adapter out of runtime-provider.tsx so it is importable in isolation, export the audio send-path and capability-sync helpers for tests, and guard attachment id generation for non-secure contexts (crypto.randomUUID is undefined over plain HTTP on a LAN, matching the existing guard in startCompare). * Studio: do not claim .webm by extension in the audio adapter A video/webm file would match the .webm extension entry and route to the audio adapter. Real audio webm (MediaRecorder output) always reports the audio/webm MIME, so matching webm by MIME only keeps video files out while keeping recorded audio working. * Studio: only send audio from the newest user message audio_base64 switches the backend onto the audio generation path (generate_whisper_response ignores chat messages entirely and generate_audio_input_response bypasses the normal streaming path), so replaying audio from an older turn hijacked text-only follow-ups: Whisper would retranscribe the stale clip instead of erroring cleanly, and audio VLMs lost tools and streaming. Stop the scan at the newest user message, matching the consumed-on-send semantics of the legacy pendingAudio path. Regenerating the audio turn itself still resends its audio since it is the newest user message in that run. Also guard extractAudioPartBase64 against null parts in deserialized history content. * Studio: forward audio input to llama-server for GGUF models (#6096) * Studio: forward audio input to llama-server for GGUF models * Studio: harden GGUF audio input handling (multi-format decode, size cap) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: carry GGUF audio in the message list so it works with tools * Studio: bound decoded audio length and make the soundfile decoder optional --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Handle audio attachment edge cases * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: gate audio file picker by loaded model capability (#6142) * Gate audio attachments by loaded model * Use conditional spread for audio attachment adapter * Preserve audio fallback while filtering picker --------- Co-authored-by: Unsloth <michaelhan@Michaels-MacBook-Pro.local> Co-authored-by: oobabooga <oobabooga4@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: imagineer99 <samleejackson0@gmail.com> Co-authored-by: Lee Jackson <130007945+Imagineer99@users.noreply.github.com> |
||
|
|
2b319e8d3a |
Studio: support separate-file MTP GGUF drafters (Gemma 4) (#6125)
* Studio: support separate-file MTP GGUF drafters (Gemma 4) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: fix review findings for separate-file MTP drafters * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: pair local MTP drafters by name and include them in reload dedup * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: manage --model-draft in extras and reject MTP/ copies as models * [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> |
||
|
|
c86872c7e9 | Merge #6156: never plan a non-sm_120 CUDA build on Blackwell, never plan CPU on an NVIDIA host | ||
|
|
0f00bc1e2a |
Studio: fix Gemma-4-12B-it not loading (#6054)
* Fix Studio Python, Gemma 4 Unified sidecar, and worker crash messages * Clean up Gemma 4 sidecar test patch contexts * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Polish inference worker crash message * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Address transformers tier review feedback * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Route Gemma 4 assistant models to transformers 5.10 * [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> |
||
|
|
1d9090ff6b |
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci |
||
|
|
1d7f9b56f4 |
Installer: never plan a non-sm_120 CUDA build on Blackwell, never plan CPU on an NVIDIA host
Two hardening fixes from the fleet-validation audit. Blackwell Windows hosts drop windows-cuda attempts that cannot offload sm_120 instead of leaving them ranked behind the b9360 pin. A cuda-12.4 upstream build loads and passes the functional validator but runs the model on a slow non-native path (an RTX 5090 measured 7.1 tok/s vs 551.2 on cuda-13.3), so one failed pin download away from that is too close. The coverage check now also reads manifest SM metadata first, so published cuda12 app bundles (toolkit 12.8, sm_120 included) stay selectable and make the pin go dormant correctly. The fork-release Linux planner no longer appends the linux-cpu bundle for NVIDIA hosts whose CUDA selection produced nothing; it raises so the caller walks back to an older release with a usable CUDA line, mirroring the deliberate ROCm policy. Today's walk-back only works because partial releases ship no CPU bundle; this keeps it working if a future partial release does. |
||
|
|
6e057ffebe |
Studio: training survives a non-writable HF datasets cache (#6148)
* Studio: training survives a non-writable HF datasets cache A shared HF datasets cache can contain subtrees owned by another user (for example populated by an earlier root-run job). datasets then dies with "[Errno 13] Permission denied: ..._builder.lock" while locking the cached builder and the training run fails. load_dataset in the training worker and trainer now goes through a wrapper that catches the EACCES and rebuilds the dataset in a Studio-owned cache under cache_root()/hf-datasets, logging the fallback. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Scope the HF_DATASETS_CACHE override to the fallback load * Route non-streaming dataset preview loads through the cache-safe wrapper --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> |
||
|
|
18d851bfeb |
Studio: mascot images degrade gracefully instead of showing alt text (#6146)
* Studio: mascot images degrade gracefully instead of showing alt text Mascots loaded from raw public-folder URLs with no error handling, so a missing or unreachable file (version skew during updates, subpath mounts, transient blips) painted the alt text, like the empty mascot on the training start overlay. A shared MascotImg now resolves against BASE_URL, retries once with a cache-buster, then swaps to a 4 KB fallback sloth bundled as a data URI that cannot 404. Applied to the chat greeting, 404 page, training start overlay, auth form, onboarding splash and wizard. * Reset mascot retry state via key remount instead of render-time tracking * Use MascotImg for the artifact generating panel sloth |
||
|
|
8129994061 |
Call patch_compiling_bitsandbytes on the FastLanguageModel path (#6144)
* Call patch_compiling_bitsandbytes on the FastLanguageModel dispatch path * Guard the FastLanguageModel patch call so an old unsloth_zoo cannot crash loads |
||
|
|
d75e765a6b |
Auto-set BNB_ROCM_VERSION from the installed wheel on Windows + ROCm (#5986)
* Auto-set BNB_ROCM_VERSION from the installed wheel on Windows + ROCm bitsandbytes derives its ROCm backend DLL name from `torch.version.hip`. AMD's Windows bitsandbytes prerelease wheel ships a single `libbitsandbytes_rocm<NN>.dll` whose suffix does not always match the torch HIP version: e.g. `torch==2.11.0+rocm7.13.0` reports HIP 7.13, so bitsandbytes looks for `libbitsandbytes_rocm713.dll`, but the wheel only ships `libbitsandbytes_rocm72.dll`. The names disagree, the native library fails to load, and every 4-bit / 8-bit path breaks for users running `import unsloth` directly (Unsloth Studio already works around this in its worker). Detect the suffix from the actually-installed wheel and pin BNB_ROCM_VERSION before bitsandbytes is first imported (unsloth_zoo.device_type imports it during `from .models import *`), so the correct backend loads. This is precisely the override bitsandbytes itself recommends when the build/runtime ROCm versions differ. Strict no-op unless ALL of: running on Windows, a ROCm torch build, the var is unset, and a `libbitsandbytes_rocm*.dll` is actually installed. Linux ROCm is untouched (its multi-backend bitsandbytes resolves the backend correctly from torch.version.hip). Honors a user-provided BNB_ROCM_VERSION and an explicit opt-out (UNSLOTH_SKIP_BNB_ROCM_VERSION=1). Verified on an AMD Radeon 8060S (gfx1151, Strix Halo) Windows 11 + ROCm box: `import unsloth` now auto-sets BNB_ROCM_VERSION=72 and a native 4-bit quantize/dequantize roundtrip succeeds with the var unset; previously it failed to load `libbitsandbytes_rocm713.dll`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Gate BNB_ROCM_VERSION on the actual torch build, not runtime hints _is_rocm_torch_build() falls back to environment and filesystem hints (HIP_PATH, ROCM_PATH, ...) that are routinely present on Windows boxes with the AMD HIP SDK installed but a CUDA or CPU torch. If such a box also has a bitsandbytes wheel that ships a rocm DLL (AMD's Windows prerelease wheel ships rocm72 alongside all the cuda DLLs), setting BNB_ROCM_VERSION makes bitsandbytes raise at import on its CUDA build and `import unsloth` breaks. Add _is_hip_torch_build(): wheel version tag first (no torch import), then torch.version.hip for untagged custom/source HIP builds, and use it as the gate. The broader hint-based helper keeps its other callers. Verified on a gfx1151 Windows box: True on the ROCm venv (2.11.0+rocm7.13.0), False on a torch-less interpreter; 4 new unit tests including the HIP-SDK-on-CUDA-box false-positive regression. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Relocate wiring so this PR composes with the bnb arch-detection PR Both this PR and the fix_bitsandbytes_rocm_arch_detection PR anchored their _gpu_init.py wiring and import_fixes.py additions on the same configure_amdgpu_asic_id_table_path lines, so whichever merged second hit a textual conflict in both files (verified by merging both onto main in each order). Move maybe_set_windows_rocm_bnb_version's wiring to a self-contained block after the import-order warning (still before `import unsloth_zoo`, which is what pulls in bitsandbytes on ROCm) and append the helpers at the end of import_fixes.py. The hunks no longer overlap, so the two PRs merge cleanly in either order. No behavior change: the env var only needs to be set before bitsandbytes is first imported, and it still is. * Redetect sitecustomize-seeded BNB_ROCM_VERSION for PR #5986 After #6048, every Studio venv process starts with BNB_ROCM_VERSION seeded by the managed sitecustomize.py block, which made this gate a no-op inside Studio venvs and blind to wheel updates. Treat values marked UNSLOTH_BNB_ROCM_VERSION_SOURCE=sitecustomize as redetectable defaults, stamp redetected values as detected, and keep the seeded value when no DLL is found. Explicit caller values still win and the opt-out is unchanged. Also merges latest main. * Make BNB_ROCM_VERSION opt-out drop the sitecustomize-seeded default for PR #5986 UNSLOTH_SKIP_BNB_ROCM_VERSION=1 previously no-opped the helper but left a sitecustomize-seeded BNB_ROCM_VERSION in the environment, so bitsandbytes still consumed the override the user disabled. The opt-out now removes values carrying the sitecustomize source marker; explicit user values have no marker and are untouched. Adds tests for the opt-out paths and the empty-string edge. * Tighten comments and docstrings for PR #5986 Comment-only pass: shorten verbose docstrings on the internal helpers, collapse multi-line inline comments, and drop wording that restates the code. Verified code-identical via the comment_tools.py AST signature check (3/3 files unchanged). --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> |
||
|
|
265c9f5db4 |
Fix UnboundLocalError in ROCm version detection dpkg/rpm fallback (#6149)
* Fix UnboundLocalError in _detect_rocm_version dpkg/rpm fallback A leftover local import re inside the amd-smi branch made re function local for the whole scope. When amd-smi and hipconfig are absent and dpkg-query or rpm reports rocm-core, the epoch strip at the dpkg/rpm fallback hit re.sub before any local binding existed and crashed the installer with UnboundLocalError. Drop the local import (the module already imports re at top level) and add a regression test covering the dpkg path without hipconfig. * [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> |
||
|
|
2599f9fe9d |
Tests: follow Compare chat into the More submenu in the Playwright chat UI driver (#6153)
* tests: follow Compare chat into the More submenu in the chat UI driver The plus-menu declutter moved the Compare chat item into a More submenu (DropdownMenuSub in thread.tsx / shared-composer.tsx), so the Playwright driver stopped finding it at the top level and Chat UI Tests went red on every PR regardless of its diff. Open the More sub-trigger (hover, then click as a fallback) before asserting the item; the direct lookup stays first so the driver still passes if the item moves back. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: danielhanchen <michaelhan2050@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> |
||
|
|
184141db99 |
Tests + CI guard: batched left-padded generation can never silently regress again (#1066, #3699) (#6145)
* Add regression guard for batched left-padded generation (#1066, #3699) Three layers of tests plus a path-filtered CI workflow so the left-padding position_ids / attention-mask bug class cannot silently return: - tests/utils/test_prepare_inputs_ast_guard.py: import-free AST checks on _fast_prepare_inputs_for_generation (cumsum-from-mask branch present, cache_position only as fallback, no mask truncation, model families wired) - tests/utils/test_prepare_inputs_leftpad.py: CPU behavioral unit test with synthetic left-padded masks and fake caches; exact expected position_ids for prefill and cached decode - tests/utils/test_batched_leftpad_generation_gpu.py: optional GPU e2e, solo vs batched prefix match, skipped without CUDA - .github/workflows/batch-inference-guard.yml: ubuntu-latest CPU job running the two deterministic layers on PRs touching unsloth/models/** Validated: all pass on main; both CPU layers fail at 6d0f8643~1 (pre #4100) and at 332eabf3~1 (pre #2216), reproducing the historical bug signatures. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cite staging proof in batch-inference-guard header (staging-2 PRs 170/171) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fold left-padding guard into consolidated Core CI; merge AST + behavioral tests No new workflow and no new CI job: the guard now runs as one HARD GATE step inside consolidated-tests-ci.yml, right after the callback signature drift detector, where the CPU torch stack is already installed. The AST structural checks and the behavioral unit tests live in a single file (tests/utils/test_prepare_inputs_leftpad.py); the AST layer stays stdlib-only with unsloth imported lazily inside the behavioral tests, so import breakage cannot mask the structural checks. Revalidated after the merge: 11 assertions pass on main, 8 fail at 6d0f8643~1 (pre #4100). * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update staging proof reference for consolidated gate (PRs 170/172) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> |
||
|
|
bc8b9dc917 |
fix(studio): forward mlx custom format mapping (#6094) | ||
|
|
154457dbf7 |
fix(studio): reject unsupported mlx training modes (#6091) | ||
|
|
8e2a983f58 |
Fix Studio MLX VLM resized image layout (#6019)
* fix(studio): infer mlx vlm resized image layout * [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> |
||
|
|
81152a079a |
Run the HF cache redirect before import fixes that can freeze Hub constants (#6150)
* Run the HF cache redirect before import fixes that can freeze Hub constants unsloth-zoo's redirect_hf_cache_if_readonly() repoints HF_HOME, HF_HUB_CACHE, and HF_XET_CACHE at import time when the default cache is read-only. In _gpu_init.py the early fix block can import vllm (disable_broken_vllm), transformers (check_fbgemm_gpu_version), or huggingface_hub (fix_huggingface_hub) before import unsloth_zoo runs the redirect. huggingface_hub computes its cache constants when its constants module is first executed, so those probes can freeze the old read-only paths and the redirect then never reaches Hub. Recent huggingface_hub and vllm releases import lazily, which masks this, but older supported versions import eagerly: older vllm pulls in transformers at top level, and on hub versions where is_offline_mode still exists the hasattr probe materializes the constants module. Load unsloth_zoo/hf_cache.py straight from its file (it is stdlib-only) and run the redirect before the fix calls. The zoo's own call later is an idempotent no-op, and an older unsloth_zoo without hf_cache.py is skipped silently. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: danielhanchen <danielhanchen@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> |
||
|
|
fa01a02a6d |
Studio UI polish: search dialog shadow, model picker pills, sidebar spacing, white Hub background (#6147)
* Studio UI polish: search dialog shadow, picker pills, sidebar spacing, white Hub - Chat search dialog: keep a page-bg shadow in dark mode so the dialog does not merge into same-color content behind it - Model selector: hover and selected rows are fully rounded pills - Sidebar: nudge nav items, recents and section labels 2px right and widen the hover pill 2px left so its side gaps match - Hub: pure white page background in light mode and drop the ambient card glow that tinted the page gray * Lighten sidebar border, widen model picker rows - Sidebar border is #f2f2f2 in light mode - Model picker rows extend 4px further left and right, lining up with the search bar * Plus menu: restore the intended 18px corner radius The global 14px !important dropdown radius overrode the menu's own 18px, so the plus menu and its submenus rendered squarer than designed. Mark the menu radius !important so it wins. * Projects: borderless import/export button; picker search border #f2f2f2 - The import/export button on the Projects page drops its outline border and matches the Sort by pill next to it - The model picker search inputs use a #f2f2f2 border in light mode, dark mode keeps the default * Model selector: align the popup with the trigger label The popover lined up with the trigger button edge, 14px left of the label text. A 10px alignOffset starts it just before the label. * Model picker: shorten search placeholder to Search models * Pill-shaped model trigger and panel controls; chat template edit button - Select model trigger: rounded pill with slightly tighter horizontal padding - KV cache, speculative decoding, draft N and preset controls in the chat settings panel are rounded pills - Chat Template: replace the three-line preview box with an edit icon button next to the label; it opens the existing editor dialog * Training overlay: borderless console, sloth attached to the box - Drop the terminal border for the training start console - Remove the gap between the sloth image and the console so they touch * Settings panel: more left padding in pill controls, smaller template edit icon - f16, Auto and draft N pills get 12px left padding, widths bumped 4px to keep the values from truncating - Chat template edit icon shrinks to 14px and the Chat Template label also opens the editor * Model picker: more padding inside row pills, hugeicons delete icon - Row pills get 12px horizontal padding so text is not pressed against the highlight boundary - The cached model delete button uses the hugeicons Delete02 icon instead of the lucide trash icon * Address review: hub bg layering, dead shadow utilities, merged paddings - Remove bg-background from the Hub root: the white rule lives in the base layer and the utility was winning the cascade, so the white background never applied - Drop the inline shadow utilities on the chat search dialog: the chat-search-surface rules sit later in the utilities layer and already win, making the inline ones dead - Merge symmetric pl/pr pairs into px on the sidebar group wrappers |
||
|
|
f3001159f9 |
Frontend CI: hard-fail unreviewed npm install scripts (#6139)
Completes the allowScripts rollout: upgrade CI npm to the 11.x line (node 22 bundles 10.x, which predates the gate) with a loud version guard so the flag can never silently degrade into a warning, then run npm ci --strict-allow-scripts. A dependency that introduces install scripts not covered by the committed policy now fails the job with npm's approve-scripts/deny-scripts instructions; the pre-commit sync hook keeps existing pins fresh after bumps. |
||
|
|
7cbf428b0a |
Fix JSON-mode smoke on Linux: bump quant, keep the hard Paris assert (#6138)
At UD-IQ2_XXS the temp-0 greedy answer to the capital-of-France probe is hardware dependent: GitHub ubuntu runners deterministically answer city=France while other CPUs answer Paris, because the 2-bit argmax flips with the SIMD kernel path. Seeds do not rescue it: a staging-fork sweep on the affected runners measured 1/5 Paris at temp 0.7 and 1/5 at temp 1.0 (different winning seeds on different hardware), so a retry-across-seeds assert would still flake about a third of runs. The same sweep on UD-Q4_K_XL answered Paris 13/13 across temp 0, 0.7 and 1.0 with 5 seeds each, including 3x deterministic greedy. Bump the job's quant (roughly 570 MiB to 1.1 GiB, cache key already includes the variant) and leave the assertion exactly as it was. |
||
|
|
8af9fe63a3 |
fix: persist Windows ROCm BNB version (#6048)
* fix: persist Windows ROCm BNB version * style: apply kwarg spacing hook * fix: avoid persisting caller ROCm overrides * fix: redetect managed BNB ROCm defaults * style: apply ROCm guard test formatting --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com> |
||
|
|
656a087593 |
Restore config use_cache in for_inference after gradient checkpointing prep (#6137)
* Restore config use_cache in for_inference after gradient checkpointing prep unsloth_zoo's prepare_model_for_training now sets use_cache=False on the model config and nested sub-configs when gradient checkpointing is on (unsloth-zoo PR 715) and records the original values. Wire the counterpart into both for_inference implementations so the original values come back for inference, and re-disable in for_training when a record exists so resumed training keeps the config consistent. Both calls import lazily and tolerate older unsloth_zoo without the helpers, so version skew in either direction is a no-op. The MLX shims in __init__.py are deliberately untouched. * [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> |
||
|
|
756e129388 |
fix/validate dataset video paths before training (#5136)
* fix(vision): validate dataset video paths before training * fix(vision): remove redundant warnings import, add pytest tests for #5085 * fix(trainer): auto-validate video paths in UnslothVisionDataCollator on first batch (#5085) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactor(vision): use str.removeprefix instead of slicing (Datta0 review) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * style: replace em dashes with hyphens in error message and docstring * Update unsloth/models/vision.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Split: keep only 1 file(s) * fix(vision): broaden video-path validation to all collator inputs - check_dataset_for_missing_videos now accepts every example shape that UnslothVisionDataCollator forwards to process_vision_info: dict rows with "messages"/"conversations"/"prompt"/"completion", and raw message-list rows. Earlier logic only handled {"messages": [...]}, so conversations/prompt/completion datasets silently skipped validation and raw message-list rows crashed on list.get. - Guard against non-dict message entries; a bare string inside a message list no longer raises AttributeError. - Decode file:// URIs via urllib so percent-encoded paths, absolute Windows URIs (file:///C:/...) and host-qualified URIs (file://localhost/abs/path) map back to their real filesystem path. - Expose an optional "checked" set so callers can reuse dedup state across invocations. - Docstring warns that passing a streaming IterableDataset consumes the iterator. UnslothVisionDataCollator now validates every batch (not just batch 0) and applies formatting_func before validation, matching the base collator's own ordering so formatter-generated video paths are also checked. The already-checked set is shared across batches, so per-batch cost stays proportional to newly seen paths. * fix(vision): robust URI + scheme handling in video-path validator - _local_path_from_video_value now treats anything with a "://" prefix as a URI and validates only file:// URIs. This prevents false FileNotFoundError on remote schemes that were silently passed through before (s3://, gs://, hf://, ftp://, az://, ...). - Non-localhost file authorities (e.g. file://nas-server/share/clip.mp4) are now skipped instead of being stripped and validated against the local filesystem; RFC 8089 only permits empty host or "localhost" for local files. - Drop the explicit unquote call: urllib.request.url2pathname already unquotes, so the previous url2pathname(unquote(path)) double-decoded any filename with a literal percent (e.g. a file named "clip%20.mp4"). - Remove the Windows drive-letter strip block; nturl2path.url2pathname handles "/C:/foo" -> "C:\\foo" itself, leaving nothing for the guard to match on either OS. - Return None when the resolved path is empty (bare "file://" or "file://hostname") so the caller skips it instead of reporting a blank " - " entry in the error message. - Add a runtime guard in check_dataset_for_missing_videos that warns and returns early when handed a datasets.IterableDataset, matching the docstring contract and preventing silent iterator exhaustion. Windows native paths like "C:/path/x.mp4" stay valid because the scheme check uses the "://" substring (not urlparse's single-letter scheme surface). * tests: consolidate video-path validation coverage into one file New coverage for tests/test_video_path_validation.py: - every-batch validation with cross-batch dedup (replaces the old first-batch-only assertion which no longer matches the implementation). - all collator-supported input shapes: messages, conversations, prompt/completion, raw-message-list rows; non-dict message entries. - file:// URI robustness: percent-encoded paths, localhost netloc, non-localhost netloc skipped, bare / hostname-only URIs skipped, double-encoded filenames single-unquote correctly. - non-file remote schemes (s3, gs, hf, ftp, az) skipped without raising. - Windows-style absolute path not mistaken for a URI scheme. - formatting_func applied before validation inside the collator wrapper. - IterableDataset runtime guard warns and returns without consuming. The pre-existing test_collator_validates_only_once assertion has been replaced by test_collator_validates_every_batch / dedupes_across_batches because the wrapper now validates every batch. The session-scoped AST fallback fixture was extended to extract the helper functions that the rewritten check_dataset_for_missing_videos depends on, so the Windows/no-triton code path still loads the module surface. * Fix CI iter 1 * Omit release-desktop.yml from the PR diff The workflow file landed on origin/main after the PR branched off; our fork-scoped push token cannot touch .github/workflows/**. Drop it from this branch so the PR diff stays within the author's authorisable surface. The file remains on origin/main and will return after this PR merges upstream. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(vision): cache only validated paths in checked set Missing paths were added to the dedup cache before the existence check, so a caller that caught FileNotFoundError and retried with the same collator/checked set would silently skip the bad path on the second call. Only add a path to checked after os.path.isfile confirms it exists, so missing paths are re-validated on every call until they are fixed. * fix(ci): resolve two CI failures introduced by this PR - Add __all__ to models/__init__.py so the HOISTED-IMPORT-UNUSED linter check passes for check_dataset_for_missing_videos - Replace Dataset.from_list() in test helpers with plain list literals; the CI environment mocks datasets with a MockDataset that only has from_dict, but check_dataset_for_missing_videos accepts any iterable so no Dataset wrapper is needed - Guard test_iterable_dataset_warns_and_skips with pytest.importorskip so it skips cleanly when the real datasets package is unavailable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(ci): correct HOISTED-IMPORT-UNUSED without breaking wildcard exports Adding __all__ to models/__init__.py was too aggressive - it restricted from .models import * in _gpu_init.py to only check_dataset_for_missing_videos, hiding FastLanguageModel, FastVisionModel etc and breaking test_fast_model_class_surface_under_spoof. Instead: remove __all__, and add an explicit named import in _gpu_init.py so the linter sees the symbol consumed in the re-export chain. * Deduplicate missing video paths and skip data URIs in validator check_dataset_for_missing_videos appended a path to the missing list on every occurrence, so a path referenced by multiple rows was reported N times and the error header read the wrong count. Track missing paths in a per-call set so each is listed once, kept separate from the checked cache so retries still re-check missing files. This restores the dedup behaviour the docstring promises and the existing test_duplicate_paths_deduplicated test asserts. Also skip data: URIs in _local_path_from_video_value so inline base64 payloads are not flagged as missing files. Add tests for the data URI case, warn-only dedup, and real integration against the unsloth_zoo UnslothVisionDataCollator base (verifying validation gates the base call, formatting_func is applied once and restored even when the base raises). * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(trainer): declare __slots__ on UnslothVisionDataCollator subclass * fix(ci): hoist check_dataset_for_missing_videos to trainer module level; guard IterableDataset skip * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(vision): accept tuple message content in video path validator * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Tighten comments and docstrings for PR #5136 Comment-only pass over the new video path validation code: shorten the collator and validator docstrings, collapse multi-line inline comments, and reduce test docstrings to one-liners. No code changes; verified with comment_tools.py check --strip-docstrings (3/3 code unchanged) and the full test suite (35 passed). * [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: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com> |
||
|
|
e4b5bec248 |
Studio chat: declutter the plus menu, session-only pre-selectable RAG (#6140) | ||
|
|
3cd0b9b18e |
Studio: fix nested dropdown submenus clipped by the menu alignment nudge (#6143)
* Studio: fix nested dropdown submenus clipped by the menu alignment nudge The 3px alignment nudge on DropdownMenuContent used translate, which makes the scroll container the containing block for nested position:fixed submenu wrappers, so overflow clipped every submenu (plus menu More and Projects, sidebar chat menus, Recents export). Use margin for the same visual shift without a containing block. * Portal dropdown submenus so ancestor styling can never clip them Rendered inline, the fixed submenu wrapper lives inside the parent menu's scroll container, so any transform there (like the 3px nudge) re-enables overflow clipping. Portaling to body removes the submenu from that subtree entirely, same as DropdownMenuContent. |
||
|
|
dfb49fd2cb |
Studio: bulk export and import in Settings Chat Data, MCP pill off switch (#6141)
* Studio: bulk export and import in Settings Chat Data, MCP pill off switch - Settings -> Chat -> Data gains Export Recents and Projects (Recents or Recents + Projects, Raw JSONL / CSV / ShareGPT, combined or per chat) and Import chats, reusing the sidebar Recents menu actions - Extract bulkExportConversationsByScope so the sidebar and Settings share one implementation; expose the export and import helpers via the chat feature index - MCP composer pill icon now swaps to an X on hover like Search, Code and RAG; clicking it turns MCP off without opening the server menu - en and zh-CN locale strings added (parity check passes) * Reveal the pill X on hover for off-switch icons regardless of active look The X was gated on data-active, so an MCP pill with no servers enabled (or a RAG pill without a model) closed on icon click but never showed the affordance. Off-switch pills only render while their feature is on, so hover now always reveals the X. * Replace the Recents hamburger menu with an Export all chats link to Settings Bulk export and import now live in Settings -> Chat -> Data, so the sidebar Recents header menu is gone. Each chat's Export submenu gains Export all chats, which opens Settings on the Chat tab. |
||
|
|
62191c4765 |
Windows/WSL installer: fix winget msstore cert failure, amd-smi DiskPart prompt, and enable AMD GPU (Strix Halo gfx1151) (#5940)
* Fix Windows installer winget msstore certificate failure
`winget install` was invoked without `--source winget`, so winget also
queried the msstore source. When msstore fails certificate pinning
(error 0x8a15005e, "The server certificate did not match any of the
expected values") winget aborts and demands `--source`, so the Python
(and uv) install fails even though the package exists in the winget
source.
- Pass `--source winget` to all winget install calls (Python x2, uv).
Both packages live in the winget source, so this is strictly correct
and skips the failing msstore round-trip entirely.
- Add a python.org fallback (Install-PythonFromPythonOrg) that downloads
the official installer and runs it silently per-user (no admin/UAC)
when winget is unavailable or fails for any reason. Mirrors the
existing uv -> astral.sh fallback so Python installs without manual
steps. Resolves the latest 3.13.x from python.org with a pinned
fallback, and selects the amd64/arm64/x86 installer per architecture.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Pin remaining setup.ps1 winget calls to --source winget
Two winget invocations in studio/setup.ps1 still queried all sources and
could hit the same msstore certificate-pinning failure (0x8a15005e) that
broke the Python install in install.ps1:
- `winget show Nvidia.CUDA --versions` (CUDA Toolkit version probe)
- `winget install ... ShiningLight.OpenSSL.Dev` (OpenSSL dev for llama-server)
Every other winget call in this file already passes `--source winget`
(Git, CMake, VS Build Tools, CUDA install, Node.js, and setup.ps1's own
Python 3.12 install), so these two were stragglers. Both packages live in
the winget source; pinning it makes setup robust to an unhealthy msstore
source, matching the rest of the file.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Stop amd-smi GPU probe from popping a DiskPart UAC prompt
On Windows, AMD GPU detection in install.ps1 and studio/setup.ps1 runs
`amd-smi list` / `static --asic` / `version`. amd-smi (shipped in
System32 by the Adrenalin driver) auto-elevates to read GPU/APU memory
details, surfacing a confusing DiskPart UAC prompt mid-install. The
Studio backend already documents and circuit-breaks on this in
studio/backend/utils/hardware/amd.py, but the installers did not.
Add an Invoke-AmdSmiNoElevate helper (both scripts) that runs amd-smi via
Start-Process under __COMPAT_LAYER=RunAsInvoker so it cannot auto-elevate
(no prompt), with a 30s timeout (matching amd.py) so a flaky amd-smi
cannot stall the install for minutes. On failure/timeout the existing WMI
name -> gfx fallback still resolves the arch, so detection is unchanged on
working hosts.
Verified on a Strix Halo (Radeon 8060S / gfx1151) box: the prompt is gone
and the probe is bounded.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Add experimental ROCm-on-WSL setup helper for Strix Halo (gfx1151)
install.sh already routes gfx1151 (Radeon 8060S / Strix Halo) to the
repo.amd.com/rocm/whl/gfx1151 wheels once a ROCm runtime is present, but
it does not install AMD's driver/ROCm stack -- a large, admin-gated
prerequisite. scripts/install_rocm_wsl_strixhalo.sh automates the Linux
side on a dedicated Ubuntu 24.04 WSL2 distro: ROCm 7.2 (wsl usecase), the
rocr4wsl HSA runtime, a librocdxg build, env setup, and a PyTorch gfx1151
GPU smoke test. A hard preflight refuses to run until the Adrenalin
>=26.3.1 driver is actually present, so it cannot half-install.
Procedure adapted from AMD's ROCm-on-WSL docs and community gfx1151 notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Detect AMD GPUs by name so native Windows gets a GPU llama.cpp
The gfx-arch inference from the WMI GPU name was gated behind $HasROCm,
which the hipinfo/amd-smi probe leaves false on the common Windows case
(Adrenalin driver only, no HIP SDK -- and amd-smi often cannot read the
arch without elevation). So an AMD GPU was detected by name but never
mapped to a gfx target, --rocm-gfx was not forwarded, and studio setup
fell back to a CPU llama.cpp build.
Un-gate the inference (install.ps1 + studio/setup.ps1) so it runs whenever
an AMD GPU name is available. The inferred gfx is forwarded as --rocm-gfx,
which makes install_llama_prebuilt.py download the matching lemonade-sdk
ROCm prebuilt (e.g. llama-bNNNN-windows-rocm-gfx1151-x64.zip) -- a
GPU-accelerated llama.cpp that bundles its own ROCm runtime, so it runs
with just the Adrenalin driver. PyTorch's ROCm wheels still require a
confirmed HIP SDK ($HasROCm), so this only affects llama.cpp / inference
and never pulls broken ROCm torch.
Also broaden the name->arch table to every family lemonade ships Windows
assets for: gfx120X (RDNA 4), gfx110X (RDNA 3), gfx1151/gfx1150
(RDNA 3.5), and gfx103X (RDNA 2). Unknown names still fall back to CPU.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Suppress amd-smi DiskPart UAC prompt in the Python install/runtime paths
The earlier PowerShell guard covered install.ps1 / setup.ps1, but the
Python installer (install_llama_prebuilt.py detect_host,
install_python_stack.py ROCm probes) and the Studio backend monitor
(amd.py) also shell out to amd-smi on Windows, where it auto-elevates and
pops the same DiskPart UAC prompt mid-install / at runtime.
Inject __COMPAT_LAYER=RunAsInvoker into the amd-smi subprocess env on
Windows so it runs un-elevated (no prompt). Callers already tolerate an
empty/failed result and fall back to WMI / name detection (installer) or
the existing circuit breaker (amd.py). Gated to Windows so Linux/macOS
amd-smi behaviour is unchanged.
- install_llama_prebuilt.py: handled centrally in run_capture (covers
detect_host's `amd-smi list` and the version probe).
- install_python_stack.py: new _amd_smi_env() helper on its 3 raw
subprocess.run amd-smi calls.
- amd.py: merge RunAsInvoker into the existing child env.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Tighten AMD GPU name->arch patterns to avoid mismatches
The W9[0-9]{3} and RX 90[0-9]{2} patterns added for RDNA 4 were
speculative and over-broad: W9xxx would also match old GCN FirePro
W9100/W9000 cards (wrong gfx1201 -> a lemonade gfx120X download that
fails validation), and RX 90[0-9]{2} was redundant with the explicit
9070/9060 entries. Drop both; keep only confirmed RDNA 4 SKUs. Unmatched
AMD names still fall back cleanly to CPU.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Fetch the llama.cpp validation model via huggingface_hub
The prebuilt validation downloads a tiny GGUF test model from huggingface
via bare urllib. On Windows / proxy setups where the server sends an
incomplete TLS chain, urllib cannot complete the Amazon CA chain (it does
no AIA intermediate fetching) and fails with CERTIFICATE_VERIFY_FAILED, so
a perfectly good GPU prebuilt is rejected and the installer falls back to a
CPU source build.
Route the validation-model download through huggingface_hub
(hf_hub_download) -- the same mechanism Studio uses for model downloads,
which completes the chain where urllib cannot -- keeping the direct URL as
a fallback. This lets the lemonade ROCm prebuilt validate and install on
cert-restricted machines (verified: hf_hub_download succeeds where urllib
returns CERTIFICATE_VERIFY_FAILED).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Guard the remaining raw amd-smi version probe via run_capture
A ROCm-version detector in install_llama_prebuilt.py called amd-smi version through a raw subprocess.run that bypassed run_capture's Windows RunAsInvoker guard, so it still triggered the DiskPart UAC prompt during setup. Route it through run_capture like the other amd-smi calls.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Forward --rocm-gfx even when the ROCm runtime is unconfirmed
setup.ps1 forwarded --rocm-gfx (and picked the windows-hip llama.cpp
prebuilt) only inside `if ($HasROCm)`. On Adrenalin-only hosts (amd-smi
present but no HIP SDK, so $HasROCm stays false) the gfx arch was
name-inferred but never forwarded, so install_llama_prebuilt.py saw
has_rocm=False and installed the CPU build -- even though the lemonade
gfx1151 GPU prebuilt runs fine there (it bundles its own ROCm runtime;
verified: llama-cli --list-devices -> ROCm0: AMD Radeon 8060S, 69 GB).
Forward --rocm-gfx whenever a gfx arch is known (it is authoritative and
implies ROCm in install_llama_prebuilt.py), and treat a known gfx arch as
windows-hip in the existing-install mismatch check. --has-rocm stays gated
on the confirmed-runtime signal.
Verified on Radeon 8060S / gfx1151: the installer now selects, validates,
and installs llama-b1286-windows-rocm-gfx1151-x64.zip (ROCm DLLs present)
instead of the CPU build.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Install AMD ROCm PyTorch on name-inferred gfx hosts (enables Train/Export)
setup.ps1 picked the AMD ROCm PyTorch wheels only inside `if ($HasROCm ...)`.
On Adrenalin-only hosts (amd-smi present but no HIP SDK, so $HasROCm is
false) the gfx arch was name-inferred but the ROCm-wheel branch never ran,
so the host got torch+cpu. With CPU torch, torch.cuda.is_available() is
False, so the Studio backend sets CHAT_ONLY=True and hides Train/Export.
Un-gate the ROCm PyTorch index resolution on a known gfx arch (mirrors the
llama.cpp --rocm-gfx fix). AMD's per-arch Windows wheels
(repo.amd.com/rocm/whl/<gfx>) bundle the ROCm runtime, so they work without
a HIP SDK; a failed install still falls back to CPU.
Verified on Radeon 8060S / gfx1151: torch 2.11.0+rocm7.13.0 installs and
torch.cuda.is_available() -> True, device "AMD Radeon(TM) 8060S Graphics",
GPU matmul OK -> CHAT_ONLY=False -> Train/Export enabled.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Force amd-smi un-elevated process-wide in the Python installers
Guarding individual amd-smi call sites kept missing some (install_python_stack.py's probe loop and its Windows GPU re-check), so the DiskPart UAC prompt kept reappearing. Set __COMPAT_LAYER=RunAsInvoker process-wide at the top of install_python_stack.py and install_llama_prebuilt.py on Windows so every amd-smi subprocess (current and future) runs un-elevated with no per-call guard. Safe: these scripts only spawn amd-smi/rocminfo/hipinfo probes and pip/uv. setup.ps1 keeps per-call guards because it also spawns winget installers that need elevation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Fix Invoke-AmdSmiNoElevate exit code on PS 5.1 + RX 7700S arch match
Start-Process -PassThru leaves the returned process object's .ExitCode
$null after WaitForExit on Windows PowerShell 5.1, so the helper set
$LASTEXITCODE to $null and every caller's `if ($LASTEXITCODE -eq 0 ...)`
was always false -- the amd-smi GPU / gfx-token / ROCm-version detection
branch was effectively dead (masked only because the un-gated WMI
name->gfx inference still ran). Reproduced on PS 5.1.26100.
Rewrite the helper to use [System.Diagnostics.Process]::Start with a
ProcessStartInfo (UseShellExecute=false), whose .ExitCode is reliable,
with async stream reads (ReadToEndAsync) to avoid a pipe-buffer deadlock
and WaitForExit(timeout) to bound a flaky amd-smi. __COMPAT_LAYER=
RunAsInvoker (inherited via the process env) still suppresses the
auto-elevation / DiskPart prompt. Also drops the temp files and the
empty-ArgumentList edge case. Verified: exit code propagates
(7 -> $LASTEXITCODE=7), output captured, env restored.
Also fix the gfx1100 name pattern `RX 7700(?! S)` -> `RX 7700(?!S)` so the
spaceless retail name "RX 7700S" is correctly excluded (it belongs to the
gfx1102 row). Both found by PR review.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Address PR review follow-ups (install.sh table, update path, tests, WSL)
From the multi-agent PR review:
- install.sh: sync the AMD name->arch table with install.ps1 / setup.ps1
(the bash table had drifted to the old narrow patterns). Adds RDNA 2
(gfx103X), workstation PRO W SKUs, and more Strix Halo/Point names, and
orders gfx1102 before gfx1100 so the spaceless retail name "RX 7700S"
resolves correctly (bash case has no negative lookahead). AMD-ROCm-only:
the name inference stays gated behind _has_amd_rocm_gpu(), so NVIDIA /
CPU / macOS are unaffected.
- setup.ps1: the "dependencies up to date" fast path skipped the torch
reinstall, so an existing user who had CPU torch (installed before
ROCm-wheel support) stayed stuck in CHAT_ONLY. Now, when an AMD gfx arch
is known AND the installed torch is CPU-only, don't skip -- force the
dependency pass so the ROCm wheels install.
- scripts/install_rocm_wsl_strixhalo.sh: resolve the real /opt/rocm dir
instead of hardcoding ROCM_VER for LD_LIBRARY_PATH / the librocdxg
symlink (breaks if amdgpu-install lays ROCm under a patch-version dir);
add a LIBROCDXG_REF pin knob and a "verified against" freshness header.
- tests/studio/install/test_pr5940_followups.py: cover _hf_resolve_url_parts,
_fetch_validation_model_bytes (hf path + urllib fallback), run_capture's
Windows-only amd-smi RunAsInvoker injection, and install.ps1 vs setup.ps1
name-table parity (catches future drift). 14 tests, all passing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Fix DiskPart UAC prompt: skip amd-smi on Windows without a HIP SDK
On Windows, amd-smi re-initialises the ROCm runtime on every invocation
(even `amd-smi version`) and, on hosts without a working HIP runtime
(consumer APUs/dGPUs with only the Adrenalin driver), elevates a child
process at runtime -- popping a UAC/DiskPart prompt. amd-smi's own
manifest is asInvoker, so __COMPAT_LAYER=RunAsInvoker cannot suppress
that runtime elevation (verified: even `amd-smi version` hangs and
times out with RunAsInvoker set).
Replace the ineffective RunAsInvoker-only approach with a real gate:
only spawn amd-smi on Windows when a HIP SDK is detectable (hipinfo
present, so amd-smi runs un-elevated) or the user opts in with
UNSLOTH_ENABLE_AMD_SMI=1. The gfx arch is already resolved from WMI
name inference (forwarded via --rocm-gfx), so ROCm wheel + lemonade
llama.cpp selection is unaffected. Linux/macOS amd-smi never elevates
and is untouched (no regression). RunAsInvoker is kept as harmless
belt-and-suspenders for tools that DO use manifest elevation.
Applied consistently across:
- studio/backend/utils/hardware/amd.py (runtime GPU polling)
- install.ps1, studio/setup.ps1 (install-time detection)
- studio/install_llama_prebuilt.py (prebuilt arch probe + version)
- studio/install_python_stack.py (ROCm version + arch probe)
Verified live on AMD Radeon 8060S (gfx1151), native Windows: fresh
install detects the GPU, installs ROCm torch (torch.cuda.is_available()
True), launches Studio with no DiskPart prompt, and inference, tool
calling, web search, LoRA finetuning, and GGUF export all run on the GPU.
Tests: add 6 _amd_smi_allowed() gating tests + PowerShell-installer gate
assertions; update the three amd-smi monitoring tests to opt in (they
mock amd-smi as available). Full suite: 267 passed, 2 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* install.sh: helpful WSL message when the GPU isn't exposed to ROCm
In WSL, an AMD GPU's ROCm-on-WSL runtime is only available with a recent
Adrenalin driver AND a distro AMD supports (currently Ubuntu 24.04). When
neither is in place, GPU detection (rocminfo/_has_amd_rocm_gpu) finds
nothing and we silently fall back to CPU.
Add an actionable hint in the CPU-fallback path, shown only on WSL and
only AFTER detection has already failed -- so it is forward-compatible:
the moment a driver/distro DOES expose the GPU (e.g. if AMD later adds
Ubuntu 26.04 support), detection succeeds and the hint never fires. The
message:
- notes a GPU is plumbed in (/dev/dxg) but no ROCm runtime is exposed,
- lists the two prerequisites (Adrenalin driver + Ubuntu 24.04),
- if the distro is not 24.04, says AMD may not support it yet,
- tells the user to `wsl --install Ubuntu-24.04` and re-run,
- links AMD's ROCm-on-WSL guide + the experimental Strix Halo helper.
Verified live: on Ubuntu-24.04 the hint shows (version-warning omitted)
and the CPU install completes; on Ubuntu-26.04 the extra "this distro may
not be supported" line appears and points to 24.04.
Also fix the experimental scripts/install_rocm_wsl_strixhalo.sh: AMD's
repo.radeon.com/amdgpu-install/ is indexed by unified installer version
(30.30, 31.30, ...), NOT ROCm version, so the hard-coded
amdgpu-install/7.2.0/ path 404'd. Scan the installer dirs newest-first
for a noble .deb matching the target ROCm major.minor (ROCm 7.2 ->
30.30.x/amdgpu-install_7.2.x), falling back to the newest available.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* WSL: fix shortcut collision + pin ROCm-on-WSL driver reqs from AMD docs
Two WSL-related fixes informed by AMD's official ROCm-on-WSL docs and
field reports for Strix Halo / Ryzen AI Max+ (Radeon 8060S, gfx1151):
1. Shortcut collision (real bug). install.sh's WSL branch wrote
"Unsloth Studio.lnk" to the SAME Desktop / Start Menu folder as the
native-Windows installer (install.ps1 New-StudioShortcuts). Running
install.sh in WSL therefore silently retargeted the native shortcut at
the WSL launcher (wt.exe -> wsl.exe), so the desktop/start-menu icon
stopped launching native GPU Studio. Now the WSL shortcut uses a
DISTINCT name -- "Unsloth Studio (WSL - <distro>).lnk" -- and fetches
the Unsloth .ico to %LOCALAPPDATA%\Unsloth Studio so it shows the
proper icon. Native and WSL shortcuts now coexist.
2. Precise ROCm-on-WSL prerequisites. Research (AMD radeon-ryzen WSL
compatibility matrix, gianni.rosagallina.com Feb-2026 guide,
ROCm/ROCm#4952/#5509/#6022) confirms WSL GPU on Strix Halo requires
AMD Adrenalin Edition >= 26.1.1 (26.2.2+ is the first production
ROCDXG/WSL release) + ROCm 7.2.1 + Ubuntu 24.04; an older driver does
not inject the ROCm/DXG runtime into /usr/lib/wsl/lib, so rocminfo sees
only the CPU. install.sh's WSL hint and the experimental
install_rocm_wsl_strixhalo.sh header/preflight now state the exact
driver version (was a guessed ">=26.3.1"), bump ROCM_VER to 7.2.1, link
AMD's radeon-ryzen docs, and document the known librocdxg caveat that
usable VRAM is currently capped at the .wslconfig memory setting.
bash -n clean; install test suite 267 passed, 2 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* installer: hint when the AMD driver is too old for ROCm-on-WSL
Adds a detect-and-guide hook for the optional WSL-GPU path. An AMD GPU on
native Windows can also be used inside WSL2, but only with AMD Adrenalin
Edition >= 26.2.2 (the first production ROCDXG/WSL release). Native Windows
GPU works with any recent driver, so this is purely about enabling the WSL
path.
We intentionally do NOT auto-install the driver: AMD referrer-gates driver
downloads (scripted curl/Invoke-WebRequest are blocked) and does not publish
Adrenalin via winget, so no installer can reliably fetch it -- and silently
swapping a live display driver is risky. Instead we point the user at AMD's
official download page (one click), after which the existing WSL detection
lights up automatically.
- install.ps1: new Show-AmdWslDriverHint -- when an AMD GPU is present and the
installed driver predates the 26.2.2 release (DriverDate < 2026-02-01),
print a concise tip with the AMD download URL. Handles DriverDate as either
a CIM DateTime or a WMI string. Suppress with UNSLOTH_SKIP_AMD_DRIVER_HINT=1.
- install.sh (WSL hint): add the direct Adrenalin 26.2.2 download URL and note
that AMD downloads are referrer-gated (open in a browser).
Verified: hint fires on a Sept-2025 driver, auto-suppresses on >= 2026-02-01;
install.ps1 parses; install.sh bash -n clean; suite 267 passed, 2 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* install.ps1: refresh shell icon cache after creating the shortcut
After writing the Desktop / Start Menu .lnk, nudge Explorer to refresh
its icon (ie4uinit.exe -show). Without this, a stale icon cache can show
a blank shortcut icon until the next explorer restart -- most visible
when a shortcut of the same name was rewritten (e.g. a native install
followed by a WSL install, which previously shared the name; now they use
distinct names, but the cache nudge makes the icon appear immediately
regardless). Best-effort and wrapped in try/catch so it never fails the
install. The bundled unsloth.ico itself is valid (verified it renders).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* setup.ps1: don't silently CPU-build llama.cpp on an AMD GPU
For AMD, GPU acceleration comes from the lemonade ROCm prebuilt (it bundles
the ROCm runtime, no HIP SDK needed) and is the preferred/default path. The
source-build fallback is CPU-only -- a HIP/ROCm *source* build would need the
full HIP SDK + ROCm clang toolchain, which the prebuilt exists to avoid.
Previously, if an AMD-GPU host ever fell through to the source build (e.g. the
prebuilt could not be downloaded), it printed "building llama.cpp (CPU-only,
no NVIDIA GPU detected)" and quietly produced a CPU binary -- masking the lost
GPU acceleration. Now that case emits a loud [WARN] explaining the GPU prebuilt
is the AMD path and how to restore it (re-run / check network / set
UNSLOTH_LLAMA_RELEASE_TAG), so AMD never silently degrades to CPU.
No behavior change on the happy path: AMD still gets the GPU prebuilt (verified
on gfx1151: ggml-hip.dll bundled, ~80% GPU compute during inference). NVIDIA
(CUDA source build) and CPU-only hosts are unchanged.
setup.ps1 parses; install suite 267 passed, 2 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* uninstall: remove shared llama.cpp build, kill lock-holders, match WSL shortcut
Three gaps found by running a real uninstall on a native-Windows + WSL host;
all fixes are scoped to Unsloth-owned paths and no-op on the other pathways
(env/custom-root, NVIDIA/AMD/CPU, Mac) so nothing else regresses.
uninstall.ps1:
- Remove the default-mode SHARED llama.cpp build + cache. setup.ps1 installs
them at ~/.unsloth/llama.cpp and ~/.unsloth/.cache -- SIBLINGS of studio,
not under it -- so deleting <studio> left hundreds of MB behind. Now removed
explicitly, then ~/.unsloth is dropped ONLY if empty (never nukes unrelated
content). No-op in env/custom mode (llama.cpp nests under the custom root,
removed already) and when absent. UNSLOTH_LLAMA_CPP_PATH (user-owned) is kept.
- New _StopProcessesLockingRoots: _StopStudioProcesses only matched the venv
unsloth/python/studio exe, so it missed (a) llama-server.exe under llama.cpp
and (b) an orphaned multiprocessing python fork that ran from the SYSTEM
python but loaded a venv DLL (bitsandbytes) -- on Windows an open DLL handle
blocks the directory delete, leaving a half-removed install. The new helper
kills any process whose image path OR loaded module is under a target root
(module scan scoped to python/unsloth/llama-server names; vendor-agnostic).
- _RemovePath now retries (transient post-kill handle release).
uninstall.sh:
- Remove the default-mode ~/.unsloth/llama.cpp + ~/.unsloth/.cache; rmdir
~/.unsloth only if empty.
- WSL Windows-side shortcut cleanup now matches by TARGET (any
"Unsloth Studio*.lnk" whose target launches wsl.exe), covering both the
legacy "Unsloth Studio.lnk" and the new "Unsloth Studio (WSL - <distro>).lnk"
-- and never removes a native-Windows shortcut (which launches wscript.exe).
uninstall.ps1 parses; uninstall.sh passes sh -n and bash -n.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* install.ps1: invalidate Win11 Start Menu tile cache after creating shortcut
The Start Menu shortcut kept showing a blank/generic icon even after the
Explorer icon-cache rebuild, because Windows 11's StartMenuExperienceHost
keeps its OWN pre-rendered tile-icon cache
(%LOCALAPPDATA%\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\
TempState\TileCache_*.bin + StartUnifiedTileModelCache.dat), separate from
Explorer's iconcache_*.db. ie4uinit and an explorer.exe restart do not touch
it, and they don't recycle the host -- so a rewritten same-name shortcut keeps
showing the first-rendered (often the generic wscript ">") tile until the host
restarts on its own.
Fix: after creating the shortcut, drop only the Start Menu RENDER caches
(TileCache_* + StartUnifiedTileModelCache.dat) and stop StartMenuExperienceHost
(Windows auto-relaunches it), so the tile re-resolves the real icon via the
shell image factory. start2.bin (the user's pinned layout) is deliberately
preserved. Guarded by Test-Path (Windows 10 has no such host -> skipped) and
wrapped in try/catch so it can never fail the install. Windows-only
(install.ps1); no effect on Linux/macOS/Studio.
Verified live: rendering the shortcut via IShellItemImageFactory::GetImage (the
API StartMenuExperienceHost uses) returns the Unsloth sloth icon, color-matched,
after this invalidation -- previously it returned the generic script tile.
install.ps1 parses; install suite 267 passed, 2 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* ROCm-on-WSL for AMD Strix Halo (gfx1151): auto-setup + runtime enablement
Make Unsloth Studio set up ROCm-on-WSL automatically for AMD Strix Halo
(Radeon 8060S / gfx1151) and use the GPU at runtime, validated end-to-end
on a Ryzen AI Max+ PRO 395 (ROCm 7.2.1 + librocdxg + Adrenalin Apr-2026):
rocminfo enumerates gfx1151, torch.cuda True, ~85.8 GB UMA pool.
Every change is a strict no-op for all other configs (NVIDIA/CUDA,
discrete + native-Linux AMD ROCm, macOS/MLX, Windows, CPU-only, non-Strix
WSL) and can never abort the installer.
- scripts/install_rocm_wsl_strixhalo.sh: rewrite to the validated recipe.
Fixes that would have broken a working box: drop the /usr/lib/wsl/lib
preflight (a working ROCDXG host has only d3d12/dxcore there); remove the
obsolete rocr4wsl step (gone from the 7.2.1 repo; would hard-fail and also
rips out the standard hsa-rocr ROCDXG needs); dynamic librocdxg soname
(was hardcoded 1.1.0; build is 1.2.0); direct apt-repo install; Windows
SDK auto-discovery; persist env to /etc/profile.d + ~/.bashrc; idempotent.
- install.sh: _maybe_bootstrap_rocm_wsl auto-offers/runs the helper when it
detects a Strix Halo APU in WSL (/dev/dxg) with no ROCm runtime, then
loads the env so detection routes to the gfx1151 wheels. Fast-path when
already configured. Fix an inaccurate WSL hint line.
- studio/backend/main.py + worker.py: set HSA_ENABLE_DXG_DETECTION=1
in-process before torch (gated on /dev/dxg AND librocdxg.so), so the
worker uses the GPU even when launched outside a login shell. Mirrors the
existing BNB_ROCM_VERSION injection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* uninstall: clean up ROCm-on-WSL artifacts + Start Menu tile cache
- uninstall.sh: remove the ROCm-on-WSL helper artifacts -- the librocdxg
build clone (~/.unsloth/librocdxg, which otherwise blocks the empty-dir
rmdir of ~/.unsloth), the throwaway smoke-test venv, the persisted env
(/etc/profile.d/unsloth-rocm-wsl.sh) and the ~/.bashrc block. The system
ROCm userspace is a shared prereq like CUDA and is kept by default;
UNSLOTH_UNINSTALL_ROCM=1 removes it too. No-ops on macOS / non-Strix Linux.
- uninstall.ps1: invalidate the Win11 Start Menu tile cache after removing
the shortcut so its tile disappears promptly (mirrors install.ps1),
preserving start2.bin.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* installer: accurate AMD ROCm messaging (HIP SDK optional, not required)
The Windows installer printed "HIP SDK not found - GPU-accelerated training
unavailable" / "ROCm wheels require the HIP SDK" whenever the HIP SDK was
absent. That is misleading: for a detected AMD GPU arch (gfx1151 etc.),
setup.ps1 installs AMD's bundled-runtime ROCm PyTorch wheels (repo.amd.com)
which ship their own ROCm runtime and do NOT need the HIP SDK -- verified
end-to-end (torch 2.11.0+rocm7.13.0, cuda True, QLoRA training on GPU) on a
Radeon 8060S with no HIP SDK installed.
Gate the GPU-detection + rocm-step messages on a detected gfx arch: when one
is known, state that GPU PyTorch uses bundled-runtime wheels and the HIP SDK
is optional; only when the arch is unknown fall back to the HIP-SDK hint.
Behavior (torch routing) is unchanged; this is messaging only. No-op for
NVIDIA/CUDA, HIP-SDK-present, and CPU paths (they hit earlier branches).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* installer: fix /opt/rocm data-loss + make WSL shortcut create/remove interop-robust
Two fixes from the 3-reviewer regression audit + live testing on a
systemd-enabled WSL distro (interop disabled):
F1 (data-loss, install_rocm_wsl_strixhalo.sh): the /opt/rocm symlink-repair
could force-delete a pre-existing REAL ROCm install. The guard only checked
that /opt/rocm is a real directory, not that it is the stray librocdxg stub.
Now it only touches /opt/rocm when it is NOT a real install (no bin/rocminfo,
bin/hipcc, or .info/version present), and MOVES it aside (rocm.unsloth-stub-bak)
instead of deleting it, so a wrong guess can never lose data.
WSL interop robustness (install.sh + uninstall.sh): both relied on
`command -v powershell.exe`, which is true even when WSL interop cannot EXECUTE
it (on systemd distros powershell.exe fails with "Exec format error"). Result:
the WSL shortcut silently failed to create (install) and to remove (uninstall).
- uninstall.sh: test that powershell.exe actually runs; if not, remove the
"Unsloth Studio (WSL...).lnk" files directly via drvfs (/mnt/<drive>), which
works without interop. The name is WSL-install-specific, so a native install's
"Unsloth Studio.lnk" is never touched.
- install.sh: when the shortcut cannot be created, warn with the manual launch
command + how to re-enable interop, instead of failing silently.
No behavior change on the interop-on path. The regression audit otherwise found
no regressions on Linux/Mac/Windows/CPU/NVIDIA install paths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* install.sh: fast-path fully restores ROCm-on-WSL env when the drop-in is gone
Reinstall regression found by uninstall->reinstall testing: after a Studio
uninstall that removed /etc/profile.d/unsloth-rocm-wsl.sh but KEPT the shared
ROCm (the default), a non-login reinstall hit the bootstrap fast-path
(librocdxg present) and its else-branch only set HSA_ENABLE_DXG_DETECTION --
NOT PATH/LD_LIBRARY_PATH. So rocminfo was not on PATH, GPU detection failed,
and the installer fell back to CPU-only PyTorch.
Fix: when librocdxg is present but the env drop-in is missing, restore the
FULL env inline (HSA + TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL + PATH +
LD_LIBRARY_PATH) so rocminfo is found and detection routes to the GPU, and
recreate /etc/profile.d/unsloth-rocm-wsl.sh so future shells and the Studio
worker get it too. No change to the env-present fast-path or any other host.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* installer: clear Explorer icon cache so shortcut icons aren't blank
Root cause of the persistent blank Desktop + Start Menu icons: Explorer caches
each shortcut's icon in iconcache_*.db and does NOT re-read the .ico when a
same-name .lnk is recreated across reinstalls. The .ico and .lnk are correct
(the shell renders them non-blank via IShellItemImageFactory; the .ico has real
image data at 16/32/48/128 px), but the stale cache entry wins. The previous
fix only ran a weak `ie4uinit -show` + the Start Menu tile-cache clear -- it
never invalidated Explorer's icon cache, so the desktop icon stayed blank.
Fix (native install.ps1 New-StudioShortcuts AND the WSL shortcut path in
install.sh):
- ie4uinit -ClearIconCache (thorough; replaces -show as the primary refresh)
- SHChangeNotify(SHCNE_ASSOCCHANGED) to force a live desktop/taskbar refresh
WITHOUT restarting explorer
- keep the Win11 Start Menu tile-cache invalidation (and add it to the WSL
shortcut path too, preserving start2.bin)
Non-disruptive (no explorer restart). install.ps1 parses clean; install.sh
passes bash -n + dash -n; the heredoc-generated WSL PowerShell parses clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* installer: per-item SHChangeNotify(UPDATEITEM) reliably fixes blank icons
The blank Desktop/Start Menu shortcut icons are a stale Explorer PER-ITEM icon
cache: when a same-name .lnk is recreated across reinstalls, Explorer caches the
previously-resolved (often generic "white page") icon for that item and won't
re-extract the .ico on its own. The .ico and the .lnk's IconLocation are correct
(every icon API renders the sloth) -- only Explorer's cached display is stale.
The previous refresh (ie4uinit -ClearIconCache + a GLOBAL SHCNE_ASSOCCHANGED
broadcast) does NOT recover a stale item -- confirmed by reproduction. The
reliable, NON-disruptive fix (no explorer restart) is a PER-ITEM
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, <lnk path>) for each created
shortcut, which forces Explorer to re-read that exact item's icon.
Verified end-to-end: deliberately staled a shortcut to the generic icon, ran the
installer's exact new refresh code, and the sloth icon recovered with NO explorer
restart (confirmed by capturing the live desktop via PrintWindow).
Applied to both native install.ps1 (New-StudioShortcuts) and the WSL shortcut
path in install.sh. Still clears the on-disk icon cache (ie4uinit) and the Win11
Start Menu tile cache (preserving start2.bin).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* uninstall: remove leftover llama.cpp .staging root so ~/.unsloth is cleaned
The llama.cpp atomic-install staging root (install_llama_prebuilt.py
INSTALL_STAGING_ROOT_NAME=.staging) is a sibling of the llama.cpp install
dir (~/.unsloth/.staging in default mode). It is normally pruned after a
successful activate, but an interrupted or retained build can leave a
<name>.staging-XXXX tree behind. The uninstallers removed llama.cpp and
.cache but not .staging, so the final empty-dir cleanup of ~/.unsloth failed
and the directory lingered. Reproduced on WSL (Ubuntu-24.04) where an empty
llama.cpp.staging-XXXX dir kept ~/.unsloth alive after uninstall.
Remove ~/.unsloth/.staging in both uninstall.sh and uninstall.ps1. No-op in
env/custom mode (staging nests under the custom root removed already) and
when absent. Cross-platform fix (the staging logic is platform-agnostic).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* installer: WSL-absent hint + fix here-string lint false positive
install.ps1: in the AMD WSL-ROCm driver hint, detect when wsl.exe is absent
and add a one-line "wsl --install -d Ubuntu-24.04" pointer so a Strix Halo
user with no WSL yet gets an actionable next step (the hint previously assumed
an Ubuntu-24.04 distro already existed). Best-effort, informational only.
test_rocm_support.py: test_no_here_strings did a crude substring check that
false-positived on the conda-style block marker
printf '# <<< Unsloth ROCm-on-WSL (gfx1151) <<<' -- a string literal written
into the /etc/profile.d drop-in, also used as a sed delimiter pair by
uninstall.sh, not a here-string. Strip quoted spans before the check so the
lint still catches a real here-string operator but ignores quoted literals.
install.sh remains POSIX-clean (sh -n / dash -n / bash -n all pass).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* installer: address PR review comments (gfx1150 mapping, amd-smi opt-out, WSL bootstrap, SDK path, make)
Apply the valid bot review findings on #5940; reject the ones that don't hold.
Fixed:
- AMD name->gfx table (setup.ps1 + install.ps1): Radeon 890M and Ryzen AI 9 HX
370/375 are Strix POINT (gfx1150), not Strix Halo (gfx1151). Move 890M / HX 37x
/ AI 9 HX to the gfx1150 row and drop the bogus HX 38x pattern (no such Strix
Halo SKU). Matches the runtime classifier in worker.py (890M/880M -> gfx1150;
8060S/8050S -> gfx1151). Prevents Strix Point hosts from getting the wrong ROCm
prebuilt/wheels.
- amd-smi opt-out (setup.ps1 + install.ps1): an explicit UNSLOTH_ENABLE_AMD_SMI=
0/false/no/off now wins over the HIP-SDK heuristic, so a host with a HIP SDK
binary but a broken runtime no longer gets the DiskPart/UAC prompt the opt-out
exists to avoid.
- amd-smi warning probes (install_python_stack.py): _has_rocm_gpu and
_detect_amd_gfx_codes now gate amd-smi behind _amd_smi_allowed() (and pass
_amd_smi_env()), closing the last unguarded amd-smi spawn on Windows.
- WSL ROCm bootstrap (install.sh): the "already-usable ROCm?" early return now
requires rocminfo to enumerate the real gfx1151 agent instead of the generic
_has_amd_rocm_gpu (whose broad gfx[1-9][0-9] match accepts a fallback
"gfx11-generic" ISA), so a Strix Halo box missing the ROCDXG bridge is no longer
skipped. The shared helper is untouched (no gfx90a regression).
- install_rocm_wsl_strixhalo.sh:
* Quote-safe Windows SDK discovery: the old for-in-$(ls -d "...Program Files
(x86)/...") word-split on the space and never matched; use find + read loop.
* Add `make` to apt prereqs (cmake only recommends it; minimal images lacked it
and the librocdxg `make -j` build failed).
* Verification requires gfx1151 exactly (not gfx1[0-9]) so a generic ISA or an
unrelated RDNA GPU can't pass while the real GPU is absent.
Reviewed but NOT changed:
- "Forward inferred ROCm arch without HasROCm" (setup.ps1): already correct --
--rocm-gfx is forwarded under `if ($script:ROCmGfxArch)`, not `if ($HasROCm)`.
- "Route inferred arch into install.ps1 torch path": not a bug -- install.ps1
installs CPU torch as a base by design and setup.ps1 swaps in the ROCm wheel for
the inferred arch (gate `($HasROCm -or $ROCmGfxArch) -and cpu`); verified live
the native install ends on torch 2.11.0+rocm7.13.0.
- "$p null guard after Start-Process" (install.ps1/setup.ps1): redundant -- the
amd-smi runner uses [Process]::Start wrapped in try/catch, so a null process
already returns "" with LASTEXITCODE=1 (no uncaught exception).
- "ls -> find for /usr/lib/wsl/lib" (gemini): stale -- that heuristic was removed;
only a comment about it remains.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* installer(rocm-wsl): auto-install the Windows 11 SDK via winget (fewer manual steps)
librocdxg's build needs the Windows SDK 'shared' headers on the Windows host.
Previously the helper just die()d with "install the Windows 11 SDK and re-run" if
they were missing -- a manual prerequisite that broke the otherwise-seamless
`curl ... install.sh | sh` one-liner on Strix Halo.
Now, when the headers aren't found, the helper installs the Windows 11 SDK on the
Windows host from inside WSL via winget (powershell.exe interop), then
re-discovers them. The SDK installer elevates -> ONE UAC prompt on the Windows
desktop; the headers appear under /mnt/c immediately (drvfs is live, no reboot).
The user already consented to the ROCm-on-WSL setup, so no extra prompt is added
beyond the OS UAC gate.
- New _find_win_sdk (space-safe find of the newest installed SDK 'shared' dir)
and _install_windows_sdk_via_winget helpers.
- winget IDs tried newest-stable first: Microsoft.WindowsSDK.10.0.26100, then
.22621. The presence of the headers (re-check) is the source of truth, not
winget's exit code. </dev/null so winget never consumes a piped `curl|sh` stdin.
- Best-effort + non-fatal: interop-off / no-winget / declined-UAC all fall
through to the existing clear manual-install die(). Opt out with
UNSLOTH_SKIP_WIN_SDK_INSTALL=1.
Removes the last avoidable manual step from the WSL Strix Halo path; only the AMD
Adrenalin driver (AMD referrer-gates the download) remains manual. Verified
_find_win_sdk resolves the spaced "Program Files (x86)" path; bash -n clean; all
winget flags validated against `winget install --help`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* installer(amd): gate install-time amd-smi probe to fix DiskPart UAC prompt
install_python_stack.py's Windows "AMD GPU detected but ROCm torch missing"
warning probe ran `amd-smi list` whenever amd-smi was on PATH -- and amd-smi
ships in C:\Windows\System32 with the AMD Adrenalin driver -- without the
_amd_smi_allowed() gate that every other amd-smi call site in the file uses.
On Adrenalin-only hosts (no HIP SDK) amd-smi elevates a child at runtime and
pops a UAC/DiskPart prompt that __COMPAT_LAYER=RunAsInvoker cannot suppress
(amd-smi's manifest is asInvoker). The probe also ran before the
ROCm-torch-installed check, so it fired on every Windows AMD install.
Gate it behind _amd_smi_allowed() and pass _amd_smi_env(), matching
_has_rocm_gpu()/_detect_amd_gfx_codes(). When skipped, the only loss is the
best-effort "AMD GPU detected" note on HIP-SDK-less hosts.
Adds a per-function AST regression test asserting every function in
install_python_stack.py that names the amd-smi command and spawns a subprocess
also references _amd_smi_allowed() (flags the pre-fix code; passes after).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* studio(cli): fix `unsloth studio stop` crashing on Windows
`stop` used the POSIX `os.kill(pid, 0)` liveness probe, but on Windows
CPython raises OSError (WinError 87, "The parameter is incorrect") for
*every* pid -- alive or dead. `stop` only catches ProcessLookupError /
PermissionError, so the OSError propagated and the command crashed with
a traceback before ever reaching its (correct) `taskkill /F` path.
Add a cross-platform `_pid_alive(pid)` helper (tasklist on Windows,
signal-0 elsewhere) and use it for both the pre-check and the post-kill
wait loop. The actual kill path is unchanged.
Verified on Windows (Python 3.13): os.kill(pid,0) raises WinError 87 for
both a live and a dead pid; `_pid_alive` returns True/False correctly and
the full stop() flow (alive -> taskkill -> dead -> "stopped") passes
end-to-end against a throwaway process.
Adds tests/studio/test_cli_studio_stop_windows.py (AST guard against a
bare os.kill(pid,0) liveness probe + mock-only _pid_alive behaviour for
the win32 tasklist branch and the POSIX signal-0 branch).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* installer(amd): fix install.sh name->arch table misrouting Strix Point to gfx1151
The bash name->arch inference table in install.sh placed Strix Point
identifiers (Radeon 890M, "Ryzen AI 9 HX 370/375", "AI 9 HX") in the
gfx1151 (Strix Halo) row, diverging from the install.ps1 / setup.ps1
PowerShell tables which correctly map them to gfx1150. It also carried a
stray "HX 38" token absent from the PowerShell source-of-truth.
Align install.sh with the PowerShell tables:
gfx1151 row: 8060S|8050S|8040S|Strix Halo|Ryzen AI Max|AI Max
gfx1150 row: 890M|880M|860M|840M|Strix Point|Krackan|HX 37|AI 9 HX|...
Impact is low (the bash table only feeds the display label _gpu_disp_gfx
and the "set UNSLOTH_ROCM_GFX_ARCH=..." hint; wheel selection is driven
by the detected ROCm version, not this name string) but a Strix Point
user would otherwise see/copy the wrong gfx arch.
Add a parity test (test_install_sh_name_arch_agrees_with_ps_for_strix_and_non_amd)
that parses install.sh's case table and asserts Strix Halo->gfx1151,
Strix Point->gfx1150, RX 7700S->gfx1102, and NVIDIA/Intel->no match,
cross-checking against install.ps1 (the previous parity test only
compared install.ps1 <-> setup.ps1, missing install.sh).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* setup.ps1: keep prebuilt-llama ownership guard within the test's block window
The AMD additions to the prebuilt-llama.cpp block (the windows-hip vs
windows-cpu existing-install kind validation) pushed the
install_llama_prebuilt.py invocation to ~1999 chars after the
"installing prebuilt llama.cpp bundle (preferred path)" anchor, right at
the edge of the 2000-char window that
test_setup_ps1_prebuilt_llama_cpp_has_ownership_guard slices -- so the
helper string was truncated and the test failed with "substring not
found" (CI: Repo tests (CPU)).
The ownership-guard invariant (Assert-StudioOwnedOrAbsent precedes the
install_llama_prebuilt.py call) was already satisfied; only the proximity
to the anchor regressed. Move the "installing prebuilt..." substep to
immediately before the install (after the existing-install pre-cleanup),
which also reads better (validate/clean existing -> then "installing"),
shrinking anchor->helper from 1999 to 413 chars. Behaviour is unchanged
(console message ordering only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* install.sh: auto-run Strix Halo ROCm-on-WSL setup by default
`curl -fsSL https://unsloth.ai/install.sh | sh` should make a Strix Halo
(gfx1151) GPU usable inside WSL with no extra commands. Previously the
ROCm-on-WSL bootstrap was opt-in: it required UNSLOTH_ROCM_WSL_AUTO=1 or an
interactive [Y/n] at a TTY, and silently skipped under a pipe (no /dev/tty),
so the piped one-liner never set the GPU up automatically.
Flip it to auto-by-default for the single narrow case the existing guards
allow (WSL + Strix Halo + /dev/dxg + no usable ROCm yet) -- exactly the GPU
setup the user ran the installer for. Opt out with
UNSLOTH_SKIP_ROCM_WSL_SETUP=1. The Tauri desktop app keeps its own consent UI
(only auto-runs when it passes UNSLOTH_ROCM_WSL_AUTO=1). All hardware/OS
guards are unchanged, so non-Strix / non-WSL / NVIDIA / native-Linux / macOS /
CPU paths are unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* PR comments: condense to be succinct (comments/docstrings only)
Shorten the verbose explanatory comments and docstrings this PR added across
the installer, scripts, backend shims, CLI, and tests -- tighter, fewer lines,
while preserving every non-obvious "why" (os.kill WinError 87, amd-smi
RunAsInvoker/UAC, /dev/dxg + librocdxg gating, the ROCm-on-WSL bootstrap guard
chain, ownership guards, etc.). No executable code, string literals, messages,
or behavior changed.
Verified comments-only: docstring-normalized AST equality (Python, 9 files),
non-comment token equality (PowerShell, 3 files), comment-stripped diff +
sh -n / bash -n (shell, 3 files). Behavior re-confirmed: get_torch_index_url +
gfx name->arch table 44/44 under dash & bash; rocm_support / pr5940_followups /
cli_studio_stop tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Installer: address PR review (amd-smi opt-out, pipefail, multi-distro, non-root)
Fixes valid findings from the Codex/Gemini PR review:
- install.ps1 / setup.ps1: gate the `amd-smi version` ROCm-version fallback with
$amdSmiAllowed so UNSLOTH_ENABLE_AMD_SMI=0 opt-out is honored (the device
probe was gated but this fallback wasn't), avoiding the DiskPart/UAC prompt.
- install_rocm_wsl_strixhalo.sh: make the post-verification rocminfo summary
best-effort (|| true) so head's early pipe-close under `set -o pipefail` can't
fail the bootstrap after gfx1151 was already enumerated; pin the Windows SDK
`winget install` to --source winget (matches the msstore-cert fix rationale).
- install.ps1: python.org fallback installs the py launcher per-user
(InstallLauncherAllUsers=0, avoids admin), and derives the fallback full
version from the requested minor so a non-default UNSLOTH_PYTHON (e.g. 3.12)
isn't silently replaced with 3.13 when the listing is unreachable.
- install.sh: recreate /etc/profile.d/unsloth-rocm-wsl.sh via `sudo tee` for a
non-root reinstall (a plain redirect failed silently, dropping the ROCm env).
- uninstall.sh: scope WSL Windows-side shortcut removal to the current
WSL_DISTRO_NAME (per-distro name or -d "<distro>" arg) so uninstalling one
distro no longer deletes other distros' launchers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Studio ROCm Windows: fix field-reported issues from Strix Halo testers
Four fixes from PR #5940 field reports (Win11 native, gfx1151):
1. bitsandbytes arch-probe spam: bnb's get_rocm_gpu_arch() runs
hipinfo.exe via subprocess PATH at import; the AMD torch wheel ships
hipInfo.exe in the venv Scripts dir, which is only on PATH for
activated venvs. Every bnb import logged "Could not detect ROCm GPU
architecture: [WinError 2]" ERROR + WARNING (even with the HIP SDK
installed, whose bin dir is not on PATH either). Prepend the Scripts
dir to PATH before bnb imports in main.py, worker.py, and
install_python_stack.py, gated on the file existing (only AMD wheels
ship it). Verified on gfx1151: ROCM_GPU_ARCH now resolves to gfx1151
with zero errors.
2. OOM-guard double-tax on native Windows unified APUs: mem_get_info's
total is the WDDM budget the driver grants HIP (BIOS carve + ~half
of remaining RAM) -- the OS share is already outside it. The 0.80
unified cap on top denied loads that fit (field report: 48.49 GiB
budget -> "38.79 GiB allowed" OOM for a 47.29 GiB load with 48.08
free). Use 1.0 on win32 unified; Linux keeps 0.80, discrete 0.90.
3. "Missing VRAM" confusion: log the WDDM budget vs physical RAM with
the fix (BIOS UMA frame buffer / AMD Software Variable Graphics
Memory) when the grant is under 75% of RAM, so a 48 GiB cap on a
96 GiB box reads as policy, not a Studio bug.
4. llama-server fit-step crash (Qwen3.6-27B-MTP + mmproj, lemonade
gfx1151): --fit defaults to 'on' upstream, so the fit step runs even
when Studio already placed the model via -ngl -1, and aborts in
ggml-cuda.cu on some ROCm hosts. Retry the spawn once with --fit off
when the server crashes during startup and Studio's own VRAM math
had placed the model (never when use_fit or an explicit fit flag was
passed). Also keep the TAIL of crash output in the error log (the
diagnostic line prints last; head-truncation cut exactly that) and
reference the full on-disk log.
Verified live on Radeon 8060S: bnb import clean, Qwen3.5-4B-MTP loads
and generates through the new spawn loop, stub-crash retry appends
--fit off and recovers, fraction probes confirm WDDM overcommit and
sub-1.0-only enforcement on current AMD wheels.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Studio ROCm Windows: GPU-name fallbacks so nothing depends on amd-smi
amd-smi does not reliably exist on Windows: the HIP SDK never ships a
CLI, inbox Windows Update drivers do not, and only some full Adrenalin
packages drop amd-smi.exe into System32 (field report: fresh Win11 +
Adrenalin + HIP SDK, still no amd-smi anywhere). Make every consumer
work without it:
- install_python_stack._detect_windows_gfx_arch: two new probes after
hipinfo/amd-smi -- (2b) the venv Scripts hipInfo.exe shipped by AMD
torch wheels (drives `studio update` on driver-only hosts), and (4) a
last-resort GPU marketing-name -> gfx table via WMI
(Win32_VideoController), mirroring setup.ps1's $nameArchTable so a
standalone repair resolves the arch with zero AMD tooling installed.
- install_llama_prebuilt._resolve_exe: also probe the venv Scripts dir
so a standalone rerun finds hipInfo.exe without HIP_PATH.
- hardware/amd.py _run_amd_smi: which() guard before spawning --
absence now disables the poller in one step instead of burning the
3-strike circuit breaker on FileNotFoundError; corrected the stale
comment claiming Adrenalin ships amd-smi.
Simulated against the real detection functions on gfx1151: amd-smi
absent, present-but-crashing (exit 1), present-but-hanging (60s sleep
vs 5-10s probe timeouts), and hard opt-out -- all resolve gfx1151, no
exceptions, bounded time. Full adversarial install (broken amd-smi
stub first on PATH + UNSLOTH_ENABLE_AMD_SMI=1, fresh uninstall first):
exit 0, name-table arch inference, lemonade gfx1151 b1292 prebuilt,
torch 2.11.0+rocm7.13.0 cuda_avail=True on the 8060S, Studio boots
healthy and stops cleanly.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Studio: per-attempt llama-server log names + amd-smi test portability
Found by cross-platform simulation of the --fit off retry (Windows +
Linux sandboxes, real load_model with stub servers):
- llama-server log filename now carries the spawn-attempt index. The
retry can respawn within the same epoch second; reusing the name
opened the same file with "w" and truncated the crash log the retry
warning had just pointed the user at (proven with a frozen
time.time: one file, crash evidence gone; with the suffix both
attempts keep their logs). Regression-pinned in
test_llama_cpp_wait_for_health.py.
- test_amd_primary_gpu_with_mock now mocks shutil.which alongside
subprocess.run: the amd-smi absence guard which()-checks before
spawning, so on hosts without a real amd-smi (Linux CI, driver-only
Windows) the subprocess mock was never reached and the test failed.
Surfaced by running the suite in a clean Linux sandbox.
Simulation coverage on both OSes: 67-case platform/edge matrix
(real shipped code blocks under win32/linux/darwin spoofs: OOM-guard
fractions + VGM-hint boundary, bnb PATH-prepend gates, retry
eligibility incl. equals-forms and decoy tokens, GPU-name table
adversarial set, WMI fallback without powershell, monitor absence
semantics), 6-scenario live retry matrix (crash-once/crash-always/
exit-zero/explicit-fit/hang/log-collision) against real llama-server
spawns on Windows and WSL (GPU success legs on the 8060S), and a
3-engine browser matrix (chromium/firefox/webkit) driving the live
backend's health + authed /v1 chat completion.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Studio: classify unified-memory via props.is_integrated first
Align the ROCm OOM-guard classifier with PR #5988's UMA gate: consult
hipDeviceProp_t.integrated (props.is_integrated) before the hardcoded
arch set. Strictly additive -- truthy upgrades to unified; 0/absent
falls through to the existing gfx1150/gfx1151 + device-name logic, so
wheels that omit or zero the field cannot downgrade the known APU set.
Extends correct unified-cap treatment to APUs outside that set (e.g.
gfx1103 Phoenix iGPUs) and keeps Studio's two unified-memory consumers
on one driver signal. Verified live on gfx1151 (is_integrated == 1 on
the AMD Windows wheel -> ('gfx1151', True) via the new path).
* AMD detection: probe rocminfo with HSA_ENABLE_DXG_DETECTION and sync setup.sh gfx table
Fleet validation on a Strix Halo WSL2 box showed the system rocminfo
(HSA 1.18, ROCm 7.2.1) only enumerates the GPU over /dev/dxg when
HSA_ENABLE_DXG_DETECTION=1, and that rocminfo can sit at /opt/rocm/bin
off PATH outside login shells. Detection probes that miss either of
these report no GPU on a working ROCDXG host and select the CPU build
even though the lemonade bundle offloads fine (95.7 tok/s measured vs
64.5 CPU on the same laptop). Seed the env (a no-op on bare metal) and
the PATH fallback in install.sh, studio/setup.sh, and the installer's
Linux rocm probe, mirroring what main.py/worker.py already do for the
runtime.
Also sync studio/setup.sh's name->gfx table with install.sh: 890M and
the HX 37/AI 9 HX SKUs are Strix Point (gfx1150, not gfx1151), RX 7700S
must match gfx1102 before the gfx1100 row, and the RDNA2/workstation
rows were missing. New parity test pins the two bash tables together so
they cannot drift again.
* Studio: persist server session logs + native-crash stacks to disk
Field report (Strix Halo, 96 GB UMA carve, WSL and native Windows):
"the studio just terminates without a warning". A native crash in the
GPU runtime kills the process with no Python traceback, and a desktop-
shortcut console closes before anything can be read. The server only
ever logged to the console, so there was nothing to send back.
run_server now tees stdout/stderr to
~/.unsloth/studio/logs/server/server-<ts>-pid<n>.log (console behavior
unchanged; file copy is best-effort), arms faulthandler at the same
file so access violations / SIGSEGV leave a stack trace on disk, and
exports PYTHONFAULTHANDLER=1 so training workers inherit crash dumps
on their captured stderr. Armed before `from main import app` so even
import-time failures leave evidence. Keeps the newest 20 session logs;
opt out with UNSLOTH_STUDIO_NO_FILE_LOG=1. Prints "Session log: <path>"
at startup so users know what to attach.
Verified on this box: a forced real segfault (faulthandler._sigsegv)
leaves the full session output plus "Fatal Python error: Segmentation
fault" and the thread stack in the file while the console shows
nothing; a normal server boot captures the startup banner and serves
health as before.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* AMD probe: honor a pre-set HSA_ENABLE_DXG_DETECTION value
Match the shell helpers, which use the parameter-default form: a user
who exports HSA_ENABLE_DXG_DETECTION=0 to deliberately hide the GPU
from DXG detection should not have the probe override it.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: danielhanchen <michaelhan2050@gmail.com>
|
||
|
|
f542ffb023 |
Studio: unify shadows, backgrounds and dark mode consistency in chat UI (#6116)
* Studio: refine chat UI shadow, background and sidebar divider - Give both chat composers a Gemini style drop shadow with a short transition, and add a visible shadow in dark mode. - Set the main chat surface to a soft off white (#fbfbfc) in light mode so the white composer reads as a card; dark mode is unchanged. - Remove the divider line between the side menu and the page. * Studio: lighten chat background to #fcfcfd * Studio: make dark mode composer shadow visible * Studio: drop composer shadow in dark mode * Studio: unify all light mode shadows on the chatbox shadow * Studio: unify dark mode surface colors and make hover states visible * Studio: fix barely visible dark mode hover in account and plus menus * Studio: lift settings dialog off the page background in dark mode * Studio: drop greeting periods and restyle projects page to match the chatbox * Studio: chatbox shadow on search dialog, grey sort pill on projects * Studio: slimmer sidebar profile row, borderless login card * Studio: match select popups to their trigger, taller profile hover target * Studio: bigger projects search pill with the original chatbox glow * Studio: keep select trigger shape while open * Studio: center and narrow the projects search pill like Gemini * Studio: borderless export card with the chatbox shadow * Studio: chatbox shadow for selectable pills, warmer composer, search pill tweaks * Studio: one warm background token for every page, softer search glow * Studio: keep off white depth when warming the page background * Studio: distinct sidebar surface from the page background * Studio: white sidebar on the warm page background * Studio: soften projects search shadow * Studio: nudge the chat zero state up 5px * Studio: darker dark mode page background, 28.5vh welcome offset * Studio: dark mode shadows match the chatbox geometry, borderless recipes empty state * Studio: revert dark mode shadows, desaturate dark recipe cards * Studio: lift dark recipe cards, no hover shadow in dark * Studio: drop the pale hover halo on recipe cards * Studio: lighter dark recipe text, flat dark menus, flush select popups * Studio: page-bg shadow on model selector, flush popovers and dropdowns * Studio: zero menu offsets so dropdowns sit flush against triggers * Studio: one 14px radius for list menus and sidebar buttons * Studio: 14px buttons, pill hover shapes, narrower slider inputs * Studio: pill buttons, keep profile row rectangular * Studio: pill Save and Delete, small gap under account menu * Studio: skinnier dropdown popups, larger account menu gap * Studio: dropdown popups slightly wider than their trigger * Restore light mode sidebar separator line * Size panel number input pills to their content * Lighten light mode page background * Pure white composer, nudge page background whiter * Use inline ch width for panel number pills * Fix panel number pills at uniform 4ch width --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com> |
||
|
|
f41617ad9f |
Studio: auto-sync allowScripts pins after dependency bumps (#6136)
* Studio: npm v12 readiness for install-script gating npm 12 (July 2026) stops running dependency install scripts unless they are approved via allowScripts, and npm 11.16 already warns. Studio has no git or remote URL deps anywhere, so script gating is the only exposure: - commit the allowScripts policy that npm approve-scripts writes for @biomejs/biome and msw, plus a manual fsevents entry: the tooling cannot match a darwin-only optional dep from Linux, but the strict check walks the platform independent ideal tree and flags it anyway - drop the minimum-release-age npmrc alias; npm >=11.16 flags it as an unknown project config that stops working in npm 12 - approve bun's postinstall in the setup.sh / setup.ps1 bun bootstrap; under npm 12 defaults npm install -g bun otherwise leaves a broken stub and setup falls back to the slower npm install path - fix the stale esbuild comment in studio-frontend-ci.yml: the vite 8 chain ships napi binaries with no install scripts * Studio: auto-sync allowScripts pins after dependency bumps The allowScripts entries from #6128 are version pinned, so a biome or msw bump strands the pin and the approval silently stops matching. Dependabot cannot maintain the field, so: - scripts/sync_allow_scripts_pins.py re-pins existing entries from the versions package-lock.json actually resolves. It never adds or removes entries, so approving a new script-bearing package stays a human decision. Bare names and non-exact specs are left alone. - a pre-commit hook runs it with --fix; pre-commit.ci pushes the fix commit to PR branches, Dependabot's included, so stale pins heal without a human in the loop - a Frontend CI step runs --check plus the offline unit tests as the backstop when pre-commit.ci is skipped No dependabot.yml change needed: the /studio/frontend entry already suppresses version PRs (security only) behind a 7 day cooldown. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Make the sync hook robust to lost executable bits The pre-commit.ci autofix commit dropped the script's exec bit, which breaks a shebang-style entry. Invoke via python instead and restore the bit. * [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> |
||
|
|
87deee7fbd |
Studio: faithful conversation export and import round trips (ShareGPT system role, CSV quoted newlines) (#6131)
* fix(studio): preserve system role in ShareGPT exports
System messages in ShareGPT conversation exports were serialized as
gpt turns, which changes the semantics of exported training data.
Map role system to from system in both the single-thread and bulk
export paths, matching the importer (sharegptToRecords), which
already maps from system back to a system role.
Extracted from #5606 by @LeoBorcherding (commit
|
||
|
|
3307561f85 |
Studio: npm v12 readiness for install-script gating (#6128)
npm 12 (July 2026) stops running dependency install scripts unless they are approved via allowScripts, and npm 11.16 already warns. Studio has no git or remote URL deps anywhere, so script gating is the only exposure: - commit the allowScripts policy that npm approve-scripts writes for @biomejs/biome and msw, plus a manual fsevents entry: the tooling cannot match a darwin-only optional dep from Linux, but the strict check walks the platform independent ideal tree and flags it anyway - drop the minimum-release-age npmrc alias; npm >=11.16 flags it as an unknown project config that stops working in npm 12 - approve bun's postinstall in the setup.sh / setup.ps1 bun bootstrap; under npm 12 defaults npm install -g bun otherwise leaves a broken stub and setup falls back to the slower npm install path - fix the stale esbuild comment in studio-frontend-ci.yml: the vite 8 chain ships napi binaries with no install scripts |
||
|
|
5f622f6c2f |
fix: clearer Studio setup error when GPU driver is too old for the installed CUDA toolkit (#5993)
* fix: clearer Studio setup error when GPU driver is too old for the installed CUDA toolkit * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Honor optional color arg in setup.sh substep so driver/toolkit warnings render in C_WARN * Add regression test that setup.sh _cuda_version_gt compares numerically for PR #5993 * Update Resolve-CudaToolkit test for the new driver-too-old messaging setup.ps1 now routes the too-new-toolkit case through Write-CudaDriverToolkitMismatch instead of the old 'is installed but INCOMPATIBLE' banner. Extract that helper alongside Resolve-CudaToolkit so the child pwsh can run it, and assert the new driver-too-old guidance (and the one-line source-build error) instead of the removed INCOMPATIBLE text. * Address review nits: document Windows hard-exit asymmetry and add toolkit/driver edge tests setup.ps1: note that only a forced source build reaches the hard-exit branch (the prebuilt path returned above), unlike setup.sh which degrades to CPU. test_selection_logic.py: cover the CUDA UMD Version variant, the empty nvcc version guard, and the too_old (< 12.4) short-circuit. * fix(studio): allow CUDA minor-version compat and try installed toolkits before CPU fallback The driver check now compares CUDA major versions only, per NVIDIA minor-version compatibility, and when the selected nvcc is still too new the setup iterates other installed toolkits and uses the newest driver-compatible one before falling back to a CPU llama.cpp build. Same rule mirrored in setup.ps1. * style: apply ruff kwarg-spacing format after rebase * Accept a same-major CUDA toolkit found only on PATH in the Windows fallback The major-only compatibility fix updated the side-by-side scan (Find-Nvcc -MaxVersion) and the CUDA_PATH check, but the fallback that runs when Find-Nvcc -MaxVersion returns null still recorded any plain Find-Nvcc result as an incompatible toolkit without re-checking the major. A same-major toolkit discoverable only via PATH, process CUDA_PATH, or a custom location (e.g. toolkit 13.3 with a driver supporting CUDA 13.2) was therefore rejected even though it is compatible. Re-apply the same major-only rule in the fallback: use the toolkit when its major is within the driver's, otherwise record it as too-new. Adds a regression test covering the PATH-only same-major case. * Add CUDA driver/toolkit selection edge-case tests for Studio setup * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Tighten comments in Studio CUDA driver/toolkit setup Collapse multi-line comments, drop obvious ones, keep the load-bearing intent (the major-compat invariant, the Windows hard-exit vs setup.sh-CPU asymmetry, the PATH-only fallback rationale). Comment-only; no code change. * Clarify the Windows source-build hard-exit comment The path is reached by any committed source build (forced, or after a prebuilt-install failure), not only a forced one. Comment-only. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Drop two obvious comments in setup.ps1 CUDA detection --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com> |
||
|
|
4d2f29ff2a |
Studio: center account avatar vertically in sidebar footer pill (#6026)
Co-authored-by: shimmyshimmer <shimmyshimmer@users.noreply.github.com> |
||
|
|
256d17e2e1 |
fix(studio): block arbitrary external image URLs in markdown renderer (#5602)
* fix(studio): block arbitrary external image URLs in markdown renderer Model-emitted <img src="http://attacker.com/..."> tags were causing the browser to issue HTTP requests to arbitrary origins, leaking the user's IP address, User-Agent, and Referer header to any domain a prompt-injected model could emit (tracking-pixel vector, issue #5596). Add a urlTransform function passed to <Streamdown> that only allows: - data: URIs (inline images, mermaid SVG, user attachments) - blob: URIs (locally generated object URLs) - relative paths without a scheme (same-origin assets) All other schemes (http:, https:, ftp:, etc.) return null, causing Streamdown to omit the <img> element entirely. Existing iframes are already stripped by Streamdown's default sanitizer; event-handler attributes (onerror, onload, etc.) are also stripped by the default schema. * fix(studio): strip control chars and block backslash URL variants Two bypass vectors found after review: 1. Backslash-normalised URLs: \\attacker.com\pixel has no colon and does not start with // so the earlier guards allowed it as a relative path. Browsers normalise leading backslash pairs to // before resolving, so the request still reaches the external origin. 2. Embedded control characters: /\n/attacker.com passes trim() unchanged, startsWith("//") is false, and no-colon check passes it as relative. Browsers strip ASCII controls (U+0000-U+001F, U+007F) before URL resolution, so the value resolves to the attacker origin. Fix: strip all ASCII control characters from the raw URL before any guard, then block any URL whose normalized form starts with two chars from [/\\] to cover //, \\, /\, and \/ in one regex. * fix(studio): delegate non-image URLs to defaultUrlTransform Returning the raw URL for non-img nodes bypassed Streamdown's built-in link sanitization, allowing model-emitted javascript: hrefs to reach the DOM unfiltered. Pass non-image URLs through defaultUrlTransform so the library's own javascript:/data: sanitization stays active for links. * fix(studio): use scheme regex instead of includes() for colon check A colon anywhere in the URL (e.g. /api/image?id=model:v2 or /snapshots/2026-06-04T12:00:00Z.png) was incorrectly treated as an explicit scheme and the URL was dropped. Replace the includes(':') check with a proper scheme regex that only matches when a valid scheme token appears before any path separator. * Studio: shorten safeImageUrl comments in markdown renderer --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com> |
||
|
|
fcfbf166ff |
studio(ui): use the --primary brand token for the avatar fallback color (#5987)
* studio(ui): use the --primary brand token for the avatar fallback color
The fallback profile avatar hardcoded #14b789, a slightly different green
from the app's general brand color (--primary = #17b88b, used by the send
button and every other primary-colored control). Next to primary-colored UI
-- e.g. the artifact preview/code panel -- the avatar's off-brand shade looked
inconsistent ("changes color weirdly"). Point avatarBgStyle() at
var(--primary) so the avatar always renders the general brand green and
follows the theme token.
Verified live in Studio: the avatar was rgb(20,183,137) (#14b789) while
--primary resolves to rgb(23,184,139) (#17b88b); the fix unifies them. This
is the only hardcoded brand-green left in the frontend -- every other
brand-green element already uses --primary / bg-primary.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* studio(ui): add literal fallback to the avatar --primary token
UserAvatar is a reusable component; if it is ever rendered outside the theme
root (where --primary is undefined), var(--primary) alone would compute to
transparent. Use var(--primary, #17b88b) so the avatar stays branded in that
edge case. When --primary is defined (the normal case, app-wide) it always
wins, so this changes nothing in practice -- verified in a browser:
var(--primary)=rgb(23,184,139), and an undefined var correctly falls back to
the literal.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
03349d1e05 |
feat: support text-only loading of Gemma 3 27B via FastLanguageModel (skip SiglipVisionModel) (#5816)
* feat: support text-only loading of Gemma 3 27B via FastLanguageModel (skip SiglipVisionModel) * test: instantiate text-only Gemma3 model and assert no vision tower Existing tests were AST source-introspection plus a config-resolves-to- text-config check; none actually instantiated a model from the text-only config. Add a small integration test that builds a shrunken Gemma3TextConfig (CPU-cheap), instantiates the matching CausalLM class, and asserts the resulting model exposes the LM head and has no vision_tower or multi_modal_projector attribute. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Deduplicate _get_text_only_config into _utils for PR #5816 * Fall back to full model when a VLM has no text-only class for PR #5816 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Preserve quantization_config and clarify warning for text-only loading for PR #5816 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Only take text-only path when the VLM has its own text decoder for PR #5816 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Convert source string-match assertions to AST checks per Gemini review * Load real VLM text weights on transformers 5.x for text-only mode in PR #5816 transformers >=5 changed Gemma3ForCausalLM base_model_prefix from language_model to model, so a VLM checkpoint's text weights (gemma3: language_model.model.*, gemma3n: model.language_model.*) no longer auto-strip onto the text decoder and were silently initialized random. Add a version-gated key_mapping that remaps them onto the text keys, returning None on transformers <5 where the prefix still strips and a mapping would break the load. Apply the same family-guarded remap on the load_in_fp8 offline path and for direct FastBaseModel callers, and remap quantization llm_int8_skip_modules off the wrapper prefix after stripping. Add a regression test that loads real VLM checkpoint weights (the prior tests only instantiated a fresh model so they missed this) and drop the bitsandbytes dependency from the quantization-config test. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Separate FP8 text-only cache and hoist the text-only guard for PR #5816 Address review of the text-only changes: (1) _offline_quantize_to_fp8 produced different artifacts for text-only vs full VLM but reused the same <name>-fp8-<mode> cache dir, so one mode could load the other's saved model; decide text-only before the cache name and add a -text-only suffix. (2) FastBaseModel.from_pretrained rewrote the VLM auto class to AutoModelForCausalLM before loading auto_config and before the family check, leaving is_vlm wrong for the fast_inference/vLLM block; hoist the family-guarded text-only decision above those checks and drop the redundant later block. (3) Wire the text-only regression test into the curated CPU pytest job so it runs in CI across the transformers matrix. * Trim text-only code comments for PR #5816 Shorten and de-duplicate the comments added for the text-only loading work; keep the non-obvious rationale (the transformers >=5 base_model_prefix change) and drop the obvious parts. Comments only, no code changes; AST-based tests still pass on transformers 4.57.6 and 5.4.0. * Make text-only loading opt-in via a public text_only argument for PR #5816 Rename the internal _force_text_only flag to a public text_only parameter on FastLanguageModel, FastModel and FastBaseModel (and the fp8 helper), defaulting False on all three. Text-only loading is now opt-in (text_only=True) instead of forced on by FastLanguageModel; the family guard and key remap are unchanged. Updated the AST tests for the new parameter and forwarding. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Trim text-only code comments for clarity --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com> |
||
|
|
8848a310df |
Studio: clean-room compact RAG (knowledge bases, hybrid search, fast indexing) (#5910)
Adds a self-contained RAG stack to Studio: knowledge bases with chunked indexing, hybrid (dense + lexical) retrieval, and an automatic first-pass context inject into chat. Embeddings run through a local llama-server GGUF backend (default unsloth/bge-small-en-v1.5-GGUF) with a sentence-transformers fallback. The chat tool loop gains a search_knowledge_base tool, a per-turn re-search cap, and source citation, layered on top of the shared ToolLoopController. |
||
|
|
436525d6de |
Studio: stop the providers dialog from resetting custom provider form state (#6051)
* fix custom provider state * Address provider seeding review feedback --------- Co-authored-by: Lee Jackson <130007945+Imagineer99@users.noreply.github.com> Co-authored-by: imagineer99 <samleejackson0@gmail.com> |
||
|
|
2554636ded |
Studio: follow-up fix for GGUF developer prompts (#6115)
* Studio: merge developer prompts for GGUF chat * [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> |
||
|
|
57be5868f9 |
Studio: improve OpenAI- and Anthropic-compatible API spec compliance (#6010)
* Studio: fix OpenAI- and Anthropic-compatible API spec compliance * Studio: fix API spec-compliance gaps on passthrough and streaming paths * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: carry context_length_exceeded through the OpenAI passthrough error path * Studio: count tool-schema tokens in the Anthropic server-tool stream, and small stream-handling guards * Studio: guard message_delta usage against None and normalize developer role before proxying * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: honor max_completion_tokens on the external-provider proxy path * Studio: forward llama-server cached_tokens into OpenAI prompt_tokens_details * Studio: sanitize messages in count_tokens to match the /v1/messages prompt * Studio: report max_tokens for truncated tool calls and guard null usage in metadata events * Studio: drop the request-id middleware (headers aren't declared in either spec) * Studio: include the required request_id field in Anthropic error bodies * Studio: honor max_completion_tokens on the audio (TTS / audio-input) paths * Studio: add the _effective_max_tokens helper and route all max-token sites through it * Studio: align API compatibility edge cases * Studio: clarify multi-choice chat support * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: clarify logprobs chat support * Studio: opt the local chat UI into the streaming usage chunk so the context bar and tok/s repopulate * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: forward seed to llama-server, and fix Anthropic server-tool stop_reason, tool_result id correlation, and parallel-tool execution cap * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: align OpenAI chat completion spec edge cases * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: align backend API compatibility tests * Studio: honor tool caps and internal stream usage * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: coerce nullable stream usage counts * Studio: preserve system prompts with developer messages --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: wasimysaid <wasimysdev@gmail.com> |
||
|
|
a01960f20f |
[pre-commit.ci] pre-commit autoupdate (#6104)
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.15 → v0.15.16](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.15...v0.15.16) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> |
||
|
|
ccb471f5bf |
Improve local chat tool call flow (#5962)
Unify the Studio local tool-call loop (GGUF + safetensors) behind a shared ToolLoopController: ordered preface-then-tool-card rendering, duplicate-call de-looping with a forced final answer, XML-leak containment, and a parser fix that accepts closed <function=...> calls followed by trailing prose. Includes backend tests for the controller, strict parser, and GGUF route cursor reset. |
||
|
|
0d6d7dd4b3 |
Studio: make Helper LLM startup pre-cache opt in (#6113)
* Studio: make Helper LLM startup pre-cache opt in * [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> |
||
|
|
33f4397b78 |
Studio fix recipe dataset preview (#6031)
* Studio: fix recipe dataset preview * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [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> |
||
|
|
aec41d17ed |
feat(studio): Hub + Download Manager (#5916)
Adds the Studio Hub and download manager: browse Hugging Face models and datasets, download GGUF and safetensors with live progress and cancellation, and manage on-device inventory. The Hub does not require a GPU, so it is available on chat-only hosts. CI: all substantive checks pass, including the three Core jobs after unsloth-zoo#736. The two red checks are non-code flakes, a transient npm-registry DNS resolution failure in the package scan and one quantized vision-model output assertion whose sibling shards passed. |
||
|
|
85314ed162 |
Studio frontend: reduce and tighten code comments (#6099)
Trim and tighten code comments across studio/frontend TS/JS. Comment-only: every changed file verified code-identical to main via the TypeScript printer signature comparison. |
||
|
|
187144d4e7 |
Reduce and tighten code comments and docstrings repo-wide (#6095)
Trim and tighten code comments and docstrings across the repository. Comment-only: every changed file verified code-identical to main via AST/token comparison. |
||
|
|
8292e699e4 |
Studio: make code comments and docstrings more succinct (#6029)
Trim and tighten code comments and docstrings across studio/ Python. Comment-only: every changed file verified code-identical to main via AST/token comparison. |
||
|
|
ebf28e7e07 |
Studio: open the MCP dialog to the server list so servers can be managed (#6100)
Co-authored-by: Lee Jackson <130007945+Imagineer99@users.noreply.github.com> |