Commit graph

4,805 commits

Author SHA1 Message Date
Daniel Han
883e99e5d1 Merge main and revert user-visible UI string in llm_assist.py
Resolve merge conflict in install_python_stack.py (keep _safe_print from
main). Revert one user_notification string back to em dash -- it renders
in the browser Dataset Preview dialog.
2026-03-25 05:27:23 +00:00
Daniel Han
a1eff8ee22 fix: replace Unicode dashes with ASCII equivalents across codebase
Box-drawing chars (U+2500), em dashes (U+2014), and en dashes (U+2013)
in comments, section dividers, log messages, and docstrings are not
representable on legacy code pages like CP1252. Replace them with plain
ASCII dashes so the codebase is consistently ASCII-safe.

User-facing UI strings (placeholders, separators, display text in the
frontend) are left unchanged since they render in the browser which
handles Unicode natively.
2026-03-25 05:16:59 +00:00
Krishna Chaitanya
9b989ee898
fix: prevent UnicodeEncodeError on Windows CP1252 consoles in studio setup (#4563)
* fix: prevent UnicodeEncodeError on Windows CP1252 consoles in studio setup

On Windows, `unsloth studio setup` crashes with a UnicodeEncodeError
when install_python_stack.py tries to print Unicode status glyphs
(, , ⚠️) to a console that uses a legacy code page like CP1252.

Add a _safe_print() helper that catches UnicodeEncodeError and
gracefully degrades emoji to ASCII equivalents ([OK], [FAIL], [!]).
Replace all print() calls that emit Unicode glyphs with _safe_print().

Fixes #4509

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

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

* Replace Unicode dashes with ASCII in install_python_stack.py

Box-drawing (U+2500) and em dash (U+2014) chars in section dividers
and comments are themselves not representable on CP1252 -- replace
with plain ASCII dashes for consistency with the fix.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-24 22:04:09 -07:00
TR-3B
8c94b461fb
Add GRPO resume vLLM cleanup guard (#4411)
* Add GRPO resume vLLM cleanup guard

* Guard GRPO resume sleep on vLLM sleep mode

* Harden GRPO resume vLLM cleanup guard

- Wrap llm.sleep(1) in try/except so a failed sleep does not block
  training resume (best-effort cleanup)
- Also check kwargs["model_path"] which transformers.Trainer.train()
  still accepts and normalizes to resume_from_checkpoint internally

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-24 21:37:45 -07:00
Wasim Yousef Said
085f9529b6
Regroup chat settings sidebar into focused sections (#4551)
* feat(chat): regroup settings sidebar into Model, Sampling, Tools, and Preferences sections

Split the monolithic Settings collapsible into focused sections with
icons. Model section shows context length and KV cache dtype for GGUF
models, trust remote code for non GGUF. Tools section groups auto heal,
max tool calls, and tool call timeout. Preferences section holds auto
title toggle.

* feat(chat): persist collapsible section open/closed state in localStorage

Remember which sections the user expanded or collapsed across sidebar
toggles, mobile sheet reopens, and browser sessions.

* fix(chat): harden collapsible state persistence and restore defaultOpen

- Validate localStorage values are booleans before using them, preventing
  corrupted entries like string "false" from being treated as truthy
- Use Object.hasOwn() instead of `in` operator to avoid prototype chain
  matches on keys like "constructor" or "toString"
- Restore defaultOpen={true} on Model and Preferences sections so they
  are expanded on first visit, matching the old Settings section behavior
- Fix misleading Context Length description to reflect it is read-only
- Downgrade console.error to console.warn for non-critical localStorage
  parse failures

* fix(chat): remove redundant disabled styles on Context Length input

The Input component already applies opacity-50 and cursor-not-allowed
via its disabled: variants. Specifying them unconditionally in the
className is redundant.

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-24 19:39:27 -07:00
Daniel Han
acc881452f
fix: pin unsloth>=2026.3.11 in install.sh and install.ps1 (#4556)
Ensures both install scripts always pull a version that has the
litellm removal fix. Without the pin, stale uv/pip caches could
resolve the older 2026.3.10 which still had litellm in
data-designer-deps.txt, causing setup to fail at step 8/11
while PyPI has litellm quarantined.
2026-03-24 07:44:07 -07:00
Daniel Han
76a2f17470
fix(studio): remove litellm dep (quarantined on PyPI) (#4553)
litellm has been quarantined on PyPI due to a supply chain attack
in version 1.82.8 (malicious credential-stealing .pth file).
No versions are currently installable, which blocks
`unsloth studio setup` at step 8/11 (data-designer deps).

Remove litellm from the single-env data-designer requirements
so setup completes. litellm can be re-added once PyPI lifts the
quarantine.

Ref: https://github.com/BerriAI/litellm/issues/24512
2026-03-24 07:10:26 -07:00
Daniel Han
fac6f7887e Versioning 2026-03-24 06:50:36 -07:00
Daniel Han
95d2748278
fix: give @0xKushwaha git history credit for completion_only_loss fix (#4552)
* Revert "fix: handle prompt/completion datasets in slow-path BOS detection (#4548)"

This reverts commit fca83182af.

* fix: support completion_only_loss=True with prompt/completion dataset columns

When completion_only_loss=True, TRL rejects formatting_func but Unsloth's
patched _prepare_dataset/_prepare_non_packed_dataloader assumed either
formatting_func or dataset_text_field was always set, causing a catch-22.

Now handles prompt/completion columns as a third case for BOS token
detection, with a safe None fallback for all other cases.

(cherry picked from commit 978f78c6f1)

* fix: handle prompt/completion datasets in slow-path BOS detection

The slow-path check_text blocks in rl_replacements.py and
tokenizer_utils.py crash when a prompt/completion dataset is used
because they unconditionally access dataset[0][dataset_text_field]
even when the dataset does not have a text field.

This fixes both files to:
- Default dataset_text_field to None instead of raising when undefined
- Detect prompt/completion columns and concatenate them for BOS check
- Guard with isinstance(str) on both prompt and completion to handle
  conversational format (list of dicts) by setting test_text to None
- Add test_text is not None guard on has_bos_token_already to prevent
  AttributeError on NoneType.startswith()

This is the slow-path complement to unslothai/unsloth-zoo#560 which
fixes the fast-path in sft_prepare_dataset.

Closes #4486

(cherry picked from commit b6ce5786d0)

* fix: preserve chat_template BOS check when test_text is None

The has_bos_token_already guard wrapped both test_text.startswith()
and bos_token in chat_template with test_text is not None, which
disabled the chat_template BOS detection for conversational datasets
where test_text is set to None.

Split the guard so test_text is not None only applies to the
startswith() call, while bos_token in chat_template is always checked.

(cherry picked from commit 40bd8b8917)

---------

Co-authored-by: Ayush Kushwaha <148432773+ayushkushwaha240@users.noreply.github.com>
2026-03-24 06:38:57 -07:00
Daniel Han
fca83182af
fix: handle prompt/completion datasets in slow-path BOS detection (#4548)
* fix: handle prompt/completion datasets in slow-path BOS detection

The slow-path check_text blocks in rl_replacements.py and
tokenizer_utils.py crash when a prompt/completion dataset is used
because they unconditionally access dataset[0][dataset_text_field]
even when the dataset does not have a text field.

This fixes both files to:
- Default dataset_text_field to None instead of raising when undefined
- Detect prompt/completion columns and concatenate them for BOS check
- Guard with isinstance(str) on both prompt and completion to handle
  conversational format (list of dicts) by setting test_text to None
- Add test_text is not None guard on has_bos_token_already to prevent
  AttributeError on NoneType.startswith()

This is the slow-path complement to unslothai/unsloth-zoo#560 which
fixes the fast-path in sft_prepare_dataset.

Closes #4486

* fix: preserve chat_template BOS check when test_text is None

The has_bos_token_already guard wrapped both test_text.startswith()
and bos_token in chat_template with test_text is not None, which
disabled the chat_template BOS detection for conversational datasets
where test_text is set to None.

Split the guard so test_text is not None only applies to the
startswith() call, while bos_token in chat_template is always checked.
2026-03-24 05:27:59 -07:00
Michael Han
a41dbb6ab2
Add r/unsloth Reddit.md 2026-03-24 04:13:38 -07:00
Michael Han
381f509695
Adding Qwen3.5 RL.md 2026-03-24 04:06:23 -07:00
Wasim Yousef Said
c8057d911b
fix: system prompt ignored in unsloth inference (#4528)
* fix: system prompt was dropped in unsloth text and vision inference

* refactor: simplify system prompt message construction

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

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

* fix: use multimodal typed content parts for vision system message and add fallback

The system message content must use typed content parts
([{"type": "text", "text": ...}]) instead of a plain string to match
the multimodal processor contract (consistent with the audio path).
Plain strings cause some processors (e.g. LLaVA) to silently drop the
system prompt.

Also wraps processor.apply_chat_template in try/except so models that
reject the system role gracefully fall back to no system message with
a warning log.

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

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

* fix: capture and log original exception in vision system prompt fallback

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-24 04:01:33 -07:00
Wasim Yousef Said
3dc212e218
fix: always show chat tool icons (#4525)
* fix: always show chat tool icons, gray out when model doesn't support them

Tool icons (Think, Search, Code) were hidden unless a model was loaded
and supported those features. Now they're always visible so users can
see and pre-select them. If a loaded model doesn't support a feature,
the button gets grayed out and disabled instead of being removed.

* refactor: centralize Qwen thinking params in store

* fix: disable tool buttons when no model is loaded

Change disabled condition from `modelLoaded && !supportsX` to
`!modelLoaded || !supportsX` so buttons are grayed out both when
no model is loaded and when the loaded model lacks the capability.

* Fix Qwen3 param clobbering and restore SuggestionItem capability guards

- Revert setReasoningEnabled() in the store to a pure boolean setter.
  Moving the Qwen3 param logic into it caused reconnect/load/refresh
  paths (which also call setReasoningEnabled) to silently overwrite
  user-customized or server-provided temperature/topP/topK/minP.

- Restore applyQwenThinkingParams() as a standalone function called
  only from explicit user toggle click handlers in thread.tsx and
  shared-composer.tsx, matching the pre-PR behavior.

- Re-add supportsReasoning/supportsTools guards in the SuggestionItem
  click handler so that clicking a suggestion card only activates
  tool toggles the loaded model actually supports.

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-24 03:26:56 -07:00
Daniel Han
77b21333fb
fix(studio): restore scroll lock on reasoning panel collapse (#4545)
PR #4543 removed useScrollLock from ReasoningRoot, causing the thread
viewport to jump when a user collapses a reasoning panel. Restore the
hook to freeze scrollTop during the 200ms collapse animation, matching
the pattern used by tool-fallback.tsx and tool-group.tsx.
2026-03-24 02:27:06 -07:00
Wasim Yousef Said
1129ea44bc
fix(studio): show Windows-specific reset-password command on login error (#4529) 2026-03-23 23:04:00 -07:00
Daniel Han
5916bcb2e3
Fix Studio port conflict detection for loopback addresses (#4532)
* Fix port conflict detection when loopback address is held by another process

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

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

* Use getaddrinfo for IPv6 host support, restore emojis in terminal output

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

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

* Guard against conn.pid being None in _get_pid_on_port

psutil.net_connections() can return entries with pid=None when the
current user lacks privileges to see the owning process (common on
macOS without root, Windows without admin, and some Linux configs).

psutil.Process(None) does not raise -- it silently returns the
current process, which would make the warning incorrectly blame
Unsloth Studio itself for blocking the port.

Skip entries with pid=None so the caller falls back to the generic
"port is already in use" message instead.

* Update studio/backend/run.py

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-23 22:34:47 -07:00
Lee Jackson
45e4a0473a
studio: stop scroll hijack during generation and fix thinking panel layout shift (#4543)
* fix(chat): stabilize thinking panel and thread scroll during generation

* fix: match ChatGPT scroll and thinking panel behavior

- Remove autoScroll={false} from thread viewport to restore default
  follow-scroll during streaming (pauses when user scrolls up, resumes
  at bottom)
- Rewrite reasoning panel state: auto-opens on stream start, user can
  close during streaming, auto-collapses when reasoning ends, user can
  re-expand after collapse

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-23 22:33:46 -07:00
Lee Jackson
01d7dce3f4
studio: persist system prompt and preset settings across navigation (#4538)
* fix(studio): harden system prompt persistence and storage fallback

* Exclude checkpoint from localStorage persistence for PR #4538

checkpoint is backend-owned state -- refresh() already syncs it from
getInferenceStatus() on every page load. Persisting it to localStorage
causes a stale model ID to survive across backend restarts, which
prevents auto-load from triggering when no model is actually loaded.

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-23 22:21:04 -07:00
金黄色葡萄球君君
2b330e2f24
fix: store embedding_learning_rate on self in UnslothTrainingArguments (#4531)
Fixes #4492

The embedding_learning_rate parameter was assigned to a local variable
instead of self.embedding_learning_rate, causing UnslothTrainer.create_optimizer()
to always get None via getattr and silently fall back to a single param group.

Bug: embedding_learning_rate = embedding_learning_rate (no-op)
Fix: self.embedding_learning_rate = embedding_learning_rate
2026-03-23 21:08:29 -07:00
pre-commit-ci[bot]
a5be6904a6
[pre-commit.ci] pre-commit autoupdate (#4542)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.6 → v0.15.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.6...v0.15.7)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-23 14:55:27 -07:00
Datta Nimmaturi
cd65584f19
Update issue template 2026-03-23 10:10:15 +05:30
Daniel Han
1ecb55faa2 Update _utils.py b8475 2026-03-22 08:23:40 -07:00
Daniel Han
797ddd201e
Fix Studio silently exiting on Windows without error output (#4527)
* Fix Studio silently exiting on Windows without error output

On Windows, `unsloth studio` launches a child process via
subprocess.Popen to run the server in the studio venv. If the child
crashes (e.g. due to a missing package), the parent just calls
typer.Exit(rc) with no message -- the user sees "Launching Unsloth
Studio... Please wait..." and then the prompt returns with zero
feedback.

Root cause: `data_designer_unstructured_seed` is imported at the top
level in seed.py. If this package is not installed in the studio venv,
the entire import chain (seed.py -> routes/__init__.py -> main.py ->
run_server()) crashes with ModuleNotFoundError. Since run.py has no
try/except around run_server() and studio.py does not report nonzero
exit codes, the failure is completely silent.

Changes:
- run.py: wrap run_server() in try/except, print clear error with
  traceback to stderr. Also reconfigure stderr encoding on Windows so
  tracebacks with non-ASCII paths do not cause secondary failures.
- studio.py: print an error message when the child process exits with
  a nonzero code on Windows, so the user knows something went wrong.
- seed.py: make data_designer_unstructured_seed import optional with
  a try/except fallback. The server starts normally and only returns
  HTTP 500 if the unstructured seed endpoints are actually called.

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

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

* Skip Anaconda/Miniconda Python when creating Studio venv on Windows

Conda-bundled CPython ships modified DLL search paths that prevent
torch from loading c10.dll on Windows. The Studio server fails
silently at startup because the venv was created with conda's Python.

Standalone CPython (python.org, winget, uv) does not have this issue.

Both install.ps1 and setup.ps1 now skip any Python binary whose path
contains conda, miniconda, anaconda, miniforge, or mambaforge when
selecting the interpreter for the studio venv. If only conda Python
is available, the scripts print an error with instructions to install
standalone CPython.

* Fix multi-file preview crash and improve setup.ps1 Python discovery

Addresses review findings [10/10] and [8/10]:

1. seed.py: _read_preview_rows_from_multi_files() had a hard import
   of build_multi_file_preview_rows inside the function body, bypassing
   the optional-plugin guard. Moved it into the top-level try/except
   block and added a None guard matching the other functions.

2. setup.ps1: Python discovery now probes py.exe (Python Launcher)
   first, uses Get-Command -All to look past conda entries that shadow
   standalone CPython further down PATH, skips WindowsApps stubs, and
   resolves the actual executable path so venv creation does not
   re-resolve back to a conda interpreter.

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

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

* Check sys.base_prefix to catch venvs created from conda Python

A venv created from conda Python (e.g. C:\Users\danie\.venv) has a
path that does not contain "conda", but sys.base_prefix still points
to the conda install (e.g. C:\Users\danie\miniconda3). The previous
path-only check missed this case entirely.

Both install.ps1 and setup.ps1 now use a Test-IsConda helper that
checks both the executable path AND sys.base_prefix against the
conda/miniconda/anaconda/miniforge/mambaforge pattern. This catches:
- Direct conda Python executables
- Venvs created from conda Python (base_prefix reveals the origin)

* Fix install.ps1 passing version string to uv venv instead of resolved path

Find-CompatiblePython returned a bare version string (e.g. "3.13")
which was passed to `uv venv --python 3.13`. uv performs its own
interpreter discovery and can resolve that version string back to a
conda Python, defeating the entire conda-skip logic.

Now Find-CompatiblePython returns a hashtable with both .Version (for
display) and .Path (the resolved absolute executable path). The venv
is created with `uv venv --python <absolute-path>`, ensuring uv uses
the exact interpreter we validated.

* Quote resolved Python path in uv venv call for paths with spaces

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-22 08:23:03 -07:00
Daniel Han
866cb33ce0 Update _utils.py 2026-03-22 06:14:35 -07:00
NuoFang
4cedeba8c2
fix(studio): prevent ModuleNotFoundError in dataset.map() on Windows (#4473)
* fix(studio): prevent ModuleNotFoundError in dataset.map() on Windows

On Windows, dataset.map() uses "spawn", which requires workers to
import compiled modules from disk. Previously, clear_unsloth_compiled_cache()
deleted the entire directory, causing workers to crash when looking for
UnslothSFTTrainer.py.

Changes:
1. Added `preserve_patterns` to cache cleanup to keep `Unsloth*Trainer.py`
   on Windows while clearing model-specific files.
2. Added the cache directory to PYTHONPATH for spawn workers.
Linux/macOS behavior is unchanged.

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

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

* Fix spawn-platform coverage, CWD path mismatch, and race condition for PR #4473

- Extend platform guard from win32-only to include macOS (also uses spawn
  since Python 3.8, same ModuleNotFoundError would occur)
- Replace fragile CWD-based PYTHONPATH registration with centralized
  register_compiled_cache_on_path() that uses the same __file__-relative
  _CACHE_DIRS already used by cache_cleanup -- fixes path mismatch when
  studio is launched from a directory other than the repo root
- Move PYTHONPATH registration to the top of _train_worker(), before any
  dataset.map() call (previously it ran late in config assembly, after
  dataset formatting which also calls dataset.map())
- Update inference.py model-unload to preserve trainer files on spawn
  platforms, preventing a race where unloading a model via inference tab
  would delete UnslothSFTTrainer.py while training workers are importing it

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

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

* Fix cache-dir precedence reversal in register_compiled_cache_on_path()

Iterating _CACHE_DIRS in forward order while calling insert(0) each time
reverses the declared priority: later entries shadow earlier ones. When
multiple compiled-cache directories exist, spawned workers could import a
stale trainer from the wrong cache.

Fix: iterate in reverse so that the highest-priority entry (first in
_CACHE_DIRS) is inserted last and ends up at position 0 in sys.path and
PYTHONPATH.

* fix: harden worker-count helpers against cpu_count=None and desired<=0

- safe_num_proc: guard os.cpu_count() with `or 1`, clamp multi-GPU
  path with max(1, min(4, desired)), clamp return with max(1, desired)
- safe_thread_num_proc: same os.cpu_count() guard and return clamp
- Add regression tests (31 L1 unit + 10 sandbox edge-case tests)

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

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

* remove regression tests from PR

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-22 06:11:24 -07:00
Daniel Han
62e3de181f
Update weather dashboard suggestion to request HTML code output (#4523)
The previous prompt "Show me a live weather dashboard, no API key needed"
was too vague. The new wording explicitly asks for HTML code, which
produces more useful and consistent responses.
2026-03-22 06:09:48 -07:00
Leo Borcherding
71c77d4e96
fix(install.ps1): fix non-NVIDIA package resolution — split torch+unsloth install (#4515)
* fix(install.ps1): split torch+unsloth install to fix non-NVIDIA package resolution

--torch-backend=auto on a non-NVIDIA Windows machine causes uv to resolve
unsloth==2024.8 (pre-CLI, no unsloth.exe). Fix: detect GPU robustly (PATH +
hardcoded fallback paths, mirrors setup.ps1), install torch first with an
explicit --index-url (CUDA variant for NVIDIA, CPU for everyone else), then
install unsloth separately without --torch-backend so the solver always picks
a modern release that ships the Studio CLI.

Closes the remaining gap flagged in #4478.

* fix(install.ps1): align warning with setup.ps1, add --upgrade, handle CUDA 11.x

- Match the no-GPU warning message to studio/setup.ps1 wording
  (chat-only GGUF mode, driver download link)
- Add CUDA 11.x floor check in Get-TorchIndexUrl so old drivers
  fall back to CPU wheels instead of silently getting cu124
- Log a warning when nvidia-smi output cannot be parsed
- Add --upgrade to both uv pip install calls so re-runs pick up
  newer package versions

* revert --upgrade from uv pip install calls

uv pip install already resolves to the latest satisfying version;
--upgrade is unnecessary and could force unwanted re-installs.

* fix: replace frozen cu124 fallbacks with cu126, guard CUDA 11.x

cu124 wheels are frozen at torch 2.6.0 -- falling back to them pins
users to an outdated PyTorch.  Three issues fixed in both install.ps1
and setup.ps1:

1. CUDA 12.0-12.5 now maps to cu126 (was cu124).
2. CUDA 11.x and older now falls back to cpu (was cu124, which would
   silently install incompatible GPU wheels).
3. Parse-failure and no-nvidia-smi fallbacks updated to cu126/cpu.

Adds tests/test_cuda_wheel_mapping.py covering the mapping logic,
nvidia-smi parsing, PS1 file sync, PyTorch index URL validation,
and sandbox torch installs.

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

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

* remove test file from PR branch

Test file kept locally, not needed in the PR.

* fix: map CUDA 11.x to cu118 instead of cpu

PyTorch still publishes cu118 wheels (up to torch 2.7.1), so CUDA 11.x
users get GPU-accelerated torch rather than being forced to CPU-only.
Only CUDA 10.x and older fall back to cpu.

* fix: revert CUDA 12.0-12.5 to cu124, handle cpu tag in setup.ps1

CUDA 12.0-12.5 drivers only support up to their reported CUDA version,
so cu126 wheels (built with CUDA 12.6) fail to load. Revert the catch-
all for 12.0-12.5 back to cu124.

Also fix setup.ps1 caller: when Get-PytorchCudaTag returns "cpu" (e.g.
CUDA 10.x driver), the installer now correctly skips Triton and prints
"CPU-only" instead of "CUDA support (cpu)".

* fix: add --upgrade to unsloth install for stale venv repair

On reruns against an existing venv, uv pip install unsloth makes no
changes if unsloth==2024.8 is already installed (it satisfies the
constraint). Adding --upgrade only to the unsloth install ensures
stale installs get repaired without forcing a multi-GB torch
re-download.

* fix: use --upgrade-package to avoid clobbering torch CUDA wheels

`--upgrade unsloth` re-resolves torch from default PyPI, stripping the
+cuXXX suffix installed in step 1.  `--upgrade-package unsloth unsloth`
upgrades only unsloth (and pulls missing deps like transformers, trl)
while preserving the pinned torch from the CUDA-specific index.

* docs: explain why split-install and --upgrade-package are needed

Expand the inline comment block to document both design decisions:
1. Why torch is installed separately (solver fallback to 2024.8)
2. Why --upgrade-package is used instead of --upgrade (preserves CUDA wheels)

---------

Co-authored-by: LeoBorcherding <LeoBorcherding@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-22 05:41:58 -07:00
Daniel Han
100b8857f2
Fix Studio crash on Anaconda/conda-forge Python (#4484)
* Fix Studio crash on Anaconda Python due to platform._sys_version() parse failure

Anaconda and conda-forge modify sys.version to include distributor
metadata between pipe characters, e.g.:

    3.12.4 | packaged by Anaconda, Inc. | (main, ...) [MSC v.1929 ...]

Python's platform._sys_version() has a hardcoded regex that cannot
parse this format, raising ValueError. CPython closed this as "not
planned" (cpython#102396) since Anaconda modified the binary.

This breaks the import chain: run.py -> structlog -> rich -> attrs,
which calls platform.python_implementation() at module scope.

Fix: before any library imports, strip the pipe segments, parse the
cleaned version string via the standard parser, and cache the result
under the original sys.version key so all subsequent platform calls
hit the cache.

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

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

* Add defensive fallback for unpaired pipe edge cases in version patch

Address Gemini review suggestion: if the paired-pipe regex leaves
residual pipes (hypothetical single-pipe distributor metadata), fall
back to extracting the version number and the parenthesized build
info directly. Wrap the entire patch in try/except so unexpected
version string formats degrade gracefully instead of crashing the
patch itself.

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

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

* Refactor into shared _platform_compat module, cover colab.py entrypoint

Address reviewer feedback:

1. Extract the Anaconda/conda-forge sys.version fix into a shared
   _platform_compat.py module that wraps platform._sys_version() with
   a retry-on-ValueError fallback. This is more robust than cache-seeding
   because it handles all future platform._sys_version() calls, not just
   the first one.

2. Import the fix from both run.py and colab.py entrypoints, so Studio
   no longer crashes on Anaconda Python regardless of the launch path.

3. The wrapper is idempotent (guarded by a flag) and handles edge cases:
   paired pipes (Anaconda, conda-forge), unpaired pipes (hypothetical),
   and standard CPython strings (no-op since ValueError is never raised).

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

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

* Replace monkey-patch with cache-prime, fix colab.py duplicate sys.path, cover main.py

- Rewrite _platform_compat.py: replace function-wrapping monkey-patch with
  one-shot cache seed (_seed_sys_version_cache). Parses cleaned sys.version
  once and seeds platform._sys_version_cache so the stdlib parser never sees
  the problematic Anaconda/conda-forge pipe-delimited string. No function
  replacement, no idempotency flag, no reload edge cases.

- colab.py: remove duplicate backend_path sys.path insertion after
  _bootstrap_studio_venv(). The early insertion (before _platform_compat
  import) already covers it. This also fixes backend/ ending up behind
  venv site-packages in sys.path ordering.

- run.py: move PYTHONWARNINGS=ignore before _platform_compat import to
  preserve original intent of suppressing warnings early.

- main.py: add sys.path + _platform_compat import before route imports,
  covering the direct `uvicorn main:app` launch path.

- Add test_platform_compat.py with 7 tests covering Anaconda, conda-forge,
  and standard CPython version strings, plus the loggers import chain.

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

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

* Remove test_platform_compat.py from PR

* Handle Format B conda-forge version strings with duplicate paren groups

Some conda-forge builds produce sys.version with the build info both
before and after the pipe label (e.g. "3.9.7 (default, ...) | packaged
by conda-forge | (default, ...) \n[GCC 7.5.0]"). After stripping the
pipe segment, two consecutive (...) groups remain, which still fails
platform._sys_version(). Add a second regex pass to drop the duplicate
paren group.

* Guard _sys_version call with try/except to avoid making things worse

If the cleaned version string is still unparseable by the stdlib regex
(e.g. nested parens, exotic multi-pipe formats), silently give up
instead of letting ValueError propagate at import time -- which would
be a worse crash than the original deferred one.

---------

Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-22 05:36:55 -07:00
Andrew Barnes
2c5d3c48ec
fix: subprocess crash during map operation on Windows (#4507)
* fix: handle Windows subprocess crash during dataset.map()

Windows uses spawn (not fork) for multiprocessing. Spawned workers
cannot resolve Unsloth's dynamically compiled cache modules from
unsloth_compiled_cache/, causing ModuleNotFoundError and RuntimeError
during dataset.map() tokenization.

Add two platform-guarded patches for sys.platform == "win32":
1. Force HF_DATASETS_MULTITHREADING_MAX_WORKERS=1 and set spawn method
2. Monkey-patch Dataset.map() to force num_proc=None

Fixes #4490

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

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

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

* address review: extend spawn fix to macOS, add multiprocess fallback

- Change platform checks from sys.platform == "win32" to
  sys.platform != "linux" so macOS (also spawn-based) is covered
- Wrap multiprocess import in try/except falling back to stdlib
  multiprocessing when the multiprocess package isn't installed
- Rename _win32_safe_map to _spawn_safe_map to reflect broader scope

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

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

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

* fix: replace global Dataset.map monkey-patch with targeted num_proc routing

The previous approach had issues: Patch 1 set HF_DATASETS_MULTITHREADING_MAX_WORKERS
and forced set_start_method (dead code on platforms already using spawn), and Patch 2
globally monkey-patched Dataset.map() (too broad, missed Dataset.filter()).

Replace with a two-layer fix:

1. Studio layer: Add dataset_map_num_proc() that returns None on spawn platforms
   (Windows, macOS). Unlike num_proc=1 which still creates Pool(1) and spawns a
   worker, num_proc=None runs Dataset.map()/filter() truly in-process.
   Update all dataset.map() callsites to use it. ThreadPoolExecutor callers
   (format_conversion.py) keep using safe_num_proc() since threads are unaffected.

2. Root-cause layer: Propagate UNSLOTH_COMPILE_LOCATION via PYTHONPATH on spawn
   platforms so spawned workers can import compiled modules. Mirrors the .venv_t5
   pattern in worker.py. Does not import unsloth_zoo.compiler (heavy torch/triton
   imports). Completely skipped on Linux.

Also extend safe_num_proc() to return 1 on macOS (was only guarding Windows),
and narrow the transformers 5.x dataloader guard from != "linux" to explicit
("win32", "darwin").

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

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

* fix: add safe_thread_num_proc() for ThreadPoolExecutor callsites

safe_num_proc() correctly caps to 1 on macOS/Windows for process-based
multiprocessing, but format_conversion.py reuses it for ThreadPoolExecutor
workers. Threads share address space and are unaffected by spawn, so
capping to 1 makes image URL downloads sequential -- a real regression.

Add safe_thread_num_proc() that skips the platform guard but keeps the
cpu_count heuristic, and switch both ThreadPoolExecutor callsites in
format_conversion.py to use it.

* fix: remove double-wrap in dataset_num_proc + fix num_proc=1 in datasets route

- trainer.py:3009: Replace safe_num_proc(max(1, os.cpu_count() // 4))
  with max(1, (os.cpu_count() or 1) // 4) to avoid double-wrapping
  inside dataset_map_num_proc which already calls safe_num_proc
- trainer.py:15-20: Clarify comment on PYTHONPATH propagation
- datasets.py:445: Change num_proc=1 to num_proc=None for 10-row
  preview slice (avoids unnecessary multiprocessing overhead)

* fix: guard os.cpu_count() against None in worker-count helpers

os.cpu_count() can return None on some platforms. Use (os.cpu_count() or 1)
to prevent TypeError in safe_num_proc() and safe_thread_num_proc().

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-22 05:21:09 -07:00
DoubleMathew
4c1a6cb962
gate on min uv version and shortcut python candidate search if known (#4489)
* gate on min uv version and shortcut python candidate search if known

* fix sort -V cross compat issue, run_quiet early exit on llamacpp, autolaunch

* update launch message

* Fix PR comments

* auto launch and find open port

* remove dev install

* Fix review findings: major-version guard, non-fatal port fallback, tty comment, restore local

* Remove autolaunch, clean up dead state and debug noise

- Remove find_open_port, TTY-gated autolaunch, and </dev/tty
  redirection from install.sh; just print launch instructions
- Remove unused BEST_MAJOR variable from studio/setup.sh
- Remove stray "finished finding best python" debug echo
- Fix stale comment "below 3.12" to "below 3.11"

* Reject prerelease uv at exact minimum version boundary

* Remove 2>/dev/null from version_ge numeric comparisons

Let non-numeric version parts surface errors on stderr
instead of being silently swallowed.

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-22 05:20:25 -07:00
Daniel Han
64f9f389a0 Created using Colab 2026-03-22 04:57:26 -07:00
Velsa
981f477e31
fix: reconfigure stdout to UTF-8 on Windows to prevent UnicodeEncodeError on startup (#4493)
* fix: reconfigure stdout UTF-8 on Windows to prevent UnicodeEncodeError from emoji

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

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

* fix: default frontend_path when None to fix blank page when venv is pre-activated

* Restore Windows UTF-8 stdout fix dropped in earlier commit

The cp1252 console encoding on Windows cannot render emoji characters
used in startup messages (e.g. print(" Frontend loaded ...")).
This causes UnicodeEncodeError and crashes the server before it starts.

Place sys.stdout.reconfigure(encoding="utf-8", errors="replace") at the
top of run_server(), unconditionally before any print() or structlog
call, so all emoji output is covered -- including the frontend status
messages and silent=True paths that the original placement missed.

Guarded by sys.platform == "win32" and hasattr check, so it is a no-op
on Linux/macOS and safe in non-standard stdout environments (Jupyter,
piped IO).

* fix: preserve run_server(None) as headless, fix CLI frontend kwarg

Remove the frontend_path=None fallback in run_server() that changed
None from "headless/API-only" to "mount bundled frontend", breaking
backwards compatibility for embedders.

The blank-page bug was actually caused by the CLI wrappers always
passing frontend_path=frontend (even when frontend=None), which
overrode run_server()'s default. Fix studio.py and ui.py to only
pass frontend_path when the user explicitly sets --frontend.

* fix: use timeout loop for shutdown event in ui command

Match studio_default()'s shutdown loop that uses a 1-second timeout
on Event.wait(). Without a timeout, the bare wait() blocks at the C
level on Linux, preventing Python from delivering SIGINT (Ctrl+C).

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-22 04:49:59 -07:00
Leo Borcherding
96edad9c95
PR: Fix/cuda minimum check and abort (#4517)
* fix: add CUDA minimum version check and abort for llama.cpp (>= 12.4)

- setup.ps1/setup.sh: abort with clear error if CUDA toolkit < 12.4
  (llama.cpp requirement); link to cuda-toolkit-archive for upgrade
- setup.ps1: promote CUDA VS integration copy failure from WARN to
  ERROR + exit 1; remove manual-copy hack instructions per Roland —
  correct fix is re-installing CUDA/MSBuild, not a manual workaround

Fixes: https://github.com/unslothai/unsloth/issues/4437
Reported by: Sebastien

* fix: wipe stale studio venv when torch CUDA tag changes

When the NVIDIA driver is updated, the required PyTorch CUDA tag changes
(e.g. cu124 -> cu130) but setup.ps1 was silently reusing the existing
.venv, leaving the old torch wheel in place and breaking the UI for
everyone on the next setup run.

Before creating/reusing the venv, inspect the installed torch version
string. If its CUDA tag does not match what the current driver requires,
wipe the venv so we always get a clean, correct install.

* Fix CUDA version check: portability, non-fatal fallback, stale venv detection

- setup.sh: Replace grep -oP with POSIX sed for macOS compatibility
- setup.sh: Replace exit 1 with NVCC_PATH="" to fall back to CPU-only build
- setup.sh: Move version check before -DGGML_CUDA=ON append
- setup.sh: Add else branch warning when nvcc version is unparseable
- setup.ps1: Replace exit 1 with $NvccPath=$null for non-fatal CUDA fallback
- setup.ps1: Add driver vs toolkit guidance in version warning
- setup.ps1: Guard CUDA env/VS integration setup with if ($NvccPath)
- setup.ps1: VS integration catch: downgrade to WARN, restore source/dest paths
- setup.ps1: Stale venv: detect CPU torch and untagged wheels, not just +cuNNN
- setup.ps1: Stale venv: rebuild on failed torch import
- setup.ps1: Stale venv: wrap Remove-Item in try/catch for locked files

* Remove incorrect CUDA >= 12.4 check, keep only stale venv detection

llama.cpp has no hard minimum CUDA version -- it builds with CUDA as old
as 11.2 and degrades features gracefully via #if CUDART_VERSION guards.
The 12.4 figure was the default Docker/CI baseline, not a build requirement.

Reverted:
- CUDA version check in setup.sh (entirely removed)
- CUDA version check in setup.ps1 (entirely removed)
- VS integration catch block cosmetic changes (restored to main)
- if ($NvccPath) guard around CUDA env setup (not needed without version check)

Kept:
- Stale venv detection in setup.ps1: detects torch CUDA tag mismatch
  (cu124 vs cu130, cpu vs cuXXX, broken torch import) and rebuilds venv

* Fix stale venv detection: incomplete venvs, timeout, fatal delete failure

- Add 30s timeout for torch import probe via ProcessStartInfo/WaitForExit
- Use Test-Path -PathType Container to reject files masquerading as venv dir
- Trigger rebuild when python.exe is missing (incomplete venv)
- Make Remove-Item failure fatal ([ERROR] + exit 1) instead of warn-and-continue
- Move $expectedTorchTag computation inside -not $shouldRebuild guard

---------

Co-authored-by: LeoBorcherding <LeoBorcherding@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-22 04:46:36 -07:00
Daniel Han
bcf28466c2
fix: exclude .ipynb from ruff pre-commit hook (#4521)
The ruff pre-commit hook runs on all file types by default, including
.ipynb notebooks. Colab notebooks are authored in Colab's editor and
can contain IPython magics (%cd, !git) that ruff cannot parse. This
causes pre-commit.ci to fail on unrelated PRs when a notebook on main
has syntax ruff does not understand.

Add `exclude: '\.ipynb$'` to the ruff hook so notebooks are skipped.
2026-03-22 03:25:58 -07:00
Daniel Han
17dc83dc34 Created using Colab 2026-03-22 01:56:35 -07:00
Michael Han
d50e605f08
Update README.md 2026-03-21 14:55:18 -07:00
Sridhar Nandigam
a20c824711
FIX: Broken link to NVIDIA DataDesigner in README (#4500) 2026-03-21 14:41:09 -07:00
Wasim Yousef Said
50cccfd55e
feat(chat): server-side timings, context display & source hover cards (#4467)
* feat(chat): add server-side timings and context display for GGUF

Extract timings/usage metadata from llama-server SSE stream and forward
through the full stack. Replace client-side estimates with accurate
server-reported metrics (prompt eval, tok/s, token counts, cache hits).
Add context window usage bar to chat top nav.

* feat(chat): source badges with hover cards and 2-row collapse

- Add hover cards to source badges showing favicon, title, URL and
  snippet description on hover
- Limit source badges to 2 rows with +X more expand/collapse
- Parse snippet from web search results for hover card descriptions
- Replace individual Source rendering with grouped SourcesGroup component

* fix(chat): add null guards for server timings edge cases

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

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

* fix(chat): reset contextUsage on thread switch, remove unused context-display

* fix(chat): stop double-counting completion tokens in tool-calling path

* fix(chat): skip metadata events in llm_assist consumers

* fix(chat): hide context usage bar in compare mode

* fix(chat): harden timings pipeline and context usage persistence

Accumulate prompt_ms, predicted_ms, and predicted_n from intermediate
tool-detection passes so the final metadata reflects total server work.
Persist contextUsage in message metadata (Dexie) and restore on thread
load. Add type guard in gguf_stream_chunks for unexpected dict events.
Clear contextUsage when entering compare mode.

* feat(chat): make GGUF stream metadata OpenAI-compatible

* fix(chat): address PR review feedback

* feat(chat): address PR review feedback

* [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>
2026-03-20 23:42:01 -07:00
Wasim Yousef Said
dd283b0605
feat(studio): multi-file unstructured seed upload with better backend extraction (#4468) b8457
* fix(recipe-studio): prevent fitView from zooming to wrong location on recipe load

* feat: add pymupdf/python-docx deps and unstructured uploads storage root

* feat: add POST /seed/upload-unstructured-file endpoint

* feat: add multi-file chunking with source_file column

* feat: update frontend types and API layer for multi-file upload

* feat: round-robin preview rows across source files

Ensures every uploaded file is represented in the preview table
by cycling through sources instead of just taking the first N rows.

* fix: disable OCR, fix auto-load timing, fix persistence on reload

- Disable pymupdf4llm OCR with write_images=False, show_progress=False
- Replace onAllUploaded callback with useEffect that detects uploading→done
  transition (avoids stale closure reading empty file IDs)
- Fix importer to preserve file IDs from saved recipes instead of clearing
  (clearing only happens at share time via sanitizeSeedForShare)

* fix: harden unstructured upload with input validation and state fixes

Validate block_id/file_id with alphanumeric regex to prevent path
traversal, use exact stem match for file deletion, add error handling
for metadata writes and empty files, fix React stale closures and
object mutations in upload loop, and correct validation logic for
unstructured seed resolved_paths.

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

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

* fix: address PR review - legacy path import, share sanitizer, sync effect

Promote legacy source.path into resolved_paths for old unstructured
recipes, clear source.paths in share sanitizer to prevent leaking local
filesystem paths, and gate file sync effect to dialog open transition
so users can actually delete all uploaded files.

* fix: CSV column fix (BOM + whitespace + unnamed index re-save) for #4470

* fix: harden unstructured upload flow and polish dialog UX

* [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>
2026-03-20 13:22:42 -07:00
Michael Han
f113f3511d
Update Install method.md 2026-03-20 05:17:05 -07:00
Daniel Han
ef0491e0fe
Fix Windows installer Python detection and winget error handling (#4483)
* Fix Windows installer Python detection and winget error handling

The PowerShell installer crashes on some Windows machines due to two
issues:

1. Windows Store App Execution Aliases: Get-Command finds the stub at
   WindowsApps\python.exe, then python --version writes to stderr.
   With $ErrorActionPreference = "Stop" on PowerShell 5.1, stderr
   from native commands becomes a terminating error, killing the
   script before it tries to install Python.

2. winget "already installed" exit code: winget returns -1978335189
   (APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE) when the package is
   already at the latest version. The script treated any non-zero exit
   as failure. The fallback Get-Command check could also find the
   Store stub or fail if Python was partially uninstalled.

Changes:

- Add Find-CompatiblePython helper that tries the py launcher first,
  then python3/python via Get-Command -All, explicitly skipping any
  WindowsApps stubs. All invocations wrapped in try-catch so stderr
  never triggers ErrorActionPreference.

- Replace exit-code-based winget error handling with outcome-based:
  re-detect Python after install, retry with --force if not found,
  show actionable manual install instructions on final failure.

- Deduplicate PATH entries in Refresh-SessionPath to prevent unbounded
  growth from repeated machine+user path prepending.

* Address reviewer feedback: wrap winget calls, remove blanket WindowsApps filter

Three fixes based on code review:

1. Wrap all winget install calls in $ErrorActionPreference = "Continue"
   blocks so that winget stderr (progress bars, warnings) does not
   become a terminating error on PowerShell 5.1. This matches the
   pattern already used in studio/setup.ps1 line 983.

2. Remove the blanket *\WindowsApps\* path filter that rejected all
   WindowsApps executables including valid Microsoft Store Python
   installs. Instead, rely on the existing try-catch + version regex
   probing to determine if a candidate is functional. Non-functional
   entries (App Execution Alias stubs) fail the try-catch and are
   skipped naturally.

3. Use $pyLauncher.Source (resolved path) instead of bare py name,
   add -CommandType Application to avoid matching aliases/functions,
   and derive winget package ID from $PythonVersion variable instead
   of hardcoding Python.Python.3.13.

* Add back WindowsApps filter for python3/python fallback path

The App Execution Alias stubs in WindowsApps can open the Microsoft
Store as a side effect when invoked, even though the try-catch handles
the error. Since the py launcher (tried first) already detects
legitimate Store Python -- Store packages include py since Python
3.11 -- filtering WindowsApps in the python3/python fallback is safe
and avoids the Store popup.

---------

Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
2026-03-20 02:01:23 -07:00
Leo Borcherding
239ca98643
fix: detect AMD/no-NVIDIA GPU early in Windows installer and guard unsloth.exe existence (#4478) v0.1.0-beta
* fix(install.ps1): detect AMD/no-NVIDIA GPU early and guard unsloth.exe existence

When a user has an AMD GPU (no nvidia-smi), uv's --torch-backend=auto
resolves to CPU torch, which constrains the solver to unsloth==2024.8.
That ancient release has no unsloth.exe CLI entry point, so the subsequent
& \ studio setup call throws a confusing PowerShell
'module could not be loaded' CommandNotFoundException instead of a
clear error.

Two fixes:
- Detect nvidia-smi early; if no NVIDIA GPU is found, print a clear
  error explaining AMD/Intel GPUs are unsupported and exit before
  wasting time installing the wrong package version.
- Guard Test-Path \ before invoking it, so any future case
  where the CLI entry point is missing produces a readable error
  instead of a cryptic PowerShell exception.

Fixes: unsloth_studio\Scripts\unsloth.exe CommandNotFoundException
on AMD GPU systems (Windows).

* fix(install.ps1): correct GPU support message - AMD is Linux-only via ROCm

* Slim down to just the unsloth.exe existence guard

Remove the early NVIDIA GPU detection gate -- Studio supports Windows
and Mac without a GPU (finetuning is simply disabled). The GPU gate
was blocking legitimate non-NVIDIA users from installing.

Keep only the Test-Path guard on unsloth.exe before invoking it. This
turns the confusing PowerShell CommandNotFoundException into a clear
error message pointing at the likely cause (older unsloth version
resolved by the package solver that does not include the Studio CLI).

* Fix quickstart link in unsloth.exe guard message

---------

Co-authored-by: LeoBorcherding <LeoBorcherding@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-03-20 01:48:45 -07:00
Roland Tannous
ebe45981dd
feat: support GGUF export for non-PEFT models + fix venv_t5 switching for local checkpoints (#4455)
* feat: support full model GGUF export, disable incompatible methods in UI

* fix: resolve base model from config.json for venv_t5 export switching

* feat: detect BNB-quantized models and disable all export methods for quantized non-PEFT checkpoints

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

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

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

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

* fix: relocate Ollama Modelfile alongside GGUFs during non-PEFT export cleanup

* [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>
2026-03-20 12:13:18 +04:00
Manan Shah
be901ecdea
Adding launch command to install scripts (#4477)
* Adding launch command to install scripts

* Making launch only for interactive env
2026-03-20 10:45:33 +04:00
Michael Han
07aabf45c0
Update Install instructions.md 2026-03-19 21:51:10 -07:00
Daniel Han
d0e5a1d61e
Fix macOS install.sh: stdin consumption and Python discovery (#4472)
* Fix macOS install.sh: stdin consumption and Python discovery

Two issues when running `curl | sh` on macOS:

1. Commands like `brew install` consume bytes from the piped stdin,
   causing the shell to lose its place in the script. The remaining
   source code gets printed as text instead of being executed, so
   users have to run the installer twice. Fixed by redirecting stdin
   from /dev/null for brew, apt-get, xcode-select, and the uv
   installer subprocess.

2. setup.sh searches for Python 3.11-3.13 on the system PATH via
   `compgen -c`. On macOS systems that only have Python 3.9 and/or
   3.14, this fails with "No Python version between 3.11 and 3.13
   found" even though uv already installed Python 3.13 into the
   venv. Fixed by adding the venv's bin/ to PATH before invoking
   `unsloth studio setup`.

* Guard PATH export against empty VENV_ABS_BIN

If cd into the venv bin/ fails, VENV_ABS_BIN would be empty and
PATH would start with ":", causing the current directory to be
searched for executables. Wrap the export in a non-empty check.
2026-03-19 11:52:32 -07:00
Michael Han
29270a3726
Data recipes now works for Mac and CPU.md 2026-03-19 07:26:28 -07:00
Daniel Han
3faa9af148 Update _utils.py 2026-03-19 02:31:45 -07:00
Daniel Han
709a611356 Update README.md 2026-03-19 02:28:53 -07:00