Commit graph

519 commits

Author SHA1 Message Date
Lee Jackson
9232126734
fix(studio): use explicit Cancel for model load toast (#4377) 2026-03-17 22:39:51 -07:00
Coenraad Loubser
ca87669937
Unused return value causes build failures (#4385)
* Unused return value causes build failures

* Update toast messages to include model loading status
2026-03-17 20:57:27 -07:00
Wasim Yousef Said
03736a82ba
Relax frontend unused local check (#4388) 2026-03-17 16:04:11 -07:00
Manan Shah
93ab09d195
[Feature] compare for 2 diff models (#4356)
* compare for 2 diff models

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

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

* resolving gemini comments

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

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

* fix(studio): refine model-load toast stop action and compare selector sizing (#4369)

Co-authored-by: imagineer99 <samleejackson0@gmail.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
Co-authored-by: imagineer99 <samleejackson0@gmail.com>
2026-03-17 22:58:34 +04:00
Daniel Han
0acd1c7eec
studio: improve onboarding UX, tooltips, and training defaults (#4355)
* studio: improve onboarding UX, tooltips, and training defaults

- Change splash text to "Train and run LLMs locally"
- Add "Chat Only" card with BubbleChatIcon to skip directly to chat
- Add Skip/Skip to Chat buttons in sidebar and footer
- Back button on step 1 returns to splash screen instead of being disabled
- Change "Watch video guide" to "Get started with our guide" with new URL
- Update intro text to mention all model types + chat
- Make all tooltips clickable (in addition to hover) via React context
- Strip surrounding quotes from pasted HF tokens
- Rename "Eval Split" to "Evaluation Split"
- Add SparklesIcon to "Auto Detect" format option
- Change step 4 heading to "Choose your training parameters"
- Default max_steps to 60
- Learning rate displayed in scientific notation with +/- stepper
- Context length options capped by model's max_position_embeddings (via AutoConfig)
- Fix "QLORA"/"LORA" to "QLoRA"/"LoRA" in summary step
- Backend: add max_position_embeddings to model config endpoint

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

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

* compare for 2 diff models

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

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

* resolving gemini comments

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

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

* studio: disable thinking for Qwen3.5 <9B and always for AI Assist

- Change Qwen3.5 thinking threshold from <=2B to <9B (0.8B, 2B, 4B
  all disable thinking by default; 9B+ enables it)
- Always pass enable_thinking=False in AI Assist helper calls
  (_run_with_helper and _generate_with_backend) regardless of chat
  thinking settings

* studio: address PR review comments

- Extract _get_max_position_embeddings helper to DRY config extraction
- Fix "Skip to Chat" to navigate to /chat on step 1 (was /studio)

* fix: comment out debug print statements

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

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

* studio: skip Shiki highlighting for incomplete SVG code fences

While streaming SVG content, the syntax highlighter (Shiki) re-parses
the entire growing SVG on every token, blocking the main thread and
freezing the code area until the fence closes. Show a plain-text
preview for incomplete SVG fences instead, similar to how Mermaid
diagrams show a placeholder while streaming.

* studio: fix default top_k from 50/40 to 20 for chat inference

Per Qwen3.5 docs (unsloth.ai/docs/models/qwen3.5), top_k should be 20
for both thinking and non-thinking modes. The model-specific config in
inference_defaults.json already had top_k=20 for Qwen3.5, but the
generic fallback defaults were wrong:
- Frontend DEFAULT_INFERENCE_PARAMS.topK: 50 -> 20
- Backend generate_chat_completion top_k: 40 -> 20
- Backend generate_chat_completion_with_tools top_k: 40 -> 20
- Frontend title generation top_k: 40 -> 20

* studio: set universal inference defaults for unknown models

Default params for any model without specific config:
  temperature=0.6, top_p=0.95, top_k=20, min_p=0.01,
  presence_penalty=0.0, repetition_penalty=1.0

Models with entries in inference_defaults.json (Qwen3.5, Gemma-3,
Llama, etc.) override these with their recommended values.

Updated in: frontend DEFAULT_INFERENCE_PARAMS, backend Pydantic
request models, and backend generate_chat_completion defaults.

* studio: only trust_remote_code for unsloth/ models in AutoConfig

Only set trust_remote_code=True when the model name starts with
"unsloth/". All other models default to False for safety.

* studio: move Generating spinner above the composer

The "Generating" spinner was below the send message bar, causing
the bar to jump up and down. Move it above the composer in both
the regular thread view and the welcome/empty view.

* studio: adjust toast close button position away from edge

Move the X close button on toasts (like "Starting model...") from
top-1.5 to top-3 and add right-3, giving more breathing room from
the top-right corner.

* studio: make Think button smaller with tighter icon-text gap

Reduce gap from 1.5 to 0.5, padding from px-2.5/py-1 to px-2/py-0.5,
and icon from size-3.5 to size-3.

* studio: multiple onboarding and chat UX improvements

- Move Generating spinner above composer (fixes jumping send bar)
- Make Think button smaller with tighter icon-text gap
- Chat card now inside grid (same size as Audio/Embeddings cards)
- Rename "Chat Only" to "Chat"
- Chat card requires Continue to proceed (no auto-advance)
- Continue on Chat selection skips onboarding and goes to /chat
- Tooltip (i) click on Chat card doesn't trigger navigation
- Step 1 footer Back button goes back to splash (label is "Back")
- Splash "Skip Onboarding" renamed to "Skip to Chat", navigates to /chat
- Toast close button moved away from edge

* studio: align Skip to Chat button, add Skip to footer

- Sidebar "Skip to Chat" now uses primary (green) Button style with
  arrow icon, full width, aligned like step items. Shows on all steps.
- Footer: added "Skip" outline button next to Continue that goes
  directly to /studio with progress saved (markOnboardingDone)

* studio: change default max steps from 30 to 60 in toggle hook

The DEFAULT_MAX_STEPS in use-max-steps-epochs-toggle.ts was still 30,
used as fallback when toggling from epochs back to max steps.

* studio: extend context length options to 262K

CONTEXT_LENGTHS now includes 65536, 131072, 262144 in addition to
the existing 512-32768 range. The onboarding step filters these by
the model's max_position_embeddings (e.g. Nemotron-3-Nano-4B has
262144), showing powers of 2 up to the model's maximum.

* studio: auto-select LoRA vs QLoRA based on model size and GPU memory

After selecting a model in onboarding, detect the total model weight
file size from HF Hub (safetensors/bin files). Then estimate memory
needed: model_size_gb * 1.5 * context_scale, where context_scale is:
  - <=8192 tokens: 1.0x
  - >8192 tokens: 1.7x
  - >=16384 tokens: 2.0x
  - >=32768 tokens: 4.0x

If the estimate fits in free GPU VRAM, default to LoRA (16-bit).
Otherwise default to QLoRA (4-bit).

Backend changes:
- Add model_size_bytes to ModelDetails (models.py)
- Add _get_model_size_bytes() using HfApi.repo_info (routes/models.py)
- Add vram_free_gb to get_gpu_summary (hardware.py)

Frontend changes:
- Add autoSelectTrainingMethod() in training-config-store.ts
- Called after model defaults are loaded
- Add model_size_bytes to ModelConfigResponse type
- Add vramFreeGb to HardwareInfo hook

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

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

* studio: rename "Importing ML libraries..." to "Importing Unsloth..."

* studio: show model/dataset in training status, fix LoRA/QLoRA casing

- Training status now shows 'Training "model_name"' and 'Dataset = ...'
  instead of generic "Starting training..."
- Fix Studio progress section to show QLoRA/LoRA instead of QLORA/LORA

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

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

* studio: rename 'Skip to Chat' to 'Skip Onboarding' on splash screen

* studio: add presence_penalty support for chat inference

Add presence_penalty as a parameter across the full stack:
- Backend: llama_cpp.py generate_chat_completion/with_tools, Pydantic
  models (inference.py), routes/inference.py pass-through
- Frontend: InferenceParams type, DEFAULT_INFERENCE_PARAMS (0.0),
  chat-adapter.ts payload, chat-settings-sheet.tsx slider (0-2),
  model defaults loading from inference_defaults.json
- Set Qwen3.5 default presence_penalty to 1.5 per official docs
- Default for unknown models is 0.0 (off)

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

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

* studio: fix Chat card deselecting Text and aligning with other cards

* studio: fix presence_penalty not loading from inference defaults

The inference_config.py load_inference_config() was not including
presence_penalty in the returned config dict, so the Qwen3.5
default of 1.5 from inference_defaults.json never reached the
frontend. Added it to the config builder.

* studio: add delete button for cached models in model selector

Add trash icon on each downloaded model row (GGUF and safetensors) with
confirmation dialog. Backend DELETE /api/models/delete-cached endpoint
uses huggingface_hub scan_cache_dir + delete_revisions to cleanly remove
cached repos, refusing if the model is currently loaded.

* studio: restore inference defaults, reasoning, and tools on page refresh

On page refresh with a model already loaded, the frontend was not
re-applying model-specific inference defaults (presence_penalty,
temperature, etc.) or restoring reasoning/tools support flags.

Backend: Add inference config, supports_reasoning, supports_tools,
and context_length to InferenceStatusResponse.

Frontend: In the refresh callback, when an active model is detected,
apply mergeRecommendedInference and restore reasoning/tools flags
with proper Qwen3.5 size-based defaults.

* studio: fix delete dialog closing before async completes

Prevent AlertDialogAction's default close behavior with
e.preventDefault() so the dialog stays open during deletion.
Also block onOpenChange dismiss while deleting is in progress.

* fix: add Dict and Any imports to inference models

* studio: fix Qwen3.5 reasoning threshold in frontend load path

The frontend loadModel handler had the old threshold (<=2) for
disabling reasoning on small Qwen3.5 models. Changed to <9 to
match the backend. This was causing 4B to not properly disable
thinking by default when auto-loaded.

* studio: move GGUF delete to per-variant level

For GGUF repos, the trash icon now appears on each downloaded variant
row inside the quantization expander instead of on the repo-level row.
Backend accepts optional variant param to delete specific GGUF files
(blob + symlink) rather than the entire repo cache.

* studio: restore ggufContextLength on page refresh

The Max Tokens slider was capped at 32768 on page refresh because
ggufContextLength was not restored from the status response.
Now set it from statusRes.context_length on reconnect.

* fix: remove <think> from Qwen3.5 response template marker

The train-on-responses-only feature uses template markers to find
where the assistant response starts. The Qwen3.5 response marker
included '<think>\n' which is only present when thinking mode is
enabled. With thinking disabled (default for <9B), the marker
never matched, causing 100% of samples to be dropped.

Changed response marker from '<|im_start|>assistant\n<think>\n'
to '<|im_start|>assistant\n' which works regardless of thinking mode.

* studio: fix sloth ASCII art alignment in training overlay

* fix: correct sloth ASCII art alignment to match Unsloth banner

* studio: add Python and terminal tool calling to chat

Register python and terminal tools alongside web search. Python
executor validates imports (stdlib only) via unsloth_zoo
rl_environments, runs code in a subprocess sandbox with 5-min
timeout and cancel support. Terminal executor blocks dangerous
commands (rm, sudo, etc.) and runs in a temp directory.

Update llama_cpp tool loop to show tool-specific status messages
and pass cancel_event through to executors. Rename composer
toggle from "Search" to "Tools" and show TerminalIcon for
execution status pills.

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

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

* studio: fix Nemotron/transformers 5.x support, onboarding navigation, port binding

Backend:
- Dynamic transformers 5.x detection via tokenizer_config.json fetch
  (checks for TokenizersBackend class, cached per-model)
- Bump transformers 5.x version from 5.2.0 to 5.3.0 across all workers,
  setup scripts (setup.sh, setup.ps1)
- Auto-enable trust_remote_code for unsloth/* models needing transformers 5.x
  (workaround for NemotronH config parsing bug in transformers)
- Auto-install mamba-ssm/causal-conv1d for SSM models (NemotronH, Falcon-H1)
  with --no-build-isolation --no-deps to avoid torch version conflicts
- Add SO_REUSEADDR to port check in run.py (fixes Colab proxy stale connection
  falsely reporting port as in-use)

Frontend:
- Fix "Skip to Chat" navigation: use window.location.href instead of React
  Router navigate() to bypass useEffect redirect race
- Fix "Skip Onboarding" on splash: navigates to /studio (not /chat)
- Fix onboarding guard: only check isOnboardingDone() on initial mount
- Fix Chat card on step 1: add sr-only spacer for consistent alignment
- Fix Chat+Text both selected: clear RadioGroup value when Chat is selected

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

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

* studio: split tools toggle into Search and Code buttons

Replace the single "Tools" toggle with two independent toggles:
- "Search" (globe icon) enables web search only
- "Code" (terminal icon) enables Python and terminal execution

Add enabled_tools list field to the inference payload so the
backend only registers the tools the user has toggled on. Both
toggles appear in the main composer and the compare composer.

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

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

* studio: fix tool calling import validation and error logging

Replace unsloth_zoo-dependent import checker with a standalone
ast-based validator using sys.stdlib_module_names. This properly
blocks non-stdlib imports (numpy, requests, etc.) and returns a
clear error message to the model so it can rewrite using only
stdlib.

Add full traceback to tool streaming error logs for debugging.

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

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

* fix: parse gpt-oss harmony channels for clean safetensors chat output

gpt-oss models emit multi-channel output via harmony protocol tokens
(<|channel|>analysis<|message|>... and <|channel|>final<|message|>...).
TextIteratorStreamer with skip_special_tokens=True strips the special
tokens but leaves channel names concatenated with content, producing
garbled output like "analysisWe need to...assistantfinalHello!".

Add HarmonyTextStreamer that decodes with skip_special_tokens=False,
parses harmony markup via regex, and emits <think>analysis</think>
for the analysis channel and plain text for the final channel --
reusing the existing frontend reasoning UI.

Also expose supports_reasoning=True for non-GGUF gpt-oss models in
the /status endpoint so the frontend enables the Think toggle.

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

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

* studio: use unsloth_zoo for Python sandbox validation

Set UNSLOTH_IS_PRESENT=1 and import check_python_modules and
check_signal_escape_patterns directly from unsloth_zoo instead
of a standalone fallback. This gives us the full Unsloth
validation including stdlib-only import checks and signal/timeout
escape pattern detection.

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

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

* studio: allow all imports in Python tool sandbox

Remove stdlib-only import restriction. Keep signal escape
pattern detection via unsloth_zoo for safety.

* studio: fix ReadTimeout on tool streaming final pass

The 0.5s read timeout used for cancel-checking during streaming
also fires when waiting for the first response from llama-server
(e.g. reasoning model thinking for 15+ seconds). Add
_stream_with_retry() context manager that retries on ReadTimeout
while checking cancel_event, so the model has unlimited time to
think before producing the first token. Applied to both the
regular streaming path and the tool-calling final pass.

* fix: rewrite HarmonyTextStreamer with stateful incremental parsing

The delta-on-transformed approach had two critical bugs:

1. Before the full <|channel|>X<|message|> pattern was complete, the
   strip-tokens fallback emitted "analysis" as plain text. Then when
   the regex matched, _transform returned a completely different format
   (<think>...</think>) and the delta was computed against the wrong
   base string, producing fragments like "think>", "nk>", ">".

2. Even with full matches, the closing </think> tag shifted position
   as content grew, so text[prev_len:] produced garbled deltas.

Replace with stateful incremental parsing that:
- Buffers until a complete channel+message pair is seen
- Emits <think> once when analysis channel first appears
- Streams analysis content deltas (computed on channel content directly)
- Emits </think> once when final channel first appears
- Streams final content deltas
- Closes open think tags in end()

Also skip the generic all_special_tokens stripping in
_clean_generated_text for gpt-oss since HarmonyTextStreamer already
produces clean output and the generic stripping was mangling <think>
tags.

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

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

* fix: strip all <|...|> tokens in gpt-oss cleanup, not just harmony subset

The gpt-oss tokenizer has added tokens like <|return|> (id=200002) that
are not part of the harmony channel protocol but can leak into output.
The previous regex only stripped channel|message|start|end tokens.

Broaden the _clean_generated_text regex for gpt-oss to <\|[a-z_]+\|>
which catches all pipe-delimited tokens (return, constrain, reserved,
etc.) without matching <think>/<\/think> tags.

Verified: gpt-oss all_special_tokens are only <|return|>,
<|reserved_200017|>, <|startoftext|> -- none overlap with <think>.
The harmony tokens (channel, message, start, end) are added_tokens
but not in all_special_tokens.

* fix: hide config-only model repos from cached models list

Repos that only have metadata/config files cached (no .safetensors or
.bin weight files) were showing up in the Downloaded list with tiny
sizes like "1.8 KB" or "24 KB". These are just leftover config
snapshots from architecture checks, not usable models.

Filter the cached-models endpoint to only include repos that contain
actual model weight files (.safetensors or .bin).

* studio: fix toast description text contrast in dark mode

Add explicit !text-muted-foreground to toast description classNames
so secondary text (e.g. "Releases VRAM and resets inference state.")
is readable in dark mode.

* studio: fix Chat card icon alignment with size-4 spacer

Replace sr-only span (takes no space) with a size-4 shrink-0 div
matching the RadioGroupItem dimensions in other cards, so the Chat
icon aligns vertically with Text/Audio/Vision/Embeddings icons.

---------

Co-authored-by: workspace <user@workspace.local>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Manan17 <shahmanan170602@gmail.com>
Co-authored-by: Roland Tannous <rolandtannous@gravityq.ai>
2026-03-17 07:46:07 -07:00
Daniel Han
37fe04f7bf
studio: add SVG preview, fix streaming bug and model selector state (#4354)
- Add SVG preview rendering below code blocks using safe data URI
  in <img> tag. Includes sanitization to block script/event handlers.
- Fix GGUF streaming crash: cache response.iter_text() iterator
  instead of creating a new one on every loop iteration.
- Fix model selector showing "Select model..." after auto-load by
  re-reading store state after setCheckpoint before setParams.
- Remove unused warmupToastShown variable (TS6133 build error).
- Change default suggestion to "Draw an SVG of a cute sloth".
2026-03-17 02:34:05 -07:00
Roland Tannous
df01a139a0
fix: remove unused warmupToastShown variable to fix TS6133 build error (#4353) 2026-03-17 02:03:15 -07:00
Daniel Han
b437c9a36d
studio: update Creative/Precise presets, show "Off" for disabled samplers (#4350)
Creative: temperature=1.5, min_p=0.1, top_p=Off (1.0), top_k=Off (0)
Precise: temperature=0.1, top_p=0.95, top_k=80, min_p=0.01

Also show "Off" in the slider label for top_p=1.0, top_k=0, and
repetition_penalty=1.0 since those values disable their respective
samplers. Changed top_k slider min from -1 to 0.
2026-03-17 01:32:18 -07:00
Daniel Han
ee6f057cc2
studio: show "Off" for repetition penalty = 1 (#4349) 2026-03-17 01:28:33 -07:00
Daniel Han
c00a993a68
studio: fix stale GGUF metadata, update helper model, auth improvements (#4346)
* studio: switch helper model to Qwen3.5-4B-GGUF

Replace Qwen3-4B-Instruct-2507-GGUF with Qwen3.5-4B-GGUF as the
default helper model for LLM-assisted dataset detection. Same
UD-Q4_K_XL variant.

* studio: fix stale GGUF metadata when switching models (#4347)

Reset _supports_reasoning, _supports_tools, _context_length, and
_chat_template at the start of _read_gguf_metadata() to prevent
stale settings from a previous model leaking into the next load.

Co-authored-by: Daniel Han <daniel@unsloth.ai>

* studio: change login error to "Incorrect password", add reset-password CLI

- Login error now says "Incorrect password" instead of the generic
  "Incorrect username or password" since Studio only has one account.
- Add `unsloth studio reset-password` command that deletes the auth
  database so a fresh admin account with a new random password is
  created on the next server start.

* studio: include reset command in login error message

* studio: change password setup subtitle wording
2026-03-17 01:22:08 -07:00
Daniel Han
eeffa4c065
studio: web search, KV cache dtype, training progress, inference fixes
## Summary
- Add web search tool calling for GGUF models (Search toggle, DuckDuckGo via ddgs)
- Add KV cache dtype dropdown (f16/bf16/q8_0/q5_1/q4_1) in Chat Settings
- Fix Qwen3/3.5 inference defaults per official docs (thinking on/off params)
- Enable reasoning by default for Qwen3.5 4B and 9B
- Replace "Generating" toast with inline spinner
- Fix stop button via asyncio.to_thread (event loop no longer blocked)
- Fix CUDA 12 compat lib paths for llama-server on CUDA 13 systems
- Fix auto-load model name not appearing in selector
- Training progress messages + dataset_num_proc fix

Integrated PRs:
- #4327 (imagineer99): BETA badge alignment (already in tree)
- #4340 (Manan Shah): prioritize training models in model selection
- #4344 (Roland Tannous): setup.sh macOS python version compatibility
- #4345 (Manan Shah): revamp model+dataset checking logic
2026-03-17 00:30:01 -07:00
Leo Borcherding
df98569f12
studio: improve Colab notebook, redesign ready popup, and clean up install output (#4339)
* Removing .precommit config

* edited colab comments

* studio: update Unsloth_Studio_Colab.ipynb

* studio: update Unsloth_Studio_Colab.ipynb

* studio: add Colab T4 GPU metadata to force T4 instance

* style: update colab popup to black/white theme with gem icon and play button

* feat: center landscape image in colab notebook

* style: shrink popup to fit content, truncate URL display

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

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

* feat: center landscape image in colab notebook

* feat: use GitHub raw URL for studio landscape image in notebook

* chore: update colab notebook

* feat: add studio landscape colab display image and update notebook

* feat: update notebook with studio landscape image

* style: remove colors, add progress bar, add VERBOSE flag to install output

* docs: add comments explaining VERBOSE flag and progress bar

* chore: update colab notebook

* fix: define VERBOSE, _STEP, _TOTAL at module level to fix NameError

---------

Co-authored-by: LeoBorcherding <LeoBorcherding@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-16 21:39:25 -07:00
Daniel Han
44dcf30b9b
studio: per-model inference defaults, GGUF slider fix, reasoning toggle (#4325)
* studio: extract param count from model name as fallback

When HuggingFace API doesn't return totalParams for a model,
extract the param count from the model name (e.g. "Qwen3-0.6B"
-> "0.6B", "Llama-3.2-1B-Instruct" -> "1B"). Applied to both
the recommended list and HF search results.

* studio: read GGUF context_length via fast header parser, set max tokens

- Fast GGUF metadata reader (~30-55ms) parses only KV header, skips
  tensor data and large arrays (tokenizer vocab etc)
- Extracts context_length and chat_template from GGUF metadata
- Returns context_length in LoadResponse for frontend to use
- Frontend sets maxTokens to actual context_length for GGUFs (e.g.
  262144 for Qwen3.5-9B, 131072 for Qwen2.5-7B)
- Max Tokens slider shows "Max" and is locked for GGUFs
- Auto-load path also uses actual context_length from load response
- Toast auto-dismiss (5s) and close button for auto-load toast

* studio: GGUF TTS audio support (from PR #4318)

Add GGUF TTS audio generation via llama-server. When a GGUF model
loads, the backend probes its vocabulary to detect audio codecs
(SNAC/BiCodec/DAC/CSM/Whisper). If detected, the codec is pre-loaded
and the model is reported as audio to the frontend.

During chat, TTS models route to the audio generation path which sends
a per-codec prompt to llama-server's /completion endpoint, extracts
generated tokens/text, and decodes to WAV using AudioCodecManager.

Also strips base64 audio data from prior assistant messages to prevent
context overflow.

Co-authored-by: Manan Shah <mananshah511@gmail.com>

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

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

* Remove package-lock.json from tracking

* studio: per-model inference defaults, GGUF max tokens fix, reasoning toggle

- Add inference_defaults.json with per-model-family sampling parameters
  for ~50 families (Qwen3.5, Qwen3, Gemma-3, Llama-3, DeepSeek, etc.).
  Values sourced from unslothai/docs and Ollama params blobs.

- Family-based lookup in inference_config.py: extracts model family from
  identifier, matches against patterns (longest match first), merges with
  priority: model-specific YAML > family JSON > default.yaml.

- Fix GGUF Max Tokens slider locked at "Max": store ggufContextLength
  separately from maxTokens so the slider is adjustable (step=64).

- Fix Ministral YAML: top_p was literal string "default", now 0.95.

- Add reasoning toggle for thinking models (Qwen3.5, Qwen3, DeepSeek-R1,
  DeepSeek-V3.1, etc.): detect enable_thinking support from GGUF chat
  template metadata, pass --jinja to llama-server, send
  chat_template_kwargs per-request. Frontend shows "Reasoning is ON/OFF"
  pill button next to attachment button in composer.

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

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

* studio: remove default system prompt injection

Backend was injecting "You are a helpful AI assistant." when no system
prompt was provided. Neither unslothai/docs nor Ollama specify a default
system prompt for most models. Now defaults to empty string, letting the
model's own chat template handle system behavior.

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

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

* studio: use lightbulb icons and "Think" label for reasoning toggle

Lightbulb on when thinking enabled, lightbulb-off when disabled.
Label is just "Think" in both states; grayed out styling when off.

* studio: fix HTML file upload breaking chat

Replace SimpleTextAttachmentAdapter with custom TextAttachmentAdapter
(excludes text/html) and HtmlAttachmentAdapter that strips tags via
DOMParser, removing scripts/styles and extracting readable text content
instead of dumping raw HTML markup into the conversation.

* studio: show chat template in Configuration panel

Display the model's Jinja2 chat template in a new "Chat Template"
section under Settings (now open by default). For GGUFs, reads from
GGUF metadata; for safetensors, reads from tokenizer.chat_template.

Template is editable with a "Restore default chat template" button
that appears when modified. Section only shows when a model with a
chat template is loaded.

* studio: editable chat template with Apply & Reload

Chat template section now functional:
- Editing the template shows "Apply & Reload" (reloads model with
  custom template) and "Revert changes" buttons
- For GGUFs: writes template to temp .jinja file, passes
  --chat-template-file to llama-server on reload
- For non-GGUF: passes chat_template_override in load request
- Settings section now open by default
- selectModel supports forceReload to reload same model

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

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

* studio: fix DeepSeek reasoning detection and auto-load metadata

- Set _model_identifier before _read_gguf_metadata so DeepSeek
  "thinking" template detection works (was always None before)
- Populate ggufContextLength, supportsReasoning, reasoningEnabled,
  defaultChatTemplate in autoLoadSmallestModel GGUF path

* studio: add spacing before BETA badge in navbar

Add gap-1.5 on the logo Link container to space the BETA label
from the wordmark.

Co-authored-by: Imagineer99 <Imagineer99@users.noreply.github.com>

* studio: vertically center BETA badge with logo

---------

Co-authored-by: Manan Shah <mananshah511@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Imagineer99 <Imagineer99@users.noreply.github.com>
2026-03-16 06:37:55 -07:00
Roland Tannous
6d12a6b13b
Improve AI Assist: Update default model, model output parsing, logging, and dataset mapping UX (#4323)
* Strip <think> blocks from LLM assist model output

* Add debug logging for raw LLM assist output

* Quiet llama-server logs, use structlog in llm_assist

* Fix think-tag stripping when response is inside tags

* Remove debug logging of raw model output

* Clarify GGUF download logs: show cache hit vs actual download

* Clarify heuristic-detected mapping in UI text

* Default helper model to Qwen3-4B-Instruct-2507 UD-Q4_K_XL

* Remove package-lock.json from tracking, add to .gitignore

* Auto-open mapping dialog on Start Training for custom_heuristic format

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

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

* Use last think block when extracting inner content (review feedback)

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-16 16:04:35 +04:00
Daniel Han
ec9a0906eb studio: GGUF unlimited context, auto-load, settings UX, recommended list
- GGUF: use -c 0 for model's native context size (no 4096 cap)
- GGUF: hide Max Seq Length slider (irrelevant), set Max Tokens to Max
- Non-GGUF: default Max Tokens to 4096
- Max Tokens slider shows "Max" label when at ceiling for GGUFs
- Run non-GGUF load_model in asyncio.to_thread for progress polling
- Auto-load smallest downloaded model when chatting without selection
- Wait for in-progress model load before inference (modelLoading store flag)
- Recommended list: 4 GGUFs + 4 hub models after case-insensitive dedup
- Model selector waits for cached data before rendering
- Toast close button repositioned, Sampling section open by default
- Add logging to _get_repo_size_cached exception handler
2026-03-16 02:46:56 -07:00
Daniel Han
991a2bfc35 studio: GGUF unlimited context, auto-load, wait-for-load, UX fixes
- Use -c 0 for llama-server (model's native context size, no 4096 cap)
- Run non-GGUF backend.load_model in asyncio.to_thread for progress polling
- Auto-load smallest downloaded model when user chats without selecting one
- Wait for in-progress model load before inference (no "No model loaded" error)
- Add modelLoading flag to zustand store for cross-component coordination
- Dynamic top models: send 8 GGUFs + 8 hub models, frontend caps 4+4 after dedup
- Case-insensitive dedup: downloaded models correctly hide from recommended list
- Prevent duplicate toasts: guard against double selectModel calls
- Model selector waits for cached data before rendering (no empty flash)
- Toast close button positioned at top-right with proper spacing
- Sampling section expanded by default in chat settings
- Global toast close button styling fix
2026-03-16 02:46:56 -07:00
Daniel Han
20c6d9a26a Set repetition_penalty default to 1.0 (disabled) everywhere
Change all repetition_penalty defaults from 1.1 (or 1.05/1.2 in
presets) to 1.0 across the entire backend and frontend. Most models
handle repetition well on their own and a non-1.0 penalty can degrade
output quality, especially for code, structured output, and creative
tasks.

Files changed:
- Backend: inference.py, llama_cpp.py, orchestrator.py, worker.py,
  models/inference.py (Field defaults)
- Frontend: chat-settings-sheet.tsx (Creative/Precise presets),
  runtime-provider.tsx (auto-title generation)
2026-03-16 02:46:56 -07:00
Daniel Han
b985471637 Increase default max tokens to 8192, disable repetition penalty
- maxTokens: 2048 -> 8192. The old 2048 limit caused generation to
  stop mid-output for longer responses (e.g. reasoning/thinking models
  that produce long chain-of-thought before the answer).
- repetitionPenalty: 1.1 -> 1.0 (disabled). Most models handle
  repetition well on their own. A penalty of 1.1 can hurt quality
  for creative tasks like code generation and ASCII art.
- Change welcome message from "Run LLMs or test your fine-tune" to
  "Chat with your model".
2026-03-16 02:46:56 -07:00
Daniel Han
8ffd86012f Change "Stop loading" to outlined "Stop" button 2026-03-16 02:46:56 -07:00
Daniel Han
9cbeecc16a Incorporate PR #4304 toast UX improvements
Merge the toast UX refactor from PR #4304 (by @Shine1i):
- Toast duration 5s default with close button (X) for manual dismiss
- Inline progress bar component (ModelLoadInlineStatus) shown in the
  header after toast is dismissed
- Model switch warning only for image compatibility (not generic)
- activeThreadId tracked in store via ActiveThreadSync
- Loading state cleanup via resetLoadingUi helper
- Toast uses Infinity duration during loading with onDismiss handler

Re-applied non-GGUF download progress additions on top:
- getDownloadProgress for all models (not just GGUF)
- hasShownProgress flag, loadingModelRef race condition checks
- First poll at 500ms, bytes-only fallback when expected size unknown
2026-03-16 02:46:56 -07:00
Daniel Han
042598d9f1 Suppress model-switch warning on empty chat threads
Don't show "Model changed for this chat" toast when the thread has
no messages. On a fresh page load with a stale thread from a previous
session, this warning is confusing. The warning is only useful
mid-conversation to alert about image compatibility with the new model.

When messages.length === 0, silently update the thread's modelId and
proceed with loading.
2026-03-16 02:46:56 -07:00
Daniel Han
2642f6d21d Add sloth emoji to section labels, friendlier network error
- Add sloth emoji prefix to "Downloaded" and "Recommended" section
  labels in the Hub model picker so they are visually distinct.
- Replace browser network errors ("NetworkError when attempting to
  fetch resource" / "Failed to fetch") with a clearer message:
  "Studio isn't running -- please relaunch it."
2026-03-16 02:46:56 -07:00
Daniel Han
f20c7ca54d Friendlier unsupported model errors, show estimated download size
1. Backend: When a model fails with "No config file found" or similar
   unsupported-model errors, wrap the message with "This model is not
   supported yet. Try a different model." instead of showing the raw
   Unsloth exception.

2. Frontend: Compute estimated download size from the HF search API's
   safetensors.parameters dtype breakdown (BF16=2B/param, I32=4B/param,
   F32=4B/param, etc.) and show it in the model picker instead of just
   the param count. For example, Kimi-K2.5 now shows "~554 GB" instead
   of "171B" (which was misleading since 171B params != 171GB download).
2026-03-16 02:46:56 -07:00
Daniel Han
1471c63b96 Fix download progress bugs: false completion, stale UI, dedup
Three fixes on top of the download progress feature:

1. Backend: Replace broken "no .incomplete = done" completion check
   with a 95% byte threshold. HF downloads files sequentially, so
   between files there are briefly no .incomplete files even though
   the download is far from done (e.g. Kimi-K2.5 reported "done"
   after downloading 22KB of config files out of 595GB).

2. Frontend: Track hasShownProgress flag. Only show "Download
   complete. Loading into memory..." if we actually displayed
   download progress before. For already-cached models where the
   first poll returns progress=1.0, this avoids the misleading
   "Download complete" message.

3. Frontend: Deduplicate recommended vs downloaded -- filter out
   models already in the "Downloaded" section. Cache the fetched
   lists at module level so re-mounting the popover does not flash
   an empty "Downloaded" section.
2026-03-16 02:46:56 -07:00
Daniel Han
b84f167d5a Add download progress bar for non-GGUF models in Chat
Previously only GGUF models showed download progress in Chat. Non-GGUF
models (safetensors, bnb quantized, etc.) showed a static message with
no progress indication. This adds progress tracking for all model types
and fixes several related issues.

Backend:
- Add /api/models/download-progress endpoint that checks the HF cache
  blobs directory for completed and .incomplete files. Uses model_info()
  (cached per repo) to determine expected total size for percentage.
- Add /api/models/cached-models endpoint that lists non-GGUF model repos
  from the HF cache via scan_cache_dir().
- Fix progress stuck at 0.99: when no .incomplete files remain, report
  1.0 immediately (blob deduplication can make byte totals mismatch).

Frontend:
- Remove the ggufVariant gate so download progress polling works for all
  non-cached models, not just GGUFs.
- Use GGUF-specific endpoint when variant + expectedBytes available,
  otherwise use the general download-progress endpoint.
- Fix toast stuck after load: check loadingModelRef.current before and
  after the async poll to prevent overwriting the success toast.
- First poll at 500ms instead of waiting for the 2s interval.
- Show downloaded non-GGUF models in the Hub model picker "Downloaded"
  section alongside GGUFs.
2026-03-16 02:46:56 -07:00
Manan Shah
164b5a5b06
[Feature] studio: user can upload eval dataset (#4307)
* user can upload eval dataset, removed bugs

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

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

* resolving merge conflicts

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

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

* resolving gpt comments

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Roland Tannous <115670425+rolandtannous@users.noreply.github.com>
2026-03-16 11:15:50 +04:00
Lee Jackson
15e7d0dd5c
fix: preserve save_steps when toggling to epochs mode (#4308) 2026-03-16 08:43:49 +04:00
Lee Jackson
7b1ea88739
studio: simplify auth UX to password-only login (#4305)
* feat(studio): switch to password-only login and simplify first-time setup

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

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

* fix: align change-password button state with validation rules

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Roland Tannous <115670425+rolandtannous@users.noreply.github.com>
2026-03-16 03:02:58 +04:00
Lee Jackson
1e3aa4ff92
studio: add max steps and epochs toggle switch (#4296)
* feat: add Epochs toggle for Max Steps

* refactor: dedupe max-steps/epochs toggle logic and fix input bug

* fix(studio): max-steps input validation and prevSaveSteps seed in epochs mode

---------

Co-authored-by: Roland Tannous <115670425+rolandtannous@users.noreply.github.com>
2026-03-16 01:33:51 +04:00
Manan Shah
b2dce8e3a8
chat only with gguf for mac devices (#4300)
* chat only with gguf for mac devices

* resolving gpt comments

* add change-password for chat only

* hide lora adaptors dropdown

* solving gpt comments

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

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

* addressing the comment

* fixing auth flow

---------

Co-authored-by: Datta Nimmaturi <venkatadattasainimmaturi@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-15 23:20:48 +04:00
Daniel Han
11612f6dc9 studio: fix GGUF download UX -- progress bar, cancel, sorting, auto-scroll
- Run GGUF load_model in asyncio.to_thread so the event loop stays free
  for progress polling during download (was blocking all requests).
- Extract download phase out of the lock in LlamaCppBackend.load_model
  so unload_model/cancel can take effect immediately during download.
- Fix "downloaded" badge for split GGUFs: check total cached bytes
  across all shards vs expected size, not just first shard existence.
- Respect CUDA_VISIBLE_DEVICES in /api/system GPU reporting so the
  frontend GGUF fit estimation uses actual available VRAM.
- Sort tight variants (need CPU offload) smallest-first instead of
  largest-first -- closer to GPU budget = faster inference.
- Fix cancel: use refs instead of React state for abort controller and
  toast ID so both cancel buttons (text + toast) work reliably. Make
  cancel synchronous (fire-and-forget unload) for instant UI response.
  Check abortCtrl.signal.aborted after loadModel returns to prevent
  ghost model state. Skip rollback and suppress errors on cancel.
- Dynamic top 4 GGUF models fetched from HF API sorted by downloads,
  prepended to the default recommended list.
- Remove turnAnchor="top" for auto-scroll to bottom during generation.
- Set default toast duration to 10s (was infinite for loading toasts).
- Deduplicate cached GGUF repos using scan_cache_dir API (fixes
  Qwen/X-GGUF vs qwen/x-gguf duplicates from lowercased HF cache).
- Pre-compile repo_id validation regex to silence CodeQL ReDoS warning.
- Change welcome text and default suggestion text.
2026-03-15 05:24:06 -07:00
Daniel Han
1dfba866be studio: fix download progress -- track per-variant, include incomplete blobs
1. Progress endpoint now takes a variant parameter and only counts
   .gguf files matching that variant (not all files in the repo cache,
   which would include previously downloaded variants)

2. Tracks .incomplete files in HF blobs dir for in-progress single-shard
   downloads, capping at 99% until the file is fully committed

3. Fixed loading text: "Loading model..." for cached, "Downloading
   model..." for new downloads, with appropriate descriptions

4. Wording: "Downloading and loading model. Large models can take a
   while." instead of "This may include downloading."
2026-03-15 05:24:06 -07:00
Daniel Han
475ba417dc studio: context-aware loading text + download progress bar
1. Loading text: shows "Loading model..." for cached models,
   "Downloading model..." for new downloads. Toast description
   adapts accordingly.

2. Download progress: polls /api/models/gguf-download-progress every
   2s during downloads, updating the toast with percentage and GB
   downloaded. Progress is estimated by checking the HF cache folder
   size against the expected total bytes.

3. Passes isDownloaded and expectedBytes through the full chain from
   variant click to selectModel for accurate UI state.
2026-03-15 05:24:06 -07:00
Daniel Han
7b65073311 studio: show 'downloaded' badge instead of 'recommended' when variant is cached 2026-03-15 05:24:06 -07:00
Daniel Han
bcb382def9 studio: sort downloaded GGUF variants before recommended
Downloaded variants now take priority over the recommended badge in
sort order. Within the same tier (downloaded+fits, etc.), recommended
still sorts first. Order: downloaded -> recommended -> fits -> tight -> OOM
2026-03-15 05:24:06 -07:00
Daniel Han
4d35699c65 studio: show downloaded status in GGUF variant list, sort downloaded first
- Backend: /gguf-variants now checks HF cache for each variant's file
  and returns a downloaded flag per variant
- Frontend: downloaded variants sort before non-downloaded (after
  recommended), and show a green "downloaded" badge
- Sort order: recommended -> downloaded+fits -> downloaded+tight ->
  fits -> tight -> OOM
2026-03-15 05:24:06 -07:00
Daniel Han
897d8b426a studio: interruptible GGUF downloads, cached models endpoint, Downloaded section
1. Interruptible downloads: load_model now checks a cancel event
   between shard downloads. unload_model sets the event so cancel
   stops the download at the next shard boundary.

2. /api/models/cached-gguf endpoint: scans the HF cache for
   already-downloaded GGUF repos with their total size and cache path.

3. "Downloaded" section in Hub model picker: shows cached GGUF repos
   at the top (before Recommended) so users can quickly re-load
   previously downloaded models without re-downloading.
2026-03-15 05:24:06 -07:00
Daniel Han
a0fdf03340 studio: add Cancel button to model loading toast popup
Replace toast.promise with a manual toast.loading that includes a
Cancel action button. Users can now cancel model downloads/loads from
the toast notification itself, not just from the header bar spinner.
2026-03-15 05:24:06 -07:00
Daniel Han
7b19cb418e studio: sort TIGHT (CPU offload) GGUF variants after GPU-only fits
Sort order is now: recommended -> fits (largest first) -> tight/CPU
offload (largest first) -> OOM (smallest first). Previously tight
variants were mixed with fits variants.
2026-03-15 05:24:06 -07:00
Daniel Han
5bb783850a studio: GGUF OOM accounts for CPU offload via --fit (GPU + system RAM)
Updated GGUF fit classification to match llama-server's --fit behavior:

- fits:  model <= 70% of total GPU memory (all GPUs)
- tight: model > 70% GPU but <= 70% GPU + 70% available system RAM
         (llama-server uses --fit to offload layers to CPU)
- OOM:   model exceeds both GPU and system RAM budgets

useGpuInfo now also returns systemRamAvailableGb from /api/system so the
frontend can compute the combined GPU+RAM budget.
2026-03-15 05:24:06 -07:00
Daniel Han
c9c485a7b0 studio: use nvidia-smi for all GPUs + 70% VRAM threshold for GGUF OOM
Two fixes for accurate GGUF OOM detection:

1. /api/system now uses nvidia-smi to enumerate all physical GPUs
   instead of torch.cuda which only sees CUDA_VISIBLE_DEVICES. This
   matches llama-server which can use all GPUs regardless of the env
   var. Falls back to torch-based detection if nvidia-smi unavailable.

2. Frontend GGUF OOM check now uses 70% of total GPU memory as the
   budget, matching the PR's _select_gpus logic (30% reserved for KV
   cache and compute buffers). Previously used checkVramFit's 100%
   threshold which was too generous.
2026-03-15 05:24:06 -07:00
Daniel Han
f5f631e5d1 studio: add cancel button for model loading/downloading
Adds a Cancel button next to the "Downloading model..." spinner so
users can abort long downloads. Clicking it aborts the in-flight load,
calls unloadModel to kill any running llama-server process, and clears
the loading state.
2026-03-15 05:24:06 -07:00
Daniel Han
4600131fea studio: sort OOM GGUF variants smallest-to-largest
OOM variants are more useful sorted ascending by size since smaller ones
are more likely to run with --fit. Non-OOM variants remain largest-first
(best quality).
2026-03-15 05:24:06 -07:00
Daniel Han
ea45370ab8 studio: use total multi-GPU VRAM for OOM checks, recommend smallest when all OOM
Two fixes for GGUF variant dropdown:

1. useGpuInfo now sums memory across all GPU devices instead of only
   reading devices[0]. This matches llama-server's multi-GPU allocation
   where models can be split across GPUs.

2. When the backend-recommended variant (e.g. UD-Q4_K_XL) exceeds total
   GPU VRAM, the frontend picks the largest variant that fits instead.
   If all variants are OOM, it recommends the smallest one (most likely
   to work with --fit).
2026-03-15 05:24:06 -07:00
Daniel Han
10c4db04d8 studio: fix React hooks order -- move useMemo before early returns
The useMemo for sortedVariants was placed after the loading/error early
returns, which violated React's rules of hooks (hooks must be called in
the same order every render). Move it before the conditional returns.

Fixes: Minified React error #310
2026-03-15 05:24:06 -07:00
Daniel Han
3c1b8d7ab7 studio: sort GGUF dropdown client-side -- recommended first, OOM last, rest by size descending
Move the sort logic from the backend to the frontend GgufVariantExpander
component where GPU VRAM info is available. The backend now does a simple
size-descending sort. The frontend pins the recommended variant at the
top, pushes OOM variants to the bottom, and sorts the rest by file size
descending (largest/best quality first).
2026-03-15 05:24:06 -07: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
Wasim Yousef Said
629199e3a6
fix: remove old comments (#4292)
* fix: quotation marks

* diceware passphrase generation

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

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

---------

Co-authored-by: Roland Tannous <rolandtannous@gravityq.ai>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-14 16:50:13 +04: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