* Studio: llama.cpp update banner redesign, About tab license info, inline system prompt editing, naming cleanup - Redesign the llama.cpp update banner to match the chat composer surface (borderless rounded card, composer shadow, Hellix Medium title), rename actions to Update and add a 15 minute Remind me later snooze - Keep the banner up until the user explicitly acts on it; drop the outside click dismissal - Add a Settings > General > Notifications toggle to disable the banner for training-only setups (on by default) - Rename the Help settings tab to About and add a License section (Unsloth Studio AGPL-3.0, Unsloth Core Apache-2.0) linking to the license files in this repo - Make the run settings system prompt box an inline editable textarea; the popup editor opens when the prompt overflows the box - Pointer cursor on the preset dropdown chevron - Dark mode toasts use the chat composer surface color - Replace standalone Studio with Unsloth in user facing strings; keep Unsloth Studio, LM Studio, Fine-tuning Studio, Recipe Studio and CLI commands unchanged * Studio: open the system prompt popup on box click, balance banner padding - The system prompt box opens the Edit System Prompt dialog on click, matching the pencil action - Slightly more bottom padding on the llama.cpp update banner so the spacing reads even next to the action pills * Studio: replace unsloth studio update with the installer commands in update guidance - The unsloth studio update command no longer works, so the About tab update section now shows the one-line installer (curl or irm) for PyPI and unknown installs, and git pull plus the local installer for checkouts - Add a short note that unsloth studio update is no longer supported - Link the Installation, Updating and Windows install docs pages - The package update banner now copies the platform installer command instead of unsloth studio update * Studio: rounder account menu, inline system prompt box with popup from the label - Account menu corners go from 14px to 18px via a specific override, since list menus pin border-radius globally - llama.cpp banner bottom padding 22px - System prompt is an inline editable textarea again; clicking the System Prompt label opens the popup editor, and an overflowing prompt opens it on box click * Studio: show the standard install commands in the About update section - Both one-line install commands (MacOS/Linux/WSL and Windows PowerShell) are always shown, labeled like the docs, since running them again updates an existing install - Drop the unsloth studio update deprecation note - Add the Mac install guide to the docs links * Studio: clearer platform toggle and layout in the About update section - Section heading is Update - Platform picker is a pair of pill buttons, MacOS / Linux and Windows, and only the selected platform's install command is shown - Intro reads: To install or update Unsloth - Local update heading separates checkout guidance from the standard install command * Studio: report GitHub branch instead of dev for source checkouts A source checkout not on an exact release tag now shows GitHub <branch> (e.g. GitHub main) as the Studio version in About. Detached or unusual HEADs still fall back to dev. * Studio: tighten the About update section copy and toggle styling - Platform toggle buttons are borderless pills - Shorter local update wording and restart note - Docs links read Mac and Windows * Studio: tighten line spacing in the sidebar account button * Studio: fix vanishing compact MCP icon on hover, single line pill tooltips - Compact caret pills (MCP, RAG) keep their icon on hover for inactive pills too; the off switch hover rules hid the icon while compact mode hid the X, leaving an empty slot - Compact icon tooltips and single line compact tooltips render as full pills; wrapped tooltips keep the 9px corners. TooltipContent measures line count in a ref callback since Radix mounts portal content without re-rendering the wrapper - 1px gap between the name and Unsloth lines in the sidebar account button * Studio: Projects hover plus button, align recents with the label - Hovering the Projects nav item reveals a plus button that opens the New project dialog, with the same circular hover treatment as the chat row actions - Recent chat titles start at the same x as the Recents label - The system prompt overflow lock only engages for a non-empty prompt with a laid-out box, so a mis-measure cannot turn clicks into the popup * Clip system prompt overflow inside the rounded box Wrap the inline system prompt textarea in a rounded overflow-hidden surface so scrolled text and the scrollbar stay inside the box. The focus ring moves to the wrapper via focus-within. * Add updating progress bar to llama banner and shorten settings copy While an update is applying, the banner action row becomes an indeterminate progress bar that keeps animating under reduced motion, matching the other loading indicators. Settings descriptions across General, Profile, Appearance, Chat, Connections, API, and About are trimmed without losing meaning. * Address review: desktop update note, server platform detection, zh-CN keys The About tab no longer shows terminal install commands in the desktop app, where the bundled backend updates through the built-in updater; it shows a short note and the docs links instead. fetchDeviceType now sends the auth token to /api/health, which only reports the server platform to authed callers, and caches only a server-reported value. Copied install commands then match the host platform rather than the browser when they differ (WSL, SSH). zh-CN gains translations for the new notification and license keys, the renamed About tab title, and the desktop update note. * Real download progress for llama.cpp updates, prompt and sidebar polish The update worker now streams the installer output and parses its download percent lines into job progress, exposed via the update-status API. The installer emits finer non-tty milestones when UNSLOTH_PROGRESS_PERCENT_STEP is set; the worker requests 5 percent steps. The banner renders a determinate bar from the reported fraction and falls back to the sweep until the first percent arrives. Also removes the focus ring on the inline system prompt box and slightly shrinks the Projects hover plus icon.
123 lines
3.7 KiB
Python
123 lines
3.7 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
|
|
"""Network-free Studio release version resolution for display-only UI."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
from utils import _studio_release_build
|
|
|
|
_DEV_VERSION = "dev"
|
|
_GIT_TIMEOUT_SECONDS = 1.0
|
|
_STUDIO_TAG_RE = re.compile(r"^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.][0-9A-Za-z.-]*)?$")
|
|
_GIT_DESCRIBE_SUFFIX_RE = re.compile(r"-\d+-g[0-9A-Fa-f]+(?:-dirty)?$")
|
|
_GIT_BRANCH_RE = re.compile(r"^[0-9A-Za-z._/-]+$")
|
|
_MAX_VERSION_LENGTH = 64
|
|
|
|
|
|
def is_valid_studio_release_version(value: object) -> bool:
|
|
"""Return True for Studio release tags such as ``v0.1.39-beta``."""
|
|
if not isinstance(value, str):
|
|
return False
|
|
version = value.strip()
|
|
if not version or len(version) > _MAX_VERSION_LENGTH:
|
|
return False
|
|
if version.endswith("-dirty") or _GIT_DESCRIBE_SUFFIX_RE.search(version):
|
|
return False
|
|
return _STUDIO_TAG_RE.fullmatch(version) is not None
|
|
|
|
|
|
def _repo_root() -> Path:
|
|
return Path(__file__).resolve().parents[3]
|
|
|
|
|
|
def _path_is_in_site_packages(path: Path) -> bool:
|
|
return any(part in {"site-packages", "dist-packages"} for part in path.parts)
|
|
|
|
|
|
def _is_source_checkout(repo_root: Path) -> bool:
|
|
return (repo_root / ".git").exists() and not _path_is_in_site_packages(Path(__file__).resolve())
|
|
|
|
|
|
def _exact_git_studio_tag(repo_root: Path) -> str | None:
|
|
try:
|
|
result = subprocess.run(
|
|
[
|
|
"git",
|
|
"describe",
|
|
"--tags",
|
|
"--exact-match",
|
|
"--match",
|
|
"v[0-9]*",
|
|
"HEAD",
|
|
],
|
|
cwd = repo_root,
|
|
check = False,
|
|
stdout = subprocess.PIPE,
|
|
stderr = subprocess.DEVNULL,
|
|
text = True,
|
|
timeout = _GIT_TIMEOUT_SECONDS,
|
|
)
|
|
except (OSError, subprocess.TimeoutExpired):
|
|
return None
|
|
|
|
if result.returncode != 0:
|
|
return None
|
|
|
|
tag = result.stdout.strip()
|
|
return tag if is_valid_studio_release_version(tag) else None
|
|
|
|
|
|
def _git_branch(repo_root: Path) -> str | None:
|
|
try:
|
|
result = subprocess.run(
|
|
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
cwd = repo_root,
|
|
check = False,
|
|
stdout = subprocess.PIPE,
|
|
stderr = subprocess.DEVNULL,
|
|
text = True,
|
|
timeout = _GIT_TIMEOUT_SECONDS,
|
|
)
|
|
except (OSError, subprocess.TimeoutExpired):
|
|
return None
|
|
|
|
if result.returncode != 0:
|
|
return None
|
|
|
|
branch = result.stdout.strip()
|
|
# "HEAD" means detached, e.g. a tag or commit checkout.
|
|
if (
|
|
not branch
|
|
or branch == "HEAD"
|
|
or len(branch) > _MAX_VERSION_LENGTH
|
|
or _GIT_BRANCH_RE.fullmatch(branch) is None
|
|
):
|
|
return None
|
|
return branch
|
|
|
|
|
|
def get_studio_version(repo_root: Path | None = None) -> str:
|
|
"""Return the installed Studio release tag for display, or ``dev``.
|
|
|
|
Intentionally separate from the PyPI ``unsloth`` package version used by
|
|
update checks. Never performs network requests.
|
|
"""
|
|
resolved_repo_root = repo_root or _repo_root()
|
|
|
|
if _is_source_checkout(resolved_repo_root):
|
|
git_tag = _exact_git_studio_tag(resolved_repo_root)
|
|
if git_tag is not None:
|
|
return git_tag
|
|
branch = _git_branch(resolved_repo_root)
|
|
return f"GitHub {branch}" if branch is not None else _DEV_VERSION
|
|
|
|
stamped_version = _studio_release_build.STUDIO_RELEASE_VERSION
|
|
if is_valid_studio_release_version(stamped_version):
|
|
return stamped_version.strip()
|
|
|
|
return _DEV_VERSION
|