diff --git a/Unsloth_Studio_Colab.ipynb b/Unsloth_Studio_Colab.ipynb new file mode 100644 index 0000000000..fb2ca72604 --- /dev/null +++ b/Unsloth_Studio_Colab.ipynb @@ -0,0 +1,99 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "447c1156", + "metadata": {}, + "outputs": [], + "source": [ + "# ===========================================\n", + "# āš ļø GPU Check - Run This First!\n", + "# ===========================================\n", + "import torch\n", + "\n", + "print(\"šŸ” Checking for GPU...\")\n", + "if not torch.cuda.is_available():\n", + " print(\"āŒ ERROR: No GPU detected!\")\n", + " print(\"\\nšŸ“‹ To enable GPU:\")\n", + " print(\" 1. Go to: Runtime → Change runtime type\")\n", + " print(\" 2. Select: Hardware accelerator → GPU (T4 is free)\")\n", + " print(\" 3. Click: Save\")\n", + " print(\" 4. Restart and re-run all cells\")\n", + " raise RuntimeError(\"ā›” GPU required for Unsloth Studio\")\n", + "else:\n", + " gpu_name = torch.cuda.get_device_name(0)\n", + " print(f\"āœ… GPU detected: {gpu_name}\")\n", + " print(\" Ready to proceed!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f04a9b46", + "metadata": {}, + "outputs": [], + "source": [ + "# ===========================================\n", + "# GitHub Authentication (Private Repo)\n", + "# ===========================================\n", + "from getpass import getpass\n", + "import os\n", + "\n", + "print(\"šŸ” GitHub Token Required\")\n", + "print(\"Get token: https://github.com/settings/tokens\")\n", + "print(\"Scope needed: 'repo'\")\n", + "print(\"-\" * 50)\n", + "\n", + "github_token = getpass(\"Enter GitHub Token: \")\n", + "os.environ['GITHUB_TOKEN'] = github_token\n", + "print(\"āœ… Token stored\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27e68f91", + "metadata": {}, + "outputs": [], + "source": [ + "# ===========================================\n", + "# Setup: Clone repo and run setup\n", + "# ===========================================\n", + "\n", + "import os\n", + "github_token = os.environ['GITHUB_TOKEN']\n", + "!git clone -b feature/colab-notebook https://{github_token}@github.com/unslothai/new-ui-prototype.git\n", + "%cd /content/new-ui-prototype\n", + "\n", + "# Run setup script\n", + "!chmod +x setup.sh\n", + "!./setup.sh" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277e431e", + "metadata": {}, + "outputs": [], + "source": [ + "# ===========================================\n", + "# Start Unsloth Studio\n", + "# ===========================================\n", + "import sys\n", + "sys.path.insert(0, '/content/new-ui-prototype/studio/backend')\n", + "\n", + "from colab import start\n", + "start()" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/setup.sh b/setup.sh index ed594f8cb9..c493d357ee 100755 --- a/setup.sh +++ b/setup.sh @@ -24,6 +24,13 @@ echo "╔═══════════════════════ echo "ā•‘ Unsloth Studio Setup Script ā•‘" echo "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" +# ── Detect Colab (like unsloth does) ── +IS_COLAB=false +keynames=$'\n'$(printenv | cut -d= -f1) +if [[ "$keynames" == *$'\nCOLAB_'* ]]; then + IS_COLAB=true +fi + # ── 1. Check existing Node/npm versions ── NEED_NODE=true if command -v node &>/dev/null && command -v npm &>/dev/null; then @@ -33,7 +40,17 @@ if command -v node &>/dev/null && command -v npm &>/dev/null; then echo "āœ… Node $(node -v) and npm $(npm -v) already meet requirements. Skipping nvm install." NEED_NODE=false else - echo "āš ļø Node $(node -v) / npm $(npm -v) too old. Installing via nvm..." + if [ "$IS_COLAB" = true ]; then + echo "āœ… Node $(node -v) and npm $(npm -v) detected in Colab." + # In Colab, just upgrade npm directly - nvm doesn't work well + if [ "$NPM_MAJOR" -lt 11 ]; then + echo " Upgrading npm to latest..." + npm install -g npm@latest > /dev/null 2>&1 + fi + NEED_NODE=false + else + echo "āš ļø Node $(node -v) / npm $(npm -v) too old. Installing via nvm..." + fi fi else echo "āš ļø Node/npm not found. Installing via nvm..." @@ -132,41 +149,61 @@ fi BEST_VER=$("$BEST_PY" --version 2>&1 | awk '{print $2}') echo "āœ… Using $BEST_PY ($BEST_VER) — compatible (≤ 3.12.x)" -# Always start fresh to preserve correct install order -rm -rf .venv -"$BEST_PY" -m venv .venv -source .venv/bin/activate -run_quiet "pip upgrade" pip install --upgrade pip -echo " Installing unsloth-zoo + unsloth..." -run_quiet "pip install unsloth" pip install -r "$SCRIPT_DIR/studio/backend/requirements/base.txt" -echo " Installing additional unsloth dependencies..." -run_quiet "pip install extras" pip install --no-cache-dir -r "$SCRIPT_DIR/studio/backend/requirements/extras.txt" -run_quiet "pip install extras" pip install --no-deps --no-cache-dir -r "$SCRIPT_DIR/studio/backend/requirements/extras-no-deps.txt" -run_quiet "pip install torchao+transformers" pip install --force-reinstall --no-cache-dir -r "$SCRIPT_DIR/studio/backend/requirements/overrides.txt" -run_quiet "pip install triton_kernels" pip install --no-deps -r "$SCRIPT_DIR/studio/backend/requirements/triton-kernels.txt" -# Patch: override llama_cpp.py with fix from unsloth-zoo branch -LLAMA_CPP_DST="$(pip show unsloth-zoo | grep -i '^Location:' | awk '{print $2}')/unsloth_zoo/llama_cpp.py" -curl -sSL "https://raw.githubusercontent.com/unslothai/unsloth-zoo/refs/heads/main/unsloth_zoo/llama_cpp.py" \ - -o "$LLAMA_CPP_DST" -echo " Installing studio dependencies..." -run_quiet "pip install studio" pip install -r "$SCRIPT_DIR/studio/backend/requirements/studio.txt" -echo "āœ… Python dependencies installed" - -# ── 7. WSL: pre-install GGUF build dependencies ── -# On WSL, sudo requires a password and can't be entered during GGUF export -# (runs in a non-interactive subprocess). Install build deps here instead. -if grep -qi microsoft /proc/version 2>/dev/null; then - echo "" - echo "āš ļø WSL detected — installing build dependencies for GGUF export..." - echo " You may be prompted for your password." - sudo apt-get update -y - sudo apt-get install -y build-essential cmake curl git libcurl4-openssl-dev - echo "āœ… GGUF build dependencies installed" +if [ "$IS_COLAB" = true ]; then + # Colab: install packages directly without venv + run_quiet "pip upgrade" pip install --upgrade pip + echo " Installing unsloth-zoo + unsloth..." + run_quiet "pip install unsloth" pip install -r "$SCRIPT_DIR/studio/backend/requirements/base.txt" + echo " Installing additional unsloth dependencies..." + run_quiet "pip install extras" pip install --no-cache-dir -r "$SCRIPT_DIR/studio/backend/requirements/extras.txt" + run_quiet "pip install extras" pip install --no-deps --no-cache-dir -r "$SCRIPT_DIR/studio/backend/requirements/extras-no-deps.txt" + run_quiet "pip install torchao+transformers" pip install --force-reinstall --no-cache-dir -r "$SCRIPT_DIR/studio/backend/requirements/overrides.txt" + run_quiet "pip install triton_kernels" pip install --no-deps -r "$SCRIPT_DIR/studio/backend/requirements/triton-kernels.txt" + # Patch: override llama_cpp.py with fix from unsloth-zoo branch + LLAMA_CPP_DST="$(pip show unsloth-zoo | grep -i '^Location:' | awk '{print $2}')/unsloth_zoo/llama_cpp.py" + curl -sSL "https://raw.githubusercontent.com/unslothai/unsloth-zoo/refs/heads/main/unsloth_zoo/llama_cpp.py" \ + -o "$LLAMA_CPP_DST" + echo " Installing studio dependencies..." + run_quiet "pip install studio" pip install -r "$SCRIPT_DIR/studio/backend/requirements/studio.txt" + echo "āœ… Python dependencies installed" +else + # Local: create venv (always start fresh to preserve correct install order) + rm -rf .venv + "$BEST_PY" -m venv .venv + source .venv/bin/activate + run_quiet "pip upgrade" pip install --upgrade pip + echo " Installing unsloth-zoo + unsloth..." + run_quiet "pip install unsloth" pip install -r "$SCRIPT_DIR/studio/backend/requirements/base.txt" + echo " Installing additional unsloth dependencies..." + run_quiet "pip install extras" pip install --no-cache-dir -r "$SCRIPT_DIR/studio/backend/requirements/extras.txt" + run_quiet "pip install extras" pip install --no-deps --no-cache-dir -r "$SCRIPT_DIR/studio/backend/requirements/extras-no-deps.txt" + run_quiet "pip install torchao+transformers" pip install --force-reinstall --no-cache-dir -r "$SCRIPT_DIR/studio/backend/requirements/overrides.txt" + run_quiet "pip install triton_kernels" pip install --no-deps -r "$SCRIPT_DIR/studio/backend/requirements/triton-kernels.txt" + # Patch: override llama_cpp.py with fix from unsloth-zoo branch + LLAMA_CPP_DST="$(pip show unsloth-zoo | grep -i '^Location:' | awk '{print $2}')/unsloth_zoo/llama_cpp.py" + curl -sSL "https://raw.githubusercontent.com/unslothai/unsloth-zoo/refs/heads/main/unsloth_zoo/llama_cpp.py" \ + -o "$LLAMA_CPP_DST" + echo " Installing studio dependencies..." + run_quiet "pip install studio" pip install -r "$SCRIPT_DIR/studio/backend/requirements/studio.txt" + echo "āœ… Python dependencies installed" + + # ── 7. WSL: pre-install GGUF build dependencies ── + # On WSL, sudo requires a password and can't be entered during GGUF export + # (runs in a non-interactive subprocess). Install build deps here instead. + if grep -qi microsoft /proc/version 2>/dev/null; then + echo "" + echo "āš ļø WSL detected — installing build dependencies for GGUF export..." + echo " You may be prompted for your password." + sudo apt-get update -y + sudo apt-get install -y build-essential cmake curl git libcurl4-openssl-dev + echo "āœ… GGUF build dependencies installed" + fi fi -# ── 8. Add shell alias ── +# ── 8. Add shell alias (skip in Colab) ── # Note: venv activation does NOT persist across terminal sessions. # This alias hardcodes the venv python path so users don't need to activate. +if [ "$IS_COLAB" = false ]; then echo "" REPO_DIR="$SCRIPT_DIR" @@ -209,16 +246,27 @@ else echo "āœ… Alias 'unsloth-ui' already exists in $SHELL_RC" fi +fi # End of "if not Colab" for shell alias setup + echo "" -echo "╔══════════════════════════════════════╗" -echo "ā•‘ Setup Complete! ā•‘" -echo "╠══════════════════════════════════════╣" -if [ "$ALIAS_ADDED" = true ]; then - echo "ā•‘ Run 'source $SHELL_RC'" - echo "ā•‘ or open a new terminal, then: ā•‘" +if [ "$IS_COLAB" = true ]; then + echo "╔══════════════════════════════════════╗" + echo "ā•‘ Setup Complete! ā•‘" + echo "╠══════════════════════════════════════╣" + echo "ā•‘ Unsloth Studio is ready to start ā•‘" + echo "ā•‘ in your Colab notebook! ā•‘" + echo "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" else - echo "ā•‘ Launch with: ā•‘" + echo "╔══════════════════════════════════════╗" + echo "ā•‘ Setup Complete! ā•‘" + echo "╠══════════════════════════════════════╣" + if [ "$ALIAS_ADDED" = true ]; then + echo "ā•‘ Run 'source $SHELL_RC'" + echo "ā•‘ or open a new terminal, then: ā•‘" + else + echo "ā•‘ Launch with: ā•‘" + fi + echo "ā•‘ ā•‘" + echo "ā•‘ unsloth-ui -H 0.0.0.0 -p 8000 ā•‘" + echo "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" fi -echo "ā•‘ ā•‘" -echo "ā•‘ unsloth-ui -H 0.0.0.0 -p 8000 ā•‘" -echo "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" diff --git a/studio/backend/colab.py b/studio/backend/colab.py new file mode 100644 index 0000000000..e3e9bc0cf0 --- /dev/null +++ b/studio/backend/colab.py @@ -0,0 +1,90 @@ +""" +Colab-specific helpers for running Unsloth Studio. +Uses Colab's built-in proxy - no external tunneling needed! +""" +from pathlib import Path + + +def get_colab_url(port: int = 8000) -> str: + """ + Get the actual Colab proxy URL for a port. + """ + try: + from google.colab.output import eval_js + + # Use Colab's proxy mechanism + url = eval_js(f"google.colab.kernel.proxyPort({port})", timeout_sec=5) + return url if url else f"http://localhost:{port}" + except Exception as e: + print(f"Note: Could not get Colab URL ({e})") + return f"http://localhost:{port}" + + +def show_link(port: int = 8000): + """Display a styled clickable link to the UI.""" + from IPython.display import display, HTML + + # Get real Colab proxy URL + url = get_colab_url(port) + + html = f""" +
+

+ 🦄 Unsloth Studio is Ready! +

+ + šŸš€ Open Unsloth Studio + +

+ {url} +

+
+ """ + display(HTML(html)) + + +def start(port: int = 8000): + """ + Start Unsloth Studio server in Colab and display the URL. + + Usage: + from colab import start + start() + """ + import sys + + print("🦄 Starting Unsloth Studio...") + + # Add backend to path + backend_path = str(Path(__file__).parent) + if backend_path not in sys.path: + sys.path.insert(0, backend_path) + + print(" Loading backend...") + from run import run_server + + # Auto-detect frontend path + repo_root = Path(__file__).parent.parent + frontend_path = repo_root / "frontend" / "dist" + + if not frontend_path.exists(): + print("āŒ Frontend not built! Please run the setup cell first.") + return + + print(" Starting server...") + # Start server silently + run_server(host="0.0.0.0", port=port, frontend_path=frontend_path, silent=True) + + print(" Server started!") + + # Show the clickable link with real URL + show_link(port) + + +if __name__ == "__main__": + start()