diff --git a/studio/Unsloth_Studio_Colab.ipynb b/studio/Unsloth_Studio_Colab.ipynb
index 42bd11bb0e..4bc8f46bca 100644
--- a/studio/Unsloth_Studio_Colab.ipynb
+++ b/studio/Unsloth_Studio_Colab.ipynb
@@ -14,19 +14,21 @@
"\n",
"To install Unsloth on your local device, follow [our guide](https://unsloth.ai/docs/get-started/install). This notebook is licensed [AGPL-3.0](https://github.com/unslothai/unsloth/blob/main/studio/LICENSE.AGPL-3.0).\n",
"\n",
- "Run **Unsloth Studio** directly in Google Colab."
+ "Run [**Unsloth Studio**](https://unsloth.ai/docs/new/unsloth-studio/start) directly in Google Colab.\n",
+ "\n",
+ "### Unsloth Studio\n",
+ "\n",
+ "Train and run local models with a unified UI interface.\n",
+ "\n",
+ "[Features](https://unsloth.ai/docs/new/unsloth-studio) • [Quickstart](https://unsloth.ai/docs/new/unsloth-studio/start) • [Data Recipes](https://unsloth.ai/docs/new/unsloth-studio/data-recipe) • [Studio Chat](https://unsloth.ai/docs/new/unsloth-studio/chat) • [Export](https://unsloth.ai/docs/new/unsloth-studio/export)"
]
},
{
"cell_type": "markdown",
- "id": "f6907711",
+ "id": "e4206349",
"metadata": {},
"source": [
- "
\n",
- "\n",
- "\n",
- "\n",
- "
"
+ "
"
]
},
{
diff --git a/studio/frontend/public/studio github landscape colab display.png b/studio/frontend/public/studio github landscape colab display.png
new file mode 100644
index 0000000000..10d6521f50
Binary files /dev/null and b/studio/frontend/public/studio github landscape colab display.png differ
diff --git a/studio/install_python_stack.py b/studio/install_python_stack.py
index e20f1eb486..20011a298a 100644
--- a/studio/install_python_stack.py
+++ b/studio/install_python_stack.py
@@ -22,6 +22,19 @@ from pathlib import Path
IS_WINDOWS = sys.platform == "win32"
+# ── Verbosity control ──────────────────────────────────────────────────────────
+# By default the installer shows a minimal progress bar (one line, in-place).
+# Set UNSLOTH_VERBOSE=1 in the environment to restore full per-step output:
+# Linux/Mac: UNSLOTH_VERBOSE=1 ./studio/setup.sh
+# Windows: $env:UNSLOTH_VERBOSE="1" ; .\studio\setup.ps1
+VERBOSE: bool = os.environ.get("UNSLOTH_VERBOSE", "0") == "1"
+
+# Progress bar state — updated by _progress() as each install step runs.
+# _TOTAL counts: pip-upgrade + 7 shared steps + triton (non-Windows) + local-plugin + finalize
+# Update _TOTAL here if you add or remove install steps in install_python_stack().
+_STEP: int = 0
+_TOTAL: int = 0 # set at runtime in install_python_stack() based on platform
+
# ── Paths ──────────────────────────────────────────────────────────────
SCRIPT_DIR = Path(__file__).resolve().parent
REQ_ROOT = SCRIPT_DIR / "backend" / "requirements"
@@ -59,7 +72,9 @@ def _enable_colors() -> bool:
return True # Unix terminals support ANSI by default
-_HAS_COLOR = _enable_colors()
+# Colors disabled — Colab and most CI runners render ANSI fine, but plain output
+# is cleaner in the notebook cell. Re-enable by setting _HAS_COLOR = _enable_colors()
+_HAS_COLOR = False
def _green(msg: str) -> str:
@@ -74,11 +89,30 @@ def _red(msg: str) -> str:
return f"\033[91m{msg}\033[0m" if _HAS_COLOR else msg
+def _progress(label: str) -> None:
+ """Print an in-place progress bar for the current install step.
+
+ Uses only stdlib (sys.stdout) — no extra packages required.
+ In VERBOSE mode this is a no-op; per-step labels are printed by run() instead.
+ """
+ global _STEP
+ _STEP += 1
+ if VERBOSE:
+ return # verbose mode: run() already printed the label
+ width = 20
+ filled = int(width * _STEP / _TOTAL)
+ bar = "=" * filled + "-" * (width - filled)
+ end = "\n" if _STEP >= _TOTAL else "" # newline only on the final step
+ sys.stdout.write(f"\r[{bar}] {_STEP:2}/{_TOTAL} {label:<40}{end}")
+ sys.stdout.flush()
+
+
def run(
label: str, cmd: list[str], *, quiet: bool = True
) -> subprocess.CompletedProcess[bytes]:
"""Run a command; on failure print output and exit."""
- print(_cyan(f" {label}..."))
+ if VERBOSE:
+ print(f" {label}...")
result = subprocess.run(
cmd,
stdout = subprocess.PIPE if quiet else None,
@@ -196,7 +230,8 @@ def pip_install(
try:
if USE_UV:
uv_cmd = _build_uv_cmd(args) + constraint_args + req_args
- print(_cyan(f" {label}..."))
+ if VERBOSE:
+ print(f" {label}...")
result = subprocess.run(
uv_cmd,
stdout = subprocess.PIPE,
@@ -250,17 +285,19 @@ def patch_package_file(package_name: str, relative_path: str, url: str) -> None:
def install_python_stack() -> int:
- global USE_UV
+ global USE_UV, _STEP, _TOTAL
+ _STEP = 0
+ _TOTAL = 10 if IS_WINDOWS else 11
# 1. Upgrade pip (needed even with uv as fallback and for bootstrapping)
+ _progress("pip upgrade")
run("Upgrading pip", [sys.executable, "-m", "pip", "install", "--upgrade", "pip"])
# Try to use uv for faster installs
USE_UV = _bootstrap_uv()
- installer = "uv" if USE_UV else "pip"
- print(_cyan(f"── Installing Python stack (via {installer}) ──"))
# 2. Core packages: unsloth-zoo + unsloth
+ _progress("base packages")
pip_install(
"Installing base packages",
"--no-cache-dir",
@@ -268,6 +305,7 @@ def install_python_stack() -> int:
)
# 3. Extra dependencies
+ _progress("unsloth extras")
pip_install(
"Installing additional unsloth dependencies",
"--no-cache-dir",
@@ -275,6 +313,7 @@ def install_python_stack() -> int:
)
# 3b. Extra dependencies (no-deps) — audio model support etc.
+ _progress("extra codecs")
pip_install(
"Installing extras (no-deps)",
"--no-deps",
@@ -283,6 +322,7 @@ def install_python_stack() -> int:
)
# 4. Overrides (torchao, transformers) — force-reinstall
+ _progress("dependency overrides")
pip_install(
"Installing dependency overrides",
"--force-reinstall",
@@ -292,6 +332,7 @@ def install_python_stack() -> int:
# 5. Triton kernels (no-deps, from source)
if not IS_WINDOWS:
+ _progress("triton kernels")
pip_install(
"Installing triton kernels",
"--no-deps",
@@ -322,6 +363,7 @@ def install_python_stack() -> int:
# )
# 8. Studio dependencies
+ _progress("studio deps")
pip_install(
"Installing studio dependencies",
"--no-cache-dir",
@@ -329,6 +371,7 @@ def install_python_stack() -> int:
)
# 9. Data-designer dependencies
+ _progress("data designer deps")
pip_install(
"Installing data-designer base dependencies",
"--no-cache-dir",
@@ -336,6 +379,7 @@ def install_python_stack() -> int:
)
# 10. Data-designer packages (no-deps to avoid conflicts)
+ _progress("data designer")
pip_install(
"Installing data-designer",
"--no-cache-dir",
@@ -351,6 +395,7 @@ def install_python_stack() -> int:
),
)
return 1
+ _progress("local plugin")
pip_install(
"Installing local data-designer unstructured plugin",
"--no-cache-dir",
@@ -360,6 +405,7 @@ def install_python_stack() -> int:
)
# 12. Patch metadata for single-env compatibility
+ _progress("finalizing")
run(
"Patching single-env metadata",
[sys.executable, str(SINGLE_ENV / "patch_metadata.py")],
diff --git a/studio/setup.sh b/studio/setup.sh
index 9754473798..5fbdce918f 100755
--- a/studio/setup.sh
+++ b/studio/setup.sh
@@ -118,8 +118,6 @@ fi
echo "✅ Node $(node -v) | npm $(npm -v)"
# ── 5. Build frontend ──
-echo ""
-echo "Building frontend..."
cd "$SCRIPT_DIR/frontend"
# Tailwind v4's oxide scanner respects .gitignore in parent directories.
@@ -156,8 +154,6 @@ echo "✅ Frontend built to frontend/dist"
fi # end frontend dist check
# ── 6. Python venv + deps ──
-echo ""
-echo "Setting up Python environment..."
# ── 6a. Discover best Python >= 3.11 and < 3.14 (i.e. 3.11.x, 3.12.x, or 3.13.x) ──
MIN_PY_MINOR=11 # minimum minor version (>= 3.11)