Commit graph

233 commits

Author SHA1 Message Date
Daniel Han
f1293fe7d8 studio: respect existing CUDA_VISIBLE_DEVICES in GPU selection
If CUDA_VISIBLE_DEVICES is already set in the environment (e.g.,
by the user or a wrapper script), only consider those GPUs when
selecting devices for llama-server. nvidia-smi reports all physical
GPUs regardless of CUDA_VISIBLE_DEVICES, so we filter its output
to match the allowed set.

Without this, the GPU selector could pick a GPU outside the user's
allowed set, overriding their restriction.
2026-03-15 05:24:06 -07:00
pre-commit-ci[bot]
e885d7308e [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-03-15 05:24:06 -07:00
Daniel Han
12183e0656 studio: smart GPU allocation for GGUF inference
Automatically select the best GPU(s) for a GGUF model based on
file size and available VRAM, instead of relying on hardcoded
-ngl -1 or letting llama-server guess.

Logic:
1. Measure total GGUF file size (including split shards)
2. Query free memory per GPU via nvidia-smi
3. If the model fits in 70% of the most-free GPU's memory,
   pin to that single GPU (CUDA_VISIBLE_DEVICES=X, no --fit)
4. If it needs multiple GPUs, pick the N most-free GPUs
   (CUDA_VISIBLE_DEVICES=X,Y, no --fit)
5. If it's too large for all GPUs combined, omit
   CUDA_VISIBLE_DEVICES and use --fit on to let llama-server
   handle partial offloading

The 70% threshold accounts for KV cache and compute buffers
that sit on top of the model weights.

Removed the -ngl parameter (was hardcoded to -1). llama-server's
default of "auto" handles layer offloading correctly, especially
with --fit on for oversized models.

Tested on 8x B200:
  - 1B model (0.75 GB):  picks 1 GPU, no --fit
  - 27B model (17 GB):   picks 1 GPU, no --fit
  - 405B model (230 GB): picks 2 GPUs, no --fit
  - 2TB model:           all GPUs, --fit on
2026-03-15 05:24:06 -07:00
pre-commit-ci[bot]
7202f81985 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-03-15 05:24:06 -07:00
Daniel Han
80d84a5b5f studio: optimize llama-server flags for single-user studio
Refactor command building (deduplicate HF/local paths) and add
flags for better performance:

- --parallel 1: studio is single-user, so only 1 inference slot
  is needed. The previous auto-detect picked 4 slots, wasting
  VRAM on 3 unused KV caches.
- --flash-attn on: force flash attention for faster inference.
  Default is "auto" which may not always enable it.
- --fit on: auto-adjust parameters to fit in available device
  memory. Already the default but now explicit.

Also cleaned up the duplicated command building for HF vs local
mode into a single block.
2026-03-15 05:24:06 -07:00
Daniel Han
887e7a31c4 studio: don't cap max_tokens for GGUF inference
Remove the hard max_tokens=2048 default and le=4096 cap for GGUF
chat completions. When max_tokens is not set (None), the field is
omitted from the llama-server payload entirely, letting the model
generate until it produces an EOS token or hits the context limit.

This is critical for thinking/reasoning models (Qwen3.5, DeepSeek-R1,
etc.) where the thinking phase alone can consume 1000+ tokens before
the actual answer. With the previous 2048 default, simple questions
like "What is 2+2?" used all tokens on thinking and produced empty
visible responses.

Changes:
- llama_cpp.py: max_tokens default None, only include in payload
  when explicitly set
- models/inference.py: default None, remove le=4096 cap
- routes/inference.py: pass max_tokens directly, no "or 2048" fallback

llama-server handles omitted max_tokens gracefully (generates until
EOS or context limit). The context size (-c flag, default 4096) acts
as the hard upper bound.
2026-03-15 05:24:06 -07:00
Daniel Han
961720c1b1 studio: handle reasoning_content in GGUF streaming
llama-server sends thinking/reasoning tokens as "reasoning_content"
in the SSE delta (separate from "content"). The studio was only
reading delta.content, so all reasoning tokens from models like
Qwen3.5, Qwen3-Thinking, DeepSeek-R1, etc. were silently dropped.

This caused "replies with nothing" for thinking models: the model
would spend its entire token budget on reasoning, produce zero
content tokens, and the user would see an empty response.

Fix: read reasoning_content from the delta and wrap it in
<think>...</think> tags. The frontend already has full support
for these tags (parse-assistant-content.ts splits them into
reasoning parts, reasoning.tsx renders a collapsible "Thinking..."
indicator).

Verified with Qwen3.5-27B-GGUF (UD-Q4_K_XL):
  - Before: "What is 2+2?" -> empty response (all tokens in reasoning)
  - After: shows collapsible thinking + answer "4"
2026-03-15 05:24:06 -07:00
Roland Tannous
477e68675b
Fix: Compare Mode Deadlock, Cancel Event Poisoning & IPC Optimization (#4303)
* fix: resolve compare mode deadlock, cancel_event poisoning, and add dispatcher-based IPC optimization

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

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

* revert to 2048 tokens

* refactor: extract dispatcher timeout values into named constants

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

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

* fix: guard dispatcher shutdown against active compare mailboxes

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-15 16:11:44 +04:00
Wasim Yousef Said
e280b0bebc
miscallenous studio (#4293)
* miscallenous studio

* chore: upload dataset misc

* chore: redudancy studio cleanup

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

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

* fix: adress the pr comments

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

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

* fix: adress comments about recipes

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-15 14:42:11 +04:00
Daniel Han
88c7b08faa
fix: prevent ai-assist model config RCE via untrusted Hugging Face repos (#4274)
* fix: disable remote code loading for ai-assist model hint lookup

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

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

---------

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>
2026-03-13 19:29:11 +04:00
Roland Tannous
e539965740 fix error for chat template 2026-03-13 15:18:04 +00:00
Roland Tannous
47654cb91c Final cleanup 2026-03-12 18:28:04 +00:00
Roland Tannous
a2baf80511 Update license headers 2026-03-12 17:23:10 +00:00
Roland Tannous
6f77c63229 refactor: remove project_root passing, use self-resolved paths and ~/.unsloth/studio
- Workers now compute backend_path and venv_t5 locally via Path(__file__)
- Moved .venv_t5 to ~/.unsloth/studio/.venv_t5
- Added ensure_studio_directories() call on server startup
- Expanded CLI studio command into sub-app with setup subcommand
2026-03-11 20:32:18 +00:00
Shine1i
904e440513 feat(studio): studio storage roots path utilities 2026-03-11 20:19:52 +00:00
Roland Tannous
9dac1bedf9 Merge remote-tracking branch 'origin/nightly' into feature/llm-assist-detection 2026-03-11 16:23:09 +00:00
Roland Tannous
817f2e8dcc feat: integrate structlog, configure workers for prod logging, and migrate print statements 2026-03-11 12:33:16 +00:00
Manan17
5ca623a166 fixing gguf export for gemma with text 2026-03-11 00:58:22 +00:00
Manan17
bc5a72dd8c fix: local directory dataset loading 2026-03-10 21:29:51 +00:00
Manan Shah
f696ef81e8 Merge branch 'nightly' into feat/embedding-models 2026-03-10 14:16:05 -07:00
Roland Tannous
08d9c84f1f Merge pull request #359 from unslothai/fix/stream-manual-slice-dataset
fix: stream HF dataset when manual slice is specified
2026-03-11 01:13:51 +04:00
Manan17
3b0b002b34 fixing logging for each step 2026-03-10 20:32:40 +00:00
Roland Tannous
21ef22a9ff fix: skip streaming when dataset_slice_start > dataset_slice_end
Prevents training on the wrong row range when start exceeds end by
falling back to full download where existing clamping handles it.
2026-03-10 20:21:34 +00:00
Roland Tannous
226f251589 fix: guard against negative dataset_slice_end before streaming
Fall back to full download when dataset_slice_end is negative,
avoiding an empty stream.take(0) that would produce a broken dataset.
2026-03-10 20:12:42 +00:00
Roland Tannous
970a029108 fix: stream HF dataset when manual slice is specified
Instead of downloading the full dataset and then slicing, use
streaming mode to only fetch the rows needed (up to slice_end + 1)
when a manual dataset slice is configured.
2026-03-10 19:50:53 +00:00
Roland Tannous
c986174c56 fix: preserve zero-valued dataset slice boundaries in embedding worker
Use explicit None checks instead of falsy `or` for slice_start and
slice_end so that a valid slice_end=0 is not replaced with the full
dataset length.
2026-03-10 19:33:10 +00:00
Roland Tannous
b84202e8db fix: restrict shard siblings to exact basename and total count
startswith(prefix) could match unrelated split variants whose names
extend the selected file's prefix (e.g. model-Q8_0-v2-00001-of-...).
Now builds an exact regex from the chosen file's base prefix and shard
total so only true siblings are downloaded.
2026-03-10 19:28:26 +00:00
Roland Tannous
b8678a3ed6 fix: pass hf_token for gated embedding models and key cache by token
- Forward hf_token to FastSentenceTransformer.from_pretrained() so
  private/gated embedding repos authenticate correctly
- Key _embedding_detection_cache by (model_name, hf_token) tuple so
  unauthenticated lookups don't shadow subsequent authenticated ones
2026-03-10 19:20:12 +00:00
Roland Tannous
d635846b8d fix: use exact variant matching and shard-prefix discovery for split GGUFs
Substring matching (e.g. "Q8_0" in filename) could match superset
variants like "IQ8_0", causing wrong quantizations to be downloaded.
Now uses word-boundary regex for variant matching and discovers split
shards by shared filename prefix rather than treating all variant
matches as shards.
2026-03-10 19:13:03 +00:00
Roland Tannous
d6ae910edc fix: propagate is_embedding into worker subprocess config
start_training() cherry-picks kwargs into a config dict but was missing
is_embedding, so config.get("is_embedding", False) in worker.py always
returned False and embedding training never ran.
2026-03-10 19:05:47 +00:00
Roland Tannous
defa761fb2 fix: download all GGUF shards for split models (e.g. 7B Q8_0)
LlamaCppBackend.load_model() only downloaded the first matching GGUF
file. For split models (e.g. 7B Q8_0 with 3 shards), llama-server
needs all shards present. Now collects and downloads all matching files.
2026-03-10 19:04:10 +00:00
Roland Tannous
5a086353ab feat: add embedding model training support
Add end-to-end embedding/sentence-transformer training pipeline using
FastSentenceTransformer, SentenceTransformerTrainer, and
MultipleNegativesRankingLoss with BatchSamplers.NO_DUPLICATES.

Backend:
- Add is_embedding_model() detection via HF tags + pipeline_tag
- Add /check-embedding/ API route and EmbeddingCheckResponse
- Extend derive_model_type() to return "embeddings"
- Add _run_embedding_training() in worker.py with progress callbacks,
  stop handling, LoRA (task_type=FEATURE_EXTRACTION), and model saving
- Add is_embedding field to TrainingStartRequest and ModelDetails
- Add YAML configs for 5 models: all-MiniLM-L6-v2, bge-m3,
  embeddinggemma-300m, gte-modernbert-base, Qwen3-Embedding-0.6B

Frontend:
- Wire isEmbeddingModel flag through store, API types, and mappers
- Force packing=false, train_on_completions=false, warmup_ratio=0.03
- Hide packing and train_on_completions checkboxes for embedding models
- Auto-set modelType to "embeddings" from backend model_type response
2026-03-10 18:10:09 +00:00
Roland Tannous
c2dd0f4cf1 fix: download all GGUF shards for split models (e.g. 7B Q8_0)
LlamaCppBackend.load_model() and precache_helper_gguf() only downloaded
the first matching GGUF file. For split models (e.g. 7B Q8_0 with 3
shards), llama-server needs all shards present. Now collects and
downloads all matching files.
2026-03-10 15:08:20 +00:00
Roland Tannous
7f1fd28acd debug: decode first sample after train_on_completions masking 2026-03-10 14:08:14 +00:00
Roland Tannous
49b29fb1fd debug: fix dataset access - result is a dict, use dataset['dataset'] 2026-03-10 13:19:31 +00:00
Roland Tannous
21cd9f9d02 debug: improve sample preview with type info and traceback 2026-03-10 12:56:24 +00:00
Roland Tannous
a36c073770 debug: switch to print() for subprocess visibility 2026-03-10 12:49:01 +00:00
Roland Tannous
97612af993 debug: add temporary log statements for dataset preview and VLM instruction 2026-03-10 12:35:55 +00:00
Roland Tannous
a26a5cc6be Merge pull request #352 from unslothai/fix/cancel-training
Fix/cancel training
2026-03-10 14:38:30 +04:00
Roland Tannous
f7ca361c5c feat: add LLM-assisted dataset detection using ephemeral GGUF helper
Uses Qwen2.5-3B-Instruct Q8_0 via LlamaCppBackend to complement
heuristic-based dataset detection when heuristics are uncertain.

- New llm_assist.py: VLM instruction generation, column classification,
  and user-friendly warning generation for dataset issues
- Pre-cache helper GGUF on FastAPI startup (background thread)
- Reorder training pipeline: dataset processing runs BEFORE model load
  to avoid VRAM contention (detect → dataset → model → train)
- Add pre_detect_and_load_tokenizer() for lightweight detection
- LLM warnings on VLM conversion failures (broken URLs, missing images)
- LLM column classification fallback when heuristics return unknown
- Graceful degradation: all paths unchanged when helper unavailable
2026-03-10 09:20:45 +00:00
Manan17
fd7ca8bda8 distinguish cancel and stop for force terminate 2026-03-10 02:35:32 +00:00
Manan17
9be55f0c1b fixing cancel training 2026-03-10 02:20:56 +00:00
Roland Tannous
daa50d0756 Revert "Merge pull request #347 from unslothai/feature/studio-storage-roots"
This reverts commit 6b43e33ff1, reversing
changes made to 9edadaf21f.
2026-03-10 01:52:47 +00:00
Shine1i
5301514775 feat(studio): studio storage roots path utilities 2026-03-09 23:48:31 +00:00
Roland Tannous
d882678fe4 Add AGPL-3.0 SPDX headers to all source files 2026-03-09 20:17:45 +00:00
Shine1i
2ccb75f2b7 Merge remote-tracking branch 'origin/nightly' into feature/fixes-client 2026-03-09 19:07:42 +01:00
Roland Tannous
b6811bc5c4 Merge pull request #342 from unslothai/local-dataset
dataset upload
2026-03-09 21:22:23 +04:00
Roland Tannous
41351e1566 fix: split dataset 80/20 when eval split matches train split 2026-03-09 16:36:44 +00:00
Shine1i
542d9126cc chore(data-recipe): bump data-designer to 0.5.2 and pin duckdb<1.5 2026-03-09 17:27:02 +01:00
Roland Tannous
2a11e79b8b fix: restore eval_enabled early signal for subprocess training 2026-03-09 15:35:49 +00:00