Commit graph

4,976 commits

Author SHA1 Message Date
Daniel Han
9a7d080e94 Remove unused field import from update_check.py 2026-04-03 14:51:05 +00:00
Daniel Han
3ccca16aac Fix null-to-None conversion and skip macOS llama.cpp override
- Handle JSON null values correctly in manifest parsing: convert to
  empty string instead of literal "None" which could be treated as a
  valid value downstream.

- Skip llama_cpp_source manifest override on macOS since
  unslothai/llama.cpp does not publish macOS binaries. macOS must
  always use ggml-org/llama.cpp for prebuilt assets.
2026-04-03 14:50:08 +00:00
Daniel Han
6b843a8e8e Use asyncio.to_thread for blocking manifest fetch in async endpoint
The /api/update-check endpoint is async but fetch_and_cache_update_status
performs a blocking HTTP request via urllib. Wrap it in asyncio.to_thread
to avoid blocking the FastAPI event loop when the background thread has
not yet completed the initial fetch.
2026-04-03 14:33:45 +00:00
Daniel Han
7bca7bf0db Validate announcement URL scheme and fix llama-only mode guard
- Only allow https:// and http:// URLs for announcement_url to prevent
  javascript: or data: scheme injection from a compromised manifest.

- Guard the install timestamp write with both _LLAMA_ONLY and
  _SKIP_PYTHON_DEPS checks to prevent uninitialized variable errors
  when running in UNSLOTH_STUDIO_LLAMA_ONLY=1 mode (set -u).
2026-04-03 14:29:51 +00:00
pre-commit-ci[bot]
5301c5daf0 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-03 13:52:45 +00:00
Daniel Han
2bcafd1c25 Fix base.txt filter to match 'unsloth' exactly, not 'unsloth-zoo'
The previous _filter_requirements approach matched any line starting
with "unsloth", which also caught "unsloth-zoo". Replace with a regex
that matches "unsloth" followed by a version specifier or end-of-line,
preserving "unsloth-zoo" and other packages with the "unsloth-" prefix.
2026-04-03 13:52:30 +00:00
Daniel Han
a1c7b95a5a Consolidate manifest parsing and add debug logging
- Replace 8 separate Python invocations for JSON field extraction with a
  single Python call that outputs shell-safe eval assignments via
  shlex.quote. This reduces setup.sh startup overhead by ~400ms.

- Add debug-level logging to exception handlers in update_check.py
  instead of silently swallowing errors, aiding troubleshooting when
  manifest fetch or critical-time comparison fails.
2026-04-03 13:50:26 +00:00
pre-commit-ci[bot]
67b09be3f0 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-03 13:45:35 +00:00
Daniel Han
a681c778bd Fix P1 review issues: race condition, fast-path bypass, git revert, timestamp guard
1. Fix cold-start race in /api/update-check: if the background thread has
   not finished fetching the manifest when the endpoint is hit, do an
   inline fetch so the UI never permanently caches empty defaults.

2. Fix PyPI fast-path blocking manifest git-main installs: evaluate the
   manifest unsloth_source directive BEFORE the PyPI version check so
   users already on latest PyPI still switch to git main when directed.

3. Fix base.txt reverting git install: filter out "unsloth" from base.txt
   requirements when the git-main path is active, preventing pip/uv from
   replacing the git checkout with the PyPI wheel.

4. Guard install timestamp write: only write UNSLOTH_STUDIO_INFO.json
   when python deps were actually updated, preventing users from clearing
   a critical-update warning by re-running setup in llama-only or
   skip-python-deps mode.
2026-04-03 13:45:05 +00:00
pre-commit-ci[bot]
42b60063f6 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-03 13:26:27 +00:00
Daniel Han
7eda1ba5a8 Add UNSLOTH_UPDATE_DETAILS.json manifest system for Studio updates
Adds a centralized manifest file that controls Studio update behavior,
allowing the team to temporarily switch update sources (PyPI vs git main),
control llama.cpp source/version, and notify users of critical updates --
all without requiring a PyPI release.

Changes:
- New UNSLOTH_UPDATE_DETAILS.json manifest at repo root
- setup.sh fetches manifest on startup, applies directives for unsloth
  source, llama.cpp repo/tag overrides, writes install timestamp
- install_python_stack.py supports STUDIO_UNSLOTH_GIT_REF env var for
  installing unsloth from git instead of PyPI
- New backend utils/update_check.py for manifest fetch and cache
- New /api/update-check endpoint in main.py (unauthenticated)
- Background thread checks manifest at startup, prints terminal banner
- New use-update-check.ts React hook (module-level cache pattern)
- navbar.tsx Update button shows dynamic badge and announcement
- __root.tsx shows non-dismissable critical update banner
2026-04-03 13:25:54 +00:00
Daniel Han
8981e6c804
Update test_pr4562_bugfixes.py for simplified install policy (#4817)
- Add TestFetchJsonRetries for JSON retry logic and max_pages
- Update TestSourceCodePatterns for simplified --simple-policy flow
- Add tests for installed prebuilt release reporting
- Add test for CUDA toolkit version-sorted nvcc discovery
- Remove assertions for removed --resolve-install-tag / --resolve-source-build paths
2026-04-03 04:06:14 -07:00
DoubleMathew
ac562bac66
Fix/llama.cppbuilding (#4804)
* Simplify llama.cpp install logic

* print release tag

* Retry failed json decode

* don't pull all ggml releases

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

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

* Remove test file changes from main PR

Test changes for test_pr4562_bugfixes.py will be submitted in a separate PR to keep this PR focused on the install path simplification.

* Fix setup.sh executable bit and direct tag lookup for pinned releases

- Restore setup.sh file mode to 100755 (was accidentally changed to 100644)
- Add direct GitHub API tag lookup in iter_release_payloads_by_time for
  non-latest requested tags (e.g. b7879) instead of relying on paginated
  release scans that may miss older releases beyond the 5-page limit
- Update stale DEFAULT_PUBLISHED_REPO comment to match new value

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

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

* Fix force-compile default ref and remove dead code in setup.ps1

- Change FORCE_COMPILE_DEFAULT_REF from "main" to "master" in all three
  files (install_llama_prebuilt.py, setup.sh, setup.ps1) since
  ggml-org/llama.cpp uses "master" as its default branch, not "main".
  Using "main" would cause git clone --branch to fail when
  UNSLOTH_LLAMA_FORCE_COMPILE=1 with UNSLOTH_LLAMA_TAG=latest.
- Remove dead if ($SkipPrebuiltInstall) block inside the else branch of
  setup.ps1 that could never be reached (the outer elseif already
  handles $SkipPrebuiltInstall=true).
- Maintain setup.sh executable bit (100755).

* Improve iter_release_payloads_by_time error handling for direct tag lookup

When a pinned release tag is not found (HTTP 404), fall through to the
paginated release scan instead of silently returning empty results.
Non-404 errors (network failures, rate limits) are propagated to the
caller so users get actionable error messages.

* [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>
2026-04-03 00:34:20 -07:00
Michael Han
c1685b9459
Gemma 4 update.md 2026-04-02 22:54:03 -07:00
Manan Shah
a7e6964117
Fix/gemma4 install script (#4815)
* transformer 5.5.0 has now been released

* fallback for python < 3.10
:
2026-04-02 22:03:35 -07:00
Roland Tannous
6644a771b4
fix: patch PEFT for Gemma4ClippableLinear in loader checkpoint path (fixes export) (#4807)
* fix: patch PEFT for Gemma4ClippableLinear in loader checkpoint path

The same Gemma4ClippableLinear monkey-patch that exists in vision.py
for training is needed in loader.py for loading existing checkpoints
(used by export and inference).

Gemma4ClippableLinear wraps nn.Linear but does not subclass it, so
PEFT's LoRA injection fails with "Target module not supported".
The patch redirects PEFT to target the inner .linear child instead.

Applied only to the vision model PeftModel.from_pretrained path.
Temporary fix until PEFT adds native support (peft#3129).

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

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

* fix: wrap ClippableLinear patch in try/finally to always restore

Ensures _create_and_replace is restored even if PeftModel.from_pretrained
raises, preventing leaked global state across subsequent model loads.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-03 04:03:54 +04:00
Roland Tannous
f91ef8f9b0
fix(studio): lazy-import transformers in model_config to fix 5.x version switch (#4806)
* fix(studio): lazy-import AutoConfig in model_config.py to fix transformers 5.x version switch

Move `from transformers import AutoConfig` from module level to inside
load_model_config() where it is actually used.

model_config.py is transitively imported at module load time via:
  core/inference/__init__ → llama_cpp → utils.models → model_config

In inference subprocesses (mp.spawn), this chain runs before
_activate_transformers_version() can prepend .venv_t5/ to sys.path.
The eager import caches transformers 4.57.6 in sys.modules, and the
subsequent sys.path change has no effect — Python always checks
sys.modules before sys.path.

Making the import lazy ensures transformers is not loaded until after
version activation, so the subprocess picks up the correct version.

* fix(studio): also lazy-import extract_model_size_b in llama_cpp.py

Belt-and-suspenders: make the import that originally triggered the
chain lazy as well, so future module-level AutoConfig additions in
utils.models cannot reintroduce the problem.

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-03 02:56:01 +04:00
Daniel Han
e553a8ad0b
fix(studio): suppress fatal error when prebuilt manifest is missing (#4799) v0.1.35-beta
When DEFAULT_PUBLISHED_REPO is ggml-org/llama.cpp, the prebuilt
resolver raises PrebuiltFallback because ggml-org releases do not
include a llama-prebuilt-manifest.json asset. This was caught by the
generic Exception handler and printed as "fatal helper error" to
stderr, which triggers NativeCommandError on PowerShell.

Catch PrebuiltFallback separately in the top-level __main__ handler
and exit with EXIT_FALLBACK (code 2) instead of EXIT_ERROR (code 1).
The message is still logged but without the "fatal helper error"
prefix. The shell scripts already handle non-zero exits and fall
back to source builds.

Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
2026-04-02 12:18:11 -07:00
Daniel Han
8ffd5826f2 Gemma-4 2026-04-02 11:59:37 -07:00
Daniel Han
934478ae31
fix(studio): revert llama.cpp default tag to latest (#4797)
* fix(studio): revert llama.cpp default tag to latest

The latest ggml-org/llama.cpp release (b8637) now includes Gemma 4
support. Revert the temporary "b8637" pin from #4796 to "latest" so
the prebuilt resolver always picks the newest release automatically
without needing manual tag bumps.

* docs: add comment explaining latest vs master for llama.cpp tag

Document in all three files why "latest" is preferred over "master"
and when "master" should be used as a temporary override.

---------

Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
2026-04-02 11:52:37 -07:00
Daniel Han
401621618b
fix(studio): don't set trust_remote_code for Gemma 4 training (#4795)
Gemma 4 is a native transformers 5.5 model and does not need
trust_remote_code=True. The auto-enable logic (added for NemotronH)
was catching all transformers 5.x models, including Gemma 4.

When trust_remote_code=True, unsloth_compile_transformers() returns
early without running the compiler. This disables the fused cross
entropy patch, causing logged training loss to be inflated by the
gradient_accumulation_steps factor.

Exclude models matching "gemma-4" or "gemma4" from the auto-enable
so the compiler runs and applies fused cross entropy correctly.
2026-04-02 11:44:26 -07:00
Daniel Han
8d1712b4ea
fix(studio): pin llama.cpp to b8637 release (Gemma 4 support) (#4796)
ggml-org/llama.cpp b8637 includes Gemma 4 support (ggml-org/llama.cpp#21309).
Revert the temporary "master" default back to a pinned release tag.

This eliminates the HTTP 422 errors from the prebuilt resolver (which
could not find a release matching "master"), avoids unnecessary source
builds, and restores prebuilt binary downloads on all platforms.

Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
2026-04-02 11:43:53 -07:00
DoubleMathew
7ae9b7f45f
fix windows llama.cpp compile from source issue (#4793)
* fix windows llama.cpp compile from source issue

* undo local repo usage

* fix llama.cpp install

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

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

* fix windows

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

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

* fix: route resolve-source-build call through Invoke-LlamaHelper

The --resolve-source-build call at the source-build resolution path
was still calling install_llama_prebuilt.py directly instead of going
through Invoke-LlamaHelper. On PS7+ with ErrorActionPreference=Stop,
stderr from the 422 response (when tag is "master") would trigger a
terminating NativeCommandError and crash setup.

* fix: suppress stderr error records from Invoke-LlamaHelper

ErrorActionPreference=Continue prevents termination but PowerShell
still displays stderr lines as visible ErrorRecord objects. Capture
all output via 2>&1 and split stdout from stderr manually so that
stderr lines never appear on the console. When StderrPath is given
the stderr content is written to that file for diagnostics.

* fix: always rebuild llama.cpp on Windows when tag is master

When the requested llama.cpp tag is "master" (a moving target), skip
the "already built" early exit so the build path runs and syncs to
the latest commit. Without this, existing llama-server binaries from
an older build (e.g. b8635 which lacks Gemma 4 support) are reused
and model loading fails.

Pinned tags (e.g. b8635) still skip the rebuild when the binary
already exists, since the tag is immutable.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
2026-04-02 11:43:46 -07:00
Daniel Han
7023e2a4ff
fix(studio): prioritize curated defaults over HF download ranking in Recommended (#4792)
The model list merge order was `top_gguf + top_hub + static_models`,
which meant the HF download-ranked models always came first. New models
like Gemma 4 have low download counts and were not in the HF top-40,
so they got buried after 80 other models despite being at the top of
the curated static defaults in defaults.py.

Flip the merge to `static_models + top_gguf + top_hub` so editorial
picks (new model launches, promoted models) always appear first in the
Recommended section, with HF popularity backfilling after.

Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
2026-04-02 10:46:53 -07:00
Roland Tannous
0446d46689
fixed name (#4791) 2026-04-02 21:04:42 +04:00
Daniel Han
1ce83c40aa
fix(studio): build llama.cpp from master instead of latest release tag (#4790)
The latest ggml-org/llama.cpp release (b8635) does not include Gemma 4
support (ggml-org/llama.cpp#21309 merged after the release was cut).
This causes `llama-server` to fail with "unknown model architecture:
gemma4" when loading Gemma 4 GGUFs.

Temporarily default _DEFAULT_LLAMA_TAG to "master" so all new installs
build from the llama.cpp master branch which includes Gemma 4 support.
Once a new upstream release is cut with Gemma 4, this can be reverted
back to "latest".

Changes:
- setup.sh: add _DEFAULT_LLAMA_TAG="master" maintainer default
- setup.ps1: add $DefaultLlamaTag="master" maintainer default
- install_llama_prebuilt.py: change DEFAULT_LLAMA_TAG fallback to "master"

Users can still override via UNSLOTH_LLAMA_TAG env var.
2026-04-02 09:45:56 -07:00
Daniel Han
2af53bf9a6
Pin transformers and huggingface-hub in main Studio venv (#4788)
Revert the >= loosening from f9c4b08 back to exact pins.
Using transformers>=4.57.6 allows pip to install 5.x into the main
Studio venv, which breaks huggingface_hub imports
(is_offline_mode removed in newer hub versions).

The main venv must stay on transformers==4.57.6 and
huggingface-hub==0.36.2. The 5.x version lives only in .venv_t5/
and is dynamically switched via sys.path at runtime.
2026-04-02 09:21:30 -07:00
Daniel Han
a241c58d84
Use transformers v5.5-release branch and pin to 5.5.0 (#4786)
The v5.5-release branch now exists on huggingface/transformers.
Use transformers==5.5.0 for all install paths and
git+transformers.git@v5.5-release for the MLX installer.

Also bumps huggingface_hub from 1.7.1 to 1.8.0 in setup.sh and
setup.ps1 to stay consistent.
2026-04-02 09:10:02 -07:00
Daniel Han
a353557249
Force llama.cpp to always use mainline ggml-org (#4785)
Hardcode the release repo to ggml-org/llama.cpp and remove the
UNSLOTH_LLAMA_RELEASE_REPO and UNSLOTH_LLAMA_SOURCE env var overrides
so that all users always build/download from mainline llama.cpp.
2026-04-02 09:03:00 -07:00
Daniel Han
f1c3b9caa9
Pin Gemma-4 transformers requirement to 5.5.0 stable (#4784)
Gemma-4 support landed in transformers main
(huggingface/transformers#45192). Update the version pin from
5.5.0.dev0 to 5.5.0 across loader, Studio version switcher,
and the MLX installer. Also fix install_gemma4_mlx.sh which
referenced a non-existent v5.5-release branch -- pin it to
the correct commit (91b1ab1) instead.
2026-04-02 08:59:21 -07:00
Daniel Han
4f9986ecb9
fix(studio): improve tool-calling re-prompt for small models (#4783)
Small GGUF models (<9B) frequently generate full code or lengthy
explanations instead of calling tools, bypassing the existing
plan-without-action re-prompt mechanism. Three issues:

1. _REPROMPT_MAX_CHARS=500 was too low -- models that output full
   HTML/code responses (often 1000+ chars) never triggered the
   re-prompt at all, since it only fires on short responses.

2. _MAX_REPROMPTS=1 gave the model only one chance to comply.
   Small models often need 2-3 nudges before switching from
   text generation to tool calling.

3. The re-prompt text ("Please use the available tools...") was
   too polite for small models to follow reliably.

4. Tool-calling detection missed chat templates using Jinja
   whitespace-trimming syntax ({%- if tools -%}) since only
   ({%- if tools %}) and ({% if tools %}) were checked.

Changes:
- Raise _REPROMPT_MAX_CHARS from 500 to 2000 so longer responses
  (code blocks, multi-paragraph plans) still trigger re-prompts
- Raise _MAX_REPROMPTS from 1 to 3 for more retry budget
- Use direct, imperative re-prompt language that small models
  follow more reliably ("STOP. You MUST call a tool NOW.")
- Strengthen the system prompt tool nudge to explicitly forbid
  outputting code blocks (redirect to the python tool instead)
- Add Jinja whitespace-trimmed variants to the tool_markers
  list so all template styles are detected correctly
2026-04-02 08:59:02 -07:00
Daniel Han
f9c4b08726
UI Changes (#4782)
* UI Changes

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

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

* Remove unrelated test file

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-02 08:05:55 -07:00
Roland Tannous
3b613eb1e8
ui improvement (#4781)
* ui

* ui

* ui
2026-04-02 07:57:47 -07:00
Daniel Han
c8d311a053
feat(studio): display images from Python tool execution in chat UI (#4778)
* feat(studio): display images from Python tool execution in chat UI

When the model calls the Python tool to create a matplotlib plot or
other image file, the image now displays inline in the chat output
instead of being invisible to the user.

Backend:
- Detect new image files (png/jpg/gif/webp/bmp) after Python subprocess
  completes by diffing os.listdir before/after execution
- Append __IMAGES__ sentinel to tool result for frontend consumption
- Strip sentinel before injecting result into LLM context (role: tool)
  so the model never sees file paths
- Add GET /sandbox/{session_id}/{filename} endpoint with JWT auth
  (header or query param), path traversal protection, extension
  allowlist, realpath containment check, and nosniff header

Frontend:
- Parse __IMAGES__ sentinel in tool_end SSE events, create structured
  result with text/images/sessionId
- Render <img> tags in Python tool UI pointing at the sandbox endpoint

Also fixes a bug where SyntaxError in user code was misreported as
"unsafe code detected" instead of showing the actual Python traceback.
The _check_code_safety function now lets SyntaxError pass through to
the subprocess for a proper error message.

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

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

* fix(studio): improve SVG detection and strip XML preamble

Handle <?xml ...?> declarations before <svg> tags in code fences,
strip XML declaration from SVGs before data URI rendering, and
update the sloth suggestion prompt to request showing code.

* fix(studio): persist parentId so retries survive reload

The append() handler was destructuring only { message } from
ExportedMessageRepositoryItem and discarding parentId. When loading
a saved thread, load() used ExportedMessageRepository.fromArray()
which chains all messages sequentially, flattening retry branches
into a linear list.

Now append() writes parentId to the MessageRecord, and load()
reconstructs the tree when parentIds are present. Old threads
without parentId fall back to the existing fromArray() behavior.

* fix(studio): address review findings for image display and retry persistence

Image detection:
- Use mtime comparison instead of filename-only diff so overwritten
  files (e.g. plt.savefig("chart.png") called twice) are detected

Sentinel parsing:
- Use rsplit/lastIndexOf instead of split/indexOf so user code that
  prints __IMAGES__: does not collide with the backend sentinel

Mixed legacy/new threads:
- For old messages without a stored parentId, infer sequential parent
  from the previous message instead of null, preventing multiple roots

Sandbox endpoint:
- Change Cache-Control from "public, max-age=3600" to "private,
  no-store" since these are authenticated responses

---------

Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-02 05:08:16 -07:00
Lee Jackson
5a5f1a4f34
studio: fix chat font changes leaking outside chat page (#4775)
* fix(frontend): scope sans font overrides to chat thread only

* fix(frontend): use font-sans fallback for heading stack and simplify chat font rules

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-04-02 05:04:23 -07:00
DoubleMathew
1ce8a8e7cd
Feat/custom llama prebuilt (#4771)
* update logic to incorporate custom prebuilt installs

* bug fixes

* update for review comments

* fix tags

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

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

* Separate test changes from main PR

Move test file changes out of this PR to keep the diff focused on
the install_llama_prebuilt.py and setup script changes. Test updates
will be submitted in a follow-up PR.

* Fix branch ref normalization and harden JSON parsing

- Add checkout_friendly_ref() to strip refs/heads/ prefix from branch
  refs before emitting them in SourceBuildPlan. git clone --branch does
  not accept fully qualified refs like refs/heads/main.
- Apply normalization in source_build_plan_for_release() and the
  direct-ref fallback in resolve_source_build_plan().
- Allow validated_checksums_for_bundle() to accept releases that carry
  only an exact-commit source archive without the legacy upstream-tag
  source tarball.
- Add 2>/dev/null || true guards to all inline python -c JSON parsing
  in setup.sh so a malformed payload does not abort the script under
  set -e.

* Fix Windows CUDA asset ordering and tag ref normalization

- Reorder windows_cuda_upstream_asset_names to prefer the main binary
  archive (llama-{tag}-bin-win-cuda-*) over the cudart sidecar archive
  (cudart-llama-bin-win-cuda-*). The cudart ZIP only contains CUDA
  runtime DLLs, not llama-server or llama-quantize binaries.
- Extend checkout_friendly_ref to also strip refs/tags/ prefix for tag
  refs, matching the refs/heads/ handling for branch refs.

* Simplify JSON parsing consistency in setup.sh

Use json.load(sys.stdin) consistently for all inline JSON parsing
in setup.sh, instead of the more complex json.loads(raw) pattern
on the install-tag resolution path. The 2>/dev/null || true guard
already handles empty/malformed input gracefully.

* Fix source build plan fallback for commit ref kind in PR #4771

* [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 <daniel@unsloth.ai>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-04-02 04:52:26 -07:00
Daniel Han
b20efc370a
Add regression tests for custom llama prebuilt installer (#4772)
Expand test coverage for install_llama_prebuilt.py:
- Add tests for source build plan resolution with custom repos
- Add tests for branch/commit/PR ref matching and normalization
- Add tests for manifest checksum validation
- Add tests for Windows CUDA upstream asset name patterns
- Update capsys checks to capture stderr after log() redirect
2026-04-02 04:45:09 -07:00
Michael Han
e2fd946fe1
Add files via upload 2026-04-02 03:00:10 -07:00
Michael Han
31d6aeb197
Unsloth new logo 2026-04-02 02:58:21 -07:00
Daniel Han
e4d1499230
fix(studio): prevent small models from stalling on tool-calling tasks (#4769)
* fix(studio): prevent small models from stalling on tool-calling tasks

Small GGUF models (< 9B params) in "Think, Search, Code" mode would
often describe what they planned to do ("Let me create this dashboard")
and then stop generating without ever calling a tool.

Three changes:

1. Simplify web_tips for small models: remove the "fetch its full content
   by calling web_search with the url parameter" guidance for models < 9B.
   This multi-step instruction causes small models to plan elaborate
   search-then-fetch-then-code sequences they cannot reliably execute.

2. Add "always call tools directly" imperative to the system prompt nudge
   so models act immediately instead of narrating their intentions.

3. Add plan-without-action re-prompt in the agentic loop: when the model
   emits planning text (matching patterns like "let me", "I'll", etc.)
   without calling any tool, inject a nudge asking it to call the tool
   and continue the loop. Capped at 2 re-prompts per request.

Benchmarked with Qwen3.5-4B-GGUF (N=5 trials per variant):
- Baseline: 40% of requests had any tool call
- Combined fix: 100% of requests had at least one tool call

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

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

---------

Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-02 02:11:07 -07:00
Daniel Han
dc0729aadf
Add regression test for shell injection fix in GGML conversion (#4773)
AST-based test ensures subprocess.Popen calls in GGML conversion functions
use argv lists instead of shell=True. Companion to PR #4768.
2026-04-02 00:10:47 -07:00
mateeaaaaaaa
752cef3299
fix(security): shell injection in GGML export conversion (#4768)
* Fix shell injection in GGML conversion paths

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

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

* Remove test file from security fix PR

Move test_save_shell_injection.py to a separate PR to keep this PR focused on the security fix itself.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
2026-04-02 00:10:43 -07:00
AdamPlatin123
ba8081fc96
fix(chat): correct loading text for cached models during inference (#4764)
Distinguish between actual network downloads and GPU memory loading for cached LoRA adapters in Studio chat.

- Add isCachedLora detection for local LoRA adapter paths using comprehensive cross-platform regex (Unix, Windows, UNC, WSL, tilde)
- Thread isCachedLora through loadInfo to chat-page inline status for proper 3-way distinction (cached / local LoRA / downloading)
- Skip download progress polling for cached LoRA models (no useless /download-progress API calls)
- Fix initial toast state to use isCachedLoad consistently instead of only checking isDownloaded
- Fix cancelLoading toast to not mention background downloads for cached/local loads
- Keep download-specific text ("Downloading model..." / "Download complete") inside the download-only polling block
2026-04-01 20:24:48 -07:00
Lee Jackson
ca4ea8b9fb
studio: align composer/code, unify fonts, and remove tool collapse jitter (#4763)
- Add min-w-0 guards to thread/message/markdown containers to prevent
  content overflow past the composer width
- Unify chat typography from Hellix/Space Grotesk to the sans stack,
  keeping monospace for code blocks and inline code
- Restructure desktop navbar right-side controls with shrink-0 wrappers
  for consistent spacing across HoverCard roots
- Soften tool-call label styling (font-medium + text-foreground/85
  instead of bold)
- Add responsive code block sizing via @container queries
- Add horizontal scrolling for wide code blocks within the thread column
- Scope list-item code block alignment CSS to .aui-thread-root
- Preserve useScrollLock in tool-fallback and tool-group collapsibles
- Fall back to bg-background on ViewportFooter when hideComposer is true
- Widen inline code monospace selector to cover th, blockquote, and
  heading elements
- Remove unused @fontsource-variable/space-grotesk import
2026-04-01 19:57:10 -07:00
DoubleMathew
71b934ef9d
Fix custom llama.cpp source builds and macos metal source builds (#4762)
* Fix script unbound variable error

* remove stale test script, add llama.cpp metal source builds, update tests

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

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

* Fix Metal precedence, test sync, and add behavioral tests

- Move macOS arm64 Metal check before CUDA/ROCm in GPU backend
  decision chain so Metal is not bypassed when nvcc is in PATH
- Remove RPATH flags from CPU fallback CMAKE_ARGS (only needed
  for Metal library linking)
- Update test_llama_pr_force_and_source.py to match _CLONE_ARGS
  rename from _CLONE_BRANCH_ARGS in setup.sh
- Add confirm_install_tree guard test for
  existing_install_matches_choice
- Add TestMacOSMetalBuildLogic bash subprocess tests verifying
  Metal flag selection, nvcc precedence, and CPU fallback behavior

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

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

* Fix Metal CPU fallback to also cover cmake build failures and update tests

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

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

* 1. _GPU_BACKEND_FRAGMENT synced -- removed dead CPU_FALLBACK_CMAKE_ARGS= init (6/8)
2. RPATH assertion replaced -- new test_macos_arm64_cpu_fallback_args_exclude_rpath checks the actual runtime CPU_FALLBACK_CMAKE_ARGS output for @loader_path and -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON (6/8)
3. _TRY_METAL_CPU_FALLBACK=false reset after both configure-failure and build-failure fallback branches in setup.sh (4/8)
4. macOS test now removes libmtmd.0.dylib instead of the platform-agnostic convert_hf_to_gguf.py (3/8)
5. Empty-string tag test added -- test_empty_tag_omits_branch_flag for resolved_tag= (2/8)
6. RPATH checks on cmake call logs -- both fallback tests now assert @loader_path and -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON are absent from CPU fallback cmake calls, plus baseline flag preservation (multiple)

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

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

* tests clean up

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-01 14:06:39 -05:00
Daniel Han
39fe23ded8
Tests for architecture-aware KV cache estimation (#4760)
* test: add 66 tests for architecture-aware KV cache estimation

Covers all 5 estimation paths (MLA, Hybrid Mamba, Sliding Window,
Standard GQA, Legacy), GGUF parser for 8 new metadata fields,
_can_estimate_kv gate conditions, quantization scaling, edge cases,
path priority ordering, and lifecycle (init/unload/reparse).

Zero external dependencies beyond pytest. No GPU or network required.
Cross-platform (Linux, macOS, Windows, WSL).

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-01 06:13:37 -07:00
Daniel Han
653eb3819a
fix(studio): allow context length slider to reach model's native limit (#4746)
* fix(studio): allow context length slider to reach model's native limit

The context length slider was hard-capped to the VRAM-estimated maximum,
preventing users from requesting higher context even though the backend
already handles it safely (multi-GPU selection, --fit fallback). Expose
the model's native context length from GGUF metadata as a separate API
field and use it as the slider ceiling instead. Add an amber warning
when the selected context exceeds the estimated VRAM capacity.

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

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

* Raise VRAM budget to 90% and add native_context_length tests

Increase the GPU memory utilization threshold from 70% to 90% across
_select_gpus and _fit_context_to_vram, allowing longer context lengths
before VRAM capping kicks in.

Add 33 tests for the native_context_length feature covering the backend
property, context value separation invariants, Pydantic models, route
completeness, edge cases, and cross-platform binary I/O.

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-01 06:12:52 -07:00
Daniel Han
d22b2a18f9
fix: add tokenizers to no-torch deps and TORCH_CONSTRAINT for arm64 macOS py313+ (#4748)
* fix: add tokenizers to no-torch runtime deps and add TORCH_CONSTRAINT for arm64 macOS py313+

Two installer fixes:

1. Add `tokenizers` to `no-torch-runtime.txt` before `transformers`.
   Without it, `from transformers import AutoConfig` crashes on startup
   because `--no-deps` skips transitive dependencies.

2. Add `TORCH_CONSTRAINT` variable to `install.sh`. On arm64 macOS with
   Python 3.13+, tighten the torch requirement to `>=2.6` since torch
   <2.6 has no cp313 arm64 wheels. The variable replaces the previously
   hard-coded constraint in the uv pip install line.

Includes 66 tests (42 pytest + 24 bash) covering:
- Structural checks on install.sh, install.ps1, no-torch-runtime.txt
- Shell snippet tests with mocked python for 13 platform/version combos
- Mock uv integration verifying correct constraint string
- E2E venv tests on Python 3.12 and 3.13 confirming AutoConfig works
- Negative control proving AutoConfig fails without tokenizers
- Full no-torch sandbox regression guards (safetensors, huggingface_hub)

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

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

* Fix incomplete no-torch manifest and align E2E tests with real --no-deps path

- Add missing transitive deps to no-torch-runtime.txt that are required
  under --no-deps: regex, typing_extensions, filelock, httpx, httpcore,
  certifi, idna, anyio, sniffio, h11. Without these, `from transformers
  import AutoConfig` still fails after install.sh --no-torch.

- Change all E2E tests to use --no-deps (matching what install.sh does)
  instead of normal dep resolution. Previous tests passed even with an
  incomplete manifest because uv backfilled transitive deps.

- Rewrite negative control to derive from the real no-torch-runtime.txt
  with tokenizers stripped, proving the specific fix matters.

- Replace GNU-only sed -i with heredoc in shell test for macOS compat.

- Remove unused os/sys imports from Python test file.

- Quote SKIP_TORCH and mock uv paths in bash -c strings.

* Assert install succeeds before checking import results in E2E tests

Address review feedback: test_torch_not_importable and
test_tokenizers_directly_importable in Group 3 now assert that
uv pip install returns 0 before checking import behavior. This
prevents false positives when the install itself fails silently.

* Assert install succeeds in negative control and tighten error check

- Add missing install-success assertion in test_negative_control_no_tokenizers
  to prevent false positives from network/install failures.

- Tighten error message check to look for "tokenizers" in stderr or
  ModuleNotFoundError, rather than the generic "No module" substring
  which could match unrelated import failures.

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

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

---------

Co-authored-by: Daniel Han <danielhanchen@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-01 06:12:17 -07:00
Daniel Han
76cb48be0b
fix: studio web search SSL failures and empty page content (#4754)
- Fix SSL handshake failures (SSLV3_ALERT_HANDSHAKE_FAILURE, CERTIFICATE_VERIFY_FAILED) when fetching HTTPS pages by introducing _PinnedHTTPSConnection that separates TCP connect (to pinned IP) from TLS handshake (with real hostname for SNI/cert verification)
- Fix SSRF DNS-rebinding vulnerability: previous impl swapped conn.host before connect(), causing fresh DNS resolution; new subclass keeps TCP pinned to validated IP
- Fix SPA/JS-rendered doc sites returning empty content by rotating real browser User-Agents (Chrome/Firefox/Safari)
- Strip nav/footer from HTML-to-Markdown output so article content is not buried under navigation chrome
- Increase raw fetch cap from 64KB to 512KB so SSR article content is reached on GitBook/Docusaurus/Next.js pages
- Fix IPv6 address bracketing in URL netloc construction
- Hoist SSL context, handler classes, and stdlib imports to module level (created once, not per-call)
- Use consistent UA across redirect hops to avoid breaking session-aware bot detection
2026-04-01 06:12:02 -07:00
Daniel Han
f84c2d03d3
Add installer test coverage for prebuilt llama.cpp changes (#4756)
Split out from #4741 to keep the main PR focused on installer logic.

- New test_install_llama_prebuilt_logic.py: tests for resolve logic,
  fallback behavior, env_int, busy/lock handling
- New test_validate_llama_prebuilt.py: validator tests for staged
  release_tag/upstream_tag handling
- New test_llama_pr_force_and_source.py: tests for PR_FORCE and
  LLAMA_SOURCE maintainer defaults
- Updated test_selection_logic.py: expanded selection/fallback coverage
- Updated test_pr4562_bugfixes.py: updated bugfix tests for new logic
- Updated smoke_test_llama_prebuilt.py: minor update
2026-04-01 06:06:29 -07:00