chore: merge upstream/main (Wave 5 fork sync)#5
Merged
Conversation
* 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>
…setup (unslothai#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 unslothai#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>
* feat(windows): add Studio desktop/Start shortcuts with health-check launcher * chore(windows): bundle sloth.ico and set shortcut icons when valid * chore(windows):add images/sloth.ico * fix(windows): guard PSScriptRoot for Studio shortcut icon in iex installs * fix(install): high-DPI sloth.ico and relocate to studio/frontend/publi * chore(studio): update sloth.ico for clearer desktop and shell icons * chore(studio): use unsloth.ico for Studio shortcut icon * feat(windows): improve Studio shortcut launcher (fast health + browser UX) * fix(windows): stable unsloth.ico URL and Unicode-safe Studio launcher scripts * fix(windows): escape $ in exe path and write launcher UTF-8 with BOM * fix(windows): skip shortcuts when Desktop or APPDATA paths are missing * fix(install): log shortcut/icon/port failures and warn early on missing paths * fix(install): guard missing LOCALAPPDATA before shortcut paths * fix(install): harden New-StudioShortcuts and improve success messaging * fix(install): include port 8908 in studio health check * fix(install): fix launch-studio.ps1 quoting * Fix launcher edge cases and normalize indentation in install.ps1 - Handle silent timeout: show a message when Studio is still starting but did not become healthy within the timeout, instead of exiting with no feedback - Add -NoProfile to the visible PowerShell terminal launch so the user profile cannot hang or error before Studio runs - Add a named mutex (Local\UnslothStudioLauncher) to prevent double-click from spawning duplicate terminals; second instance polls for health and opens the browser when ready - Normalize indentation inside New-StudioShortcuts outer try block from mixed 8/12-space to consistent 12-space * Simplify Get-CandidatePorts port dedup with Sort-Object -Unique Replace the foreach/-notcontains loop with a single pipeline: $ports = (@($basePort) + $listening) | Sort-Object -Unique * Harden health probe and handle abandoned mutex in launcher - Test-StudioHealth now checks resp.service == 'Unsloth UI Backend' to avoid fingerprinting collisions with other local services on the same port range. - Wrap the mutex WaitOne(0) call in a try/catch for AbandonedMutexException so the launcher recovers gracefully when a previous instance was killed while holding the mutex. --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com>
…nslothai#4567) The wheel currently ships frontend/public/, frontend/src/, and frontend/*.lock alongside frontend/dist/. These are build-time inputs that Vite already copies into dist/ during the build step: - public/ is copied verbatim into dist/ by vite build (28.6 MB duplicate) - src/ is TSX source compiled into dist/assets/*.js (2.1 MB, not used at runtime) - *.lock files are package manager lockfiles (0.9 MB, not used at runtime) The backend only serves from frontend/dist/ (see main.py setup_frontend and run.py frontend_path). Nothing references public/ or src/ at runtime. This drops the wheel from ~62.7 MB to ~31 MB.
…othai#4501) * feat(db): add SQLite storage layer for training history * feat(api): add training history endpoints and response models * feat(training): integrate DB persistence into training event loop * feat(ui): add training history views and card grid * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(studio): address review issues in training history persistence - Strip hf_token/wandb_token from config before SQLite storage - Add UUID suffix to job_id for collision resistance - Use isfinite() for 0.0 metric handling throughout - Respect _should_stop in error event finalization - Run schema DDL once per process, not per connection - Close connection on schema init failure - Guard cleanup_orphaned_runs at startup - Cap _metric_buffer at 500 entries - Make FLUSH_THRESHOLD a class constant - Map 'running' to 'training' phase in historical view - Derive LR/GradNorm from history arrays in historical view - Fix nested button with div[role=button] in history cards - Guard String(value) against null/undefined in config popover - Clear selectedHistoryRunId on auto tab switch * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(studio): address round-2 review findings across training backend and frontend Backend (training.py): - Move state mutation after proc.start() so a failed spawn does not wedge the backend with is_training=True - Create DB run row eagerly after proc.start() so runs appear in history during model loading, not after first metric event - Rewrite _flush_metrics_to_db() with snapshot-before-insert pattern to preserve metrics arriving during the write and retain buffer on failure - Guard eval_loss with float() coercion and math.isfinite(), matching the existing grad_norm guard - Increase pump thread join timeout from 3s to 8s to cover SQLite's default 5s lock timeout Frontend (studio-page.tsx): - Fix history navigation: check isTrainingRunning instead of showTrainingView in onSelectRun so completed runs are not misrouted - Replace activeTab state + auto-switch useEffect with derived tab to eliminate react-hooks/set-state-in-effect lint violation Frontend (historical-training-view.tsx): - Add explicit "running" branch to message ternary so running runs no longer fall through to "Training errored" - Derive loading from detail/error state and move cleanup to effect return to eliminate react-hooks/set-state-in-effect lint violation Frontend (progress-section.tsx): - Derive stopRequested from isTrainingRunning && stopRequestedLocal to eliminate react-hooks/set-state-in-effect lint violation and remove unused useEffect import * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(studio): resolve 3 remaining bugs from round-2 review 1. Stuck on Current Run tab [12/20]: Only force "current-run" tab when isTrainingRunning is true, not when stale completed-run data exists. After training ends, users can freely navigate to Configure. 2. Incomplete metric sanitization [7/20]: Apply float() coercion and isfinite() guards to loss and learning_rate, matching the existing pattern used by grad_norm and eval_loss. Prevents TypeError from string values and NaN leaks into history arrays. 3. Stop button state leak across runs [10/20]: Add key={runtime.jobId} to ProgressSection so React remounts it when a new run starts, resetting stopRequestedLocal state. * fix(studio): deduplicate loss/lr sanitization in training event handler Reuse _safe_loss/_safe_lr from the progress update block instead of re-sanitizing the same raw event values for metric history. * fix(studio): restore loss > 0 guard to prevent eval steps injecting 0.0 into metric histories Round-2/3 fixes relaxed the history append guard from `loss > 0` to `loss is not None`, which let eval-only log events (where loss defaults to 0.0) append fake zeros into loss_history and lr_history. Restore the `loss > 0` check to match the worker's own has_train_loss gate. The float() coercion and isfinite() sanitization from round-3 remain intact. * fix(studio): resolve training history bugs — nullable loss/lr, tab nav, sparkline * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
…evaluate() (unslothai#4564) * fix: remove auto wandb.finish() after train() to allow post-training evaluate() The prepare_for_training_mode wrapper unconditionally called wandb.finish() after trainer.train() completed. This terminated the active W&B run, causing trainer.evaluate() to fail with "You must call wandb.init() before wandb.log()". Users who need multiple training runs in one session can call wandb.finish() manually between runs to avoid data overwriting. Fixes unslothai#3954 * fix: defer wandb.finish() to next train() call instead of removing it Instead of calling wandb.finish() at the end of train() (which breaks evaluate/log) or removing it entirely (which causes data overwriting on multiple train() calls), defer it to the start of the next train() call. This way: - train() + evaluate() works (run stays open after train) - train() + train() gets separate W&B runs (previous run finished first) - train() + evaluate() + train() also works correctly Also resets HF's WandbCallback._initialized flag so it re-calls wandb.init() for the new run. Fixes unslothai#3954 --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com>
unslothai#4511) * feat: Implement Q-GaLore optimizer and custom embedding learning rate in the Unsloth trainer. * feat: Implement QGaLoreAdamW8bit optimizer with 8-bit states, GaLore low-rank gradient projection, and optional INT8 weight quantization, along with supporting projector and tests. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * feat: Introduce Q-GaLore AdamW optimizer with low-rank quantized gradient projection and integrate into the trainer, along with dedicated tests. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * feat: Implement Q-GaLore AdamW optimizer with gradient projection and quantization, including trainer integration and corresponding tests. * [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 3 bugs in Q-GaLore optimizer and add weight_quant forward hooks 1. Fix use-after-delete crash: move `del p._saved_data` after the weight decay block so decoupled weight decay can reference the current weights correctly (p.data). 2. Fix substring matching in make_q_galore_param_groups: split parameter names on "." and check exact component matches to prevent false positives (e.g. "not_q_proj" matching "q_proj"). 3. Implement forward pre-hooks for weight_quant: after the optimizer quantizes weights to INT8, replace p.data with a 1-element placeholder to free float memory. A register_forward_pre_hook dequantizes back to float before each forward pass. The trainer calls install_weight_quant_hooks() when weight_quant is enabled. 4. Update test_weight_decay_uses_saved_data to match the fixed code path (decoupled decay uses p.data, expected value 2.7). Add test_weight_quant_hook_restores_float to verify the INT8-to-float hook round-trip. All 24/24 Q-GaLore tests pass. Benchmarked on Llama-3.2-1B-Instruct FFT: Q-GaLore saves 32% VRAM (10.63 -> 7.24 GB) with better loss convergence (1.3 vs 2.0 at step 100). No regressions in 31-notebook sweep across Llama, Qwen, Mistral, Phi, Gemma, vision, and GRPO. * Default weight_quant to False in QGaloreConfig Benchmarks show weight_quant=True adds ~1 GB on Llama-3.2-1B due to INT8 copy/scale overhead exceeding savings from the placeholder trick. Users can still opt in explicitly. The optimizer logic is unchanged. * Optimize Q-GaLore projector and optimizer step performance Projector (q_galore_projector.py): - Use torch.svd_lowrank with oversampling p=10 (Halko et al. 2009) instead of full SVD for large matrices. Falls back to full SVD when min(m,n) <= 2*rank. SVD steps are 6-8x faster on Llama-3.2-1B (22s -> 3s for first step). - Cache the dequantized ortho matrix between project() and project_back() to avoid redundant dequantization when quant=True. - Replace F.cosine_similarity with torch.dot for 1-D unit vectors in the adaptive schedule. Remove unused torch.nn.functional import. - Use collections.deque(maxlen=queue_size) instead of list with manual pop(0). Optimizer (q_galore_adamw.py): - Remove redundant .clone() on dequantized weights (line 151) and on float data before re-quantization (line 211). _dequantize already returns a fresh tensor and _quantize/_quantize_stochastic only reads its input. - Consolidate per-group torch.cuda.synchronize() into a single call after all param groups complete. - Use torch.empty instead of torch.zeros for the scalar placeholder tensor that is never read. Verified: 24/24 unit tests pass. Llama-3.2-1B 61-step training produces losses within 0.24% relative diff (correlation >0.9999) of the original. * [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: Daniel Han <danielhanchen@gmail.com>
) * Bump Data Designer to 0.5.4 (removes litellm dependency) NVIDIA Data Designer v0.5.4 removes litellm entirely and replaces it with native OpenAI and Anthropic adapters. This follows the litellm supply chain incident where versions 1.82.7 and 1.82.8 were compromised with a credential stealer. Release notes: https://github.com/NVIDIA-NeMo/DataDesigner/releases/tag/v0.5.4 Changes: - Bump data-designer, data-designer-config, data-designer-engine to 0.5.4 - Sync data-designer-deps.txt with 0.5.4 engine requirements: - Added: chardet, fsspec, mcp - Removed: python-json-logger, pymupdf, pymupdf4llm, mammoth (these remain in the unstructured-seed plugin which still needs them) - duckdb constraint relaxed from <1.5 to <2 (upstream fixed record_batch) - Bump plugin lower bound to >=0.5.4 * Keep pymupdf, pymupdf4llm, mammoth in data-designer-deps The unstructured-seed plugin is installed with --no-deps, so its pyproject.toml dependencies are not auto-resolved. These three packages are needed by the seed route (studio/backend/routes/ data_recipe/seed.py) and must remain in the explicit deps list.
…#4561) * feat(chat): ghost-style tool containers Remove borders and card styling from tool call UI. ToolFallback uses minimal padding with indented content. ToolGroup defaults to ghost variant with subtle background for multi-tool grouping. * feat(chat): compact web search source pills Switch sources from vertical full-width badges to horizontal wrapping pills with smaller icons. * feat(chat): left-accent code and terminal tool UI Replace bordered card layout with a left border accent for Python and Terminal tool output. Add timer cleanup on unmount for the copy button in both components. * feat(chat): inline latex and clickable links Enable single-dollar $...$ math rendering via createMathPlugin. Add styled link component with target=_blank for external links. * fix(chat): inline generating indicator, static tailwind classes, misc fixes Move generating indicator from viewport footer into assistant message using AnimatedShinyText shimmer. Only shows when message content is empty, hides once tool calls or text appear. Use static size class map in SourceIcon for Tailwind v4 compat. Use unique keys for web search sources. Remove px-3 from ghost tool group variant. * fix(chat): only show generating indicator while message is running Hide the shimmer when message is cancelled or errored with no content, preventing stale loading UI on empty completed messages. * fix: escape currency dollar signs in LaTeX math rendering and fix TS build error - Add preprocessLaTeX() in lib/latex.ts to escape currency patterns ($5, $1,000, $5.99, $100K) before they reach the math parser, preventing false positives when singleDollarTextMath is enabled. Code blocks and already-escaped dollars are left untouched. - Use preprocessLaTeX via useMemo in markdown-text.tsx so Streamdown receives clean input. - Fix TS18048 in thread.tsx: message.status?.type (optional chaining) since status can be undefined. --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com>
…le (unslothai#4547) * Try installing causal-conv1d from prebuilt wheels if avialable * Prefer installing mamba-ssm from wheel to speed up things * undo python stack install changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Revert "undo python stack install changes" This reverts commit d943551. * add comments * Fix wheel installer: model detection, platform tags, torch pin, error handling - Add nemotron-h (hyphen) and granite-4.0-h / granitemoehybrid to model detection for both causal-conv1d and mamba-ssm. These hybrid Mamba models were silently skipped since nemotron_h (underscore) never matches real HF model IDs like nvidia/Nemotron-H-8B-Base, and granite was missing entirely despite being a supported model in model_config.py and loader.py. - Fix _causal_conv1d_platform_tag to detect linux_aarch64 via platform.machine() instead of hardcoding linux_x86_64. Both upstream releases publish aarch64 wheels. Drop win_amd64 since neither repo publishes Windows wheels (avoids a wasted HTTP probe on every run). - Pin torch to >=2.6.0,<2.11.0 instead of <=2.10.0 to add a version floor and document the wheel coverage range with upstream release links. - Strip non-numeric suffixes from torch minor version so nightly builds like 2.7a0 correctly resolve to wheel tag torch2.7 instead of torch2.7a0. - Use stderr=_sp.PIPE instead of stderr=_sp.STDOUT in the env probe so torch import warnings do not corrupt the JSON output. - Add timeout=30 to the env probe subprocess to prevent indefinite hangs. - Catch Exception (not just ImportError) on the existing-install check so ABI-broken installs with OSError/RuntimeError are retried rather than silently accepted. - Guard uv invocation with shutil.which("uv") to prevent FileNotFoundError crash when uv is not on PATH. Wrap the top-level ensure calls in try/except so failures do not kill the training worker. - Hoist _SSM_MODEL_SUBSTRINGS to module level. - Remove redundant --torch-backend=auto flag from direct wheel URL install. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add LFM2 to causal-conv1d detection; stop training on install failure - Add "lfm2" to _model_wants_causal_conv1d so Studio picks up the fast kernel path for Liquid Foundation Model 2. - Replace silent logger.warning on SSM dependency install failure with an error event that tells the user to choose another model and stops the training job immediately. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Catch subprocess timeout in torch probe; narrow import guard to ImportError - _probe_causal_conv1d_env: wrap subprocess.run in try/except for TimeoutExpired so a slow torch import returns None (falls back to PyPI) instead of killing the training job. - _install_package_wheel_first: narrow except Exception to except ImportError on the __import__ check so unexpected errors from a broken module still propagate. * Remove unconditional torch pin from install_python_stack The torch>=2.6.0,<2.11.0 pin was added to ensure prebuilt causal-conv1d / mamba-ssm wheels exist, but it runs at install time for all users regardless of model choice. This can downgrade or unnecessarily upgrade torch. The worker already handles wheel compatibility at training time by probing the environment and falling back to PyPI, so the install-time pin is not needed. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
* Add CodeQL analysis workflow configuration * Add Dependabot configuration for package updates Configure Dependabot to check for updates in various ecosystems weekly. * Fix dependabot.yml: bun ecosystem, missing dir, grouping for PR unslothai#4479 1. studio/frontend uses bun.lock not package-lock.json, so change npm to bun 2. Add missing studio/backend/requirements/ pip entry (consumed by studio/setup.sh) 3. Add groups with patterns ["*"] to all pip/bun/npm entries to batch updates and avoid 30+ individual Dependabot PRs on the first run * Consolidate pip blocks to fix overlapping directory violation GitHub Dependabot forbids multiple same-ecosystem entries with overlapping directories on the same branch. The root "/" directory overlapped the 3 nested pip dirs. Merge all 4 pip blocks into one using the `directories:` (plural) key. Also remove redundant open-pull-requests-limit from the bun block since grouping with patterns: ["*"] already limits PR count. --------- Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
Bumps the actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [github/codeql-action](https://github.com/github/codeql-action). Updates `actions/checkout` from 4 to 6 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v6) Updates `github/codeql-action` from 3 to 4 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the npm-oxc-validator group in /studio/backend/core/data_recipe/oxc-validator with 1 update: [oxc-parser](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser). Updates `oxc-parser` from 0.116.0 to 0.121.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/parser/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.121.0/napi/parser) --- updated-dependencies: - dependency-name: oxc-parser dependency-version: 0.121.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: npm-oxc-validator ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…4584) The repo has both the CodeQL "default setup" (configured in repo settings) and this advanced workflow file enabled. GitHub does not allow both simultaneously, causing all PR CI runs to fail with: "CodeQL analyses from advanced configurations cannot be processed when the default setup is enabled" Since the default setup already covers the same languages (Python, JavaScript/TypeScript) with the same build-mode (none), remove the redundant advanced workflow file.
* Add macOS and Linux desktop shortcuts to install.sh Adds create_studio_shortcuts() function that creates platform-native shortcuts after `unsloth studio setup` completes, mirroring the Windows shortcut behavior from PR unslothai#4558. Linux: .desktop file in ~/.local/share/applications/ and ~/Desktop/ macOS: .app bundle in ~/Applications/ with Info.plist, exec stub, and optional .icns icon built from unsloth-gem.png via sips+iconutil Both platforms share a Bash launcher script at ~/.local/share/unsloth/launch-studio.sh that provides: - Health check with service fingerprint verification - Port scanning (8888-8908) via ss/lsof - PID-file single-instance guard (no flock dependency) - Terminal spawning (macOS: Terminal.app; Linux: gnome-terminal etc.) - Browser open after health poll with 60s timeout WSL is skipped (no native desktop environment). * Fix 6 issues found by 10 parallel reviewers 1. [10/10] Health check now supports wget as fallback to curl via _http_get() helper, matching the installer's own download() pattern. Previously wget-only systems would time out on every launch. 2. [9/10] Exe path substitution now escapes sed metacharacters (&, \, |) and shell single-quotes before injection, preventing launcher corruption for paths like /opt/R&D/bin/unsloth. 3. [4/10] Linux .desktop Exec= field now quotes the launcher path, fixing launches from home directories containing spaces. 4. [3/10] macOS AppleScript command now escapes backslashes and double-quotes before interpolation into do script "...", fixing Terminal.app launch failures. 5. [3/10] Single-instance guard now uses atomic mkdir instead of racy check-then-write PID file, preventing duplicate concurrent launches on rapid double-click. 6. [1/10] Launcher now scans for a free port via _find_launch_port() instead of always hardcoding -p 8888, so Studio starts correctly when another service already occupies port 8888. Also fixed: `open` command on Linux (openvt) no longer incorrectly triggers the macOS browser-open path -- now gated on uname=Darwin. * Fix mktemp guard and exe path escaping from PR review comments Two real issues identified from automated review comments: 1. Guard mktemp -d failure in macOS icns generation. If mktemp -d returned empty, dirname would resolve to / and rm -rf would attempt to delete the root directory. Now checks that the temp dir was actually created before proceeding. 2. Replace sed-based exe path substitution with a conf file approach. The previous sed escaping broke paths containing apostrophes (e.g. /home/O'Connor/) because the '\'' escape introduced backslashes that were then double-escaped by the metacharacter pass. Now writes UNSLOTH_EXE to a separate studio.conf file that the launcher sources at runtime, eliminating all sed metacharacter and shell quoting interaction issues. This also addresses the sed -i.bak portability concern (now moot since sed is no longer used on the launcher file). * Fix unbound variable crash and per-user lock in launcher - Use ${UNSLOTH_EXE:-} so set -u does not crash before the friendly error message when studio.conf is missing or empty. - Append $(id -u) to the fallback lock path so each user gets their own lock directory when XDG_RUNTIME_DIR is unset. * Mark desktop shortcut as trusted for GNOME/Nautilus On modern GNOME desktops, chmod +x alone is not sufficient to make a .desktop file launchable by double-click on ~/Desktop. Nautilus requires the metadata::trusted attribute to be set via gio, otherwise it shows a warning dialog instead of launching the application.
…d builds (unslothai#4522) * perf(studio): upgrade to Vite 8 + auto-install bun for 3x faster frontend builds * fix(studio): make bun-to-npm fallback actually reachable setup.sh used run_quiet() for the bun install attempt, but run_quiet calls exit on failure. This killed the script before the npm fallback could run, making the "falling back to npm" branch dead code. Replace the run_quiet call with a direct bun invocation that captures output to a temp file (same pattern, but returns instead of exiting). Also clean up partial node_modules left by a failed bun install before falling back to npm, in both setup.sh and build.sh. Without this, npm inherits a corrupted node_modules tree from the failed bun run. * fix(studio): restore commonjsOptions for dagre CJS interop The previous commit removed build.commonjsOptions, assuming Vite 8's Rolldown handles CJS natively. While optimizeDeps.include covers the dev server (pre-bundling), it does NOT apply to production builds. The resolve.alias still points @dagrejs/dagre to its .cjs.js entry, so without commonjsOptions the production bundle fails to resolve the CJS default export. This causes "TypeError: e is not a function" on /chat after build (while dev mode works fine). Restore the original commonjsOptions block to fix production builds. * fix(studio): use motion/react instead of legacy framer-motion import * fix(studio): address PR review findings for Vite 8 + bun upgrade Fixes: - Remove bun.lock from repo and add to .gitignore (npm is source of truth) - Use & bun install *> $null pattern in setup.ps1 for reliable $LASTEXITCODE - Add Remove-Item node_modules before npm fallback in setup.ps1 - Print bun install failure log in setup.sh before discarding - Add Refresh-Environment after npm install -g bun in setup.ps1 - Tighten Node version check to ^20.19.0 || >=22.12.0 (Vite 8 requirement) - Add engines field to package.json - Use string comparison for _install_ok in build.sh - Remove explicit framer-motion ^11.18.2 from package.json (motion pulls framer-motion ^12.38.0 as its own dependency — the old pin caused a version conflict) * Fix Colab Node bypass and bun.lock stale-build trigger Gate the Colab Node shortcut on NODE_OK=true so Colab environments with a Node version too old for Vite 8 fall through to the nvm install path instead of silently proceeding. Exclude bun.lock from the stale-build probe in both setup.sh and setup.ps1 so it does not force unnecessary frontend rebuilds on every run. --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com> Co-authored-by: Shine1i <wasimysdev@gmail.com>
…i#4436) * feat(tokenizer): add get_tokenizer_info() diagnostic helper Adds get_tokenizer_info(tokenizer) to tokenizer_utils.py returning a concise dict of key tokenizer properties class name, is_fast, vocab size, added token count, model_max_length, padding side, special tokens (bos, eos, pad, unk), chat template presence, and total special token count. All fields use getattr(..., None) fallbacks so the function never raises on unusual or partially initialized tokenizers. Exported via __all__ alongside the existing public helpers. Useful for logging, debugging, and surfacing tokenizer state in the Unsloth Studio UI. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix docstring, remove artifact, restore valuable comments in tokenizer_utils.py - Fix get_tokenizer_info() docstring example: correct tokenizer_class to PreTrainedTokenizerFast, vocab_size to 128000, swap added_tokens_count (256) and special_tokens_count (3) to match actual Llama-3.2-1B-Instruct output - Remove accidentally committed "# ... (rest of file unchanged)" diff artifact - Restore fix_sentencepiece_gguf() docstring with llama.cpp upstream link - Restore 10 comments containing upstream URLs, model-specific workarounds, and non-obvious context (issue unslothai#292, sentencepiece#121, Starling hack, Kaggle /tmp limit, Deepseek slow tokenizer, twitter/danielhanchen references) * Revert "Fix docstring, remove artifact, restore valuable comments in tokenizer_utils.py" This reverts commit 4e525b7. * Revert all deletions, keep only get_tokenizer_info() addition Restore tokenizer_utils.py to main and add only the new get_tokenizer_info() function and its __all__ entry. All comment removals, dead code cleanup, and formatting changes from the original PR are reverted. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
* Add support for ROCm in studio setup
* Fix ROCm detection bugs: ROCM_PATH resolution, CUDA guard, compiler selection
- Set GPU_BACKEND="cuda" when nvcc is found (CUDA path was unreachable)
- Guard ROCm detection with `if [ -z "$GPU_BACKEND" ]` so CUDA takes
priority on mixed-toolchain hosts
- Rename ROCM_PATH to ROCM_HIPCC for the hipcc binary; resolve the
actual ROCm root via readlink -f and hipconfig -R into ROCM_ROOT
- Export both ROCM_PATH and HIP_PATH as the resolved root directory
- Use HIPCXX via hipconfig -l instead of legacy CMAKE_C_COMPILER=hipcc
- Switch grep -oP to grep -oE for portability across Linux distros
- Use GPU_TARGETS (upstream cmake variable) instead of AMDGPU_TARGETS
- Remove stale hardcoded fallback targets; let cmake auto-detect instead
* Fix gfx regex to match gfx90a (MI210/MI250/MI250X)
The grep and bash regex used {3,4} digits after 'gfx', which silently
excluded gfx90a (2 digits + letter 'a') -- the architecture for AMD
Instinct MI210, MI250, and MI250X data-center GPUs. Change to {2,4}
so all real gfx targets from gfx90a through gfx1200 are matched.
---------
Co-authored-by: edamamez <eda.zhou@amd.com>
* refactor: consolidate dual venvs into single ~/.unsloth/studio/unsloth_studio * refactor: separate install.sh (first-time) from setup.sh (smart update with PyPI version check) * fix: install.sh calls setup.sh directly, keep both setup and update CLI commands * fix: use importlib.resources.files() directly without _path attribute * fix: bootstrap uv before pip upgrade to handle uv venvs without pip * fix: frontend 404 when launched via CLI, add global symlink to ~/.local/bin * feat: add --local flag to install.sh and unsloth studio update for branch testing * fix: resolve repo root from script location for --local installs * feat: add --package flag to install.sh for testing with custom package names * feat: add --package flag to unsloth studio update * fix: always nuke venv in install.sh for clean installs * revert: remove Windows changes, will handle in separate PR * fix: error when --package is passed without an argument * revert: restore Windows scripts to current main * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: always explicitly set STUDIO_LOCAL_INSTALL and STUDIO_PACKAGE_NAME env vars * fix: pass explicit STUDIO_LOCAL_REPO env var for --local installs * fix: align banner box for Setup vs Update labels * deprecate: hide 'unsloth studio setup' command, point users to update/install.sh * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: check stdout not stdin for auto-launch detection (curl pipe fix) * fix: update install URL to unsloth.ai/install.sh * fix: update install.sh usage comments to unsloth.ai/install.sh * fix: use --upgrade-package for base deps to preserve existing torch/CUDA installs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: --local install now also installs unsloth-zoo via base.txt before editable overlay * fix: don't skip base packages for --local installs (editable needs unsloth-zoo) * refactor: move --local full dep install to install.sh, keep SKIP_STUDIO_BASE for all paths * feat: add migration support for old .venv and CWD-based installs in setup.sh * Revert "feat: add migration support for old .venv and CWD-based installs in setup.sh" This reverts commit 301291d. * feat: migrate old .venv layout in install.sh instead of always nuking * feat: validate old .venv with torch CUDA test before migration, recovery message on launch failure * fix: try CUDA then fall back to CPU for migration validation * fix: upgrade unsloth/unsloth-zoo with --reinstall-package on migration to preserve torch * remove: delete unused unsloth ui command (use unsloth studio instead) * Fix Windows venv path mismatch between install.ps1, setup.ps1, and studio.py install.ps1 was creating the venv CWD-relative ($VenvName = "unsloth_studio"), setup.ps1 was using an absolute path to ".unsloth\studio\.venv", and studio.py looks for ".unsloth\studio\unsloth_studio". All three paths were different, so the Windows installer would never produce a working Studio setup. install.ps1: - Use absolute $StudioHome + $VenvDir matching the Linux install.sh layout - Add 3-way migration: old .venv at STUDIO_HOME, CWD-relative ~/unsloth_studio from the previous install.ps1, or fresh creation with torch validation - For migrated envs, upgrade unsloth while preserving existing torch/CUDA wheels - Set SKIP_STUDIO_BASE=1 before calling setup.ps1 (matches install.sh behavior) - Fix launch instructions to use the absolute venv path setup.ps1: - Change $VenvDir from ".unsloth\studio\.venv" to ".unsloth\studio\unsloth_studio" - Add SKIP_STUDIO_BASE guard: error out if venv is missing when called from install.ps1 (which should have already created it) - Differentiate "Setup" vs "Update" in banners based on SKIP_STUDIO_BASE * setup.ps1: unconditionally error if venv missing, matching setup.sh setup.sh always errors out if the venv does not exist (line 224-228), telling the user to run install.sh first. setup.ps1 was conditionally creating a bare venv with python -m venv when SKIP_STUDIO_BASE was not set, which would produce an empty venv with no torch or unsloth. Now setup.ps1 matches setup.sh: always error, always point to install.ps1. * Fix --torch-backend=auto CPU solver dead-end on Linux, macOS, and Windows On CPU-only machines, `uv pip install unsloth --torch-backend=auto` falls back to unsloth==2024.8 because the CPU solver cannot satisfy newer unsloth's dependencies. install.ps1 already solved this with a two-step approach; this applies the same fix to install.sh and install_python_stack.py. install.sh: add get_torch_index_url() that detects GPU via nvidia-smi and maps CUDA versions to PyTorch index URLs (matching install.ps1's Get-TorchIndexUrl). Fresh installs now install torch first via explicit --index-url, then install unsloth with --upgrade-package to preserve the pre-installed torch. All 5 --torch-backend=auto removed from primary paths. install.ps1: add fallback else-branch when TorchIndexUrl is empty, using --torch-backend=auto as last resort (matching install.sh). install_python_stack.py: remove unconditional --torch-backend=auto from _build_uv_cmd. Torch is pre-installed by install.sh/setup.ps1 by the time this runs. Callers that need it can set UV_TORCH_BACKEND. Both install.sh and install.ps1 now share the same three-branch logic: migrated env (upgrade-package only), normal (torch-first + index-url), and fallback (--torch-backend=auto if URL detection fails). * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Use --reinstall-package for migrated envs on both Linux and Windows For migrated environments (moved from legacy venv location), --reinstall-package is better than --upgrade-package because it forces a clean reinstall even if the same version is already installed. This ensures proper .dist-info and .pyc state in the new venv location. --upgrade-package remains correct for the fresh install path where torch is already installed and we just want to add unsloth without re-resolving torch. * Address review findings: portability, parity, and stale comments - Replace grep -oP (GNU Perl regex) with POSIX sed in get_torch_index_url() so the script works on BSD grep (macOS is already guarded by the Darwin early-return, but Alpine/BusyBox would silently get the wrong CUDA tag) - Add LC_ALL=C before nvidia-smi invocation to prevent locale-dependent output parsing issues - Add warning on stderr when nvidia-smi output is unparseable, matching install.ps1's [WARN] message - Add explicit unsloth-zoo positional arg to install.ps1 migrated path, matching install.sh (--reinstall-package alone won't install it if it was never present in the migrated env) - Fix stale comment in install_python_stack.py line 392 that still claimed --torch-backend=auto is added by _build_uv_cmd - Add sed to test tools directory (function now uses sed instead of grep) * Add --index-url to migrated env path to prevent CPU torch resolution The migrated path runs uv pip install with --reinstall-package for unsloth/unsloth-zoo. While uv should keep existing torch as satisfied, the resolver could still re-resolve torch as a transitive dependency. Without --index-url pointing at the correct CUDA wheel index, the resolver would fall back to plain PyPI and potentially pull CPU-only torch. Adding --index-url $TORCH_INDEX_URL ensures CUDA wheels are available if the resolver needs them. Applied to both install.sh and install.ps1. * Revert --index-url on migrated env path The original install.ps1 on main already handles the migrated path without --index-url and it works correctly. --reinstall-package only forces reinstall of the named packages while uv keeps existing torch as satisfied. No need for the extra flag. * Fix unsloth studio update --local not installing local checkout studio.py sets STUDIO_LOCAL_REPO when --local is passed, but install_python_stack.py never read it. The update path always installed from PyPI regardless of the --local flag. Add a local_repo branch that first updates deps from base.txt (with --upgrade-package to preserve torch), then overlays the local checkout as an editable install with --no-deps. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
…r overlap (unslothai#4587) * fix(studio): reasoning panel scroll and thread footer overlap * refactor(studio): dedupe reasoning scroll lock teardown
* Use prebuilt llama.cpp for unsloth studio setup * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix 3 issues that cause unnecessary fallback to source build 1. Make filelock import optional -- environments without filelock (e.g. minimal installs) crashed at import time instead of gracefully skipping the lock. 2. Use already-verified converter script from the hydrated source tree instead of re-downloading from raw.githubusercontent.com with no checksum. Adds symlink with copy fallback for the legacy filename. 3. Initialize $SkipPrebuiltInstall in setup.ps1 before first use to prevent potential uninitialized variable errors. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Keep network fallback in ensure_converter_scripts Prefer the local verified copy from the hydrated source tree, but retain the original network download as a fallback if the file is missing. Create the legacy hyphenated filename as a symlink with a copy fallback instead of writing a second full copy. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix 4 bugs in source-build fallback and binary_env paths - setup.ps1: Replace git pull + checkout FETCH_HEAD with fetch + checkout -B to avoid detached HEAD state that breaks re-runs. Use pinned tag in both fetch and clone paths. - setup.sh: Move rm -rf after cmake/git prerequisite checks so a missing tool no longer deletes the existing install. Add --branch tag to clone. - install_llama_prebuilt.py: Add binary_path.parent to Linux LD_LIBRARY_PATH in binary_env() so bundled .so files in build/bin are found even without RPATH, matching the existing Windows PATH logic. - Add test for binary_env LD_LIBRARY_PATH on Linux. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Handle unresolved "latest" tag in source-build fallback clone When tag resolution fails and the requested tag is "latest", both setup scripts now omit --branch from git clone so the default branch is cloned instead of failing on a nonexistent "latest" branch/tag. Similarly, the PS1 fetch path fetches the default ref when the tag is "latest". * Resolve actual latest ggml-org tag instead of using literal "latest" When both Python tag resolution attempts fail and the requested tag is "latest", query the GitHub API for the actual latest release tag from ggml-org/llama.cpp (e.g. b8508) instead of passing the literal string "latest" to git clone --branch, which would fail since no such branch/tag exists. setup.sh uses curl + python json parsing; setup.ps1 uses Invoke-RestMethod. Both fall back to the raw requested tag if the API call also fails. * Try Unsloth release repo before ggml-org when resolving latest tag When falling back to the GitHub API to resolve "latest", query the Unsloth release repo (unslothai/llama.cpp) first since it has the prebuilt binaries pinned to tested tags. Only fall back to ggml-org/llama.cpp if the Unsloth repo query fails. * Add comprehensive sandbox tests for PR unslothai#4562 bug fixes 35 tests covering all fixes across platforms: - binary_env cross-platform (Linux LD_LIBRARY_PATH, Windows PATH, macOS DYLD_LIBRARY_PATH) with edge cases (dedup, ordering, existing paths) - resolve_requested_llama_tag (concrete, latest, None, empty) - setup.sh logic via subprocess: prereq check ordering (cmake/git missing preserves install), pinned tag in clone, fetch+checkout -B pattern, fetch failure warns instead of aborting - "latest" tag resolution fallback chain (Unsloth API -> ggml-org -> raw) with mock curl: success, failure, malformed JSON, empty body, empty tag_name, env overrides - Source code pattern verification for both .sh and .ps1 files All 138 tests pass in isolated uv venv. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add binary_path.parent to macOS DYLD_LIBRARY_PATH in binary_env macOS prebuilt .dylib files are overlaid into build/bin (same as Linux), but binary_env only added install_dir to DYLD_LIBRARY_PATH. Add binary_path.parent so the loader can find sibling dylibs even without embedded loader paths. Mirrors the existing fix for Linux LD_LIBRARY_PATH and the Windows PATH pattern. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Guard --branch when resolved tag is "latest"; fix broken test assertion When all API fallbacks fail and the tag stays as literal "latest", omit --branch from git clone (clones default branch instead of failing). Both setup.sh and setup.ps1 now check for "latest" before passing --branch to git clone/fetch. Also fix test_setup_ps1_clone_uses_branch_tag which used Python tuple syntax (assert "x", "y" in z) that always passes. Changed to assert "x" in z and "y" in z. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix macOS DYLD trailing colon, install_lock no-op, and debug log - binary_env macOS: use dedupe_existing_dirs instead of raw string concatenation. Eliminates trailing colon in DYLD_LIBRARY_PATH (which causes dyld to search CWD for libraries) and deduplicates when binary_path.parent == install_dir. Now consistent with the Linux and Windows branches. - install_lock: when filelock is not installed, use os.O_CREAT|O_EXCL as a fallback exclusive file lock with timeout, instead of yielding with no locking. Prevents concurrent installs from corrupting each other's staging directories. - setup.ps1: remove [DEBUG] log line that printed to every user on every Windows setup run. * Add stale-lock detection and atomic clone-then-swap install_lock fallback (no filelock): write PID to lock file and check if the holder process is still alive on contention. Dead PIDs (ProcessLookupError) and unreadable lock files trigger immediate cleanup. Live processes owned by other users (PermissionError) are correctly recognized as alive -- the lock is not removed. setup.sh/setup.ps1 source-build: clone into a temporary directory first, then swap into place only on success. If git clone fails, the existing install is preserved instead of being deleted by the premature rm -rf. * Remove redundant upstream_tag != release_tag check load_approved_release_checksums compared checksums.upstream_tag against the Unsloth release_tag, which are different namespaces (upstream ggml-org tag vs Unsloth published tag). This only worked because both happened to be "b8508" by convention. Would break if Unsloth ever uses a different release naming scheme. The existing check at parse_approved_release_checksums (line 950) already validates the release_tag field correctly. * Fix lock TOCTOU race and build-in-temp-dir swap install_lock fallback: add os.fsync(fd) after writing PID to ensure the PID is visible to racing processes before they check. Treat empty lock files (PID not yet written) as "wait and retry" instead of stale, closing the window where two processes could both see an empty file, both unlink it, and both acquire the lock. setup.sh/setup.ps1 source-build: clone AND build in a temp directory (LLAMA_CPP_DIR.build.$$). Only swap into the final LLAMA_CPP_DIR after the build succeeds. If clone or cmake or build fails, the temp dir is cleaned up and the existing working install is preserved. Previously, rm -rf ran after clone but before build, destroying the existing install even if the build later failed. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
…ing (unslothai#4588) When _select_gpus determines that a GGUF model fits on the selected GPU(s), the code sets CUDA_VISIBLE_DEVICES but never passes -ngl (number of GPU layers) to llama-server. Without -ngl or --fit, llama-server defaults to 0 GPU layers and runs entirely on CPU. This adds -ngl -1 (offload all layers) in the elif branch where gpu_indices is set and use_fit is False, so models that fit in VRAM actually use the GPU for inference. Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
…r llama-server (unslothai#4590) The prebuilt llama.cpp binary (cuda13-newer) links against libcudart.so.13 and libcublas.so.13. When torch is installed via pip, these libraries live in the venv's site-packages under nvidia/cu13/lib/, not in /usr/local/cuda/. The existing LD_LIBRARY_PATH logic only searched /usr/local/cuda* paths (which have CUDA 12.x), so the CUDA backend failed to load silently and llama-server fell back to CPU -- even with -ngl -1. This adds a glob scan of the venv's nvidia package directories (cu*, cudnn, nvjitlink) to LD_LIBRARY_PATH before launching llama-server, matching where pip puts the CUDA runtime. Tested on Colab with RTX PRO 6000 Blackwell (CUDA 13.0, pip torch): before -- 3 MiB GPU, 0% util, CPU inference after -- 13317 MiB GPU, 77% util, full GPU inference Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
… LM Studio)" This reverts commit d56b115.
…ailure (unslothai#4589) bun install (specifically the npm "bun" shim v1.3.x installed via npm install -g bun) can exit 0 while silently failing to install packages. This causes the frontend build to fail with "tsc: not found" or missing type declarations, since the fallback to npm only triggers on a non-zero exit code. Changes: 1. Initial bun install now tries the official bun.sh installer first (which gives a real bun runtime), falling back to npm install -g bun only if that fails. 2. After bun install reports success, verify that critical binaries (tsc, vite) actually exist in node_modules/.bin/. If they are missing, reinstall bun from the official source and retry once before falling back to npm. 3. Extract the bun install + validation logic into _try_bun_install() to avoid duplicating the check/cleanup across both attempts.
… to npm (unslothai#4594) bun's package cache can become corrupt, storing only package metadata (package.json, README) without actual content (bin/, lib/). When this happens, bun install exits 0 and reports packages as installed, but binaries like tsc are missing from node_modules/.bin/. For example, a corrupt typescript cache entry is 64KB (metadata only) vs 23MB when correctly downloaded. Changes: - After bun install, verify tsc and vite exist in node_modules/.bin/ - If missing, clear the bun cache with bun pm cache rm and retry once - Only fall back to npm if the retry also fails - Revert bun installation to npm install -g bun (the binary is fine, the cache was the problem)
torch 2.11.0 has a torch.compile/dynamo bug that causes a StopIteration crash in dict_keys_getitem when compiling MoE router functions (e.g. GptOssTopKRouter_forward). Pin to <2.11.0 until the upstream fix lands. Applies to both install.sh (Linux/macOS) and install.ps1 (Windows) fresh install paths.
…upstream latest (unslothai#4593) * fix(studio): source-build fallback prefers Unsloth's tested tag over upstream latest When the prebuilt install fails and falls back to source build, --resolve-llama-tag now queries the Unsloth release repo (unslothai/llama.cpp) first to get the latest tested/approved tag (e.g. b8508), instead of going straight to ggml-org/llama.cpp which may return a newer untested tag (e.g. b8514). This ensures the source-build fallback compiles the same version that the prebuilt path would have installed, rather than a potentially incompatible bleeding-edge release. Resolution order for "latest": 1. Unsloth release repo (tested/approved) 2. ggml-org upstream (bleeding-edge) 3. Raw requested tag string (last resort) Changes: - resolve_requested_llama_tag() accepts optional published_repo param with docstring explaining the resolution order - CLI --resolve-llama-tag passes --published-repo through - setup.sh and setup.ps1 pass --published-repo to --resolve-llama-tag with inline comments explaining the preference * [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>
…#4596) Port the bun cache corruption fix from setup.sh to setup.ps1. bun's package cache can become corrupt, storing only package metadata without actual content. This causes bun install to exit 0 but leave binaries like tsc missing from node_modules/.bin/. Changes: - After bun install, verify tsc and vite exist in node_modules\.bin\ - Check for both bare names and .cmd wrappers (Windows creates both) - If missing, clear the bun cache and retry once - Only fall back to npm if the retry also fails
…nslothai#4805) * feat: allow non-LLM recipes to run without provider block * feat: reorder execution tabs and add generation-aware data tab empty state * fix: add accessibility attrs to data tab spinner and use literal ellipsis * fix(studio): use shared spinner, stub provider, and hide unused LLM metrics Backend: inject stub model provider for sampler-only recipes so DataDesigner init does not reject empty provider lists. Frontend: use shared Spinner component, hide LLM columns metric and model usage card when recipe has no LLM columns. * Fix tab reset and terminal auto-scroll regressions for PR unslothai#4805 Reset detailTab to "data" when switching between executions so the Data tab default is applied consistently, not only on first mount. Also add detailTab to the terminal scroll effect deps so auto-scroll-to-bottom fires when the user opens the Overview tab after landing on Data. * Guard terminal scroll reset to only fire on Overview tab The previous scroll effect ran on every tab switch, which could reset the user's manual scroll position if they scrolled up in the terminal and briefly switched tabs. Now the scroll-to-bottom and sticky-bottom reset only fires when navigating to the Overview tab. * Use None for stub provider api_key instead of literal string The stub ModelProvider that satisfies the DataDesigner registry for non-LLM recipes should not carry a fake credential string. Using None avoids sending an Authorization header if the provider is ever inadvertently invoked. --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com>
…nslothai#4822) * fix(studio): reuse HF cached repo casing to prevent duplicate downloads * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Move cache case resolution tests to separate PR Tests for resolve_cached_repo_id_case and get_model_config case resolution belong in their own PR to keep this change focused on the runtime fix. * fix(studio): debug-log HF_HUB_CACHE fallback in path_utils * Fix stale memoization in resolve_cached_repo_id_case - Check exact-case path before memo to ensure a newly-appeared exact match always wins over a previously memoized variant - Validate memoized entries still exist on disk before returning them to prevent stale results when cache dirs are deleted/recreated * Minor cleanups for cache case resolution - Use .is_dir() instead of .exists() for exact-case cache check (cache entries are always directories) - Remove redundant fallback in _detect_audio_from_tokenizer since get_cache_path already handles case resolution and returns None when the model is not cached --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
…nslothai#4810) Fixes unslothai#4809 On a new Studio chat, the first tool call could start before the frontend initializes the thread ID. That meant the first request could go out without a session_id, so the backend started the tool in the shared sandbox root instead of the chat's session sandbox. Frontend: - Eagerly initialize the thread when switching to a new chat - Resolve the thread ID once at request time and keep it stable through async model-load waits - Disable ActiveThreadSync during new-chat initialization to prevent stale thread IDs from being written back - Add error handling for thread initialization failures - Clear activeThreadId on all compare-mode entry paths to prevent cross-session leakage - Fix exitCompare to restore context usage from the saved view - Coerce falsy thread IDs to undefined for consistent backend/frontend fallback behavior - Use _default as the image sessionId fallback to match the backend Backend: - Use ~/studio_sandbox/_default when a request arrives without a session_id
…nslothai#4827) * fix(studio): harden sandbox security for terminal and python tools The existing command blocklist used naive str.split() which is trivially bypassable via quoting, full paths, nested shells, variable expansion, and cross-tool pivoting through Python os.system/subprocess. Fixes unslothai#4818. Changes: - Replace str.split() blocklist with shlex.split() + os.path.basename() tokenization and regex scanning at shell command boundaries - Add sanitized subprocess environment (_build_safe_env) that strips credentials (HF_TOKEN, WANDB_API_KEY, GH_TOKEN, AWS_*, etc.) and restricts PATH to /usr/local/bin:/usr/bin:/bin - Add PR_SET_NO_NEW_PRIVS via prctl on Linux so sudo/su/pkexec fail at the kernel level regardless of how they are invoked - Add RLIMIT_NPROC (256) and RLIMIT_FSIZE (100MB) to prevent fork bombs and disk filling attacks - Extend AST safety checker to detect os.system(), os.popen(), subprocess.run/Popen/call/check_output, os.exec*, os.spawn* calls containing blocked commands or dynamic (non-literal) arguments - Add cross-platform support: cmd.exe on Windows, bash on Unix; CREATE_NO_WINDOW flag on Windows, preexec_fn on Unix - Expand blocklist from 7 to 14 commands: add su, chown, passwd, mount, umount, fdisk, kill, killall, pkill - Apply all layers to both _bash_exec and _python_exec Zero measurable performance overhead -- shlex parsing and a single prctl syscall per subprocess fork. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix review findings: exception_catching dead code, false positives, process substitution - Include exception_catching reasons in _check_code_safety so bare except-in-loop timeout evasion is actually blocked (was computed in _check_signal_escape_patterns but never read by the caller) - Remove base.split() inner loop that caused false positives on quoted text arguments containing blocked words (e.g. echo "kill this process") - Add targeted nested shell detection for bash/sh/zsh -c arguments instead, which catches bash -c 'sudo whoami' without false positives - Add <() process substitution to the regex character class so diff <(rm -rf /path) is also caught - Fix error message to say "unsafe patterns" instead of specifically mentioning signal manipulation when other categories trigger * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Address review feedback: regex paths, keyword args, list element scanning - Regex now matches blocked commands after optional path prefix at shell boundaries (catches ls; /usr/bin/sudo and similar) - Nested shell detection uses os.path.basename so bash -c "/bin/rm" is caught - AST checker now inspects keyword arguments (not just positional) so subprocess.run(args="sudo ...", shell=True) is detected - List elements in subprocess calls are now checked via _find_blocked_commands for consistency (catches subprocess.run(["bash", "-c", "rm -rf /"])) - Dynamic argument check uses _is_safe_literal that validates list contents are all string literals * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix nested shell scan to only check the script body, not positional args bash -c 'script' arg0 arg1 -- only tokens[i+1] is the script body; subsequent tokens are $0, $1 positional parameters passed to the script and are not executed as shell commands. Scanning all remaining tokens caused false positives. * Add subshell parentheses to regex command boundary detection (sudo whoami) was not caught because ( was not in the regex character class for shell command boundaries. Add ( to the set alongside ;, &, |, backtick, newline. * Address high-priority review findings from 7 parallel reviewers - Track from-imports of dangerous functions (from os import system, from subprocess import run as r, etc.) via shell_exec_aliases dict so bare-name calls are detected by the AST checker - Include the active Python interpreter and virtualenv directories in the sanitized PATH so pip, uv, and Studio packages remain accessible in the sandbox - Add Windows-specific blocked commands (rmdir, takeown, icacls, runas, powershell, pwsh) only on win32 platform - Add os.posix_spawn and os.posix_spawnp to _SHELL_EXEC_FUNCS - Handle tuple literals same as list literals in AST argument inspection (both _extract_strings_from_list and _is_safe_literal) * Fix false positive on check=True kwargs and recursive nested shell scanning - Only inspect command-carrying keyword arguments (args, command, executable, path, file) in the AST checker, not control flags like check=True, text=True, capture_output=True which are booleans and were incorrectly flagged as non-literal dynamic arguments - Replace split() in nested shell detection with recursive call to _find_blocked_commands so that quoted commands (bash -c '"sudo" whoami') and semicolons (bash -c "sudo;ls") within nested shells are properly detected through the full shlex + regex pipeline * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Move preexec_fn imports to module level and use find_library for libc Addresses two Gemini review findings: 1. preexec_fn thread safety: _sandbox_preexec previously imported ctypes and resource inside the function body, which runs between fork() and exec() in the child process. In a multi-threaded server, this could deadlock if the import machinery locks were held by another thread at fork time. Now all imports and the libc handle are resolved once at module load time, so _sandbox_preexec only calls C-level functions (prctl, setrlimit) with no Python import activity. 2. Hardcoded libc.so.6 path: replaced with ctypes.util.find_library("c") which works on glibc (libc.so.6), musl (libc.musl-*.so.1), and other Linux distributions where libc has a different soname. * Apply Gemini style suggestions: combined regex, dict.fromkeys, constant hoisting - Combine per-word regex loop into a single re.findall with alternation pattern, avoiding repeated regex compilation and searching - Replace manual dedup loop with dict.fromkeys for PATH entries - Hoist _CMD_KWARGS frozenset out of visit_Call to avoid recreating it on every AST node visit * Add cmd /c nested shell detection for Windows parity The nested shell scan only checked for Unix shells (bash -c, sh -c, etc). Add cmd /c and cmd.exe /c detection so that Windows nested shell invocations are also recursively scanned for blocked commands. The token scan already catches blocked commands at any position, so this is defense-in-depth for consistency across platforms. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Handle combined shell flags (-lc, -xc) and interleaved flags (--login -c) The nested shell scan only matched token == "-c" with the immediately preceding token being a shell name. This missed: - Combined flags: bash -lc 'rm ...' (-lc ends with c, is a valid combined flag meaning -l -c) - Interleaved flags: bash --login -c 'sudo ...' (--login sits between bash and -c) Now matches any short flag ending in 'c' (e.g. -lc, -xc, -ic) and walks backwards past intermediate flags to find the shell binary. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix /bin/bash bypass, remove RLIMIT_NPROC, reduce AST false positives Addresses three high-consensus findings from 20-reviewer pass: 1. /bin/bash -c 'sudo whoami' bypassed nested shell scan because the backwards flag-skip logic treated paths starting with / as flags. Now only skips tokens starting with - as Unix flags; on Windows only skips short /X flags (not /bin/bash style paths). [9/20] 2. RLIMIT_NPROC=256 caused subprocess.run to fail with EAGAIN because Linux enforces NPROC per real UID, not per process tree. Removed RLIMIT_NPROC entirely; RLIMIT_FSIZE and PR_SET_NO_NEW_PRIVS remain as the primary resource and privilege controls. [5/20] 3. AST checker rejected safe dynamic subprocess usage like cmd=["git","status"]; subprocess.run(cmd) as shell_escape_dynamic. Now only flags dynamic args for shell-string functions (os.system, os.popen, subprocess.getoutput, etc.) or when shell=True is explicitly set. List-based subprocess calls with shell=False (the default) do not pass through a shell and are not flagged. [12/20] * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Handle Windows drive letter paths and .exe extensions in command detection Gemini review found that Windows absolute paths (C:\Windows\System32\ shutdown.exe) and executable extensions (.exe, .com, .bat, .cmd) were not handled: - Token scan now strips .exe/.com/.bat/.cmd extensions before checking the blocklist, so sudo.exe matches sudo, shutdown.bat matches shutdown - Regex pattern now includes optional Windows drive letter prefix ([a-zA-Z]:[/\\]) and optional executable extension suffix, so commands after shell metacharacters with full Windows paths are also caught * Handle **kwargs dict expansion, non-literal shell=, and except Exception false positive Addresses three findings from second 20-reviewer pass: 1. **kwargs dict expansion (9/20): subprocess.run(**{"args": "rm ...", "shell": True}) bypassed the AST checker because **kwargs were treated as opaque. Now expands literal dict **kwargs to inspect their keys, and flags opaque **kwargs (variable dicts) as unsafe. 2. Non-literal shell= values (7/20): shell=variable was treated as shell=False (safe). Now any shell= value that is not literally False is treated as potentially True (conservative default). 3. except Exception false positive (1/20): except Exception in a loop was flagged as timeout evasion, but Exception does not catch SystemExit or KeyboardInterrupt which are used for timeout enforcement. Narrowed to only flag except BaseException and except TimeoutError in loops. * [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>
…nslothai#4836) * studio: add speculative decoding support (ngram-mod, on by default) Enable n-gram speculative decoding for GGUF models in Unsloth Studio. Uses llama.cpp's ngram-mod mode which gives 10-40% faster generation with zero VRAM cost via a 4MB fixed hash table that auto-resets on low acceptance rates. Backend: - Add speculative_type field to LoadRequest, LoadResponse, and InferenceStatusResponse pydantic models - Add speculative_type parameter to LlamaCppBackend.load_model() with allowlist validation (ngram-simple, ngram-mod) - Pass --spec-type, --spec-ngram-size-n 16, --draft-max 24 flags to llama-server when ngram-mod is active - Default to ngram-mod for non-vision GGUF models server-side - Silently skip speculative decoding for vision models (unsupported in llama.cpp server-context.cpp) Frontend: - Add speculative_type to TS API types - Add speculativeType/loadedSpeculativeType to chat runtime store with default value of "ngram-mod" - Add On/Off toggle in Model settings section (GGUF only, hidden for vision models), included in dirty check for Apply/Reset - Wire speculative_type through model load request and response - Restore speculative type state on page refresh/reconnect * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: remove server-side speculative decoding override The backend was overriding speculative_type=None to "ngram-mod" for non-vision GGUF models, which prevented users from disabling spec decoding via the UI toggle. The frontend store already defaults to "ngram-mod", so the backend fallback was redundant and blocked the explicit "Off" setting. * fix: use recommended ngram-mod params from llama.cpp docs Update speculative decoding params to match the recommended values from llama.cpp docs (docs/speculative.md): --spec-ngram-size-n 24 (was 16, docs say small n not recommended) --draft-min 48 (was 0) --draft-max 64 (was 24, docs note MoEs need long drafts) Also fix comment: ngram-mod uses ~16 MB (4M entries * 4 bytes), not 4 MB. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add benchmark table and references to speculative decoding comment Include speedup numbers from llama.cpp PRs #18471 and #19164 as an inline comment so future readers understand the expected gains. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Add per-model YAML configs and MODEL_NAME_MAPPING entries for all 8 Gemma 4 models (4 instruct + 4 base): - gemma-4-31B-it / gemma-4-31B - gemma-4-26B-A4B-it / gemma-4-26B-A4B - gemma-4-E2B-it / gemma-4-E2B - gemma-4-E4B-it / gemma-4-E4B GGUF variants (only for -it models) resolve via the gemma-4 family entry in inference_defaults.json. Sampling defaults: temperature=1.0, top_p=0.95, top_k=64, min_p=0.0, no repetition or presence penalty. Matches gemma-3n and gemma-3.
…ai#4823) Tests for resolve_cached_repo_id_case and get_model_config case resolution, separated from the runtime changes in PR unslothai#4822.
…o button when proxy URL fails (unslothai#4866) * Add fallback message for Colab Studio button when localhost link doesn't work * Make fallback message darker grey for better readability * Make fallback message bold for better visibility --------- Co-authored-by: LeoBorcherding <LeoBorcherding@users.noreply.github.com>
…unslothai#4853) * Add vision detection cache to is_vision_model() to avoid redundant subprocess spawns is_vision_model() is called 4-5 times per training run for the same model with zero caching. For transformers 5.x models, each call spawns a full subprocess (~6s each). This adds a module-level _vision_detection_cache dict following the same pattern as the existing _audio_detection_cache used by detect_audio_type(). The function is refactored into a thin cache wrapper around _is_vision_model_uncached(), saving ~12s per training run. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Include hf_token in vision cache key for gated model correctness Cache key is now (model_name, hf_token) instead of just model_name. This prevents stale False results when an unauthenticated probe for a gated model is followed by an authenticated call. * Remove test file from main PR - will be submitted separately * Fix vision cache: normalize model names and skip caching transient failures - Normalize model names in cache key using resolve_cached_repo_id_case() to avoid duplicate entries for different casings of the same HF repo (aligns with case normalization from unslothai#4822) - Return None instead of False on transient failures (network errors, subprocess timeouts, HF API issues) so the cache layer can distinguish "definitely not a vision model" from "failed to check" - Only cache definitive True/False results; transient failures are retried on the next call instead of being permanently locked in as False * Refine failure handling: cache deterministic failures, guard normalization - Subprocess non-zero exit, JSON errors, and general exceptions return False (deterministic, cached) instead of None (retryable). Only subprocess.TimeoutExpired returns None since timeouts are transient. - Wrap cache key normalization in try/except so resolve_cached_repo_id_case or normalize_path failures fall back to raw model_name instead of crashing callers. * Harden vision detection cache: fix transient failure handling, thread safety, token security - All subprocess failure paths now return None (transient) instead of False, preventing permanent misclassification of VLMs after temporary HF/auth/network errors - Use SHA256 fingerprint for hf_token in cache key instead of raw bearer token - Add threading.Lock with double-checked locking to prevent thundering herd of concurrent subprocess spawns for the same uncached model - Distinguish permanent failures (RepositoryNotFoundError, GatedRepoError, ValueError) from transient ones in _is_vision_model_uncached - Pass resolved/normalized model name to detection (not just cache key) - Log normalization fallback at debug level instead of silent swallow - Thread hf_token through callers in routes/models.py and trainer.py that previously omitted it * Refine lock strategy and token fingerprint - Move detection computation outside the lock to avoid serializing long-running subprocess spawns (60s timeout) and HF API calls across all concurrent model checks. Lock is now only held for cache writes. - Use full SHA256 digest for token fingerprint instead of truncated 16-char prefix to eliminate collision risk. * Fix huggingface_hub import fallback and use atomic cache read - Add fallback import path for RepositoryNotFoundError/GatedRepoError from huggingface_hub.utils (older hub versions) when .errors is not available - Use sentinel-based dict.get() for single atomic cache read instead of two-step in/[] pattern (future-proof for no-GIL runtimes) * [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: Daniel Han <danielhanchen@gmail.com>
* Add tests for is_vision_model() caching behaviour * Fix review feedback: remove dead helper, fix exception test - Remove unused _make_config() helper function (dead code) - Fix test_exception_result_cached to actually exercise the exception path by mocking load_model_config to raise OSError instead of using side_effect=[False] which only tested normal False returns * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Use strict mock specs so tests exercise intended detection paths Use MagicMock(spec=[]) for all config mocks so hasattr() only returns True for explicitly set attributes. Without this, MagicMock defaults make all hasattr checks truthy, allowing tests to pass via unintended detection paths (e.g. img_processor instead of vision_config). --------- Co-authored-by: Roland Tannous <rolandtannous@gravityq.ai> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Gemma-4 does not need FORCE_FLOAT32. Testing shows that both float16 and bfloat16 work correctly without the forced float32 override: - Inference: identical outputs for float16 and bfloat16 (greedy decoding) - Training (100 steps, 4-bit LoRA, SFT on FineTome-100k): - float16 final loss: 3.048 - bfloat16 final loss: 3.065 - Losses converge to within 0.02 by step 60 - Grad norms healthy and comparable for both dtypes The FORCE_FLOAT32 path was actually causing training divergence. With it enabled, the compiled float32 run diverged at step ~28 with grad norms collapsing to near zero and loss plateauing at ~12.4. Without it, both dtypes train normally. This enables float16 on Tesla T4 and other GPUs without bfloat16 support.
…ai#4852) * fix: skip redundant HfFileSystem().glob() calls in loader.py Guard the SUPPORTS_LLAMA32 glob blocks with `is_model and is_peft` so the HfFileSystem HTTP call is only made when both configs could actually exist. This prevents indefinite hangs on slow/unreliable networks since the glob result is redundant when either AutoConfig or PeftConfig already failed to load. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove test file from main PR - moved to separate PR Tests for the glob skip guard belong in their own PR to keep the loader change minimal and reviewable. * Harden HfFileSystem glob: fix Windows path splitting, add try/except - Use str.rsplit("/", 1) instead of os.path.split to extract filenames from HfFileSystem paths. HfFileSystem always returns POSIX-style paths, but os.path.split uses the OS separator, so on Windows the entire path was returned as the "filename" and the config name comparison always failed. - Wrap the HfFileSystem().glob() call in try/except to gracefully handle network failures (offline mode, timeouts, unreachable Hub). On failure both_exist stays False, which is the safe default. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove redundant HfFileSystem().glob() call for remote repos When is_model and is_peft are both True, AutoConfig and PeftConfig have already loaded successfully, proving both config.json and adapter_config.json exist. The HfFileSystem network call to re-verify this was redundant and could cause hangs on slow networks. Replace the glob + try/except block with a direct both_exist = True assignment. * Remove unused HfFileSystem import HfFileSystem was only used for the glob() calls that were replaced with direct both_exist = True assignments in the previous commit. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
…ting directly at a model directory (unslothai#4860) Fix custom folder scanning when pointing directly at a model directory. When a user adds a custom scan folder that points directly at a model directory (e.g. /path/to/gemma-4-e2b-it-gguf/ containing config.json and gemma-4-E2B-it-BF16.gguf), the model list previously showed individual .gguf files as separate entries instead of recognizing the directory as a single model. Clicking any entry showed "No GGUF variants found" because list_local_gguf_variants received a file path and immediately returned empty. Changes: - Add _is_model_directory() helper that detects directories with both config metadata and actual model weight files (excludes mmproj GGUFs and non-weight .bin files like tokenizer.bin) - _scan_models_dir: detect self-model and return single directory entry - _scan_lmstudio_dir: surface model directories directly instead of descending into them as publisher folders; handle both root and child model directories - Add _resolve_gguf_dir() helper for GGUF path resolution that only falls back to parent directory when parent has model metadata - list_local_gguf_variants / _find_local_gguf_by_variant: use resolver so .gguf file paths inside model directories work correctly
Tests verifying that HfFileSystem().glob() is correctly skipped when is_model or is_peft is False, matching the guard added in PR unslothai#4852.
…low (unslothai#4872) * fix(chat): prevent implicit empty thread creation and stabilize new-chat flow * fix(chat): harden compare thread sync and simplify sidebar thread query * fix(chat): harden new-thread state sync and isolate compare active thread updates * fix(chat): stabilize new-thread state sync and prevent compare/session bleed * Fix thread restoration, handleNewThread guard, sidebar filter, and delete flow - Remove __LOCALID_ filter from getInitialSingleChatView: in this Dexie-backed adapter, AUI's __LOCALID_ prefixed IDs ARE the real persistent thread IDs stored by initialize(). Filtering them out breaks thread restoration on navigation. - Simplify handleNewThread to synchronous: the async Dexie message check is redundant (persistence is already deferred to first append) and strands users on legacy empty threads. Use a simple guard that checks the store's activeThreadId to detect unsent drafts. - Add message-count filter to sidebar: filter threads to only show those with at least one message, hiding legacy empty threads. - Add store-based sidebar highlighting fallback: use activeThreadId from the store when view.threadId is not set (nonce-backed chats). - Fix handleDelete to call onNewThread() instead of onSelect(), and clear activeThreadId, so the runtime properly resets after deleting the active thread. * Fix handleDelete nonce path and restore __LOCALID_ filter handleDelete was calling onNewThread() after clearing activeThreadId, but the handleNewThread guard sees !view.threadId && !activeThreadId and returns early, leaving the UI stuck on the deleted thread. Fix by directly calling onSelect with a new nonce instead. Restore __LOCALID_ filter in getInitialSingleChatView to prevent restoring unpersisted AUI local thread IDs on navigation. Without this filter, navigating away from /chat before sending a message would restore a non-existent thread that Dexie cannot fetch. --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com>
…othai#4878) * split venv_t5 into venv_t5_530 and venv_t5_550 for tiered transformers 5.x support * fix bfloat16 crash on T4 for FORCE_FLOAT32 models and disable trust_remote_code auto-enable for native t5 models * revert FORCE_FLOAT32 dtype change * restrict trust_remote_code auto-enable to Nemotron models only * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * use config.json model_type for tier detection, add unsloth/nvidia namespace guard * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Revert "[pre-commit.ci] auto fixes from pre-commit.com hooks" This reverts commit fb43d46. * Revert "use config.json model_type for tier detection, add unsloth/nvidia namespace guard" This reverts commit fc49ae2. * add unsloth/nvidia namespace guard to Nemotron trust_remote_code auto-enable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * reorder tier checks: all substring matches before config.json fetches * extract shared activate_transformers_for_subprocess into transformers_version.py * narrow Nemotron trust_remote_code to nemotron_h/nemotron-3-nano, add to export worker * clean venv_t5 dirs before re-install in setup.sh, clarify version alias comment * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * run venv_t5 migration outside deps fast-path gate in both setup scripts --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.8 → v0.15.9](astral-sh/ruff-pre-commit@v0.15.8...v0.15.9) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Bumps the npm-oxc-validator group in /studio/backend/core/data_recipe/oxc-validator with 1 update: [oxc-parser](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser). Updates `oxc-parser` from 0.121.0 to 0.123.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/parser/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.123.0/napi/parser) --- updated-dependencies: - dependency-name: oxc-parser dependency-version: 0.123.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: npm-oxc-validator ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the npm-oxc-validator group in /studio/backend/core/data_recipe/oxc-validator with 1 update: [oxc-parser](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser). Updates `oxc-parser` from 0.121.0 to 0.123.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/parser/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.123.0/napi/parser) --- updated-dependencies: - dependency-name: oxc-parser dependency-version: 0.123.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: npm-oxc-validator ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat: inject local model provider into recipe jobs via JWT * feat: auto-generate JWT for local model providers in recipes * feat: add is_local flag to model provider config types and utils * fix(studio): skip endpoint validation for local providers * feat(studio): add local/external model source toggle to provider dialog * feat(studio): thread localProviderNames through model config dialog chain * feat(studio): show 'Local model (Chat)' label for local model_provider configs * fix: hardcode loopback for local endpoint, clear stale creds on toggle * fix: document TOCTOU/JWT rotation, add deferred import comments, fix is_local serialization * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(studio): clear stale local model state on provider toggle and validation * fix(studio): override empty local endpoint in validation and skip model gate for unused providers * fix(studio): resolve loopback port from app.state, clear stale local provider fields, sync model id on toggle Address review feedback on the local-model-provider flow: - Backend (jobs.py): _resolve_local_v1_endpoint now reads the actual bound port from app.state.server_port (set in run.py after binding) instead of parsing it out of request.base_url, which is wrong behind any reverse proxy or non-default port. The two duplicated urlparse blocks are gone. - Backend (jobs.py): defensively pop api_key_env, extra_headers, extra_body from local providers so a previously external provider that flipped to local cannot leak invalid JSON or rogue auth headers into the local /v1 call. Also dedupe the post-loop assignment and tighten the local-name intersection so empty names cannot match. - Backend (jobs.py): hoist datetime and urllib.parse imports to the top import block for consistency with the rest of the file. - Backend (run.py): expose the bound port on app.state.server_port after the uvicorn server is constructed. - Frontend (model-provider-dialog.tsx): clear extra_headers and extra_body when toggling to local mode. Hidden inputs would otherwise keep stale JSON blocking validate/run. - Frontend (model-config-dialog.tsx): factor the local-aware provider selection logic into applyProviderChange and call it from both onValueChange and onBlur, so manually typing a provider name and tabbing away keeps the model field consistent. - Frontend (recipe-studio.ts store): handle both directions of the is_local toggle in the cascade. external -> local now backfills model: "local" on already-linked model_configs so they pass validation immediately, mirroring the existing local -> external clear path. - Frontend (validate.ts + build-payload.ts): thread localProviderNames into validateModelConfigProviders and skip the "model is required" check for local-linked configs. Local providers do not need a real model id since the inference endpoint uses the loaded Chat model. * fix(studio): narrow store cascade types, sync model placeholder on graph relink and node removal, harden ephemeral port path Loop 2 review fixes: - recipe-studio.ts: type-narrow next.is_local by also checking next.kind === "model_provider". TS otherwise raised TS2339 because next was typed as the union NodeConfig after the spread. The behavior is unchanged but the code now compiles cleanly. - model-config-dialog.tsx: convert the lastProviderRef / providerInputRef ref-during-render pattern (pre-existing react-hooks/refs lint error) to a useEffect that syncs providerInputRef from config.provider. The combobox blur path still uses applyProviderChange and remains stable. - recipe-graph-connection.ts: when a graph drag links a model_provider to a model_config, mirror the dialog applyProviderChange behavior: fill model: "local" if the new provider is local and the model field is blank, clear model when relinking from a local placeholder to an external provider, otherwise leave the model alone. - reference-sync.ts: when a referenced provider node is removed, clear the synthetic model: "local" placeholder along with the provider field, so a future relink to an external provider does not pass validation with a stale value that fails at runtime. - run.py: only publish app.state.server_port when the bound port is a real positive integer; for ephemeral binds (port==0) leave it unset and let request handlers fall back to request.base_url. - jobs.py: _resolve_local_v1_endpoint also falls back when app.state.server_port is non-positive, and uses `is None` instead of the truthy fallback so a literal 0 is handled correctly. * fix(studio): strict is_local check, narrow loaded-model gate to LLM-reachable configs, add scope-server port fallback Loop 3 review fixes: - jobs.py, validate.py: require `is_local is True` instead of truthy check. Malformed payloads such as is_local: "false" or is_local: 1 would otherwise be treated as local and silently rewritten to the loopback endpoint. - jobs.py: _resolve_local_v1_endpoint now tries request.scope["server"] (the actual uvicorn-assigned (host, port) tuple) as a second resolution step before falling back to parsing request.base_url. This covers direct-uvicorn startup paths and ephemeral binds that never publish app.state.server_port. - jobs.py: new _used_llm_model_aliases helper collects the set of model_aliases that an LLM column actually references, and the "Chat model loaded" gate is now only triggered when a local provider is reachable from that set. Orphan model_config nodes on the canvas no longer block unrelated recipe runs. * fix(studio): force skip_health_check on local-linked configs, skip JSON parsing for local providers, local-aware inline editor Loop 4 review fixes: - jobs.py: after rewriting local providers, also force skip_health_check: true on any model_config linked to a local provider. The /v1/models endpoint only advertises the real loaded model id, so data_designer's default model-availability health check would otherwise fail against the placeholder "local" id before the first chat completion call. The inference route already ignores the model id in chat completions, so skipping the check is safe. - builders-model.ts: buildModelProvider now short-circuits for local providers and emits only { name, endpoint: "", provider_type, is_local } without running parseJsonObject on the hidden extra_headers/extra_body inputs. Imported or hydrated recipes with stale invalid JSON in those fields no longer block client-side validate/run. - inline-model.tsx: the model_config branch now accepts an optional localProviderNames prop and mirrors the dialog applyProviderChange behavior. Changing provider to/from a local one auto-fills or clears the "local" placeholder consistently with the other edit paths. - recipe-graph-node.tsx: derive localProviderNames from the store via useMemo (stable identity) and pass it through renderNodeBody to <InlineModel>. Hooks order is preserved by declaring them above the early return for markdown_note nodes. - run.py: minor comment tweak - loop 3 already added the scope-server fallback path, note that in the comment. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: danielhanchen <info@unsloth.ai>
…pdates (unslothai#4586) * build(deps): bump the bun-frontend group across 1 directory with 16 updates Bumps the bun-frontend group with 16 updates in the /studio/frontend directory: | Package | From | To | | --- | --- | --- | | [@dagrejs/dagre](https://github.com/dagrejs/dagre) | `2.0.4` | `3.0.0` | | [@dagrejs/graphlib](https://github.com/dagrejs/graphlib) | `3.0.4` | `4.0.1` | | @hugeicons/core-free-icons | `3.3.0` | `4.0.0` | | [@streamdown/cjk](https://github.com/vercel/streamdown/tree/HEAD/packages/streamdown-cjk) | `1.0.2` | `1.0.3` | | [@streamdown/code](https://github.com/vercel/streamdown/tree/HEAD/packages/streamdown-code) | `1.0.2` | `1.1.1` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `0.577.0` | `1.6.0` | | [recharts](https://github.com/recharts/recharts) | `3.7.0` | `3.8.0` | | [shadcn](https://github.com/shadcn-ui/ui/tree/HEAD/packages/shadcn) | `3.8.5` | `4.1.0` | | [streamdown](https://github.com/vercel/streamdown/tree/HEAD/packages/streamdown) | `2.3.0` | `2.5.0` | | [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) | `1.9.4` | `2.4.8` | | [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) | `9.39.4` | `10.0.1` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `24.12.0` | `25.5.0` | | [eslint](https://github.com/eslint/eslint) | `9.39.4` | `10.1.0` | | [eslint-plugin-react-refresh](https://github.com/ArnaudBarre/eslint-plugin-react-refresh) | `0.4.26` | `0.5.2` | | [globals](https://github.com/sindresorhus/globals) | `16.5.0` | `17.4.0` | | [typescript](https://github.com/microsoft/TypeScript) | `5.9.3` | `6.0.2` | Updates `@dagrejs/dagre` from 2.0.4 to 3.0.0 - [Release notes](https://github.com/dagrejs/dagre/releases) - [Changelog](https://github.com/dagrejs/dagre/blob/master/changelog.md) - [Commits](dagrejs/dagre@v2.0.4...v3.0.0) Updates `@dagrejs/graphlib` from 3.0.4 to 4.0.1 - [Release notes](https://github.com/dagrejs/graphlib/releases) - [Changelog](https://github.com/dagrejs/graphlib/blob/master/changelog.md) - [Commits](dagrejs/graphlib@v3.0.4...v4.0.1) Updates `@hugeicons/core-free-icons` from 3.3.0 to 4.0.0 Updates `@streamdown/cjk` from 1.0.2 to 1.0.3 - [Release notes](https://github.com/vercel/streamdown/releases) - [Changelog](https://github.com/vercel/streamdown/blob/main/packages/streamdown-cjk/CHANGELOG.md) - [Commits](https://github.com/vercel/streamdown/commits/@streamdown/cjk@1.0.3/packages/streamdown-cjk) Updates `@streamdown/code` from 1.0.2 to 1.1.1 - [Release notes](https://github.com/vercel/streamdown/releases) - [Changelog](https://github.com/vercel/streamdown/blob/main/packages/streamdown-code/CHANGELOG.md) - [Commits](https://github.com/vercel/streamdown/commits/@streamdown/code@1.1.1/packages/streamdown-code) Updates `lucide-react` from 0.577.0 to 1.6.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/1.6.0/packages/lucide-react) Updates `recharts` from 3.7.0 to 3.8.0 - [Release notes](https://github.com/recharts/recharts/releases) - [Changelog](https://github.com/recharts/recharts/blob/main/CHANGELOG.md) - [Commits](recharts/recharts@v3.7.0...v3.8.0) Updates `shadcn` from 3.8.5 to 4.1.0 - [Release notes](https://github.com/shadcn-ui/ui/releases) - [Changelog](https://github.com/shadcn-ui/ui/blob/main/packages/shadcn/CHANGELOG.md) - [Commits](https://github.com/shadcn-ui/ui/commits/shadcn@4.1.0/packages/shadcn) Updates `streamdown` from 2.3.0 to 2.5.0 - [Release notes](https://github.com/vercel/streamdown/releases) - [Changelog](https://github.com/vercel/streamdown/blob/main/packages/streamdown/CHANGELOG.md) - [Commits](https://github.com/vercel/streamdown/commits/streamdown@2.5.0/packages/streamdown) Updates `@biomejs/biome` from 1.9.4 to 2.4.8 - [Release notes](https://github.com/biomejs/biome/releases) - [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md) - [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.4.8/packages/@biomejs/biome) Updates `@eslint/js` from 9.39.4 to 10.0.1 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/commits/v10.0.1/packages/js) Updates `@types/node` from 24.12.0 to 25.5.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `eslint` from 9.39.4 to 10.1.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](eslint/eslint@v9.39.4...v10.1.0) Updates `eslint-plugin-react-refresh` from 0.4.26 to 0.5.2 - [Release notes](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/releases) - [Changelog](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/blob/main/CHANGELOG.md) - [Commits](ArnaudBarre/eslint-plugin-react-refresh@v0.4.26...v0.5.2) Updates `globals` from 16.5.0 to 17.4.0 - [Release notes](https://github.com/sindresorhus/globals/releases) - [Commits](sindresorhus/globals@v16.5.0...v17.4.0) Updates `typescript` from 5.9.3 to 6.0.2 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](microsoft/TypeScript@v5.9.3...v6.0.2) --- updated-dependencies: - dependency-name: "@dagrejs/dagre" dependency-version: 3.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: bun-frontend - dependency-name: "@dagrejs/graphlib" dependency-version: 4.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: bun-frontend - dependency-name: "@hugeicons/core-free-icons" dependency-version: 4.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: bun-frontend - dependency-name: "@streamdown/cjk" dependency-version: 1.0.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: bun-frontend - dependency-name: "@streamdown/code" dependency-version: 1.1.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: bun-frontend - dependency-name: lucide-react dependency-version: 1.6.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: bun-frontend - dependency-name: recharts dependency-version: 3.8.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: bun-frontend - dependency-name: shadcn dependency-version: 4.1.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: bun-frontend - dependency-name: streamdown dependency-version: 2.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: bun-frontend - dependency-name: "@biomejs/biome" dependency-version: 2.4.8 dependency-type: direct:development update-type: version-update:semver-major dependency-group: bun-frontend - dependency-name: "@eslint/js" dependency-version: 10.0.1 dependency-type: direct:development update-type: version-update:semver-major dependency-group: bun-frontend - dependency-name: "@types/node" dependency-version: 25.5.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: bun-frontend - dependency-name: eslint dependency-version: 10.1.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: bun-frontend - dependency-name: eslint-plugin-react-refresh dependency-version: 0.5.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: bun-frontend - dependency-name: globals dependency-version: 17.4.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: bun-frontend - dependency-name: typescript dependency-version: 6.0.2 dependency-type: direct:development update-type: version-update:semver-major dependency-group: bun-frontend ... Signed-off-by: dependabot[bot] <support@github.com> * Revert dagrejs upgrades Keep @dagrejs/dagre at ^2.0.4 and @dagrejs/graphlib at ^3.0.4. * Revert biome, eslint, typescript, and recharts upgrades These upgrades break studio/frontend locally: - @biomejs/biome 2.4.10 fails to parse the existing biome.json (files.ignore and organizeImports keys removed in v2; schema version mismatch). - typescript 6.0.2 emits TS5101 on tsconfig.app.json baseUrl ("Option 'baseUrl' is deprecated and will stop functioning in TypeScript 7.0"), so tsc -b exits 2. - eslint 10.2.0 conflicts with eslint-plugin-react-hooks@7.0.1, which peers on eslint ^9; npm install fails with ERESOLVE. - recharts 3.8.1 widened LegendPayload.dataKey to include a function type, which breaks the React key={item.dataKey} usage in src/components/ui/chart.tsx (TS2322). Hold these at their current pinned versions until the upstream peer deps and config migrations are ready. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
Sync AiFeatures/unsloth with unslothai/unsloth main (192 commits behind). All iAiFy ahead-commits are governance/docs/dependabot only — no product code conflicts. Sole conflict was .github/dependabot.yml (kept iAiFy version with github-actions ecosystem entry). Per FORK-CUSTOMIZATIONS.md: fork carries no upstream-source patches. Refs: enterprise-analysis-v2 Wave 5 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wave 5 unsloth fork sync
Sync AiFeatures/unsloth with upstream unslothai/unsloth main (192 commits behind at start).
Strategy
git merge upstream/main— only 1 conflict (.github/dependabot.ymladd/add)--oursto preserve iAiFy github-actions ecosystem entryVerification
grep -rn '^<<<<<<<')Refs: enterprise-analysis-v2 Wave 5
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com