Notebook dependency coverage (base Dockerfile):
- Bake omegaconf, einx, librosa, decord, ftfy so the TTS/STT and vision
notebooks stop dying on a silent No module named X. Installed in the
notebook-deps layer (after the torch/vLLM resolve) with an assertion that
the resolve did not move torch 2.10.0 / numpy>=2.3 / numba>=0.65.
Image size (no functional change):
- Base: prune npp to the two libs torchcodec actually dlopens
(libnppicc + libnppc), drop link-time-only .a archives and the nvshmem
device bitcode. Headers (torch/include etc) are kept so causal-conv1d /
mamba-ssm still build at notebook time with --no-build-isolation.
- Studio: pin the Studio venv to Python 3.12 (matches base) so its
nvidia-*-cu12 wheels are byte-identical to the base venv's, then symlink
the heavy arch-independent CUDA libs (cudnn/cublas/nccl/...) into the base
venv copy. cuda_nvrtc and cuda_runtime are excluded (the arm64 nvrtc swap
mutates nvrtc in place). Also remove the build-only frontend node_modules
(runtime serves the committed dist). Studio image drops ~4.8GB.
Per-notebook transformers version, run notebooks unchanged:
- Bake coherent transformers sidecars (4.57.6 default + 5.3.0/5.5.0/5.10.2),
each transformers==X with its matched huggingface_hub/tokenizers/
safetensors installed --no-deps into its own dir. Companion versions are
resolved at build time so they satisfy each transformers' requirements.
- unsloth_nb_compat.py: pick the sidecar from the notebook's pin or the
model name and activate it (prepend to sys.path) before any ML import,
without touching the base cu128 torch/vLLM/unsloth stack.
- pip/uv shim on PATH: a notebook install cell becomes safe and idempotent
inside a kernel (keeps the baked stack, records the requested transformers
for its sidecar); passthrough to the real tool everywhere else.
- IPython startup hook for manual JupyterLab, and unsloth-run for the
headless driven path.
Continuation of the notebook-dep prebaking: the in-image notebook runner
neutralises pip cells, so declared deps must be prebaked. evaluate's WER
metric imports jiwer (Whisper), DeepSeek-R1 GRPO's reward uses langid,
some vision trust_remote_code files need easydict, and sentencepiece
tokenizer conversion needs protobuf. All pure-Python; torch pin intact.
* Studio: decide diffusion routing before the SWA resolver
Loading a DiffusionGemma GGUF could fail with "llama-server failed to start. Check that the GGUF file is valid" while llama-diffusion-cli ran the same model fine.
_read_gguf_metadata set self._is_diffusion after calling _resolve_swa_pattern, both inside one try/except. The resolver reaches into transformers/HF, which can raise for an architecture transformers does not know (diffusion-gemma); the shared except then swallowed it and left _is_diffusion False, so the model was routed to plain llama-server instead of the diffusion runner. It only reproduced where the SWA pattern was not already cached/inline.
Set _is_diffusion right after the KV parse loop (before the resolver) so a resolver error can no longer drop the routing, and skip the resolver for diffusion models, which do not use Studio's SWA pattern.
* [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>
UNSLOTH_STUDIO_LLAMA_ONLY=1, the llama.cpp-only fast path used by 'unsloth studio update', skipped the base-install block where STUDIO_HOME, _STUDIO_HOME_IS_CUSTOM, _STUDIO_OWNED_MARKER and _assert_studio_owned_or_absent are defined. setup.sh runs under 'set -euo pipefail', so the llama.cpp section aborted with '_STUDIO_HOME_IS_CUSTOM: unbound variable' before installing anything.
Hoist the studio-home and ownership resolution above the llama-only guard so both paths share it. Full installs are unchanged: the values are computed earlier but identically.
The update banner is driven by update_available from
/api/llama/update-status. Prebuilt (marker) installs decide this via
freshness.is_behind, which is mix-aware: a release at the same upstream
base build but with a new -mix-<sha> suffix counts as behind.
Source-build installs went through _source_build_status, which compared
only the numeric base build (installed_build < latest_build). When a new
prebuilt shared the installed upstream base (our usual mix re-tag at the
same base), it returned update_available=False and the banner never
showed. This is the common macOS case, where a failed prebuilt fetch
falls back to a source build that lacks the mix patches.
Make the source-build path mix-aware to match the marker path: same base
plus a -mix-<sha> tag now offers the update (and displays the mix tag),
a bare same-base rebuild does not, and the downgrade guard and unknown
-version fallback are unchanged.
* Studio: derive training output dir from model basename for local-drive models
A LoRA/QLoRA run started from a model loaded by absolute path (common when
models live on a non-system drive, e.g. G:\modelsAI\...\gemma-4-12B-it) seeded
the default output dir with that full path. resolve_output_dir then raised
"path escapes root ... is not under the studio outputs folder", so training
could not start from a model stored off the system drive.
Add default_run_dir_name(): Hugging Face repo ids keep their namespace
(org/model becomes org_model), while local paths collapse to their final
component so an absolute source path can no longer leak into the output dir.
Use it at the three worker derivation sites and add a regression test.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Studio: cap run dir name length and drop redundant resolve
Apply PR review feedback: length-cap the auto-generated output dir component so an unusually long model name stays under the filesystem name limit, and drop the redundant double resolve_output_dir at the embedding site so all three derivation sites match.
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
The TTS notebooks (Sesame CSM, Orpheus) read audio via soundfile, the
Whisper notebook computes WER via evaluate, and TrainingArguments
defaults report_to to tensorboard. These are declared by notebook pip
cells that the in-image notebook runner neutralises (deps are meant to be
prebaked), so without them those notebooks die on import. All are
pure-Python or self-contained wheels and never name torch, so the cu128
pin set is undisturbed.
The generated install command builds unsloth from git with
--no-build-isolation, so pip uses the environment's existing setuptools
rather than the pinned build-system requirement. On setuptools < 77 the
PEP 639 license string in pyproject.toml fails to validate and the
install aborts. Upgrade setuptools and wheel up front so the source
build always has PEP 639 support.
* Studio: UI polish for sidebar, menus, hub and toasts
- Pinned chats: add a Pinned section above Recents with a Pin/Unpin menu
action and a hover unpin button on each pinned row
- Sidebar: align the Recents and Pinned section labels with Train, and
switch the chat Archive icon to archive-03
- Dropdown menus: pull separators in to match item width, and even out the
projects menu padding and radius to match the composer menu
- Account menu: match the divider width to the row hover width
- Hub toolbar: equalize the format, capability and sort filter widths so
the search field has more room
- Model run bar: make the quant selector hover a full pill
- Tooltips: slightly larger corner radius
- Auth: reduce horizontal padding on the submit button
- Toasts: center the model load content so top and bottom padding match
* Studio: shrink pinned unpin icon, match projects menu to composer menu
* Studio: inset dropdown separators slightly from item width
* Studio: move archived chats from the sidebar into chat settings
Archive now shows a toast pointing to Settings. Archived chats are listed
and managed (open, unarchive, delete) under Settings, Chat, Data.
* Studio: make the archive toast open the archived chats list
* Studio: keep chats archived when opened from the archived list
* Chat settings: optional delete confirmation and menu cleanup
Add a "Confirm before deleting" toggle in Settings > Chat > Data. When
turned off, deleting a chat from the sidebar skips the confirm dialog
and removes it instantly. The preference is stored client-side.
Add a separator above Archive in the chat row menu so Archive and
Delete read as a group separate from Export.
Drop the duplicate divider above "Clear all chats". The section already
draws a divide-y separator between rows, so the destructive row no
longer adds its own top border.
* Hub, dialog and page polish
Hub catalog:
- Drop "inference" from the Hub subtitle.
- Make the HF token shield a circle in the header.
- Equal, narrower filter triggers that stay a fixed width; long labels
truncate with an ellipsis instead of clipping.
- Tighter gap between the label and the dropdown chevron so one more
character fits.
- Keep the toolbar on one row at desktop widths.
- Align the model list and row hover with the Discover tab, add right
side breathing room.
- Make the dark-mode scrollbar lighter so it is easier to see.
- Filled size and quant chips no longer draw a border.
- Column divider uses the sidebar border color.
- Hub card extends into the bottom space.
Filters and channels:
- Add an MLX format option; detect MLX repos by library or tag.
- "Fine-tune ready" now includes 16bit safetensors, not only bnb-4bit.
- Shorten the curated channel descriptions.
Shared UI:
- New standard chevron icons (down and submenu right) shared across the
Hub, dropdown menu, menubar, project switcher and model selector.
- Dialogs get more top and bottom padding and a larger heading.
- Guided Tour hides itself on pages with no tour.
Other pages:
- Export and Data Recipes use the same page padding as the Hub.
* Soften popup overlays and refine hub filters
Dialogs, alert dialogs and sheets now use a light blur with a slight
darken instead of a heavy black scrim, so popups stay legible without
dimming the whole screen. Edit System Prompt and Edit Chat Template
drop their custom overlays and share the same look.
Also limit the Fine-tune ready channel to checkpoints Unsloth can
actually fine-tune (drops fp8, nvfp4, w4a16 and similar), revert chip
borders so filled chips stay visible, nudge the Run icon to center it,
and shrink the submenu chevron a touch.
* Inline chat rename and review-feedback fixes
Rename a chat inline as a rounded pill on the row instead of opening a
dialog. Enter saves, Escape cancels, clicking away saves. Projects and
training runs keep the existing dialog.
Address review feedback:
- Route the no-confirm chat delete through the shared cleanup helper so
it gets the same error toast and pin removal as the confirmed path.
- Archived chats now confirm before deleting (respecting the setting)
and pass the active thread, so deleting the open chat resets to a new
chat instead of leaving a stale thread URL.
- Only offer the MLX format filter on Discover, since downloaded
inventory rows are never tagged mlx and would filter to empty.
- Tighten the non-finetunable name match so a dot-delimited token such
as .tflite is also excluded from the Fine-tune ready channel.
* Sidebar: borderless inline rename, no name flash, align titles
Rename now edits the title as plain highlighted text with no pill or
box. While the debounced sidebar refresh catches up after a rename the
row shows the new name optimistically, so the old name no longer flashes
back in. Recent chat titles now line up flush under the Recents label.
* Profile: preferred name and avatar shape options
Add a "What should Unsloth call you?" preferred name that drives the
new-chat greeting, falling back to the first name then the login id.
Add a toggle to show the avatar as a full circle or a rounded
rectangle, applied everywhere the avatar appears.
* Settings: show version block at the top of General
Surface the Unsloth and package versions at the top of General, not
just in About. Both tabs share one StudioVersionSection so the version
fetch and markup live in a single place.
* Profile: pick a sloth sticker as your avatar
Add a curated set of sloth stickers users can choose as a profile
picture, shown below the picture shape toggle in Settings > Profile.
- Only squarish, low-whitespace stickers are listed so each one fills
the avatar frame cleanly.
- Selecting one reuses the existing avatar persistence (localStorage),
so it sits alongside the photo upload with no backend changes.
- Image avatars now get a neutral background so transparent stickers
read cleanly in both shapes.
* Profile: keep transparent avatars transparent
A transparent upload is now kept as WebP and shrunk to fit instead of
falling back to JPEG, which has no alpha and was painting a background
behind the image. The image avatar is also explicitly transparent.
* Profile: fall back to PNG for transparent avatars on Safari
Safari cannot encode WebP via canvas, so a transparent upload was
throwing instead of saving. Fall back to PNG, which keeps alpha and
works in every browser, before giving up. Still never uses JPEG.
* Archived chats: reset nav when deleting the open compare too
Compare panes do not write the active thread to the store, so the pair
id only lives in the route search. Derive the open chat id from the
route (thread or compare) like the sidebar does, so deleting the open
archived compare resets to a new chat instead of a dead URL.
* Profile: trim three sloth stickers from the picker
Drop the duplicate computer sloth without text on the screen (keeping
the Local one), the nature-screen sloth, and the gift sloth.
* Chat: tighten model selector right padding
Use less right padding than left so the trailing chevron does not leave
a heavy gap on the right of the trigger.
* Address review feedback on profile, reset and rename
- Give each sloth avatar button an accessible name so screen readers
can tell the options apart.
- Reset all local preferences now clears the delete-confirmation pref,
so it returns to the safe confirm default.
- A no-op Enter in inline rename now closes the editor instead of
leaving the row stuck as an input with its blur suppressed.
* Chat: rebalance model selector padding and chevron
More left padding than right and pull the chevron close to the label so
the trigger reads balanced around the text instead of heavy on the right.
* Hub: shrink the external link dialog icon
Set the icon to size-6 so it sits proportionally in the media circle
instead of filling it.
* Hub: shrink the external link dialog media circle
Override the media circle to size-12 with a size-5 icon on this dialog
only, so it is more compact without changing the shared component.
* Installer: drop redundant -WindowStyle Hidden from the Windows launcher VBS
The desktop / Start Menu shortcut launches Studio through a generated
launch-studio.vbs that runs:
shell.Run "powershell ... -WindowStyle Hidden -File launch-studio.ps1", 0, False
The second argument to shell.Run is intWindowStyle 0 (hidden), so WScript
already launches the child windowless. The child -WindowStyle Hidden is
therefore redundant: dropping it keeps the launcher hidden and behaviour
identical, while removing the WScript-spawns-hidden-ExecutionPolicy-Bypass
PowerShell token combination that antivirus heuristics weight. That shape was
reported as a Kaspersky HEUR:Trojan.VBS.Agent false positive during install.
Adds tests/studio/install/test_launch_studio_launcher.py to stop the flag from
being reintroduced and to assert the launcher stays windowless via
shell.Run(cmd, 0, False).
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---------
Co-authored-by: Daniel Han <michaelhan2050@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* Fix llama.cpp prebuilt: skip the already-installed same-release fallback
install_prebuilt computes diffusion_visual_server_backfill_needed from the
newest candidate (plan.attempts[0]); when that is True it passed
existing_install_dir=None to validate_prebuilt_attempts, which disabled the
"existing install already matches this candidate" skip for the WHOLE plan. So
when the newest bundle failed validation the installer re-downloaded and
re-extracted an older fallback bundle that was already correctly installed.
Pass the real install dir always and gate the skip per-attempt: a matching
candidate is skipped unless that specific candidate still needs the
DiffusionGemma backfill re-extract.
Also make test_llama_cpp_search_roots_handles_studio_root_oserror read the full
_find_llama_server_binary / _kill_orphaned_servers method bodies instead of a
fixed 4000-char window. The except handler it asserts already exists, but the
function grew past the window so the guard silently failed; slicing to the next
sibling def keeps the check correct as the file grows.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---------
Co-authored-by: Daniel Han <michaelhan2050@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
flash-linear-attention's TileLang backend JIT-compiles CUDA kernels via
nvcc at runtime for gated-delta-rule models (Qwen3.5 family). The -base
image only ships runtime libraries, so Studio vision training of
unsloth/Qwen3.5-2B died on the first backward pass with
[Errno 2] No such file or directory: /usr/local/cuda/bin/nvcc.
Install cuda-nvcc and cuda-cudart-dev matching the image CUDA version
and assert nvcc is executable at build time. Found by driving a real
Qwen3.5-2B training run through the Studio UI in the image.
* Studio: keep llama-server discovery from crashing on an access-denied candidate
_find_llama_server_binary probed candidates with Path.is_file(), which raises
PermissionError (WinError 5) when a path exists but is momentarily inaccessible
(antivirus lock, an install replace in flight, an elevated-install ACL),
aborting model validation. Treat a denied-but-present path as the real binary
so discovery returns it; absent paths still skip.
* Retry a transiently locked binary instead of returning a denied path
Returning a still-denied path only moved the PermissionError to the next
is_file() (probe_server_capabilities). Retry briefly so a transient lock
clears and discovery returns an accessible path; on a persistent lock return
nothing rather than a path downstream cannot stat.
* Studio: do not fall back to another llama-server when a pinned one is locked
A denied LLAMA_SERVER_PATH made discovery skip the explicit pin and run a
lower-priority managed or PATH binary, so a load could silently use a stale or
incompatible server. Split the probe into a file/absent/denied status: when the
pinned path exists but stays access-denied, warn and stop rather than falling
back to a different executable.
* Studio: never downgrade past a denied pinned or managed llama-server
Extend the no-fallback rule beyond LLAMA_SERVER_PATH: a present-but-denied
UNSLOTH_LLAMA_CPP_PATH or managed ($STUDIO_HOME/llama.cpp, ~/.unsloth/llama.cpp)
binary now reports temporarily-unavailable instead of silently launching a
lower-priority legacy or PATH server. Shared _scan_pinned/_unavailable helpers;
legacy in-tree and PATH stay genuine fallbacks (a denied candidate there just
continues).
* Studio: let diffusion asset lookup use a locked llama-server path for its dir
DiffusionGemma does not run llama-server; _find_diffusion_assets only needs the
install dir to find the adjacent llama-diffusion-gemma-visual-server. The
no-fallback rule returning None on a transiently locked llama-server therefore
hid an available visual-server and raised 'runner not found'. Add an
include_denied option so diffusion lookup gets the locked path (its dir is all
it needs), while inference keeps the no-denied-path, no-downgrade behavior.
* Studio: report a locked llama-server as temporarily unavailable, not missing
When the pinned/managed binary stays access-denied through the retries, discovery
returns None and load_model raised 'binary not found', a terminal error that
points users at reinstalling rather than retrying a transient AV/install lock.
Reuse include_denied to detect the locked path and raise a distinct
temporarily-unavailable, retry message instead.
* Studio: GGUF preflight treats a locked llama-server as present
The pre-download preflight (and so /api/inference/validate) used the default
discovery, which returns None for a transiently access-denied binary, so it
raised 'binary not found' for a binary that merely needs the lock to clear. Use
include_denied so the existence check counts a locked binary as present; the
load itself still reports a still-locked binary as temporarily unavailable.
A locally built tag (test_locally.sh or docker build) is not on a
registry, so the pull phase reported hard failures on a machine that was
actually fine. Degrade to a warn when the image is present locally;
missing images still fail.
* Studio: backfill the DiffusionGemma visual-server on a tag-matching update
An install made before the visual-server entered the copy allowlist (#6254)
matches on tag yet lacks the binary, so the update skip never backfills it and
DiffusionGemma fails with "runner not found". Re-extract the bundle when a
matching install is missing it, gated to the fork bundles that ship it so
upstream installs never thrash and so it self-limits once the binary lands.
* Studio: make the visual-server backfill bypass the existing-install short-circuit
The tag-matching skip is checked twice: once in install_prebuilt and again
inside validate_prebuilt_attempts via existing_install_matches_choice, which
only compares metadata and primary executables, not the DiffusionGemma
visual-server. So a stale install missing that binary still raised
ExistingInstallSatisfied and skipped the reinstall. Pass existing_install_dir
as None when a backfill is needed so the bundle actually re-extracts.
Polishes the in-app update notification banners and the sidebar account menu.
Update banners (web and llama.cpp):
- Add a download icon next to the title, aligned with the title text.
- Even out the action row so the buttons share balanced padding and equal spacing.
- Let the row wrap on narrow viewports so Copy command is never clipped under overflow-hidden.
Sidebar:
- Use the setting-07 cog for the profile button and the Settings menu item.
- Use the sun-03 icon for the Light Mode menu item.
- Slightly enlarge the profile cog.
Frontend only. Build and typecheck pass.
* fix: extend llama.cpp first-token timeout
* fix: timeout label pluralization
* studio: distinguish llama stream timeout phases
* [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/adjust timeout handling for PR #5841
* Fix lint failure for PR #5841
* Fix/adjust stream timeout handling for PR #5841
* Fix/adjust first token timeout for PR #5841
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Fix/adjust passthrough timeouts for PR #5841
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Fix/adjust preheader stream cancellation for PR #5841
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Fix/adjust timeout PR diff for PR #5841
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Fix/adjust Python 3.9 stream iteration for PR #5841
* Fix first body timeout for PR #5841
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Fix first token timeout deadlines for PR #5841
---------
Co-authored-by: Roland Tannous <115670425+rolandtannous@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: wasimysaid <wasimysdev@gmail.com>
llama-quantize exits nonzero on --help/--version while still printing
usage, so a bare invocation fails the build even when the binary is
healthy. Grep for the usage banner instead; a loader failure prints
error while loading shared libraries and no usage text.
The build/bin hardlink mirror skipped symlinks, so the soname links
(libllama-common.so.0 and friends) never reached build/bin. Studio's
setup.sh relinks the root llama-quantize to build/bin/llama-quantize,
whose RUNPATH is $ORIGIN, so the loader failed with libllama-common.so.0
not found and GGUF export from Studio died with No working quantizer
found, then hit the interactive source-build prompt in a non-TTY export
subprocess (EOFError). Mirror same-directory soname symlinks into
build/bin and extend the bake sanity check to execute llama-quantize from
both the install root and build/bin. Dockerfile.studio now also runs the
studio-visible quantizer after install.sh so a regression fails the
image build instead of runtime exports.
The studio backend lazily imports unsloth_zoo submodules (export's
llama_cpp, hardware's vllm_utils, mlx, chat_templates). Each imports the
top-level unsloth_zoo, which raises 'Please install Unsloth via pip
install unsloth' unless UNSLOTH_IS_PRESENT is set (normally by import
unsloth). Only hardware.py set it per-site; the rest crash on a clean
install, and it surfaced on Windows. Set it once at backend startup (as
unsloth does on import) so every site, and the DiffusionGemma runner,
works across platforms. Follow-up to #6259 (which covered only the shim
subprocess env).
unsloth_zoo.device_type.get_device_type() deliberately returns cuda when
UNSLOTH_ALLOW_CPU=1 and no accelerator exists (CPU CI, Docker Desktop
without GPU passthrough), but the DEVICE_TYPE == cuda import paths probed
torch.cuda.get_device_capability() unconditionally and raised
RuntimeError: Found no NVIDIA driver. Guard the module level probes with
torch.cuda.is_available(); bf16 stays enabled in the degrade branch since
CPU bf16 kernels exist while fp16 ones largely do not. Healthy GPU hosts
take the original branches unchanged. Found by the cross platform CPU
mode validation of the Docker images.
list_gguf_variants() keys files by _extract_quant_label(), which only captured the base quant token. Repos that ship the same base quant at multiple bits-per-weight (e.g. byteshape/Qwen3.6-35B-A3B-MTP-GGUF with three IQ4_XS files at 3.53/3.97/4.19 bpw) collapsed into a single row and Studio summed their sizes (~48 GB).
Extend the regex to capture an optional trailing -<N>(.<N>)?bpw modifier so each flavor produces a unique label. Round-trips through _find_local_gguf_by_variant and _download_gguf since both sides use the same extractor.
Fixes#5728.
Co-authored-by: Etherll <61019402+Etherll@users.noreply.github.com>
* Fix model picker keyboard navigation
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Address model picker review feedback
* Fix model picker tab order with row actions
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Remove model picker keyboard contract test
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Etherll <61019402+Etherll@users.noreply.github.com>
A clean install could not run DiffusionGemma: the runner spawns
python -m unsloth_zoo.diffusion_studio.shim, and unsloth_zoo refuses to
import unless UNSLOTH_IS_PRESENT is set (normally by import unsloth). The
shim never imports unsloth, so the subprocess died with
'Please install Unsloth via pip install unsloth!' and the model load
failed with a 500. Set the flag in the runner child env, as unsloth does
on import.
When installed via 'curl -fsSL https://unsloth.ai/install.sh | sh', the
shell reads the script from the pipe on stdin. The optional 'start now'
step launches 'unsloth studio' in the foreground, and as a server it
inherits and drains the rest of the piped script from stdin. The shell
then hits EOF partway through the trailing if/case and dies with
'Syntax error: end of file unexpected (expecting fi)' (reported on WSL,
where /bin/sh is dash). Redirect the launch's stdin away from the pipe so
the script stays intact. The server does not read stdin and Ctrl+C still
works via the controlling terminal.
_detect_cuda_torch_index_url now respects the explicit family override
before probing nvidia-smi, matching install.sh get_torch_index_url and
install.ps1 Get-TorchIndexUrl. Without it, a GPU-less environment falls
back to cu126 wheels which lack sm_100/sm_120 kernels and break training
on Blackwell. ROCm repair path is intentionally unchanged.
Dockerfile.studio now fails the build if the Studio venv torch local
version tag does not match the pinned TORCH_FAMILY, so a studio ref whose
installer ignores the override can never ship a silently wrong image.
Metadata-only check so QEMU arm64 builds do not need to load torch.
* fix(studio): keep local GGUF vision on llama-server
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* fix(studio): lower local GGUF vision log level
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* fix(studio): find GGUF companions from variant dirs
* [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: Lee Jackson <130007945+Imagineer99@users.noreply.github.com>
Co-authored-by: imagineer99 <samleejackson0@gmail.com>
* Studio: account for mmproj VRAM in GGUF fit budget (#5825)
Vision GGUFs load the mmproj projector onto the GPU via --mmproj
alongside the weights, but the context auto-sizing / GPU-selection
budget sized off _get_gguf_size_bytes(model_path), which counts only
the weight file(s). The projector was never added, so the budget was
too optimistic: context got mis-estimated and tight vision loads
spilled to system RAM / OOM'd.
Resolve the launch projector once before GPU selection and fold its
size into the fit budget. The same resolved path feeds both the budget
and the --mmproj launch flag, so the two cannot disagree. The summary
log now reports the projector size separately, keeping "GGUF size"
accurate.
Adds _mmproj_vram_bytes() + unit tests (no GPU / network / subprocess).
* Studio: simplify mmproj summary-log concatenation (#5825)
Address review: the summary log mixed explicit `+` with implicit
f-string concatenation. Extract the optional projector fragment into
`mmproj_note` so the logger.info uses uniform implicit concatenation.
No behavioral change.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Studio: trim mmproj VRAM comments
---------
Co-authored-by: Lee Jackson <130007945+Imagineer99@users.noreply.github.com>
Co-authored-by: imagineer99 <samleejackson0@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
DiffusionGemma serves via the visual runner, which streams per-step
canvas frames so the answer resolves live in the bubble. The agentic
tool loop (generate_chat_completion_with_tools) does not forward those
frames, so whenever a tool pill (Search/Code) was on the live canvas
silently vanished while text still streamed. DiffusionGemma is not a
tool-calling target anyway, so report supports_tools=False for it: the
chat always takes the frame-forwarding path, and the Search/Code pills
disable themselves (a local model has no builtin web search either).
Also turn the artifacts canvas on by default for DiffusionGemma so a
full-HTML answer (e.g. a playable game) renders as an interactive
sandboxed card without the user flipping the global artifacts toggle.
* Studio: use a clearer check mark for the app-wide tick
Route every check mark through a shared tick-icon module with a plain
tick geometry, sized slightly larger than the stock icon so it reads
clearly in menus. No Pro icon assets are vendored.
* Studio: even out plus-menu hover padding on all sides
Trim the plus-menu side gutter to 9px so it matches the 0.5rem top/bottom
padding, and drop the container radius to 21px to keep corners concentric.
* Studio: bake the tick fallback stroke at 1.5 to match the icon set
---------
Co-authored-by: shimmyshimmer <info@unsloth.ai>
* Studio: make project sources work with RAG and polish project UI
Projects had a disabled Sources tab with an Add sources placeholder.
This wires it up end to end on top of the RAG engine:
- Add a project scope to the RAG store, ingestion and retrieval
- New endpoints: POST/GET /api/rag/projects/{id}/documents
- search_knowledge_base resolves kb, project and thread scopes; an
explicit KB stays exclusive, project and thread scopes combine
- Multi-scope search: FTS uses scope IN (...), vec0 KNN runs per
scope and merges by cosine score
- Lazy ALTER TABLE adds documents.project_id on existing databases
- Deleting a project also removes its indexed sources
- Sources tab now uploads with progress chips and drag and drop
- Chats inside a project auto-enable retrieval over project sources
when the project has indexed documents (cached probe, no Docs pill
needed); external providers still never receive rag_scope
UI polish:
- Rounder project cards with folder icon chip and softer shadow
- Project header icon in a rounded chip
- Chats/Sources pills and Add sources button without borders
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Studio: match Add sources button shadow to the chat composer in light mode
* Studio: round project switcher hover pill and pad the folder icon
* Studio: remove border from project sources box
* Studio: grey hover on project cards and menu, move search into header, widen page spacing
* Studio: shorten sources copy, white header pills with composer shadow, fixed-width search, hub-size page headings
* Studio: align project landing blocks to the composer width
* Studio: restore muted background and flat look on projects header controls
* Studio: darker grey hover on project cards in light mode
* Studio: soften project card hover grey
* Studio: keep project card menu button visible while its menu is open
* Studio: drop focus outlines and rings on buttons and clickable icons, keep input focus styles
* Studio: address review feedback on project sources
- Remove uploaded files from disk when a project is deleted, confined
to the uploads root
- 404 project uploads when the project does not exist, matching the KB
endpoint
- Guard lexical search against an empty scope list
- Re-invalidate the project sources probe after uploads and removals
settle so a chat sent mid-upload cannot cache a stale negative
- Keep keyboard focus rings: only mouse focus drops the Tailwind ring,
the browser default outline stays removed
* Studio: add a green New badge to the project Sources tab
* Studio: unify New pills, fully round with soft emerald fill and no border
* Studio: a touch more vertical padding on New pills
* Fix project RAG source edge cases for PR #6205
* Fix duplicate RAG upload cleanup for PR #6205
* [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: Lee Jackson <130007945+Imagineer99@users.noreply.github.com>
Co-authored-by: wasimysaid <wasimysdev@gmail.com>
The prebuilt bundles ship llama-diffusion-gemma-visual-server, but
runtime_patterns_for_choice pruned it, so a fresh install never placed
it next to llama-server. ensure_diffusion_visual_server then found no
standalone release asset and skipped it, leaving Studio unable to serve
DiffusionGemma GGUFs natively (it required DG_VISUAL_BIN or a source
build). Keep the binary in the runtime allowlist on Linux, macOS and
Windows so it lands in build/bin and is activated automatically.