mirror of
https://github.com/unslothai/unsloth.git
synced 2026-08-25 00:33:49 +00:00
318 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
08853803d9
|
Update README to include Intel XPU support description for unsloth studio (#9250) | ||
|
|
6371f46a99
|
Update README.md | ||
|
|
ea687ef520
|
studio: add settings-managed LAN access (#8951)
* studio: add settings-managed LAN access Studio binds 127.0.0.1, so reaching it from a phone on the same Wi-Fi meant relaunching with -H 0.0.0.0, which the desktop app cannot do at all. Settings > API keys > LAN access now adds a second uvicorn listener over the running app, bound to each detected non-loopback IPv4 at the same port, on the primary server's event loop with lifespan="off". The loopback socket is untouched, so the desktop app keeps working while it is on. lan_access.py owns the listener: address detection, binding, and a teardown that waits on the sockets rather than on serve(), since uvicorn closes the passed sockets at the top of shutdown and only then drains in-flight responses. Waiting on the serve task made a Stop pressed from a LAN device wait out its own response for the full timeout. utils/lan_access_settings.py holds the launch policy and the persisted lan_access_auto_start preference. Start is blocked on Colab, on a --secure launch (which forces the loopback bind so the raw port is never exposed), on a launch that already binds the network, and until the seeded admin password has been changed. main.py's desktop SPA gate now opens for requests arriving on a LAN listener socket, identified by scope["server"] rather than any client header, so a desktop api-only backend serves its packaged web UI over LAN without changing the local api-only surface. host_policy tracks the LAN listener alongside the Cloudflare tunnel, so turning it on suspends the loopback stdio-MCP default. The startup banner reports the bound addresses when a persisted auto-start has already brought the listener up, instead of claiming loopback only. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * studio: keep LAN listener ownership until the port is confirmed closed A stop that timed out dropped every reference to the server and sockets before returning False, so the wait could never be retried and a second stop found _server None, reported success, and cleared both the stop_timed_out error and the beyond-loopback trust flag while the port could still be accepting. The references are now retained on that path, so the status keeps offering Stop and a retry waits on the same sockets. start_lan_access also marks the LAN connector active before the listener can accept, rolling back if startup fails. A request served between the socket accepting and the flag being set would otherwise read the loopback-only stdio MCP default in core/inference/mcp_client.py. * studio: keep the LAN trust flag under the listener lock start_lan_access and stop_lan_access are sync routes, so FastAPI runs them in separate worker threads. Publishing the beyond-loopback flag from those callers left it outside lan_access._lock: a stop that observed no server could set the flag false between a concurrent start setting it true and that start acquiring the lock, leaving a live LAN listener while remote_connector_active() read false and core/inference/mcp_client.py still allowed the loopback-default stdio MCP transport. The flag now changes only where the listener state does, under the same lock: start_lan_listener sets it before the socket can accept and rolls it back if startup fails, and _release_listener_state clears it with the references. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * studio: enumerate interfaces for LAN access and hold ownership through loop shutdown detect_lan_addresses only had the route to 8.8.8.8 and a hostname lookup. The probe yields one route-selected source address and hostname resolution is not an enumeration, so a multihomed host lost its second adapter and an isolated LAN with no default route reported no_lan_address while having a usable address, the Linux name-to-127.0.1.1 mapping making it worse. It now enumerates IPv4 addresses on every interface that is up via psutil, keeping the default-route address first so it stays the shown URL, and falls back to the hostname lookup only when psutil is unavailable. stop_lan_listener no longer releases ownership on the event-loop branch. /api/shutdown reaches it from a task on the serving loop, and uvicorn cannot close the sockets until that loop is free, which _graceful_shutdown then holds for seconds while it stops the inference, export and training subprocesses. Only _bound_addresses is dropped there, so the frontend gate closes at once while the sockets and the beyond-loopback trust flag stay owned until they really close. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * studio: hold LAN trust until the stopped listener's requests drain Closing the listening sockets stops new connections, but uvicorn then drains the ones it already accepted. Clearing the beyond-loopback flag at that point let a LAN request still executing reach stdio_mcp_enabled() in core/inference/mcp_client.py and be treated as loopback-only, so a remote caller could spawn stdio MCP subprocesses under the local default. Stopping LAN access while a long inference from a phone is in flight is an ordinary way to hit it. The confirmed-close path now releases the listener references without the flag and hands it to a drain watcher, which clears it once server_state.connections empties. The watcher leaves the flag active if the connections never drain, and skips the clear when a new listener has since taken ownership. The Stop response itself still returns as soon as the sockets close. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * studio: derive the LAN trust flag from listener and drain ownership A second stop arriving after the first closed the sockets but while accepted requests were still draining found _server already None and released the flag outright, so those still-remote requests passed the loopback-default stdio MCP gate. The idempotent stop path bypassed the drain watcher entirely. The flag is no longer assigned by any path. _sync_lan_trust publishes it from the authoritative state, a live listener or any stopped listener still draining, counted by _pending_drains and taken under the same lock as the references. A watcher whose connections never drain keeps its count rather than releasing, so the flag fails closed. start_lan_listener still raises it explicitly before the socket can accept. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * studio: release bound sockets when the listener cannot be scheduled asyncio.run_coroutine_threadsafe raises RuntimeError("Event loop is closed") if the primary loop closes between _server_loop validating it and the schedule call. That escaped past the failure cleanup with every socket already bound and the trust flag already raised, so the port stayed listening with nothing serving it, status reported no listener, later starts hit bind_failed, and the flag stayed on until the process exited. The cleanup is now shared by both start failure paths in _fail_start, which closes the sockets, resyncs the trust flag from ownership and records listener_start_failed. The unscheduled coroutine is closed so the failure does not also emit a never-awaited warning. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * studio: unblock a loop-side LAN stop and release the listener when its loop ends start_lan_listener holds _lock while waiting for the serving loop to run serve(). /api/shutdown reaches stop_lan_listener from a task on that same loop, so blocking on the lock left the two waiting each other out: the loop could not advance the scheduled listener, the start burned its full timeout and shutdown stalled with it. A stop running on the serving loop now takes the lock without blocking and reports an unconfirmed stop instead. The event-loop branch keeps ownership so uvicorn can close the sockets once the loop is free, but nothing released it afterwards. An embedded host that calls run_server again in-process therefore saw a stale _server, reported the previous addresses as online and never bound a new listener. run.py's server thread now releases the listener in the same finally that closes the loop, which reaches the loop-gone path and drops the sockets, references and trust flag. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * studio: advertise only reachable LAN origins --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Etherll <61019402+Etherll@users.noreply.github.com> |
||
|
|
5bb8a6f5bf
|
Update README.md | ||
|
|
c9cda7bf32
|
Update README.md | ||
|
|
6f443b5ccd
|
docs: fix dead Linux .deb download link (#8891)
Some checks failed
Wheel CI / Wheel build + content sanity + import smoke (push) Waiting to run
Windows Unsloth GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Waiting to run
Windows Unsloth GGUF CI / real-VS detection (VS 2022) (push) Waiting to run
Windows Unsloth GGUF CI / real-VS detection (VS 2026) (push) Waiting to run
Windows Unsloth GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Waiting to run
Windows Unsloth GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Waiting to run
Windows Unsloth GGUF CI / GGUF inference smoke (API, tools, vision) (push) Waiting to run
Windows Unsloth UI CI / Chat UI Tests (push) Waiting to run
Windows Unsloth Update CI / Unsloth Updating Tests (push) Waiting to run
Windows Application Control CI / installer survives a denied unsloth.exe (push) Waiting to run
Workflow trigger lint / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Waiting to run
Local Agent Guides CI / resume (pi) (push) Has been cancelled
Local Agent Guides CI / prompt-cache (gemma-3-270m) (push) Has been cancelled
Kaggle T4 Studio GPU CI / gate (push) Has been cancelled
Local Agent Guides CI / connection (claude) (push) Has been cancelled
Local Agent Guides CI / connection (codex) (push) Has been cancelled
Local Agent Guides CI / connection (hermes) (push) Has been cancelled
Local Agent Guides CI / connection (openclaw) (push) Has been cancelled
Local Agent Guides CI / connection (opencode) (push) Has been cancelled
Local Agent Guides CI / connection (pi) (push) Has been cancelled
Local Agent Guides CI / file-edit (claude) (push) Has been cancelled
Local Agent Guides CI / file-edit (codex) (push) Has been cancelled
Local Agent Guides CI / file-edit (hermes) (push) Has been cancelled
Local Agent Guides CI / file-edit (openclaw) (push) Has been cancelled
Local Agent Guides CI / file-edit (opencode) (push) Has been cancelled
Local Agent Guides CI / file-edit (pi) (push) Has been cancelled
Local Agent Guides CI / resume (claude) (push) Has been cancelled
Local Agent Guides CI / resume (codex) (push) Has been cancelled
Local Agent Guides CI / resume (opencode) (push) Has been cancelled
Kaggle T4 Studio GPU CI / Studio GPU smoke (push) Has been cancelled
|
||
|
|
ba466ca095 | Update README.md | ||
|
|
72e72572f8
|
Update README.md | ||
|
|
d5a2160ef8
|
Say ROCm does not cover RDNA 1 instead of advising a fix that cannot work (#8577)
* Say ROCm does not cover RDNA 1 instead of advising an impossible fix An RX 5700 XT (Navi 10, gfx1010, RDNA 1) correctly lands on CPU PyTorch: AMD publishes Windows torch indexes for gfx103X, gfx110X, gfx1150, gfx1151 and gfx120X, and there is no gfx101X index. Because the name-inference table covers only arches that have wheels, the arch stayed null and the installer fell into the "arch unknown" arm, which tells the user to install the HIP SDK or set UNSLOTH_ROCM_GFX_ARCH. Neither can work: UNSLOTH_ROCM_GFX_ARCH=gfx1010 lands on the unmapped-arch path and returns CPU anyway. Add a separate name lookup for AMD generations ROCm PyTorch does not cover, read only to word the report. Product names come from LLVM's AMDGPU GFX10.1 processor table. The lookup never sets the arch the installers route on, so CPU fallback is reached by exactly the same path as before. Mirrored across install.ps1, studio/setup.ps1, install.sh, studio/setup.sh and studio/install_python_stack.py so every install path agrees. * Point pre-RDNA 2 AMD users at the Vulkan llama.cpp path The previous commit stopped at "ROCm does not cover this GPU", which is true and still a dead end. There is a working path: llama.cpp's Vulkan bundle drives these cards, which is how #8458's reporter got an RX 580 running and how LM Studio drives the same hardware. Nothing routes these users there automatically. _should_auto_vulkan_for_amd_windows opens with `active = _active_rocm_gfx_target(host); if not active: return False`, and a pre-RDNA 2 card resolves to no gfx target at all, so the Windows auto-Vulkan fallback structurally cannot fire for exactly the cards that need it. The environment variable is their only route, so the message now names it. Two things about that advice are load-bearing and both are tested: - The current spelling, UNSLOTH_LLAMA_CPP_BACKEND=vulkan. The legacy UNSLOTH_FORCE_VULKAN still works but force_vulkan_requested() resolves the new variable first and consults the legacy one only when the new one is absent or unparseable, deliberately, so =hip stays a real opt-out that a stale legacy variable cannot overrule. New text must not spread the legacy name. - WHEN to set it. The variable picks the llama.cpp bundle at install/download time; nothing reads it at runtime to choose a binary. #8458's reporter set it after installing, saw no change, and only a clean reinstall worked. Advice that names the variable without naming the moment is worse than none. Also adds Polaris 10/20/30 (RX 470/480/570/580/590, gfx803) to the messaging-only table so #8458's card gets the right message. gfx803 stays out of _GFX_TO_AMD_INDEX_ARCH and every supported-arch table, and routing is untouched; a test pins that directly. Polaris 11/12 (RX 460/550/560) is left out because gfx803 vs gfx804 could not be confirmed for that die, and this table is only worth having while it never guesses. "RX 570" is a prefix of "RX 5700" and "RX 550" of "RX 5500". Python and PowerShell carry (?!0) lookahead guards; POSIX `case` has no lookahead, so in install.sh and studio/setup.sh correctness rests on arm order and the RDNA 1 arms come first. That order is now documented and asserted, and the shipped arms are evaluated in a real shell rather than checked by eye. README leads with the current spelling, since the installer now names a variable and the README is where users check it. Finally, test_cpu_index_note_respects_explicit_pin asserted a pin check appeared within a character window before a note. That is a budget on intervening source, not the ordering property it is for, and this work had already pushed it from 400 to 1400. It now walks the enclosing if/elif chain by indentation, so it tests order and has no distance left to re-tune. * Stop the HIP SDK arm outranking the pre-RDNA 2 message, and fix its advice An RDNA 1 or Polaris user who already installed the HIP SDK never saw the new message: the $HipSdkInstalled arm sits earlier in the chain and told them the ROCm compute driver was missing, which is the impossible remediation this change exists to remove, and those users installed the SDK because the old advice said to. Guard that arm, plus the matching CPU-hint arm in install.ps1, on the unsupported arch. The Vulkan setter was printed as UNSLOTH_LLAMA_CPP_BACKEND=vulkan by the two PowerShell installers and by the Windows-only branch in install_python_stack.py. PowerShell parses that as a command name, so a user who pastes it sets nothing and the next install picks the same CPU bundle. Print $env:UNSLOTH_LLAMA_CPP_BACKEND = "vulkan" there, as the README already does. The arms also said PyTorch training runs on CPU on these GPUs. It does not: with no CUDA or XPU accelerator, unsloth raises NotImplementedError at import, which is why studio/setup.sh already tells its other CPU-torch hosts that training and GPU inference are unavailable. Say the same thing here. README: Vega 20 (Radeon VII, MI50, gfx906) is older than RDNA 2 and does have a ROCm PyTorch path (install.sh routes it to rocm6.3), so name Polaris and RDNA 1 instead of every pre-RDNA 2 AMD GPU. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Guard the README gfx906 carve-out against any spelling of the cutoff The ban was on one exact literal, so "every AMD GPU older than RDNA 2" passed while contradicting the Vega 20 carve-out two sentences later. Match the phrase family instead, and assert the group is named by its members (Polaris, RDNA 1) rather than by a generation cutoff. * Tighten the comments added by this PR Comments only, no code or user-facing string touched. Every reason a guard exists is kept, just said in fewer lines. * Keep the note on why the rationale sits above the arm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: initialise the unsupported-arch state outside the AMD detection block The ROCm summary reads $script:ROCmUnsupportedGfxArch unconditionally, but the assignment sat inside `if (-not $HasNvidiaSmi)`, so an NVIDIA host never defined it and a caller's Set-StrictMode turned the summary into an aborting undefined-variable error. ROCmGfxArch beside it was always initialised at top level; this one was not. The guard asks the PowerShell parser whether any assignment is unnested rather than comparing line numbers: the file has three -not $HasNvidiaSmi blocks, so an ordering check picks the wrong one and passes for the wrong reason. * Name the remaining RDNA 1 boards, diagnose the KFD-only host, and keep mixed AMD hosts on the arch-unknown advice Adds the Navi 10 / Navi 14 professional boards LLVM's processor table omits (Radeon Pro W5700/W5700X -> gfx1010, Pro W5500/W5500M/W5300M and RX 5300/5300M -> gfx1012) to all five copies of the unsupported-name table. Each mapping comes from libdrm data/amdgpu.ids read against pci.ids and the kernel amdgpu PCI table, not from a guess; the tables still route nothing. studio/setup.sh's KFD sysfs fallback detects the GPU without rocminfo or amd-smi, so it left the marketing name empty and the report fell through to a plain AMD ROCm line on a host with no ROCm. It now reads lspci for that report only, never writing it back into the name the supported table and --rocm-gfx key on. install.ps1's WMI fallback classifies adapter 0 only, so a host pairing an RX 5700 with an RX 7900 was told nothing could enable ROCm, which is false there. The verdict is now withheld when another adapter is covered, leaving the arch-unknown advice that does apply. install_python_stack.py and studio/setup.ps1 already scored every adapter. * Scope the uncovered-arch verdict to the card it names, and name the boards it was missing A host is not one GPU. On a box pairing an uncovered card with one that has wheels -- an RX 580 beside an RX 7900 XTX, or beside an Instinct MI210 -- "setting UNSLOTH_ROCM_GFX_ARCH will not enable ROCm PyTorch" was false: masking to the other card and pinning its arch installs them, and install.sh routes exactly that host to gfx110X-all a few lines earlier. Every advice site now says what is true of the card it just named and claims nothing beyond it. Deciding it at runtime was tried and dropped. Reading "an AMD adapter neither table names" as a working peer misfires on the Vega-class iGPU (Raven through Cezanne, Mendocino) that sits beside the dGPU on most Ryzen desktops and has no ROCm torch path of its own, which would trade a correct dead stop for the open-ended errand this change exists to remove. Reading only the supported table misses the Instinct and V620 parts that are routable and appear in no name table at all. Neither rule is right often enough to speak for a host. Four real boards are added to all five copies of the message-only table: Radeon Pro 5700 / 5700 XT (pci.ids 7319 and 731b, Navi 10, gfx1010) are the only Navi 10 retail parts whose name carries neither "RX 5700" nor a W prefix, and Radeon Pro WX 7100 / WX 5100 (Ellesmere, gfx803) carry no RX number at all, so both fell through to the generic advice. Provenance from pci.ids as before, and the shell case arms are matched case-sensitively, which is now stated where only the arm ordering was. Tests: - The absolute host-wide phrasings are banned from all five sources, with the scoped replacements required per site so deleting the sentence cannot pass. - test_unsupported_arch_routing_guards_8529.py drives the real index resolvers rather than asserting table shape: install.sh's get_torch_index_url and _amd_arch_index_family_for_gfx under sh, the .ps1 family maps under pwsh (including writes after the declaration, and the -contains list), the Python resolvers on both platforms, the Strix per-arch reroute, and studio/setup.sh's report-only lookup. Positive controls throughout. - _assert_guarded_by_pin_arm matches the pin arm exactly rather than by prefix, binds the message's own enclosing arm, requires the pin arm to still say its note, ignores comment lines, and detects inline and re-indented chain closes. Mutation tested: 13 mutants covering a post-declaration map write in each .ps1, a single-quoted arch in _rocmWheelArches, a bypass inside get_torch_index_url, the Strix arm, feeding the unsupported lookup into _setup_gfx directly and one hop later, an inline-closed pin chain, a re-indented fi, and removing each new table row. All killed. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Say whose wheels are missing: AMD ships RDNA 1 PyTorch, Unsloth does not install it "No ROCm PyTorch wheels exist for that arch" is no longer true of RDNA 1. AMD's TheRock lists device-gfx1010, device-gfx1011 and device-gfx1012 as installable torch extras on its multi-arch index, and SUPPORTED_GPUS.md marks all three Build Passing, Sanity Tested and Release Ready. gfx803 is absent from that table entirely, and no GCN4 family appears at all, so the Polaris half stands. The claim these installers can honestly make is about their own routing, not about ROCm at large: repo.amd.com publishes gfx103X/110X/1150/1151/120X and nothing for gfx101X or gfx80X, so UNSLOTH_ROCM_GFX_ARCH=gfx1010 still lands on the unmapped path and still returns CPU. Every site now says Unsloth has no wheels for the arch rather than that none exist, and the tables carry a note saying why the wording is scoped. Routing, the CPU fallback and the Vulkan advice are all unchanged. Verified by running the merge base and this branch side by side under identical stubbed hardware, and diffing: - POSIX shell, 127 simulated hosts x 2 blocks = 254 rows, each run in both trees. Selected torch index URL differed in 0 rows, exit code in 0 rows, get_torch_index_url stdout in 0 rows. The 32 rows whose end-of-run summary text moved are all AMD arch-unknown hosts resolving to gfx1010/1011/1012/803. Covers linux/wsl/macos/aarch64, NVIDIA at five CUDA levels, 15 supported gfx arches, multi-GPU lspci mixes, every override, dash and bash, set -eu, and lspci absent/failing/hanging. Asserted separately that the new WARN lines go to stderr, so TORCH_INDEX_URL=$(get_torch_index_url) is never polluted: 0 of 127 index rows had anything but one URL on stdout. - PowerShell, 232 cells over 116 adapter inventories x install.ps1 and setup.ps1. Resolved arch, index URL, arch family, routing flag and the gfx handoff are identical in every cell. 80 cells changed text, all RDNA 1 or Polaris. An RDNA 1 card beside an RX 7900 keeps the old wording and still resolves gfx1100, which is the covered-peer guard doing its job. Under Set-StrictMode the new code passes only because of the variable initialisation added outside the detection block; removing it fails. - Python stack, 23034 value comparisons over 5 platforms x 4 GPU classes x 38 arch inputs x 99 adapter names x masks x overrides x 6 mirror configs. 0 value differences. No unsupported arch produced an AMD index URL by any path in either tree, and no supported arch changed URL. A negative control that adds gfx1010 to the routing map reports 83 differences, so the zero is real. - Existing installs: a manifest written by the old code verifies identically under the new code and vice versa, over 12 write/read combinations; a manifest poisoned with gfx_arch and index-url keys changes no verdict, because nothing arch-shaped is persisted. The legacy UNSLOTH_FORCE_VULKAN resolves identically across all 90 combinations with UNSLOTH_LLAMA_CPP_BACKEND, including falsey values and =hip overriding a stale truthy legacy value. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Declare the unsupported-arch variable where its readers can see it $ROCmUnsupportedGfxArch was declared inside `if (-not $HasNvidiaSmi)` in install.ps1, but the arms that read it sit outside that gate, so on an NVIDIA host the read is of a variable that was never assigned. Its five neighbours (HasROCm, HipSdkInstalled, ROCmGpuLabel, ROCmVersion, ROCmGfxArch) are all declared above the gate, and studio/setup.ps1 already hoists its own copy for exactly this reason, so this one was the odd one out. Harmless as shipped, because Install-UnslothStudio runs with Set-StrictMode off. Under a caller's `Set-StrictMode -Version Latest` it is a hard stop: driving the extracted blocks under pwsh, an NVIDIA host that lands on the /cpu leaf (a pre-CUDA-11 driver, or UNSLOTH_TORCH_INDEX_URL pinned to cpu) throws on the read. Found by running this branch and its merge base side by side over 240 Windows cells; every resolved arch, index URL, arch family, routing flag and gfx handoff matched, and this was the only asymmetry that was not message text. A test pins the declaration above the gate in install.ps1 and at script scope in setup.ps1. Moving it back inside the block fails the test. * Let an identified uncovered card outrank the generic ROCm report, and teach export Two review findings, both reproduced first. amd-smi can report a GPU with no gfx token anywhere in `list` or `static --asic` and only a market name. That sets $HasROCm with no arch, so the generic `} elseif ($HasROCm)` arm fired and called an RX 5700 XT "AMD ROCm (AMD Radeon RX 5700 XT)" while the wheel note in the same run said gfx1010 has none. Driving the real detection and step chain under pwsh with a stubbed amd-smi reproduces it exactly, and the host is not hypothetical: amd-smi is only probed when the HIP SDK is present, which is what the #8529 and #8458 reporters installed because the old message told them to. Both scripts now carry the same `-and -not $ROCmUnsupportedGfxArch` guard the HIP SDK arm below already had. A supported gfx1100 host and an unmapped Instinct MI210 host are unchanged on the same harness, since the guard is a no-op when no arch was identified. The POSIX advice said to `set UNSLOTH_LLAMA_CPP_BACKEND=vulkan` and re-run the installer. A bare assignment is a shell variable, not an environment entry, so the installer subprocess never sees it and the user gets the CPU bundle again: $ sh -c 'UNSLOTH_LLAMA_CPP_BACKEND=vulkan ./installer' -> installer sees: [<unset>] $ sh -c 'export UNSLOTH_LLAMA_CPP_BACKEND=vulkan ./installer' -> installer sees: [vulkan] That is the #8458 failure mode reintroduced by the fix for it. The README block has always used export; the three POSIX message sites now agree with it. The PowerShell sites already used `$env:`, which is the process environment, so they were correct and are untouched. Tests pin both: the emitted POSIX setter is now `export ...`, and each generic ROCm arm must carry the unsupported guard. Dropping either guard fails. * Guard the ROCm summary chain the same way its two siblings are The summary at studio/setup.ps1 opens with a bare `if ($HasROCm)` rather than an `} elseif`, so it was missed when the other two chains were guarded. Its own third arm names the uncovered card, and that arm is only reached when nothing outranks it. On a host where amd-smi enumerates an RDNA 1 card with no gfx token, the "ROCm x.y" arm wins and the arm written for that card never runs, so the same run reports ROCm here and no wheels below. The existing check only matched arms opening with `} elseif ($HasROCm`, which is why it walked past this one. The new test finds the chain by its own body. * Keep banning the bare POSIX setter in the PowerShell sources Requiring `export` in the .sh advice was done by redefining _POSIX_SETTER, which is also the needle two PowerShell bans read. With the export folded in, a .ps1 that printed a bare UNSLOTH_LLAMA_CPP_BACKEND=vulkan no longer matched either ban: a mutant adding exactly that line passed all 261 tests in the file. Split the two. _POSIX_ASSIGNMENT is the bare form the Windows sources must never print, and _POSIX_SETTER stays the exported form the POSIX ones must teach. The same mutant now fails, and reverting either export still fails. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Tighten the comments on the unsupported-arch path Final pass over the comments this branch added. The five copies of the table header each repeated the same four points at four different lengths, so they are now one block of the same wording everywhere, and the provenance and scoped-claim notes are folded into it rather than trailing it. Same for the report arms and the test rationales: no reason removed, fewer lines to read. Comments, docstrings and wrapping only. AST-checked with comment_tools.py (4/4 code-unchanged), `bash -n` on both POSIX scripts, and the PowerShell parser on all three .ps1 files. Suites re-run: 1036 passed, 1 skipped, and the pwsh behavioural suite green. * Blame the right card in the CPU summary, and stop promising macOS Vulkan Two more review findings, both reproduced first. The end-of-run CPU summary calls the lspci lookup unconditionally, so unlike the arm in get_torch_index_url it is not covered by the empty-probe gate. On a host pairing an RX 5700 with an RX 7900, a CPU fallback caused by the 7900's ROCm being older than 6.0 was attributed to the 5700, replacing the "upgrade ROCm" advice with advice that is false for the card that actually caused it. Running the shipped guard under sh with a stubbed lspci, only that host moves: lone RX 5700 uncovered-card message -> unchanged lone RX 580 uncovered-card message -> unchanged lone RX 7900 generic message -> unchanged lone MI210 generic message -> unchanged RX 5700 + RX 7900 uncovered-card message -> generic message The summary now asks _infer_linux_amd_gfx_arch, which scans every display adapter, and stays quiet when any of them is covered. Same shape as the peer guard install.ps1 already carries. The README's Vulkan paragraph sat under the combined "macOS, Linux, WSL" heading. macOS has no Vulkan llama.cpp bundle: install_llama_prebuilt.py logs that the variable is ignored and installs the Metal build, and upstream ships no macOS Vulkan asset either (Metal is the default there, and Vulkan on macOS only exists through MoltenVK, which you have to build yourself). An Intel Mac carrying one of these very cards, the 16-inch MacBook Pro shipped the Radeon Pro 5300M/5500M/5600M, would follow the command and get nothing. The paragraph now names Linux and WSL and macOS gets its own sentence. The installer itself needed no change: get_torch_index_url returns before any AMD probe on Darwin and the summary is already gated on it, so the advice was never emitted there. Both guards are pinned by tests that fail when the guard is dropped. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Tighten the comments added in the last pass * Guard the Studio report's peer scan, explicit index pins, and Windows ARM64 Three more review findings, all reproduced first. studio/setup.sh had the same misattribution I fixed in install.sh last round, on the KFD path where neither rocminfo nor amd-smi answers and the lookup falls back to lspci. First match wins, so a host whose RX 5700 enumerates before an RX 7900 was told no override could help, which is false there. The supported name table is now a matcher, _setup_supported_gfx_from_name, with its arms byte-identical to before, so the scan can ask about a peer without touching $_setup_gfx. Same fixtures as the install.sh guard, run under sh: lone RX 5700 / RX 580 named -> unchanged RX 5700 + RX 7900, either order named -> quiet RX 580 + RX 7900 named -> quiet An explicit UNSLOTH_TORCH_INDEX_URL or _FAMILY reaches the ROCm install path for any gfx*/rocm* leaf, so "torch stays CPU-only and neither the HIP SDK nor UNSLOTH_ROCM_GFX_ARCH changes that" was false on a pinned run. install.sh's CPU note already skipped its guidance when pinned; install.ps1, studio/setup.ps1, studio/setup.sh and install_python_stack.py now agree with it. install.ps1's second site needed nothing, since it already sits behind -not $ROCmIndexUrl. studio/setup.ps1 throws on UNSLOTH_LLAMA_CPP_BACKEND=vulkan on Windows ARM64, where no Vulkan bundle is published, so the advice aborted the next update instead of enabling GGUF acceleration. Both PowerShell sites now branch on Get-HostMachineArch and point at a source build there. The advice-window test had to change with them: the claims now sit in if/else arms, and a fixed 8-line window either stopped mid-branch or spilled into the next arm, which is the failure its own docstring warns about. It walks to the end of the enclosing arm instead, capped. Both bash table-parity tests follow the matcher's new variable names and still compare the same rows. Each guard is pinned by a test that fails when it is dropped: the peer loop, the pin check, and the Get-HostMachineArch call were each mutated and killed. * Cut the comment lines that restated the message below them * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fill the Windows peer list on the amd-smi path, and three smaller corrections Four more review findings, all reproduced first. install.ps1's WMI scan sat behind `if (-not $HasROCm)`. amd-smi can report GPUs with no gfx token and only the first market name, which sets $HasROCm with no arch, so the scan was skipped and the peer guard added last round saw an empty list on exactly the multi-GPU host it exists for. It is now keyed on the arch, which is the condition under which the unsupported lookup can run at all: a host that already has an arch still does no WMI work here, and amd-smi's label still wins when it had one. The pwsh suite asserts the gate through the AST. studio/setup.sh returned the failure of a nonempty market name immediately, so a generic "AMD Radeon Graphics" from rocminfo ended the lookup before the lspci scan and the report fell back to the plain "AMD ROCm" line this change is meant to replace. It now returns only on a hit; a name that maps still short-circuits without touching lspci, and the peer guard still covers the mixed host. install_python_stack.py prints the same Vulkan advice as install.ps1 on the Windows WMI path, and the same ARM64 throw applies to it. Added _is_windows_arm64(), mirroring Get-HostMachineArch down to the PROCESSOR_ARCHITEW6432 case an emulated x64 Python needs. The setup.sh pin check treated a whitespace-only value as a pin, while get_torch_index_url trims both variables and treats a blank one as unset. It is trimmed the same way now. The variable also collided with the XPU block's _setup_pin, which is a global in POSIX sh, so it is renamed _setup_unsup_pin. One existing test asserted the behaviour the second finding calls a bug, that an unrecognised reported name claims nothing. Rewritten to the corrected intent, keeping a case that proves a covered card is still never claimed from lspci. test_windows_amd_gpu_scan_fallback.py extracts the WMI block by its literal gate, so its anchor follows the new condition. Each guard is pinned by a test that fails when it is dropped: all four were mutated and killed. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cover the Python emitter in the ARM64 Vulkan-offer guard * Fill the Studio peer list on the amd-smi path too studio/setup.ps1 carries the same WMI scan install.ps1 does, for its own name inference, and it had the same gate. amd-smi can report GPUs with no gfx token and only the first market name, which sets $HasROCm with no arch, so the scan was skipped, $script:ROCmGpuLabels held one name, $gpuNames was one entry, and a host pairing an RX 5700 with an RX 7900 was judged entirely on the 5700 even with HIP_VISIBLE_DEVICES=1 selecting the 7900. The scan is now gated on the arch, which is the condition the inference block below already runs under, so the two can no longer disagree about whether there is anything to infer from. A host that already has an arch still does no WMI work here, and amd-smi's label still wins when it had one: only the peer list is new on that path. The pwsh suite walks the AST from the $script:ROCmGpuLabels assignment to its enclosing if and asserts the condition names the arch and not $HasROCm, as it already does for install.ps1. Restoring the old gate fails it. tests/test_windows_amd_gpu_scan_fallback.py extracts that block with a regex anchored on the literal gate, so its pattern follows the new condition. * Run the covered-peer guard before the named hit too The guard added last round only covered setup.sh's lspci fallback, so a named hit still walked past it. amd-smi reports one market name, the first device's, so on a host whose RX 5700 precedes an RX 7900 the name IS the uncovered card and the false verdict came back through the other door. The scan now runs first, for both paths: lone RX 5700, named named -> unchanged lone RX 580, named named -> unchanged RX 5700 + RX 7900, named 5700 named -> quiet RX 5700 + RX 7900, no name quiet -> unchanged It must not become a silencer, so it applies only when lspci can answer: with no adapter list there is no peer to find, and the single-card host this report exists for still has to be told. A test drives the lookup with lspci off PATH and requires the verdict to survive. Putting the named hit back in front fails the two mixed-host cases while that no-lspci control still passes, so the ordering is what is pinned, not just the presence of the guard. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Tighten the comments added since the last pass * Ask CIM for the adapter list, not the cmdlet PowerShell 7 dropped install.ps1:3331 was the only live Get-WmiObject call left in the file; every other WMI query in it already asks Get-CimInstance, and the script handles PSEdition Core explicitly. Get-WmiObject was superseded by Get-CimInstance in PowerShell 3.0 and removed outright in PowerShell 7, so on pwsh the call threw, the block's own catch swallowed it, and $wmiAmdNames came back empty. That is the peer list the guard added two rounds ago reads, so on pwsh the guard could never fire and the amd-smi path went back to blaming the uncovered card. Same class, same properties, and the CIM cmdlets ship with 5.1 as well, so the swap costs nothing downlevel and matches what studio/setup.ps1 already does. The harness stubbed Get-WmiObject, so it was answering for a cmdlet the installer no longer calls. It now stubs Get-CimInstance and defines Get-WmiObject to throw, so a revert fails loudly instead of quietly returning nothing through that catch. Reverting the call fails four tests. * Tighten the two comments from the last pass --------- 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> Co-authored-by: danielhanchen <unslothshared@gmail.com> |
||
|
|
5426a78c39
|
Studio: switch llama.cpp backends from the UI (#8520)
* Studio: add llama.cpp backend selector * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix llama.cpp backend selection edge cases * Fix llama.cpp backend selection review issues * Fix remaining llama.cpp backend review issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix llama.cpp backend switch consistency * Re-pair whisper after llama runtime changes * Re-pair whisper when llama runtime identity changes * Unify llama backend selection invariants * Preserve llama backend fallback constraints * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Publish llama planning job state * Handle llama frontend job transitions * Simplify the llama.cpp backend selection contracts * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Address the open Codex findings on backend precedence and job ownership * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Drop the unused llama backend imports the hoist check rejects The Source lint job fails on studio/install_llama_prebuilt.py: scripts/verify_import_hoist.py reports INSTALL_KIND_BACKENDS and marker_backend as hoisted but unused. marker_backend is genuinely dead here, so it goes. INSTALL_KIND_BACKENDS is not: the installer is meant to share one vocabulary with the marker readers, and two tests assert that through this module. Give it a real use instead of a bare re-export by deriving VULKAN_INSTALL_KINDS from the map rather than spelling the same two names out again, which also removes a mirrored definition of the kind this file warns about elsewhere. * Treat an install marker that is not a JSON object as no marker read_install_marker returns whatever json.loads produced, so a marker holding [] or 123 reaches callers as something without .get. Every caller assumes a mapping, and the new backend picker adds one more: get_backend_status calls marker_backend(marker) and raises AttributeError, so GET /api/llama/backend answers 500 and Settings > System shows a load error for what is only a corrupt file. This is not new on this branch (get_update_status raises the same way on main), but the picker makes it reachable from a page users open. Guard it where the file is read, so the update planner, the picker and crash recovery all degrade to the source-build path together, exactly as they already do for unparseable JSON. * Disable Apply when the environment pins the backend The Select is disabled whenever env_backend is set, but Apply is gated only on dirtiness, and the two are computed independently. An automatic install whose detection has since drifted reports selection_applied false, so the row is dirty while the Select is disabled and Apply becomes the only live control. POST /api/llama/backend then refuses it with environment_override, which is correct, but the button should not have offered it. Also fills in the status shapes the payload tests did not reach: every unsupported reason, a macOS install reporting metal, and the terminal job states. * Run the setup.ps1 exit routing under pwsh instead of matching its text The Windows half of the fail-closed change was asserted by comparing source strings, which cannot catch a branch that reads the same and behaves differently. Extract the routing block the way the bash harness already does, run it under pwsh, and require the same decision from both: identical exit code, and identical answer on whether a source build was queued. Covers exit codes 0/1/2/3/4/5/137 against each explicit backend and both install states, 70 pairs, and pins the two branches the picker depends on: exit 5 fails closed everywhere, exit 2 stays the one automatic path allowed to fall back to a compile. Skipped where pwsh is absent, like the other PowerShell tests here. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Let a failed whisper re-pair be retried from the same selection The llama phase runs first and records the new backend, so a retryable whisper failure (a dropped download, an install that was busy) ends with llama.cpp on the requested backend and dictation still hardlinked to the old runtime. Retrying that selection is then already_selected, which skips the llama phase, and the whisper planner refused to run without one. The reported failure was unfixable except by switching away and back. Allow a repair-only job for that one refusal, gated on the pairing actually being stale so an ordinary already-selected request stays a refusal instead of becoming a no-op job that reports success. slim_pairing_is_stale exposes the comparison run_repair_phase already makes before it does any work. * [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: danielhanchen <danielhanchen@gmail.com> |
||
|
|
51fac012be
|
Fix Cloudflare documentation link (#8572) | ||
|
|
4caf91c87e
|
Update README.md | ||
|
|
4acc36b107
|
Update README.md
Some checks are pending
Backend CI / Repo tests (CPU) (push) Waiting to run
Unsloth export capability / capability (ubuntu-latest) (push) Waiting to run
Unsloth export capability / capability (windows-latest) (push) Waiting to run
Frontend CI / Frontend build + bundle sanity (push) Waiting to run
Unsloth GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Unsloth GGUF CI / Tool calling Tests (push) Waiting to run
Unsloth GGUF CI / JSON, images (push) Waiting to run
Unsloth load-orchestrator CI / test (push) Waiting to run
Mac Studio GGUF CI / GGUF inference smoke (API, tools, vision) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Waiting to run
Mac Studio UI + API + Update CI / Chat UI, API and Update Tests (push) Waiting to run
Unsloth Tauri CI / Tauri Linux debug build (no codesign) (push) Waiting to run
Unsloth Tauri CI / Rust unit tests (windows) (push) Waiting to run
Unsloth UI CI / Chat UI Tests (push) Waiting to run
Unsloth Update CI / Unsloth Updating Tests (push) Waiting to run
Windows Unsloth API CI / Unsloth API & Auth Tests (push) Waiting to run
Windows Unsloth GGUF CI / GGUF inference smoke (API, tools, vision) (push) Waiting to run
Windows Unsloth GGUF CI / Unsloth install + inference without Visual Studio (push) Waiting to run
Windows Unsloth GGUF CI / GPU prebuilt resolves without Visual Studio (push) Waiting to run
Windows Unsloth GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Waiting to run
Windows Unsloth GGUF CI / real-VS detection (VS 2022) (push) Waiting to run
Windows Unsloth GGUF CI / real-VS detection (VS 2026) (push) Waiting to run
Windows Unsloth GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Waiting to run
Windows Unsloth GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Waiting to run
Windows Unsloth UI CI / Chat UI Tests (push) Waiting to run
Windows Unsloth Update CI / Unsloth Updating Tests (push) Waiting to run
Wheel CI / Wheel build + content sanity + import smoke (push) Waiting to run
|
||
|
|
8fd61b5f05 | Update README.md | ||
|
|
49164812ad | Update README.md | ||
|
|
d96eef50b9 | Update README.md | ||
|
|
c80a3d1039 | Update README.md | ||
|
|
8a237be645 | Update README.md | ||
|
|
f8b8bb720f | Update README.md | ||
|
|
798006882a | Update README.md | ||
|
|
c52e035801
|
Update README.md | ||
|
|
c2a91dbe6d
|
Revert "Add option to launch Studio without opening the default browser (#7016)" (#8040)
This reverts commit
|
||
|
|
3860d69ab2
|
Add option to launch Studio without opening the default browser (#7016)
* Add no-browser launch option for the Studio desktop launcher The generated launchers (launch-studio.sh / launch-studio.ps1) always opened the default browser once the server became healthy. Add a --no-browser launcher flag, the UNSLOTH_STUDIO_NO_BROWSER env var, and a persisted installer preference (studio.conf / baked into the ps1 launcher) with an interactive install prompt. When auto-open is off the launcher still starts or attaches to the server and prints the URL, for users who run Studio as a browser PWA or app window. The --shortcuts-only refresh run by studio update preserves the choice. * Make UNSLOTH_STUDIO_NO_BROWSER falsy check case-insensitive in the shell launcher Simulation testing caught that False or Off disabled the browser in launch-studio.sh while the PowerShell launcher's -notin treats them as falsy case-insensitively. Lowercase the value before matching so both launchers agree, and pin the behavior in the launcher test. * Run the launcher no-browser shell test in CI The shell installer test step runs a hardcoded list, so the new tests/sh/test_launcher_no_browser.sh was only bash -n parsed by lint and never executed. Add it to the list; it only reads install.sh and install.ps1 and writes to mktemp sandboxes, so it fits the step's no-writable-tree constraint. * Keep the saved browser preference when the reinstall prompt is accepted An interactive reinstall over an install that had persisted STUDIO_OPEN_BROWSER='0' would flip it back to 1 when the user pressed Enter, because the prompt default was hardcoded to yes and the answer then overrode the preserve logic. Seed the prompt default from the existing preference (studio.conf on macOS/Linux/WSL, the value baked in launch-studio.ps1 on Windows) and flip the hint to [y/N] accordingly. Explicit y/n answers still override. * Silence SIGPIPE noise in the launcher no-browser test grep -q exiting on first match SIGPIPEs the echo feeding it when the haystack is the whole installer, spamming 'write error: Broken pipe' in the CI job log. Feed grep from here-strings instead. * Address review feedback: reroute flags, curl EPIPE, post-install browser open Three fixes from PR review and field testing: - Forward an explicit --no-browser/--browser choice into the WSL Strix Halo reroute so the rerouted install honors the flag. - Drain piped stdin before the --shortcuts-only early exit so curl | sh -s -- --shortcuts-only no longer dies with curl error 23. - Open the browser after the installer's own foreground launch when the preference is on: a background watcher polls /api/health, verifies the per-install studio_root_id so a different Studio on the port is never opened, then opens the URL once. Mirrored in install.ps1 with a Start-Job watcher. When the preference is off nothing changes; the server already prints its URL. * Use selected port for post-install browser watcher * Gate the browser prompt on skip-autostart, stub the WSL openers in its test Addresses the two open review items, plus one leak found while confirming them. UNSLOTH_SKIP_AUTOSTART is documented for automated installs, but the new browser prompt did not check it: under `curl ... | sh` only stdin is the pipe, so [ -t 1 ] is still true and the prompt blocked an install that previously ran unattended. Both installers now gate it exactly as they gate the launch prompt below it. Reproduced under a real pty before and after, and pinned by two assertions that are absent at the previous head. tests/sh/test_launcher_no_browser.sh stubbed only open and xdg-open, but _open_browser prefers powershell.exe then cmd.exe on WSL, so running the suite on a WSL machine opened a real browser window and then failed on the empty recorder. All four openers are stubbed, PATH is pinned to them plus coreutils, and /proc/version is a fixture, so one Linux runner now covers every rung of the WSL ladder and the no-opener fallback. The post-install watcher also outlived the installer: its pid was never kept, so Ctrl-C on the foreground server left it polling out its 120s deadline and it could open a browser after the install had visibly finished. It is now reaped once the server exits, and the PowerShell job is stopped and removed in a finally, matching what install.ps1 already does for its other Start-Job. The watcher no longer treats a missing studio_install_id as "any backend will do". --------- Co-authored-by: Unsloth <michaelhan@Michaels-MacBook-Pro.local> |
||
|
|
fbf599a289
|
Studio: settings UI cleanup for Agents, System and Connections (#7594)
* Studio: tidy up the Agents, System and Connections settings Agents tab - Coding agent, model and quantization now sit on one row of three columns instead of stacking. - Smaller agent logos so the picker reads as a list, not a row of tiles. - Command boxes lose their outer border and carry the copy control inside the box, top right. - The `unsloth start` chip is now the link to the docs. - Quantization dropdown drops the extra tags that wrapped every row onto two lines, and the size sits flush right so the list scans cleanly. - More breathing room between sections. System tab - Rounder metric cards with more padding, and no resting border in dark mode. - Darker meter track in dark mode so the bars stand out again. - GPU devices collapse to one row per device; a full width bar under a single GPU read like a page-wide rule. Connections - The add-provider box matches the corner radius used elsewhere. Appearance - Colour palette cards drop the resting border in dark mode, matching the rest of Settings. Hover and selected states are unchanged. * Studio: keep per-GPU memory figures on narrow screens The one-row device layout hid used, free and total below 1024px, so tablets and small laptops were left with only the utilization percentage. Those widths now stack the row instead of dropping the figures, and the bar goes full width under them. * Studio: let the GPU meter shrink beside its percentage The stacked layout gave the meter the full row width while keeping it shrink-0, so each device row ran wider than its container once the figures moved onto their own line. It now fills whatever the percentage label leaves, and keeps the fixed width at lg and above. * Studio: size the GPU row to its pane and fix the docs link name - The device row keyed its layout off the viewport, but the Settings dialog caps the tab at roughly 650px no matter how wide the window is, so on every desktop the row stayed horizontal and squeezed the device name down to a fragment. It now uses a container query against the pane and only goes to one row when there is genuinely space. - The docs link carried an aria-label that replaced its visible "unsloth start" text as the accessible name, so it could not be targeted by the label users can see. The title keeps the description. - Dropped a stale agent from the swap hint so it matches the picker. * Studio: size Agents and System to their pane, restore palette focus - The GPU row's breakpoint could never fire. The settings pane is about 664px at any window size, and the name, three figures, meter and percentage need more than that, so the one-row layout was dead code. Each device is now two compact lines: name with the percentage, then the figures with a fixed-width meter. The name stays readable, the meter no longer runs the width of the pane, and there is no breakpoint that cannot be reached. - The agent, model and quantization grid switched on the viewport, so between 768px and 1024px it went to three columns inside a 440px pane and crushed every control. It now keys on the pane and stacks until the columns genuinely fit. - Dark mode dropped the palette card's resting border, but that border is the only focus indicator since the card removes its outline, and an unlayered rule beats Tailwind's layered focus-visible utility. Keyboard focus paints the ring colour again. * Studio: align the localized agent swap hints with the picker The English hint was updated to match the agents the picker offers, but the ten translated copies were not, so every non-English user was told to pick an option the UI does not show. * Studio: rework the GPU device row Restores the earlier shape, name over backend on the left, but lifts the memory figures and the meter up beside them instead of stacking them underneath. The meter is short rather than full width, which was the original complaint: at full width it read as a rule across the pane rather than a reading for one device. VRAM utilization becomes a rounded pill next to the backend label. The row wraps rather than squeezing the device name when the pane is narrow. * Studio: align the three Agents column headers The Coding agent header carries a padded docs link, so it measured taller than the plain labels beside it and pushed its select down. Pin every column header to the same height. * Studio: stack the GPU meter under its figures and tint the VRAM pill The meter sat beside the used/free/total figures with only a fixed 24 width, so it read as squeezed. Move it under them where it spans their width. The pill was muted grey and washed out in light mode; give it the same accent fill the New tags use. * Studio: rule the GPU figures apart and drop trailing zeros Used, free and total ran together with only a gap between them, so the three readings were easy to misread as one. Rule them apart, size the numbers down a step, and widen the block so it starts further left. Whole values now read as 64 GiB rather than 64.0 GiB. * Studio: widen the GPU figures to the Model downloads edge Match the 392px of the Model downloads control so both blocks start on the same edge, and open up the gap between the figures and the meter. Below 992px the dialog no longer fills its width cap, so stack the row rather than squeezing the device name. * Studio: let the GPU readings truncate instead of overflowing The three figures carry truncate but sit in a flex row, where the default min-width:auto stops an item shrinking below its text. The longest locales would push past the block rather than ellipsize. --------- Co-authored-by: Unsloth <michaelhan@Michaels-MacBook-Pro.local> |
||
|
|
cd5011f288
|
Studio: add UNSLOTH_STUDIO_DISABLE_PUBLIC_CHECK to switch off the startup public lookups (#7433)
Some checks are pending
Unsloth GGUF CI / JSON, images (push) Waiting to run
Unsloth load-orchestrator CI / test (push) Waiting to run
Mac Studio API CI / Unsloth API & Auth Tests (push) Waiting to run
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Mac Studio GGUF CI / Tool calling Tests (push) Waiting to run
Mac Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Waiting to run
Mac Studio UI CI / Chat UI Tests (push) Waiting to run
Mac Studio Update CI / Unsloth Updating Tests (push) Waiting to run
Unsloth Tauri CI / Tauri Linux debug build (no codesign) (push) Waiting to run
Unsloth UI CI / Chat UI Tests (push) Waiting to run
Unsloth Update CI / Unsloth Updating Tests (push) Waiting to run
Windows Unsloth API CI / Unsloth API & Auth Tests (push) Waiting to run
Wheel CI / Wheel build + content sanity + import smoke (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Waiting to run
Windows Unsloth GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Windows Unsloth GGUF CI / Tool calling Tests (push) Waiting to run
Windows Unsloth GGUF CI / JSON, images (push) Waiting to run
Windows Unsloth GGUF CI / Unsloth install + inference without Visual Studio (push) Waiting to run
Windows Unsloth GGUF CI / GPU prebuilt resolves without Visual Studio (push) Waiting to run
Windows Unsloth GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Waiting to run
Windows Unsloth GGUF CI / real-VS detection (VS 2022) (push) Waiting to run
Windows Unsloth GGUF CI / real-VS detection (VS 2026) (push) Waiting to run
Windows Unsloth GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Waiting to run
Windows Unsloth GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Waiting to run
Windows Unsloth UI CI / Chat UI Tests (push) Waiting to run
Windows Unsloth Update CI / Unsloth Updating Tests (push) Waiting to run
* Studio: make the startup public-IP lookup opt-in (#7307 P8) Startup resolved the machine's external IP by asking ifconfig.me whenever Studio bound to 0.0.0.0 or ::. That tells whoever runs that service this host is running Unsloth, which the user never agreed to. The lookup is now gated behind UNSLOTH_STUDIO_PUBLIC_IP_PROBE, off by default, and logs plainly what it sends and where when enabled. Only 1/true/yes/on enable it, so a typo leaves the private default in place. The other two steps stay unconditional because neither discloses anything: the GCE metadata server is link-local, and the UDP connect only asks the kernel which local address routes to 8.8.8.8 without putting a packet on the wire. Disabling the probe therefore still yields a usable LAN address for the access banner. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: gate the check-host.net reachability probe too, and keep the cloud address Problem 8 of #7307 is the check-host.net probe in _verify_global_reachability: it hands this machine's address and port to a third party and asks its nodes to connect back. That was still unconditional, so on GCE and on any host whose routing address is already public the reported behaviour was unchanged. It is now behind the same UNSLOTH_STUDIO_PUBLIC_IP_PROBE opt-in. A private interface address is not proof the port is unreachable, since a NAT or cloud firewall can forward it. The private-address path no longer sets _public_reachable = False, so the banner keeps its warning instead of claiming local network only. To stop the privacy default from costing cloud users their shareable address, step 1 now reads AWS IMDSv2 and Azure IMDS alongside GCE on link-local 169.254.169.254. Also drops the redundant function-local import os and documents the variable in the README remote access section. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: switch to a single UNSLOTH_STUDIO_DISABLE_PUBLIC_CHECK opt-out Replaces the opt-in variable and the cloud metadata work from the previous commit. Both third-party startup lookups stay on by default, so nothing changes for existing users, and one variable turns both off for lab and privacy-sensitive deployments. That is the fallback the reporter offered in #7307 Problem 8, and it keeps the firewall diagnostic that the reachability check exists to provide. The ifconfig.me lookup and the check-host.net probe are now both guarded by public_check_disabled(). Parsing matches the nearest existing switch, _trust_forwarded_for in utils/client_ip.py. The reachability guard sits after the private-address branch, which makes no network call, so a LAN user who opts out still gets the address note. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: danielhanchen <danielhanchen@gmail.com> |
||
|
|
217e8f036c
|
fix(studio): report Vulkan GPUs in system UI (#7476)
* fix(studio): report Vulkan GPUs in system UI * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(studio): separate Vulkan inference GPU reporting * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(studio): keep retrying Vulkan probe refreshes * fix(studio): preserve known zero GPU budgets --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> |
||
|
|
968e6230a0
|
Unsloth start: add local subagents for Claude Code, Codex, OpenCode and Pi (#7326)
Bring the local-subagent support onto main. The original change (#7316) merged into the stacked pr/daniel-unsloth-start-audit branch rather than main, and #7313 reached main via squash, so these files never landed on main. Adds --as-subagent for claude, codex, opencode and pi: the parent agent keeps its own cloud model while a locally served GGUF is registered as a delegated subagent, using ephemeral per-session config that never touches the user's real agent config. |
||
|
|
39a999c056
|
Update README with latest features and Unsloth Start (#7258)
* Document local agent connections * Refresh README features and news * Tighten README feature copy * Restore selective README emphasis * Add Unsloth Start quickstart * Update * Mention * Update README.md * Reduce * Restore-inference-order * Split-agent-API-features |
||
|
|
6d8c18cd1a
|
Replace standalone Studio wording with Unsloth (#7221)
* Replace standalone Studio wording with Unsloth Replace the single word Studio with Unsloth wherever it is used as shorthand for Unsloth Studio in docs, CLI output, UI strings, i18n locales, workflow display names, comments and docstrings. Kept unchanged: the full name Unsloth Studio, third party product names (LM Studio, Visual Studio, Mac Studio), feature names (Recipe Studio, Fine-tuning Studio and its translations), and all identifiers such as env vars, commands, paths and filenames. * Address review feedback on the Studio wording rename Use "an" before Unsloth where the rename left the article as "a". Restore the split brand where Unsloth and Studio render as two halves of the full product name: the onboarding sidebar subtitle and the IPv6 localhost warning. Scope two messages to the full name Unsloth Studio where plain Unsloth was misleading: the AMD README bullet and the CLI studio setup error. |
||
|
|
91a0df9514
|
Studio: make the Cloudflare tunnel opt-in (off by default) (#7046)
* Studio: make the Cloudflare tunnel opt-in (off by default) A wildcard bind (`-H 0.0.0.0`) auto-started a public trycloudflare.com tunnel, so exposing Studio on the LAN also published it to the public internet. Flip the default so the tunnel is opt-in. - `--cloudflare` is now tri-state (Optional[bool], default None = off), mirroring the existing --enable-tools/--disable-tools handling. Pass --cloudflare to expose a public HTTPS link for a wildcard bind; --secure still implies the tunnel. - --secure + --no-cloudflare is still rejected as a contradiction. - Update the parent-command guard, re-exec forwarding, startup-banner wording, the colab comment, README, and tests. * Studio: update installer/setup launch hints for opt-in Cloudflare The post-install launch hints only mentioned --secure for a public link. Now that the tunnel is opt-in, clarify that -H 0.0.0.0 exposes the raw port on the LAN (not a public URL), and surface --cloudflare as the explicit opt-in for a public HTTPS link (--secure keeps the raw port private). Applied to install.ps1, install.sh, and studio/setup.sh. * Studio: address review - keep cloudflare tri-state + harden run re-exec Two review points from the bots: - Gemini: keep `cloudflare` as Optional[bool] in run_server instead of casting None -> False, so the startup banner can distinguish "OFF (default)" (unset) from "OFF (--no-cloudflare)" (explicit). `_cloudflare_flag` and the banner branch now carry the tri-state. - Codex (P1): `unsloth studio run` re-execs the studio venv's console script, which can be an older build whose --cloudflare defaulted on; omitting the flag let it re-enable the tunnel. That path now forwards the default polarity explicitly (--no-cloudflare, or nothing under --secure since --secure implies the tunnel). The plain `unsloth studio` path runs the same-version in-tree run.py (resolved via _find_run_py), so it keeps forwarding only an explicit polarity and still shows the accurate "(default)" banner. Tests updated for the tri-state banner labels, the None gate cases, and the new re-exec forwarding. * Studio: forward --no-cloudflare on plain re-exec too (mixed install) Codex follow-up: _find_run_py falls back to STUDIO_HOME/.../studio/backend/ run.py when the package copy is absent, so the plain `unsloth studio` re-exec can land on an older run.py whose --cloudflare defaults on. Forward the default polarity explicitly there too (--no-cloudflare, or nothing under --secure), matching the run subcommand. The common in-venv launch skips the re-exec and still shows the tri-state "(default)" banner. * Studio: fix launch hint - --cloudflare needs the wildcard bind Codex P3: the launch hint listed --cloudflare next to the loopback `unsloth studio -p 8888` command, but the tunnel only starts for wildcard binds, so `--cloudflare` alone on 127.0.0.1 does nothing. Show `-H 0.0.0.0 --cloudflare` in the hints (install.ps1, install.sh, studio/setup.sh) and clarify the same in the README. * Studio: cross-platform masked terminal password prompt helper Per-keystroke '*' echo (POSIX termios cbreak / Windows msvcrt.getwch), backspace editing, Ctrl-C abort, EOF handling, confirmation loop with re-prompt on mismatch or policy failure. Pure should_prompt gate for the --secure/--cloudflare exposure paths. * Studio CLI: force a terminal password change before public tunnel exposure When a launch will start the Cloudflare tunnel (--secure, or --cloudflare on a non-api-only wildcard bind) and the admin account still has its seeded bootstrap password, prompt for a new password in the terminal (masked with '*', confirmed, re-prompting until valid) before any re-exec or server exists. The change is committed in the parent so it never crosses argv or the environment and older studio-venv children see it immediately. Without a terminal, warn and fall back to the backend bootstrap shutdown timer. Mirrors backend update_password semantics in one transaction: rehash, rotate the JWT secret, clear must_change_password, revoke refresh tokens, drop the desktop secret, then remove the stale credential files. * Studio: terminal password gate before the public tunnel (backend backstop) Never publish a trycloudflare URL while the seeded admin password is active: run_server now runs a terminal password-change gate after the tunnel decision and strictly before start_studio_tunnel. Interactive refusal fails closed (shutdown + exit 1, mirroring the secure gate); without a tty it warns and keeps the bootstrap deadline. Success applies the same effects as the change-password route (update_password + revoke_user_refresh_tokens) and drops the stale app.state.bootstrap_password. MIN_PASSWORD_LENGTH centralised in auth/storage.py and referenced by the HTTP schema. terminal_prompt.py carries the pure gate helper (interactive loop stubbed; supplied by the masked-input module). Also migrates the studio/setup.ps1 launch footer that still showed the bare wildcard hint. * README: reconcile remote-access section with opt-in Cloudflare tunnel * Studio: harden the terminal password gate after review - run.py: run the gate BEFORE the uvicorn socket binds. On a wildcard --cloudflare launch the served HTML injects the bootstrap credential for first login, so a pre-gate listener would hand the default password to anyone who reaches the raw port while the operator is still typing. The gate now also seeds the admin row itself (it can run before lifespan startup). - Headless launches that nothing would protect now fail closed: the bootstrap deadline never arms for api-only serving and UNSLOTH_STUDIO_BOOTSTRAP_TIMEOUT=0 disables it, so warn-and-proceed would have promised a shutdown that never comes. Both the CLI and the backend refuse to publish in that case; the ordinary headless path still warns and relies on the 1h deadline, and no longer auto-fills the default credential into HTML served on a public URL. - storage.update_password gains revoke_refresh_tokens to delete the user's refresh tokens in the SAME transaction as the password commit; the change-password route and the backend gate use it (a separable follow-up delete could fail after the commit and leave a stale refresh token able to mint access tokens under the rotated secret). - clear_bootstrap_password is best-effort: a locked/undeletable file must not surface as a failed password change. - CLI masked reader: disable ISIG like the backend so Ctrl-Z cannot suspend the process with the shared terminal stuck in no-echo mode; handle Ctrl-C/Ctrl-Z as characters; treat stream EOF mid-line as an abort instead of submitting a partial password. Both readers restore terminal attrs from a SIGTERM/SIGHUP handler since a finally block cannot run when a default-disposition signal terminates the process. - Backend reader: decode byte-at-a-time through an incremental UTF-8 decoder so multi-byte characters split across read boundaries are no longer dropped; isatty checks tolerate closed/None streams. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: persist bootstrap suppression through lifespan startup The pre-bind password gate nulled app.state.bootstrap_password, but the FastAPI lifespan runs after it and re-reads the bootstrap password into app.state on both admin paths, so a headless public launch could still serve the injected credential in HTML. Carry a persistent suppress_bootstrap_injection flag that the lifespan honors instead. Also drop the quoted Tuple annotation on _terminal_password_gate that tripped the import-hoist lint (the typing import looked unused). * Studio CLI: keep the pre-exec auth DB private (0700 dir, 0600 db) On a fresh install the pre-exposure password gate creates auth/ and auth.db through the CLI before the backend ever runs, and sqlite3.connect leaves the DB 0644 under a 022 umask. Mirror backend storage.get_connection's chmod so the committed password hash and JWT secret are never world-readable, even if the launch aborts before the backend applies its own modes. * Tighten pre-exposure password gate comments * Studio: delete seeded bootstrap password before headless public re-exec The headless warn-and-proceed path returns with the default admin password still active, then re-execs a child Studio process. An old studio-venv child (mixed-version install) predates the pre-bind gate and its injection-suppress flag, so its lifespan reads .bootstrap_password and injects the seeded credential into the public HTML for up to the bootstrap deadline. A CLI-flag handshake cannot fix this uniformly: the studio run path uses ignore_unknown_options and an old in-venv child runs in-process, so it would never reject the flag. Delete the seeded .bootstrap_password file in the parent before re-exec so a fresh child of any version reads None and never serves it. This covers both re-exec paths and both child versions. must_change_password stays set, so the login page still forces a change and the bootstrap shutdown timer still arms; only the plaintext-on-disk copy is removed. Recovery is via a terminal-attached run or reset-password. Backend gate and CLI warnings updated to match. * Studio: commit the seeded admin before headless public re-exec The headless-warn path deletes the seeded .bootstrap_password so a re-exec'd child cannot inject it, but _ensure_cli_default_admin's INSERT was never committed and rolled back on conn.close(). On a fresh STUDIO_HOME an old studio-venv child then found no admin, regenerated a fresh bootstrap password + file, and injected THAT into the public page, defeating the deletion. Commit the seeded admin right after _ensure_cli_default_admin so any re-exec'd child sees the existing account and does not regenerate. Regression tests cover both re-exec paths on a fresh (unseeded) DB. * Studio: fail closed when the bootstrap password file cannot be removed On the headless public path, deleting .bootstrap_password is the protection against an old re-exec'd child injecting the seeded credential. If unlink fails (locked file, read-only auth dir) the file is still on disk, so warning and proceeding would still leak it for the bootstrap-timeout window. Abort with a clear error instead. Regression test covers the unlink-failure fail-closed path. * Studio: hold no-echo for the whole password line, not per keystroke The POSIX masked reader set cbreak/no-echo inside _getch_posix and restored the terminal to echo-on in a finally after every single keystroke, because _read_password calls _getch once per character. Between one char returning and the next call re-entering cbreak, ECHO was on, so a keystroke arriving in that window echoed the password in cleartext. Move the terminal mode into a _prompt_raw_mode context that _read_password holds around the entire line (mirroring unsloth_cli/commands/_password_prompt.py, which already did this), restoring once when the line completes or aborts. _getch_posix now only reads, since the mode is held by the caller. The context is a no-op when stdin is not a real terminal, keeping the _getch test seam. Add a regression test asserting the raw-mode context wraps the read exactly once and every keystroke is read while it is active. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: strip the seeded bootstrap password when the auth DB check fails The pre-exposure gate returned early on two auth-DB inspection failures and proceeded to re-exec without removing the seeded .bootstrap_password: - _connect_auth_db() failure: a seeded credential from a prior run may still be on disk. - the must_change_password read-back failure: worse, _ensure_cli_default_admin had already seeded the admin and the code committed it (writing .bootstrap_password) right before the failing SELECT. In the mixed-version case (a new outer CLI re-execing an old studio-venv child that predates the pre-bind gate), that child would read the file back and inject the default admin credential into the public Cloudflare page. The sibling headless branch already deletes the file for exactly this reason, so these returns were an inconsistent gap. Factor the delete-or-fail-closed logic into _strip_seeded_bootstrap_password_or_exit and call it on both inspection failures (and reuse it in the headless branch): strip the seeded file first (version-independent protection), failing closed if the removal itself fails. must_change_password stays set, so the login page still forces a change and the bootstrap shutdown timer still arms. Add tests for both new paths (connect failure and post-commit read-back failure strip the file and proceed; a failed strip fails closed). * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: fail closed when the seeded admin cannot be committed before exposure The pre-exposure gate wrapped _ensure_cli_default_admin (the INSERT), its conn.commit(), and the must_change_password read-back in one try, and the except recovered by stripping .bootstrap_password and proceeding to re-exec on the assumption the admin was already committed. That assumption only holds when the failing statement is the SELECT. When the INSERT or the commit itself fails (e.g. a write lock held past the busy timeout on a fresh install), no admin row is committed: it rolls back on conn.close(), and a re-exec'd old studio-venv child (no pre-bind gate) then finds no admin, regenerates a fresh bootstrap password + file, and serves that default credential on the public Cloudflare page. Stripping the file cannot stop a regeneration. Split the seed+commit into its own try that fails closed (refuse the public launch, best-effort removing any half-written seed file) since we cannot prove a committed admin; keep the separate read-back failure on the strip-and-proceed path, where the admin is committed so an old child finds it and will not regenerate. Add a test for the seed-commit-failure path. * Studio: decode the CLI masked password reader with errors="replace" The CLI reader read keystrokes with text-mode sys.stdin.read(1), which raises UnicodeDecodeError on a pasted non-UTF-8 password (e.g. Latin-1 bytes), or under PYTHONUTF8 yields a lone surrogate that later crashes the pbkdf2 encode -- either aborts the launch with a traceback. The backend mirror (terminal_prompt.py) already reads raw bytes through an incremental decoder with errors="replace". Mirror that here: read with os.read and an incremental decoder so invalid bytes map to U+FFFD, iterating over each emitted char (one byte can complete a replacement plus the next char). * Studio: resolve the child launcher before the pre-exposure gate The gate strips the seeded .bootstrap_password on a headless public launch, and it ran before the re-exec launchability check (studio venv / run.py / console script present). So a headless launch with an incomplete studio setup would seed the admin, delete the bootstrap password, then abort because the child could not be found, leaving the admin at must_change_password=1 with no password ever shown or injectable: locked out until `unsloth studio reset-password`. Resolve and validate the child launcher first, in both `studio` (studio_default) and `studio run`, and only then run the gate, so an unlaunchable setup exits before anything is stripped. Add a regression test that a missing venv exits without removing the seeded file. * Studio: fail closed when the auth DB cannot be opened before exposure The connect-failure branch of the pre-exposure gate stripped .bootstrap_password and proceeded, on the assumption a committed admin from a prior run made an old child find it and not regenerate. But on a fresh public launch whose _connect_auth_db() itself fails (transient lock during the schema/seed step, or an unwritable home), no admin is committed, so a mixed-version re-exec child that predates the backend gate can find no user, generate a fresh bootstrap password, and serve it on the public Cloudflare page. Stripping a file we cannot vouch for cannot stop a regeneration. Make this branch fail closed like the seed/commit failure path: we only continue past the DB inspection once a committed admin is confirmed. The existing file is left untouched so a retry (after a transient lock clears) can still prompt. Update the connect-failure test to assert fail-closed, and give the in-venv --secure flag test a real STUDIO_HOME with an already-changed admin so the gate is a no-op rather than relying on a DB-open failure. * Studio: invalidate seeded bootstrap files before deleting auth.db on reset reset-password deleted auth.db first, then best-effort unlinked the seeded .bootstrap_password and desktop secret. unlink() only ignores FileNotFoundError, so a locked or read-only file (Windows AV, read-only auth dir) survived while auth.db was gone. The next server start then re-seeded from that stale plaintext and re-validated the exact credential the reset was meant to revoke. Invalidate the credential files first, truncating any that cannot be unlinked, then delete the DB, so a surviving file can never carry a reusable secret. clear_bootstrap_password now truncates on unlink failure for the same reason, and its warning says the contents were cleared rather than claiming the stale password is already invalid. * Studio: require a servable frontend before the pre-exposure gate can strip the seeded password A headless public launch strips the seeded .bootstrap_password before the re-exec'd child starts. If the child then cannot serve the login page (the only in-band way to change the seeded password) the admin is locked out (must_change_password=1, no file, no UI) until reset-password. Add _require_servable_frontend_or_exit and call it before the gate on both `unsloth studio` and `unsloth studio run` public launches: fail closed if a non-api-only public launch has no built frontend dist, before anything is stripped. A user-supplied --frontend is validated to contain index.html so a bad path cannot silently bypass the check; an auto-resolved dist is trusted (_find_frontend_dist already requires index.html) and forwarded to the child. Model-load aborts on `studio run` remain a residual: the parent must strip for mixed-version safety (an old studio-venv child has no pre-bind gate) and model loadability cannot be proven before exec, so that path stays recoverable via reset-password. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: harden reset-password ordering and validate the in-venv backend before the strip Three follow-ups to the pre-exposure hardening: reset-password now deletes auth.db FIRST and proves it is gone before touching the seeded credential files. If the DB cannot be removed (a running Studio or Windows holds it open, or a read-only auth dir) it aborts with the credential files untouched, so a forgotten-password reset is not left half-done with the recovery credentials deleted while an un-resettable must_change_password=1 DB survives. After the DB is gone it invalidates the stale credential files (unlink, else truncate) and fails closed if a file can be neither removed nor truncated, since a surviving plaintext would be re-seeded by generate_bootstrap_password() and re-validate the revoked password. The in-venv (in-process) launch path had no analogue of the re-exec launcher check: a headless public launch would seed the admin and strip the seeded .bootstrap_password in the gate before _load_run_module() later failed on a broken/partial venv, leaving must_change_password=1 with no password to log in. Add _validate_inproc_backend_before_strip, called on the in-venv path (both `unsloth studio` and `unsloth studio run`) before the gate on the headless public path, so a broken backend fails cleanly before anything is stripped. It is scoped to the headless path so an interactive prompt is not delayed behind a full backend import. * Studio: validate the frontend and tunnel before the strip on every public path Five follow-ups closing the remaining pre-exposure-strip lockouts: The in-venv (in-process) paths of both `unsloth studio` and `unsloth studio run` validated the backend but not the frontend before the gate, so a headless public launch with a missing/bad dist would strip the seeded .bootstrap_password and then abort in run_server() during frontend setup, leaving must_change_password=1 with no login page. Both now validate a servable frontend before the strip (cheap check first, backend import after) and serve the resolved dist in-process. The `studio run` re-exec discarded the dist that satisfied the pre-strip check and only forwarded a user-supplied --frontend. In a shadowed install where the parent finds a built dist the child cannot, it stripped and exec'd without the path, and the child aborted during frontend setup. It now forwards the resolved dist, matching `unsloth studio`. On a headless --secure launch the bind is loopback, so the Cloudflare tunnel is the only public exposure. If cloudflared is provably unavailable (found nowhere and undownloadable) the tunnel cannot start, so stripping the recovery credential would just lock the user out with no public URL ever served. Add _tunnel_binary_confirmed_unavailable and, on --secure only, refuse the launch with the credential preserved rather than strip. Wildcard --cloudflare binds 0.0.0.0 publicly regardless of the tunnel, so it still strips; any uncertainty (helper not loadable) also still strips, since a possible credential leak outweighs a recoverable lockout. clear_bootstrap_password no longer claims it cleared the file's contents when both unlink and truncate failed; it now reports the stale password is still on disk and asks the user to remove it manually. * Studio: fix cloudflared probe path and skip the bootstrap strip for a self-suppressing child Two follow-ups to the --secure pre-exposure hardening: The cloudflared availability probe loaded cloudflare_tunnel by file path but not its backend deps: ensure_cloudflared() -> _cache_path() lazily imports utils.paths.storage_roots, which only resolves when studio/backend is on sys.path. From the outer CLI it is not, so the probe saw ensure_cloudflared() return None (cache unresolvable) and wrongly treated the tunnel as unavailable, refusing --secure even when cloudflared was cached or downloadable. Add the backend dir to sys.path for the probe (and remove it after) so the cache path resolves as it will in the child. A headless --secure launch stripped the seeded .bootstrap_password before the child proved the tunnel could actually connect, so a cloudflared that is present but cannot establish the tunnel (blocked connectivity, Cloudflare outage) left must_change_password=1 with no recovery credential. But the strip is only needed when the re-exec'd child is an OLD studio-venv backend with no pre-bind suppression: this install's own run.py sets app.state.suppress_bootstrap_injection before binding and never serves the seeded credential publicly. Add _child_self_suppresses (true in-process, or when the re-exec target is this install's own run.py by path identity) and skip the strip in that case, keeping .bootstrap_password as a local recovery credential; the strip stays fully in force for the studio-venv console-script path and any venv-fallback run.py, where an old child is actually possible. * Studio: reword the pre-exposure terminal password prompt * Studio: warn when -H is overridden by --secure; align pre-exposure prompt wording - --secure/--secure run: emit a Note (not an error) when -H is a non-loopback host, since --secure forces the loopback bind and would otherwise discard -H silently. - Reword the pre-exposure terminal prompt to 'exposed on the public internet' in both the backend gate and the CLI mirror. - Align the CLI success line with the backend ("Password updated for '<user>'."). - Tests for the new -H warning (present when overridden, absent on loopback). * Studio: add non-interactive --password to set the initial admin password Headless hosts (CI, containers, systemd units) have no TTY, so the forced first-exposure password change could not be completed unattended. Add a non-interactive way to set the INITIAL admin password before the server binds: - --password <value>, the UNSLOTH_STUDIO_PASSWORD env var, or --password - (read one line from stdin). Off by default; unset falls back to the normal interactive terminal prompt / browser setup. - Applies on any launch (public --secure/--cloudflare or a headless -H 0.0.0.0 bind), only when the account still has its seeded bootstrap password. An already-set password is a hard error, never an override; an invalid value (too short, or equal to the bootstrap) fails closed before bind. - The CLI applies the change in the parent, never forwards --password to the re-exec child, and strips UNSLOTH_STUDIO_PASSWORD from the child env so the secret never crosses to the child. run.py does the same on the direct path and strips the env var so spawned subprocesses (cloudflared, llama-server, tools) cannot inherit it. Mirrors resolve_supplied_password across the CLI and backend, documents the option in the README (including the argv-visibility caveat), and covers all flows (env/stdin/literal, fail-closed cases, no-forward, env-strip, reset-password roundtrip) in the CLI, backend, and unit suites. * Studio: truncate the stale bootstrap file when unlink fails on a CLI password change The post-change cleanup in _cli_update_password only warned when .bootstrap_password could not be unlinked but was still writable (locked file, read-only auth dir), leaving the old plaintext on disk. If auth.db is later reset or removed, generate_bootstrap_password() reads that file back and re-validates the revoked bootstrap password. Truncate the file on unlink failure so its stale plaintext cannot be re-seeded, mirroring the backend clear_bootstrap_password(); the password change is already committed, so this never rolls it back. The warning now states truthfully whether the contents were cleared or the file must be removed manually. * Studio: tighten comments --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> |
||
|
|
ca979e9643
|
Studio: add UNSLOTH_SKIP_AUTOSTART installer flag (#7093)
* Studio: add installer autostart opt-out * CI: run installer autostart tests cross-platform * Tests: combine Studio installer skip flags |
||
|
|
b0b8aea618
|
Clarify in README that -H 0.0.0.0 starts a public Cloudflare tunnel (#7007)
Some checks are pending
Studio GGUF CI / Tool calling Tests (push) Waiting to run
Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio API CI / Studio API & Auth Tests (push) Waiting to run
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Mac Studio Update CI / Studio Updating Tests (push) Waiting to run
Studio Update CI / Studio Updating Tests (push) Waiting to run
Windows Studio API CI / Studio API & Auth Tests (push) Waiting to run
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Waiting to run
Mac Studio GGUF CI / Tool calling Tests (push) Waiting to run
Mac Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Waiting to run
Mac Studio UI CI / Chat UI Tests (push) Waiting to run
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Waiting to run
Studio UI CI / Chat UI Tests (push) Waiting to run
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Waiting to run
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Waiting to run
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Windows Studio GGUF CI / Tool calling Tests (push) Waiting to run
Windows Studio GGUF CI / JSON, images (push) Waiting to run
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Waiting to run
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Waiting to run
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Waiting to run
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Waiting to run
Windows Studio UI CI / Chat UI Tests (push) Waiting to run
Windows Studio Update CI / Studio Updating Tests (push) Waiting to run
Wheel CI / Wheel build + content sanity + import smoke (push) Waiting to run
* Clarify in README that -H 0.0.0.0 starts a public Cloudflare tunnel * Hedge tunnel URL wording and restore trusted-network caution * Tighten the 0.0.0.0 tunnel note * Drop trust-the-network caution from tunnel note * Restore trusted-network note on the raw-bind sentence * Use Cloudflare's quick tunnel terminology and consolidate the trust warning |
||
|
|
4a5d41eb3d
|
fix(install): enable UV_NATIVE_TLS on macOS for corporate TLS-inspection proxies (#6671)
--------- Co-authored-by: Luca Cesarano <luca.cesarano@sygnum.com> Co-authored-by: oobabooga <112222186+oobabooga@users.noreply.github.com> |
||
|
|
54f25bf17e
|
Studio: UNSLOTH_NPM_REGISTRY opt-in for corporate npm mirrors (#6491) (#6663)
* Studio: UNSLOTH_NPM_REGISTRY opt-in for corporate npm mirrors (#6491) studio/frontend/.npmrc pins registry=https://registry.npmjs.org/ as a supply-chain lock. A project-level pin takes precedence over a user's ~/.npmrc, so behind a corporate firewall that blocks npmjs.org the frontend bun/npm install hit npmjs.org directly and failed with 403. Add an opt-in UNSLOTH_NPM_REGISTRY env var (off by default). When set it is threaded as --registry into every registry-touching install in setup.sh, setup.ps1 and build.sh (bun bootstrap, bun install + retry, npm fallback, OXC validator runtime). --registry is the highest-precedence override for both bun and npm and leaves min-release-age and save-exact in force, so the default lock is unchanged for everyone else. On an install failure that looks like a blocked registry, print guidance pointing at UNSLOTH_NPM_REGISTRY and auto-suggest the mirror already set in the user's npm config. Registries are never switched automatically. Also correct the .npmrc comment: the pin does not block an ambient NPM_CONFIG_REGISTRY env var (npm and bun honor that at higher precedence); it only guards against a lower-precedence stale ~/.npmrc. * Studio: make the registry hint reachable under set -e; clean temp log (#6491) run_quiet_no_exit returns non-zero on failure, which under `set -euo pipefail` exits the script at the call site before the exit code is captured, so the new UNSLOTH_NPM_REGISTRY hint never printed on the npm fallback and OXC validator paths. Guard both with `|| _rc=$?` (the same idiom every other run_quiet_no_exit caller already uses) so the failure branch runs, and remove the _FRONTEND_INSTALL_LOG temp file on the early-exit path. * Studio: detect the user's mirror outside the pinned frontend dir (#6491) _suggest_npm_registry / Show-NpmRegistryHint run while the cwd is still studio/frontend, whose .npmrc pins registry=https://registry.npmjs.org/. So `npm config get registry` returned that pin instead of the user's ~/.npmrc mirror, and the "Detected a registry" branch was skipped for the main corporate case (mirror set in ~/.npmrc). Run the lookup from a directory with no project .npmrc (/ in bash, the temp dir in PowerShell) so the user/global mirror is surfaced. The NPM_CONFIG_REGISTRY env check is unchanged and still takes precedence. |
||
|
|
b964d349fc
|
Clarify in README that studio --secure creates a public tunnel (#6632)
The Launch-section blurb described --secure as a secure HTTPS link instead of a raw port and stressed that the raw port is never exposed, which reads as the more private option. The Cloudflare tunnel actually publishes Studio at a public trycloudflare.com URL, and server-side tools let anyone with the API key run code. Reword to state the public exposure and the code-execution caveat, matching the installer hints and the Remote access section. |
||
|
|
c53456adf7 | Remove Windows Blackwell tips line from README | ||
|
|
4fddae3840
|
docs: add Windows installation & troubleshooting guide for RTX 50-series (Blackwell) (#6286)
* Added Windows RTX 50-series troubleshooting guide * Update README.md to link Blackwell Windows Troubleshooting Guide Added a troubleshooting guide for Windows installation issues with RTX 50-Series. * Fix install path, troubleshooting accuracy and markdown for PR #6286 * Condense Windows Blackwell tips into README and drop standalone guide for PR #6286 --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com> |
||
|
|
b094bf8590
|
README: document unsloth studio --secure (HTTPS) in the Launch section (#6500)
Some checks are pending
Backend CI / (Python 3.10) (push) Waiting to run
Backend CI / (Python 3.11) (push) Waiting to run
Backend CI / (Python 3.12) (push) Waiting to run
Backend CI / (Python 3.13) (push) Waiting to run
Backend CI / Repo tests (CPU) (push) Waiting to run
Frontend CI / Frontend build + bundle sanity (push) Waiting to run
Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Studio GGUF CI / Tool calling Tests (push) Waiting to run
Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio API CI / Studio API & Auth Tests (push) Waiting to run
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Mac Studio GGUF CI / Tool calling Tests (push) Waiting to run
Mac Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Waiting to run
Mac Studio UI CI / Chat UI Tests (push) Waiting to run
Mac Studio Update CI / Studio Updating Tests (push) Waiting to run
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Waiting to run
Studio UI CI / Chat UI Tests (push) Waiting to run
Studio Update CI / Studio Updating Tests (push) Waiting to run
Windows Studio API CI / Studio API & Auth Tests (push) Waiting to run
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Windows Studio GGUF CI / Tool calling Tests (push) Waiting to run
Windows Studio GGUF CI / JSON, images (push) Waiting to run
Windows Studio UI CI / Chat UI Tests (push) Waiting to run
Windows Studio Update CI / Studio Updating Tests (push) Waiting to run
Wheel CI / Wheel build + content sanity + import smoke (push) Waiting to run
|
||
|
|
3452b91764
|
README: Studio developer install tracks main (nightly), add UNSLOTH_STUDIO_HOME and --secure (#6498)
* README: Studio developer install tracks main (nightly), add UNSLOTH_STUDIO_HOME and --secure * README: rename heading to Developer / Nightly / Experimental installs |
||
|
|
92e7563fa2
|
Document install env vars in README advanced launch options (#5972)
Add copyable per-platform examples for UNSLOTH_NO_TORCH, UNSLOTH_PYTHON and UNSLOTH_STUDIO_HOME (curl | sh after the pipe; $env: before irm | iex), and move the UNSLOTH_CPU_THREADS note to the end of the section. |
||
|
|
6bf101107a
|
Removing unsloth studio update.md
Some checks are pending
Backend CI / (Python 3.10) (push) Waiting to run
Backend CI / (Python 3.11) (push) Waiting to run
Backend CI / (Python 3.12) (push) Waiting to run
Backend CI / (Python 3.13) (push) Waiting to run
Backend CI / Repo tests (CPU) (push) Waiting to run
Frontend CI / Frontend build + bundle sanity (push) Waiting to run
Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Studio GGUF CI / Tool calling Tests (push) Waiting to run
Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio API CI / Studio API & Auth Tests (push) Waiting to run
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Mac Studio GGUF CI / Tool calling Tests (push) Waiting to run
Mac Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Waiting to run
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Waiting to run
Mac Studio UI CI / Chat UI Tests (push) Waiting to run
Mac Studio Update CI / Studio Updating Tests (push) Waiting to run
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Waiting to run
Studio UI CI / Chat UI Tests (push) Waiting to run
Studio Update CI / Studio Updating Tests (push) Waiting to run
Windows Studio API CI / Studio API & Auth Tests (push) Waiting to run
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Windows Studio GGUF CI / Tool calling Tests (push) Waiting to run
Windows Studio GGUF CI / JSON, images (push) Waiting to run
Windows Studio UI CI / Chat UI Tests (push) Waiting to run
Windows Studio Update CI / Studio Updating Tests (push) Waiting to run
Wheel CI / Wheel build + content sanity + import smoke (push) Waiting to run
|
||
|
|
998feaf9d0 | Update README.md | ||
|
|
faea796c35
|
fix: wrong code block language for nightly Windows install (#5877)
Some checks are pending
Security audit / npm scan-packages (Studio frontend tarballs) (push) Waiting to run
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Waiting to run
Security audit / pytest tests/security (push) Waiting to run
Security audit / npm provenance + new install-script diff (push) Waiting to run
Studio API CI / Studio API & Auth Tests (push) Waiting to run
Backend CI / (Python 3.10) (push) Waiting to run
Backend CI / (Python 3.11) (push) Waiting to run
Backend CI / (Python 3.12) (push) Waiting to run
Backend CI / (Python 3.13) (push) Waiting to run
Backend CI / Repo tests (CPU) (push) Waiting to run
Frontend CI / Frontend build + bundle sanity (push) Waiting to run
Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Studio GGUF CI / Tool calling Tests (push) Waiting to run
Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio API CI / Studio API & Auth Tests (push) Waiting to run
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Mac Studio GGUF CI / Tool calling Tests (push) Waiting to run
Mac Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio UI CI / Chat UI Tests (push) Waiting to run
Mac Studio Update CI / Studio Updating Tests (push) Waiting to run
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Waiting to run
Studio UI CI / Chat UI Tests (push) Waiting to run
Studio Update CI / Studio Updating Tests (push) Waiting to run
Windows Studio API CI / Studio API & Auth Tests (push) Waiting to run
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Windows Studio GGUF CI / Tool calling Tests (push) Waiting to run
Windows Studio GGUF CI / JSON, images (push) Waiting to run
Windows Studio UI CI / Chat UI Tests (push) Waiting to run
Windows Studio Update CI / Studio Updating Tests (push) Waiting to run
Wheel CI / Wheel build + content sanity + import smoke (push) Waiting to run
Co-authored-by: danielhanchen <michaelhan2050@gmail.com> |
||
|
|
131fa4fcae
|
Trim README Advanced launch options blurb (#5809) | ||
|
|
9222ffd9b4
|
Studio: add configurable CPU thread pool limit (#5760)
* Studio: add configurable CPU thread pool limit * Studio: report invalid CPU thread setting cleanly * Studio: also cover uvicorn main:app, harden tests, move docs to advanced --------- Co-authored-by: Roland Tannous <115670425+rolandtannous@users.noreply.github.com> Co-authored-by: danielhanchen <danielhanchen@gmail.com> |
||
|
|
b180ae7dd6
|
Adding Connect a Provider README changes | ||
|
|
a74a1080e0
|
Move uninstall scripts into scripts/ and fix references (#5644)
Some checks are pending
Mac Studio UI CI / Chat UI Tests (push) Waiting to run
Security audit / npm scan-packages (Studio frontend tarballs) (push) Waiting to run
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Waiting to run
Security audit / pytest tests/security (push) Waiting to run
Security audit / npm provenance + new install-script diff (push) Waiting to run
Studio API CI / Studio API & Auth Tests (push) Waiting to run
Backend CI / (Python 3.10) (push) Waiting to run
Backend CI / (Python 3.11) (push) Waiting to run
Backend CI / (Python 3.12) (push) Waiting to run
Backend CI / (Python 3.13) (push) Waiting to run
Backend CI / Repo tests (CPU) (push) Waiting to run
Frontend CI / Frontend build + bundle sanity (push) Waiting to run
Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Studio GGUF CI / Tool calling Tests (push) Waiting to run
Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio API CI / Studio API & Auth Tests (push) Waiting to run
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Mac Studio GGUF CI / Tool calling Tests (push) Waiting to run
Mac Studio GGUF CI / JSON, images (push) Waiting to run
Mac Studio Update CI / Studio Updating Tests (push) Waiting to run
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Waiting to run
Studio UI CI / Chat UI Tests (push) Waiting to run
Studio Update CI / Studio Updating Tests (push) Waiting to run
Windows Studio API CI / Studio API & Auth Tests (push) Waiting to run
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Waiting to run
Windows Studio GGUF CI / Tool calling Tests (push) Waiting to run
Windows Studio GGUF CI / JSON, images (push) Waiting to run
Windows Studio UI CI / Chat UI Tests (push) Waiting to run
Windows Studio Update CI / Studio Updating Tests (push) Waiting to run
Wheel CI / Wheel build + content sanity + import smoke (push) Waiting to run
* Move uninstall scripts into scripts/ and fix all references Relocates `uninstall.sh` and `uninstall.ps1` from the repo root into the existing `scripts/` directory, alongside the other helper scripts. Reference fixes: * `README.md`: Studio uninstall instructions now point at the raw GitHub URLs under `scripts/`. The previous `unsloth.ai/uninstall.*` short URLs currently 404 (unlike `unsloth.ai/install.sh`, which 301s to the raw github URL), so the raw URL is the working entry point until that redirect is configured. * `scripts/uninstall.sh` header `Usage:` example updated to the new raw GitHub path. * `scripts/uninstall.ps1` header `Usage:` example updated to the new raw GitHub path. * `.github/workflows/studio-update-smoke.yml`: `paths:` trigger and round-trip exec/exists checks now use `scripts/uninstall.sh`. * `.github/workflows/studio-mac-update-smoke.yml`: same. * `.github/workflows/studio-windows-update-smoke.yml`: `paths:` trigger and round-trip exec/exists checks now use `scripts/uninstall.ps1`. The in-script help hints (e.g. `sh uninstall.sh`, `.\uninstall.ps1`) are left unchanged because they are user-facing examples shown after the user already has the file locally, and the basename form works regardless of which directory the user downloaded the script into. Follow-up note for unsloth.ai: once this lands, please add the `unsloth.ai/uninstall.sh` and `unsloth.ai/uninstall.ps1` short-URL redirects to `raw.githubusercontent.com/unslothai/unsloth/main/scripts/...` (matching the existing `unsloth.ai/install.sh` redirect pattern). * Update remaining uninstall script help hints for new scripts/ path Three user-facing strings inside the uninstall scripts still showed the old basename form, which became misleading after the move: * `scripts/uninstall.ps1` header `# Local:` example: now references `.\scripts\uninstall.ps1` (the actual path from the cloned repo root). * `scripts/uninstall.sh` env-var re-run hint: now shows the canonical curl-pipe form documented in README, since callers who came via `curl -fsSL ... | sh` never had a local `uninstall.sh` to invoke. * `scripts/uninstall.ps1` env-var re-run hint: same, switched to the `irm ... | iex` form documented in README. Pure string changes, no behavior change. --------- Co-authored-by: Lee Jackson <130007945+Imagineer99@users.noreply.github.com> |
||
|
|
47167885b0
|
studio: add uninstall.ps1 for Windows (#5513)
* studio: add uninstall.ps1 and document it in README for Windows The previous Windows uninstall guidance was Remove-Item -Recurse -Force on $HOME\.unsloth\studio, which only deletes the install dir and leaves behind: * %LOCALAPPDATA%\Unsloth Studio (data dir) * Desktop\Unsloth Studio.lnk (Desktop shortcut) * %APPDATA%\Microsoft\Windows\Start Menu\Programs\Unsloth Studio.lnk * Custom UNSLOTH_STUDIO_HOME / STUDIO_HOME roots * Running unsloth_studio venv processes * User PATH entry under .unsloth\studio * HKCU\Software\Unsloth\PathBackup This script mirrors uninstall.sh for Windows. It stops listening backends by reading the port from share\studio.port (with a Win32_Process sweep anchored on \unsloth_studio\ as a fallback), removes the install dir, data dir, both shortcuts, the Studio PATH entry, and the PathBackup registry key. Custom roots discovered from env vars or share\studio.conf are accepted only if they contain a Studio sentinel (share\studio.conf, unsloth_studio\.unsloth-studio-owned, or bin\unsloth.exe) and are not on a hard deny list (drive root, %USERPROFILE%, parent of %USERPROFILE%, or top-level system paths). README now points Windows users at the script. * Scope port-file kill and PATH cleanup to known Studio roots for PR #5513 Three findings from the reviewer round: 1. _StopByPortFile killed whatever owned the recorded port without proving the PID belonged to this Studio install. A stale studio.port pointing at a port a different local service later bound would force-kill that service. New _PidUnderKnownRoot checks the listening PID's exe path against the same $KnownRoots that _StopStudioProcesses already uses. 2. The netstat.exe fallback matched ":$port " anywhere in the line, so a stale port file with 443 (or any common port) could match an ESTABLISHED row whose remote endpoint was that port, killing an unrelated process (browser, IDE). Now requires the row contain LISTENING, and applies the same _PidUnderKnownRoot ownership check. 3. PATH cleanup removed any entry whose expanded path contained \unsloth_studio\, which would also clobber an unrelated user virtualenv that shared the name. Now only removes entries that resolve inside a known Studio root (default %USERPROFILE%\.unsloth\studio plus any custom roots discovered from UNSLOTH_STUDIO_HOME / STUDIO_HOME / share\studio.conf). * Expand tilde and honor UNSLOTH_STUDIO_HOME precedence for PR #5513 Two findings from the latest review round: 1. install.ps1 (lines 152-154) expands ~ and ~\path to $env:USERPROFILE before resolving the install root, but uninstall.ps1 was passing the raw env value to [System.IO.Path]::GetFullPath. That resolved ~\foo relative to the current directory rather than the user profile, so a user who installed with UNSLOTH_STUDIO_HOME='~\custom' could not uninstall through the same variable. New _ExpandTilde helper matches install.ps1's behavior. 2. Mirror install.ps1's env-var precedence: UNSLOTH_STUDIO_HOME wins, STUDIO_HOME is ignored when both are set. Otherwise uninstalling install A could also touch install B if the user has a stale STUDIO_HOME pointing at B. --------- Co-authored-by: Daniel Han <info@unsloth.ai> |
||
|
|
c41ce170ec
|
studio: add uninstall.sh and document it in README (#5497)
* studio: add uninstall.sh and document it in README
The current uninstall guidance in README.md is `rm -rf ~/.unsloth/studio`,
which leaves behind everything that lives outside that path:
- ~/.local/share/unsloth/ (launcher script, studio.conf, studio.log,
icon assets)
- ~/Applications/Unsloth Studio.app (macOS bundle, orphaned and
pointing nowhere on next reinstall)
- ~/Desktop/Unsloth Studio (broken symlink after the bundle is gone)
- ~/Desktop/unsloth-studio.desktop (Linux)
- ~/.local/share/applications/unsloth-studio.desktop (Linux)
- /tmp/unsloth-studio-launcher-<uid>*.lock (lock dir, possibly stale)
- Launch Services cache entry for ai.unsloth.studio on macOS
- Any running `unsloth studio -p N` processes
Users who follow the documented uninstall and reinstall end up with the
new launcher layered on top of stale state from the previous install,
which has produced concrete bugs (e.g. self-referential symlink inside
the .app bundle after a reinstall over leftover state).
Add uninstall.sh at the repo root that handles all of the above, and
update README.md to point at it as the recommended path. The plain
`rm -rf ~/.unsloth/studio` line is kept as a "partial uninstall, keep
launcher for a later reinstall" alternative. The model cache at
~/.cache/huggingface is intentionally left untouched, with a note in
the script suggesting how to remove it if desired.
Script is POSIX sh, idempotent (every removal is gated on existence
and uses `2>/dev/null || true`), and handles macOS, Linux, and WSL.
Windows is intentionally not covered here; the existing PowerShell
Remove-Item line in README is kept for that.
* studio: trim uninstall.sh header
* studio: address PR review feedback on uninstall.sh
Four findings from automated review, all verified real:
1. pkill pattern only matched `-p N`, not `--port N`. Studio
instances launched with the long option form survived the
uninstall. Fix: run two pkill passes, one for each form, with
`[ =]` covering both space and `=` separators.
2. CLI shim at ~/.local/bin/unsloth (symlink into the venv created
by install.sh:2167) was left behind, becoming a broken symlink
after the venv directory is removed. Fix: add it to the removals.
3. Custom install roots via UNSLOTH_STUDIO_HOME / STUDIO_HOME were
not removed. install.sh records the install location in
~/.local/share/unsloth/studio.conf as UNSLOTH_EXE; parse it,
derive the root as three dirnames up, and remove the root if it
is non-default.
4. On WSL the installer creates 'Unsloth Studio.lnk' on the Windows
Desktop and Start Menu Programs folder via powershell.exe.
Mirror that path on uninstall by invoking powershell.exe to
Remove-Item the same two locations. Best-effort, gated on
powershell.exe being available.
Tests (T2.8b, T2.15, T2.16, T2.17, T2.18, T2.5b) added behind the
scenes; all pass on macOS Darwin 25.3 with `dash -n`, `sh -n`,
shellcheck-clean (SC2016 suppressed on the PowerShell single-quoted
heredoc since the $env: expansions must remain literal to the
shell so PowerShell receives them verbatim).
* studio: harden uninstall.sh against env-mode and shim collisions
- Honor UNSLOTH_STUDIO_HOME / STUDIO_HOME at uninstall time and read
env-mode studio.conf at $<root>/share/studio.conf, not just the
default-mode conf under $HOME/.local/share/unsloth/. Without this,
installs done with a custom STUDIO_HOME leak the install tree even
when the env var is re-exported.
- Guard the custom-root resolver against "/" and empty so a corrupted
studio.conf (UNSLOTH_EXE='/etc/passwd' or similar) or an
UNSLOTH_STUDIO_HOME=/ cannot trick the script into rm -rf'ing root.
- Only remove $HOME/.local/bin/unsloth when it is a symlink resolving
to a Studio venv. pyproject.toml declares unsloth as a console
script, so pip install --user unsloth places a regular file at the
same path; the previous unconditional rm wiped that unrelated CLI.
- When neither env var is set, print a tail hint so users with custom
install roots know to re-run with the variable.
Verified with a sandboxed harness covering 24 scenarios (default and
env-mode installs across macOS / Linux / WSL, idempotency, hostile
lockfile names, path-traversal attempts, malformed conf, pkill long
and short forms, pip-conflict shim, broken-symlink bundle path).
Script remains POSIX (shellcheck -s sh clean, runs under /bin/dash).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Refuse non-Studio uninstall roots and tighten process matching for PR #5497
Three issues found while testing custom-root paths and process cleanup:
1. UNSLOTH_STUDIO_HOME=$HOME sh uninstall.sh rm -rf'd $HOME (same for
STUDIO_HOME and parent-of-$HOME). install.sh accepts any writable
directory for STUDIO_HOME, so the uninstaller must validate ownership
before deletion. _is_studio_root accepts a candidate root only if it
contains share/studio.conf, an unsloth_studio/ directory, or a
bin/unsloth shim pointing into unsloth_studio/bin. _is_unsafe_root is
a defense-in-depth deny list (/, $HOME, $HOME's parent, system paths).
2. pkill -f patterns "unsloth studio.*-p[ =][0-9]" over-matched on argv
substrings. A user running `less notes.md` whose filename contained
"unsloth studio ... -p N" had their less killed. New patterns anchor
on /unsloth_studio/bin/ so only processes whose actual exe lives in a
Studio venv match.
3. pkill missed processes that exec into studio/backend/run.py --port N
(the post-exec form when the unsloth CLI replaces itself). Added a
third pattern for that shape, and prefer PID files written by
install.sh's _spawn_terminal (studio-$port.pid in DATA_DIR) over
argv matching for installs that have them.
* Tighten ownership guards from review round for PR #5497
Three findings from the second reviewer round:
1. _is_studio_root accepted any directory containing an unsloth_studio/
subdir as Studio-owned. A user workspace that happens to contain a
folder named unsloth_studio/ would be deleted. install.sh's env-mode
guard at install.sh:1358-1361 already requires .unsloth-studio-owned
before treating the venv as replaceable. Mirror that: require the
owner marker, share/studio.conf, or the bin/unsloth shim target.
2. The pkill -f fallback patterns were global, so uninstalling install A
would also kill install B's running server. Scope each pattern to the
actual install root being removed by interpolating the root path into
the regex. Also adds a third pattern shape for `unsloth studio` with
no -p / --port flag (the CLI default-port form).
3. Desktop/Unsloth Studio is created by install.sh as a symlink to the
.app bundle. If a user has a regular directory by that name (photos,
notes, etc.), the previous _remove_path call rm -rf'd it. Now we only
remove it when it is a symlink or does not exist.
* Canonicalize env roots and honor UNSLOTH_STUDIO_HOME precedence for PR #5497
Two findings from the latest review round:
1. Canonicalize env-derived roots before the safety check. The deny list
only string-compares against $HOME, so a syntactic variant like
UNSLOTH_STUDIO_HOME=$HOME/../$USER (or trailing slash, or relative
path) bypassed _is_unsafe_root even though it resolves to $HOME. Now
_emit runs CDPATH= cd -P -- + pwd -P first, so all variants normalize
to the same canonical path before the deny check. Also added the same
tilde expansion install.sh's _resolve_studio_destinations does.
2. Mirror install.sh's env-var precedence (install.sh:282-290). When
both UNSLOTH_STUDIO_HOME and STUDIO_HOME are set, install.sh resolves
only UNSLOTH_STUDIO_HOME and ignores STUDIO_HOME. Uninstall was
emitting both, so running uninstall.sh for install A would also
delete install B if the user had a stale STUDIO_HOME pointing at B.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Daniel Han <info@unsloth.ai>
|
||
|
|
9bab3b955e
|
Add API Inference endpoint |