From 17df5bf3bfe22d70b0f552ae36157ccb2e1628e3 Mon Sep 17 00:00:00 2001 From: Leo Borcherding Date: Tue, 17 Feb 2026 04:57:30 -0600 Subject: [PATCH 1/9] feat: Add simple 2-cell Colab notebook (no tunnel needed) - Create studio/backend/colab.py using Colab's built-in proxy - Uses google.colab.kernel.proxyPort() for URL (no cloudflare) - Shows nice clickable link with IPython.display.HTML - Notebook has just 2 cells: setup and start - Much simpler than external tunneling approach --- Unsloth_Studio_Colab.ipynb | 584 +++++++++++++++++++++++++++++++++++++ studio/backend/colab.py | 90 ++++++ 2 files changed, 674 insertions(+) create mode 100644 Unsloth_Studio_Colab.ipynb create mode 100644 studio/backend/colab.py diff --git a/Unsloth_Studio_Colab.ipynb b/Unsloth_Studio_Colab.ipynb new file mode 100644 index 0000000000..7fe4805b45 --- /dev/null +++ b/Unsloth_Studio_Colab.ipynb @@ -0,0 +1,584 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "27e68f91", + "metadata": {}, + "outputs": [], + "source": [ + "# ===========================================\n", + "# Setup: Clone repo and build frontend\n", + "# ===========================================\n", + "\n", + "# Clone repository\n", + "!git clone https://github.com/unslothai/new-ui-prototype.git\n", + "%cd /content/new-ui-prototype\n", + "\n", + "# Install Node.js 20.x\n", + "!curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - > /dev/null 2>&1\n", + "!sudo apt-get install -y nodejs > /dev/null 2>&1\n", + "print(\"āœ… Node.js installed\")\n", + "\n", + "# Build frontend\n", + "%cd studio/frontend\n", + "!npm install --silent\n", + "!npm run build\n", + "print(\"āœ… Frontend built\")\n", + "\n", + "# Install Python dependencies\n", + "%cd /content/new-ui-prototype\n", + "!pip install -q unsloth\n", + "!pip install -q -r studio/backend/requirements.txt\n", + "!pip install -q huggingface_hub datasets python-jose[cryptography] passlib[bcrypt] python-multipart matplotlib pandas ujson\n", + "print(\"āœ… Python dependencies installed\")" + ] + }, + { + "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()" + ] + }, + { + "cell_type": "markdown", + "id": "c98b28eb", + "metadata": {}, + "source": [ + "
\n", + " \n", + "
\n", + "\n", + "# 🦄 Unsloth Studio on Google Colab\n", + "\n", + "A modern, full-stack web interface for fine-tuning, managing, and chatting with large language models.\n", + "\n", + "**Features:**\n", + "- šŸŽÆ **Training**: LoRA/QLoRA fine-tuning with real-time progress streaming\n", + "- šŸ¤– **Model Management**: Browse and load Hugging Face models\n", + "- šŸ’¬ **Inference**: Interactive chat playground\n", + "- šŸ“Š **Dataset Tools**: Upload and preview datasets\n", + "- šŸš€ **Export**: Push trained adapters to Hugging Face Hub\n", + "\n", + "---\n", + "\n", + "**āš ļø Important Notes:**\n", + "- **Private Repository**: You'll need a GitHub Personal Access Token to clone\n", + "- Use a **GPU runtime** for training (Runtime → Change runtime type → T4 GPU)\n", + "- The notebook will expose the UI via **Cloudflare Tunnel** (no account needed)\n", + "- Your first launch will generate a **setup token** for creating an admin account\n", + "\n", + "---\n", + "\n", + "**Repository**: [github.com/unslothai/new-ui-prototype](https://github.com/unslothai/new-ui-prototype/tree/nightly)" + ] + }, + { + "cell_type": "markdown", + "id": "9a0cb9d9", + "metadata": {}, + "source": [ + "## šŸ“‹ Step 1: Install System Dependencies\n", + "\n", + "Install Node.js and required system packages." + ] + }, + { + "cell_type": "markdown", + "id": "0b08e19f", + "metadata": {}, + "source": [ + "## šŸ” Step 1.5: Authenticate with GitHub (Private Repo)\n", + "\n", + "Since this is a private repository, you need to authenticate with GitHub. \n", + "\n", + "**Get a Personal Access Token (classic):**\n", + "1. Go to https://github.com/settings/tokens\n", + "2. Click \"Generate new token (classic)\"\n", + "3. Give it `repo` scope\n", + "4. Copy the token\n", + "\n", + "**Or use GitHub CLI:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a16a1ed9", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from getpass import getpass\n", + "\n", + "# Option 1: Use GitHub Personal Access Token\n", + "print(\"šŸ” GitHub Authentication Required (Private Repo)\")\n", + "print(\"=\" * 60)\n", + "print(\"Get a token from: https://github.com/settings/tokens\")\n", + "print(\"Required scope: 'repo'\")\n", + "print(\"=\" * 60)\n", + "\n", + "github_token = getpass(\"Enter your GitHub Personal Access Token: \")\n", + "\n", + "if github_token:\n", + " # Store token for git operations\n", + " os.environ['GITHUB_TOKEN'] = github_token\n", + " print(\"āœ… Token stored (will be used for cloning)\")\n", + "else:\n", + " print(\"āš ļø No token provided - clone may fail for private repo\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b4ad8e1", + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "# Install Node.js 20.x\n", + "echo \"šŸ“¦ Installing Node.js...\"\n", + "curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -\n", + "sudo apt-get install -y nodejs\n", + "\n", + "# Verify installation\n", + "echo \"āœ… Node.js $(node -v) installed\"\n", + "echo \"āœ… npm $(npm -v) installed\"" + ] + }, + { + "cell_type": "markdown", + "id": "7065daf9", + "metadata": {}, + "source": [ + "## šŸ“„ Step 2: Clone Repository and Install Python Dependencies\n", + "\n", + "Clone the repository and install Unsloth + backend dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c563597", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from pathlib import Path\n", + "\n", + "# Clone repository (use nightly branch)\n", + "repo_path = Path(\"/content/new-ui-prototype\")\n", + "if not repo_path.exists():\n", + " print(\"šŸ“„ Cloning repository...\")\n", + " \n", + " # Use token if available (for private repo)\n", + " github_token = os.environ.get('GITHUB_TOKEN', '')\n", + " if github_token:\n", + " # Clone with token embedded in URL\n", + " repo_url = f\"https://{github_token}@github.com/unslothai/new-ui-prototype.git\"\n", + " !git clone -b nightly {repo_url}\n", + " else:\n", + " # Try without token (will work for public repo)\n", + " !git clone -b nightly https://github.com/unslothai/new-ui-prototype.git\n", + "else:\n", + " print(\"āœ… Repository already cloned\")\n", + "\n", + "# Change to repo directory\n", + "os.chdir(repo_path)\n", + "print(f\"šŸ“‚ Working directory: {os.getcwd()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1266536b", + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "# Install Unsloth\n", + "echo \"šŸ“¦ Installing Unsloth...\"\n", + "pip install --no-cache-dir unsloth\n", + "\n", + "# Install backend dependencies\n", + "echo \"šŸ“¦ Installing backend dependencies...\"\n", + "cd studio/backend\n", + "pip install --no-cache-dir -r requirements.txt\n", + "\n", + "# Install additional required packages\n", + "pip install --no-cache-dir \\\n", + " huggingface_hub \\\n", + " datasets \\\n", + " python-jose[cryptography] \\\n", + " passlib[bcrypt] \\\n", + " python-multipart \\\n", + " matplotlib \\\n", + " pandas \\\n", + " ujson\n", + "\n", + "echo \"āœ… Python dependencies installed\"" + ] + }, + { + "cell_type": "markdown", + "id": "30ca4f1d", + "metadata": {}, + "source": [ + "## šŸŽØ Step 3: Build Frontend\n", + "\n", + "Build the React/TypeScript frontend." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb65b9b2", + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "cd studio/frontend\n", + "\n", + "echo \"šŸ“¦ Installing frontend dependencies...\"\n", + "npm install --legacy-peer-deps\n", + "\n", + "echo \"šŸ—ļø Building frontend...\"\n", + "npm run build\n", + "\n", + "echo \"āœ… Frontend built to studio/frontend/dist\"" + ] + }, + { + "cell_type": "markdown", + "id": "87ecf129", + "metadata": {}, + "source": [ + "## 🌐 Step 4: Set Up Cloudflare Tunnel\n", + "\n", + "Install cloudflared to expose the backend server to the internet." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6bb2086", + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "# Install cloudflared\n", + "if ! command -v cloudflared &> /dev/null; then\n", + " echo \"šŸ“¦ Installing Cloudflare Tunnel...\"\n", + " wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb\n", + " sudo dpkg -i cloudflared-linux-amd64.deb\n", + " rm cloudflared-linux-amd64.deb\n", + " echo \"āœ… Cloudflared installed\"\n", + "else\n", + " echo \"āœ… Cloudflared already installed\"\n", + "fi\n", + "\n", + "cloudflared version" + ] + }, + { + "cell_type": "markdown", + "id": "3c48401b", + "metadata": {}, + "source": [ + "## šŸš€ Step 5: Launch Unsloth Studio\n", + "\n", + "Start the backend server and create a public tunnel.\n", + "\n", + "**āš ļø IMPORTANT:**\n", + "1. Look for the **šŸ”— Public URL** in the output below\n", + "2. On first launch, look for the **šŸ”‘ Setup Token** (one-time use)\n", + "3. Open the URL in your browser\n", + "4. Use the setup token to create your admin account\n", + "\n", + "**Note**: This cell will run continuously. To stop the server, click the ā¹ļø stop button or interrupt the kernel." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80f2f7c5", + "metadata": {}, + "outputs": [], + "source": [ + "import subprocess\n", + "import time\n", + "import sys\n", + "import re\n", + "from threading import Thread\n", + "from queue import Queue, Empty\n", + "\n", + "def stream_output(pipe, queue, prefix=\"\"):\n", + " \"\"\"Stream subprocess output to queue\"\"\"\n", + " for line in iter(pipe.readline, b''):\n", + " queue.put((prefix, line.decode('utf-8')))\n", + " pipe.close()\n", + "\n", + "# Change to backend directory\n", + "os.chdir(\"/content/new-ui-prototype/studio/backend\")\n", + "\n", + "print(\"šŸš€ Starting Unsloth Studio...\\n\")\n", + "print(\"=\" * 70)\n", + "\n", + "# Start backend server\n", + "backend_process = subprocess.Popen(\n", + " [\"python\", \"run.py\"],\n", + " stdout=subprocess.PIPE,\n", + " stderr=subprocess.PIPE,\n", + " bufsize=1\n", + ")\n", + "\n", + "# Wait for server to start\n", + "print(\"ā³ Waiting for backend server to start...\")\n", + "time.sleep(10)\n", + "\n", + "# Start cloudflared tunnel\n", + "tunnel_process = subprocess.Popen(\n", + " [\"cloudflared\", \"tunnel\", \"--url\", \"http://localhost:8000\"],\n", + " stdout=subprocess.PIPE,\n", + " stderr=subprocess.PIPE,\n", + " bufsize=1\n", + ")\n", + "\n", + "# Create queues for output\n", + "backend_queue = Queue()\n", + "tunnel_queue = Queue()\n", + "\n", + "# Start output streaming threads\n", + "Thread(target=stream_output, args=(backend_process.stdout, backend_queue, \"[BACKEND]\"), daemon=True).start()\n", + "Thread(target=stream_output, args=(backend_process.stderr, backend_queue, \"[BACKEND]\"), daemon=True).start()\n", + "Thread(target=stream_output, args=(tunnel_process.stdout, tunnel_queue, \"[TUNNEL]\"), daemon=True).start()\n", + "Thread(target=stream_output, args=(tunnel_process.stderr, tunnel_queue, \"[TUNNEL]\"), daemon=True).start()\n", + "\n", + "# Monitor output and extract public URL and setup token\n", + "public_url = None\n", + "setup_token = None\n", + "url_pattern = re.compile(r'https://[a-zA-Z0-9-]+\\.trycloudflare\\.com')\n", + "token_pattern = re.compile(r'Setup token: ([a-f0-9-]+)')\n", + "\n", + "print(\"\\nšŸ“” Monitoring server output...\\n\")\n", + "print(\"=\" * 70)\n", + "\n", + "try:\n", + " while True:\n", + " # Check backend output\n", + " try:\n", + " prefix, line = backend_queue.get(timeout=0.1)\n", + " print(f\"{prefix} {line}\", end='')\n", + " \n", + " # Look for setup token\n", + " if setup_token is None:\n", + " token_match = token_pattern.search(line)\n", + " if token_match:\n", + " setup_token = token_match.group(1)\n", + " print(f\"\\n{'=' * 70}\")\n", + " print(f\"šŸ”‘ SETUP TOKEN (save this!): {setup_token}\")\n", + " print(f\"{'=' * 70}\\n\")\n", + " except Empty:\n", + " pass\n", + " \n", + " # Check tunnel output\n", + " try:\n", + " prefix, line = tunnel_queue.get(timeout=0.1)\n", + " print(f\"{prefix} {line}\", end='')\n", + " \n", + " # Look for public URL\n", + " if public_url is None:\n", + " url_match = url_pattern.search(line)\n", + " if url_match:\n", + " public_url = url_match.group(0)\n", + " print(f\"\\n{'=' * 70}\")\n", + " print(f\"šŸ”— PUBLIC URL: {public_url}\")\n", + " print(f\"{'=' * 70}\\n\")\n", + " print(\"āœ… Unsloth Studio is now accessible!\\n\")\n", + " if setup_token:\n", + " print(f\"šŸ“ Next steps:\")\n", + " print(f\" 1. Open: {public_url}\")\n", + " print(f\" 2. Use setup token: {setup_token}\")\n", + " print(f\" 3. Create your admin account\\n\")\n", + " print(f\"{'=' * 70}\\n\")\n", + " except Empty:\n", + " pass\n", + " \n", + " # Check if processes are still running\n", + " if backend_process.poll() is not None:\n", + " print(\"\\nāŒ Backend server stopped unexpectedly\")\n", + " break\n", + " if tunnel_process.poll() is not None:\n", + " print(\"\\nāŒ Tunnel stopped unexpectedly\")\n", + " break\n", + " \n", + " time.sleep(0.1)\n", + "\n", + "except KeyboardInterrupt:\n", + " print(\"\\n\\nšŸ›‘ Shutting down...\")\n", + "finally:\n", + " backend_process.terminate()\n", + " tunnel_process.terminate()\n", + " backend_process.wait()\n", + " tunnel_process.wait()\n", + " print(\"āœ… Server stopped\")" + ] + }, + { + "cell_type": "markdown", + "id": "356ec946", + "metadata": {}, + "source": [ + "## āš™ļø Optional: Configure Hugging Face Token\n", + "\n", + "If you want to use gated models or push to Hugging Face Hub, set your token here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d18f8728", + "metadata": {}, + "outputs": [], + "source": [ + "# Optional: Set your Hugging Face token\n", + "# Get your token from: https://huggingface.co/settings/tokens\n", + "\n", + "from huggingface_hub import login\n", + "\n", + "# Uncomment and set your token\n", + "# HF_TOKEN = \"hf_...\"\n", + "# login(token=HF_TOKEN)\n", + "# print(\"āœ… Logged in to Hugging Face\")\n", + "\n", + "print(\"šŸ’” To set your HF token, uncomment the code above or use the UI settings\")" + ] + }, + { + "cell_type": "markdown", + "id": "77b1b874", + "metadata": {}, + "source": [ + "## šŸ”§ Troubleshooting\n", + "\n", + "### Server won't start\n", + "- Make sure you're using a **GPU runtime** (Runtime → Change runtime type)\n", + "- Check that all previous cells completed successfully\n", + "- Try restarting the runtime and running all cells again\n", + "\n", + "### Can't access the URL\n", + "- The Cloudflare tunnel URL is temporary and changes each time\n", + "- Make sure the server is still running (cell shows \"šŸ”— PUBLIC URL\")\n", + "- Try opening the URL in an incognito/private window\n", + "\n", + "### Training fails\n", + "- Check that you have enough GPU memory for your model\n", + "- Try using 4-bit quantization (enabled by default)\n", + "- Use smaller batch sizes if you get OOM errors\n", + "\n", + "### Lost setup token\n", + "- The token is shown once in the server output above\n", + "- If you need a new one, delete `/content/new-ui-prototype/studio/backend/auth.db` and restart\n", + "\n", + "---\n", + "\n", + "## šŸ“š Additional Resources\n", + "\n", + "- **GitHub Repository**: https://github.com/unslothai/new-ui-prototype/tree/nightly\n", + "- **Unsloth Documentation**: https://github.com/unslothai/unsloth\n", + "- **API Documentation**: Access `/docs` on your running instance\n", + "\n", + "---\n", + "\n", + "## šŸ’¾ Saving Your Work\n", + "\n", + "To save trained models and datasets:\n", + "\n", + "1. **Mount Google Drive:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8543c470", + "metadata": {}, + "outputs": [], + "source": [ + "from google.colab import drive\n", + "drive.mount('/content/drive')\n", + "\n", + "# Create directory for outputs\n", + "!mkdir -p /content/drive/MyDrive/unsloth_studio_outputs" + ] + }, + { + "cell_type": "markdown", + "id": "f964762d", + "metadata": {}, + "source": [ + "2. **Copy outputs to Drive:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2a3c1c7", + "metadata": {}, + "outputs": [], + "source": [ + "# Copy trained models to Google Drive\n", + "!cp -r /content/new-ui-prototype/outputs/* /content/drive/MyDrive/unsloth_studio_outputs/\n", + "print(\"āœ… Outputs saved to Google Drive\")" + ] + }, + { + "cell_type": "markdown", + "id": "67fcb4db", + "metadata": {}, + "source": [ + "---\n", + "\n", + "## šŸŽÆ Quick Training Tips\n", + "\n", + "### Recommended Settings for Colab Free (T4 GPU)\n", + "\n", + "| Setting | Value | Note |\n", + "|---------|-------|------|\n", + "| Model | `unsloth/Qwen2.5-1.5B-Instruct` | Small but capable |\n", + "| Training Method | LoRA/QLoRA | Memory efficient |\n", + "| 4-bit Quantization | āœ… Enabled | Reduces memory usage |\n", + "| Batch Size | 2-4 | Depends on model size |\n", + "| Context Length | 2048 | Balance memory/performance |\n", + "| LoRA Rank | 16-64 | Higher = more capacity |\n", + "| Gradient Accumulation | 4 | Simulates larger batch |\n", + "\n", + "### Sample Datasets (Quick Start)\n", + "- `mlabonne/FineTome-100k` - General instruction following\n", + "- `HuggingFaceH4/no_robots` - Clean chat conversations\n", + "- `vicgalle/alpaca-gpt4` - High-quality instructions\n", + "\n", + "---\n", + "\n", + "**šŸŽ‰ Happy Fine-tuning!**" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} 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() From bebb45d8471ccec612b5cfa59d41b3fbe845be36 Mon Sep 17 00:00:00 2001 From: Leo Borcherding Date: Tue, 17 Feb 2026 05:03:59 -0600 Subject: [PATCH 2/9] fix: Clean up notebook to just 2 cells Remove all the overcomplicated markdown and extra cells. Now it's exactly like the POC: setup and start only. --- Unsloth_Studio_Colab.ipynb | 522 ------------------------------------- 1 file changed, 522 deletions(-) diff --git a/Unsloth_Studio_Colab.ipynb b/Unsloth_Studio_Colab.ipynb index 7fe4805b45..b4ac1a5186 100644 --- a/Unsloth_Studio_Colab.ipynb +++ b/Unsloth_Studio_Colab.ipynb @@ -50,528 +50,6 @@ "from colab import start\n", "start()" ] - }, - { - "cell_type": "markdown", - "id": "c98b28eb", - "metadata": {}, - "source": [ - "
\n", - " \n", - "
\n", - "\n", - "# 🦄 Unsloth Studio on Google Colab\n", - "\n", - "A modern, full-stack web interface for fine-tuning, managing, and chatting with large language models.\n", - "\n", - "**Features:**\n", - "- šŸŽÆ **Training**: LoRA/QLoRA fine-tuning with real-time progress streaming\n", - "- šŸ¤– **Model Management**: Browse and load Hugging Face models\n", - "- šŸ’¬ **Inference**: Interactive chat playground\n", - "- šŸ“Š **Dataset Tools**: Upload and preview datasets\n", - "- šŸš€ **Export**: Push trained adapters to Hugging Face Hub\n", - "\n", - "---\n", - "\n", - "**āš ļø Important Notes:**\n", - "- **Private Repository**: You'll need a GitHub Personal Access Token to clone\n", - "- Use a **GPU runtime** for training (Runtime → Change runtime type → T4 GPU)\n", - "- The notebook will expose the UI via **Cloudflare Tunnel** (no account needed)\n", - "- Your first launch will generate a **setup token** for creating an admin account\n", - "\n", - "---\n", - "\n", - "**Repository**: [github.com/unslothai/new-ui-prototype](https://github.com/unslothai/new-ui-prototype/tree/nightly)" - ] - }, - { - "cell_type": "markdown", - "id": "9a0cb9d9", - "metadata": {}, - "source": [ - "## šŸ“‹ Step 1: Install System Dependencies\n", - "\n", - "Install Node.js and required system packages." - ] - }, - { - "cell_type": "markdown", - "id": "0b08e19f", - "metadata": {}, - "source": [ - "## šŸ” Step 1.5: Authenticate with GitHub (Private Repo)\n", - "\n", - "Since this is a private repository, you need to authenticate with GitHub. \n", - "\n", - "**Get a Personal Access Token (classic):**\n", - "1. Go to https://github.com/settings/tokens\n", - "2. Click \"Generate new token (classic)\"\n", - "3. Give it `repo` scope\n", - "4. Copy the token\n", - "\n", - "**Or use GitHub CLI:**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a16a1ed9", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from getpass import getpass\n", - "\n", - "# Option 1: Use GitHub Personal Access Token\n", - "print(\"šŸ” GitHub Authentication Required (Private Repo)\")\n", - "print(\"=\" * 60)\n", - "print(\"Get a token from: https://github.com/settings/tokens\")\n", - "print(\"Required scope: 'repo'\")\n", - "print(\"=\" * 60)\n", - "\n", - "github_token = getpass(\"Enter your GitHub Personal Access Token: \")\n", - "\n", - "if github_token:\n", - " # Store token for git operations\n", - " os.environ['GITHUB_TOKEN'] = github_token\n", - " print(\"āœ… Token stored (will be used for cloning)\")\n", - "else:\n", - " print(\"āš ļø No token provided - clone may fail for private repo\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4b4ad8e1", - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "# Install Node.js 20.x\n", - "echo \"šŸ“¦ Installing Node.js...\"\n", - "curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -\n", - "sudo apt-get install -y nodejs\n", - "\n", - "# Verify installation\n", - "echo \"āœ… Node.js $(node -v) installed\"\n", - "echo \"āœ… npm $(npm -v) installed\"" - ] - }, - { - "cell_type": "markdown", - "id": "7065daf9", - "metadata": {}, - "source": [ - "## šŸ“„ Step 2: Clone Repository and Install Python Dependencies\n", - "\n", - "Clone the repository and install Unsloth + backend dependencies." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6c563597", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from pathlib import Path\n", - "\n", - "# Clone repository (use nightly branch)\n", - "repo_path = Path(\"/content/new-ui-prototype\")\n", - "if not repo_path.exists():\n", - " print(\"šŸ“„ Cloning repository...\")\n", - " \n", - " # Use token if available (for private repo)\n", - " github_token = os.environ.get('GITHUB_TOKEN', '')\n", - " if github_token:\n", - " # Clone with token embedded in URL\n", - " repo_url = f\"https://{github_token}@github.com/unslothai/new-ui-prototype.git\"\n", - " !git clone -b nightly {repo_url}\n", - " else:\n", - " # Try without token (will work for public repo)\n", - " !git clone -b nightly https://github.com/unslothai/new-ui-prototype.git\n", - "else:\n", - " print(\"āœ… Repository already cloned\")\n", - "\n", - "# Change to repo directory\n", - "os.chdir(repo_path)\n", - "print(f\"šŸ“‚ Working directory: {os.getcwd()}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1266536b", - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "# Install Unsloth\n", - "echo \"šŸ“¦ Installing Unsloth...\"\n", - "pip install --no-cache-dir unsloth\n", - "\n", - "# Install backend dependencies\n", - "echo \"šŸ“¦ Installing backend dependencies...\"\n", - "cd studio/backend\n", - "pip install --no-cache-dir -r requirements.txt\n", - "\n", - "# Install additional required packages\n", - "pip install --no-cache-dir \\\n", - " huggingface_hub \\\n", - " datasets \\\n", - " python-jose[cryptography] \\\n", - " passlib[bcrypt] \\\n", - " python-multipart \\\n", - " matplotlib \\\n", - " pandas \\\n", - " ujson\n", - "\n", - "echo \"āœ… Python dependencies installed\"" - ] - }, - { - "cell_type": "markdown", - "id": "30ca4f1d", - "metadata": {}, - "source": [ - "## šŸŽØ Step 3: Build Frontend\n", - "\n", - "Build the React/TypeScript frontend." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb65b9b2", - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "cd studio/frontend\n", - "\n", - "echo \"šŸ“¦ Installing frontend dependencies...\"\n", - "npm install --legacy-peer-deps\n", - "\n", - "echo \"šŸ—ļø Building frontend...\"\n", - "npm run build\n", - "\n", - "echo \"āœ… Frontend built to studio/frontend/dist\"" - ] - }, - { - "cell_type": "markdown", - "id": "87ecf129", - "metadata": {}, - "source": [ - "## 🌐 Step 4: Set Up Cloudflare Tunnel\n", - "\n", - "Install cloudflared to expose the backend server to the internet." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e6bb2086", - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "# Install cloudflared\n", - "if ! command -v cloudflared &> /dev/null; then\n", - " echo \"šŸ“¦ Installing Cloudflare Tunnel...\"\n", - " wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb\n", - " sudo dpkg -i cloudflared-linux-amd64.deb\n", - " rm cloudflared-linux-amd64.deb\n", - " echo \"āœ… Cloudflared installed\"\n", - "else\n", - " echo \"āœ… Cloudflared already installed\"\n", - "fi\n", - "\n", - "cloudflared version" - ] - }, - { - "cell_type": "markdown", - "id": "3c48401b", - "metadata": {}, - "source": [ - "## šŸš€ Step 5: Launch Unsloth Studio\n", - "\n", - "Start the backend server and create a public tunnel.\n", - "\n", - "**āš ļø IMPORTANT:**\n", - "1. Look for the **šŸ”— Public URL** in the output below\n", - "2. On first launch, look for the **šŸ”‘ Setup Token** (one-time use)\n", - "3. Open the URL in your browser\n", - "4. Use the setup token to create your admin account\n", - "\n", - "**Note**: This cell will run continuously. To stop the server, click the ā¹ļø stop button or interrupt the kernel." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "80f2f7c5", - "metadata": {}, - "outputs": [], - "source": [ - "import subprocess\n", - "import time\n", - "import sys\n", - "import re\n", - "from threading import Thread\n", - "from queue import Queue, Empty\n", - "\n", - "def stream_output(pipe, queue, prefix=\"\"):\n", - " \"\"\"Stream subprocess output to queue\"\"\"\n", - " for line in iter(pipe.readline, b''):\n", - " queue.put((prefix, line.decode('utf-8')))\n", - " pipe.close()\n", - "\n", - "# Change to backend directory\n", - "os.chdir(\"/content/new-ui-prototype/studio/backend\")\n", - "\n", - "print(\"šŸš€ Starting Unsloth Studio...\\n\")\n", - "print(\"=\" * 70)\n", - "\n", - "# Start backend server\n", - "backend_process = subprocess.Popen(\n", - " [\"python\", \"run.py\"],\n", - " stdout=subprocess.PIPE,\n", - " stderr=subprocess.PIPE,\n", - " bufsize=1\n", - ")\n", - "\n", - "# Wait for server to start\n", - "print(\"ā³ Waiting for backend server to start...\")\n", - "time.sleep(10)\n", - "\n", - "# Start cloudflared tunnel\n", - "tunnel_process = subprocess.Popen(\n", - " [\"cloudflared\", \"tunnel\", \"--url\", \"http://localhost:8000\"],\n", - " stdout=subprocess.PIPE,\n", - " stderr=subprocess.PIPE,\n", - " bufsize=1\n", - ")\n", - "\n", - "# Create queues for output\n", - "backend_queue = Queue()\n", - "tunnel_queue = Queue()\n", - "\n", - "# Start output streaming threads\n", - "Thread(target=stream_output, args=(backend_process.stdout, backend_queue, \"[BACKEND]\"), daemon=True).start()\n", - "Thread(target=stream_output, args=(backend_process.stderr, backend_queue, \"[BACKEND]\"), daemon=True).start()\n", - "Thread(target=stream_output, args=(tunnel_process.stdout, tunnel_queue, \"[TUNNEL]\"), daemon=True).start()\n", - "Thread(target=stream_output, args=(tunnel_process.stderr, tunnel_queue, \"[TUNNEL]\"), daemon=True).start()\n", - "\n", - "# Monitor output and extract public URL and setup token\n", - "public_url = None\n", - "setup_token = None\n", - "url_pattern = re.compile(r'https://[a-zA-Z0-9-]+\\.trycloudflare\\.com')\n", - "token_pattern = re.compile(r'Setup token: ([a-f0-9-]+)')\n", - "\n", - "print(\"\\nšŸ“” Monitoring server output...\\n\")\n", - "print(\"=\" * 70)\n", - "\n", - "try:\n", - " while True:\n", - " # Check backend output\n", - " try:\n", - " prefix, line = backend_queue.get(timeout=0.1)\n", - " print(f\"{prefix} {line}\", end='')\n", - " \n", - " # Look for setup token\n", - " if setup_token is None:\n", - " token_match = token_pattern.search(line)\n", - " if token_match:\n", - " setup_token = token_match.group(1)\n", - " print(f\"\\n{'=' * 70}\")\n", - " print(f\"šŸ”‘ SETUP TOKEN (save this!): {setup_token}\")\n", - " print(f\"{'=' * 70}\\n\")\n", - " except Empty:\n", - " pass\n", - " \n", - " # Check tunnel output\n", - " try:\n", - " prefix, line = tunnel_queue.get(timeout=0.1)\n", - " print(f\"{prefix} {line}\", end='')\n", - " \n", - " # Look for public URL\n", - " if public_url is None:\n", - " url_match = url_pattern.search(line)\n", - " if url_match:\n", - " public_url = url_match.group(0)\n", - " print(f\"\\n{'=' * 70}\")\n", - " print(f\"šŸ”— PUBLIC URL: {public_url}\")\n", - " print(f\"{'=' * 70}\\n\")\n", - " print(\"āœ… Unsloth Studio is now accessible!\\n\")\n", - " if setup_token:\n", - " print(f\"šŸ“ Next steps:\")\n", - " print(f\" 1. Open: {public_url}\")\n", - " print(f\" 2. Use setup token: {setup_token}\")\n", - " print(f\" 3. Create your admin account\\n\")\n", - " print(f\"{'=' * 70}\\n\")\n", - " except Empty:\n", - " pass\n", - " \n", - " # Check if processes are still running\n", - " if backend_process.poll() is not None:\n", - " print(\"\\nāŒ Backend server stopped unexpectedly\")\n", - " break\n", - " if tunnel_process.poll() is not None:\n", - " print(\"\\nāŒ Tunnel stopped unexpectedly\")\n", - " break\n", - " \n", - " time.sleep(0.1)\n", - "\n", - "except KeyboardInterrupt:\n", - " print(\"\\n\\nšŸ›‘ Shutting down...\")\n", - "finally:\n", - " backend_process.terminate()\n", - " tunnel_process.terminate()\n", - " backend_process.wait()\n", - " tunnel_process.wait()\n", - " print(\"āœ… Server stopped\")" - ] - }, - { - "cell_type": "markdown", - "id": "356ec946", - "metadata": {}, - "source": [ - "## āš™ļø Optional: Configure Hugging Face Token\n", - "\n", - "If you want to use gated models or push to Hugging Face Hub, set your token here." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d18f8728", - "metadata": {}, - "outputs": [], - "source": [ - "# Optional: Set your Hugging Face token\n", - "# Get your token from: https://huggingface.co/settings/tokens\n", - "\n", - "from huggingface_hub import login\n", - "\n", - "# Uncomment and set your token\n", - "# HF_TOKEN = \"hf_...\"\n", - "# login(token=HF_TOKEN)\n", - "# print(\"āœ… Logged in to Hugging Face\")\n", - "\n", - "print(\"šŸ’” To set your HF token, uncomment the code above or use the UI settings\")" - ] - }, - { - "cell_type": "markdown", - "id": "77b1b874", - "metadata": {}, - "source": [ - "## šŸ”§ Troubleshooting\n", - "\n", - "### Server won't start\n", - "- Make sure you're using a **GPU runtime** (Runtime → Change runtime type)\n", - "- Check that all previous cells completed successfully\n", - "- Try restarting the runtime and running all cells again\n", - "\n", - "### Can't access the URL\n", - "- The Cloudflare tunnel URL is temporary and changes each time\n", - "- Make sure the server is still running (cell shows \"šŸ”— PUBLIC URL\")\n", - "- Try opening the URL in an incognito/private window\n", - "\n", - "### Training fails\n", - "- Check that you have enough GPU memory for your model\n", - "- Try using 4-bit quantization (enabled by default)\n", - "- Use smaller batch sizes if you get OOM errors\n", - "\n", - "### Lost setup token\n", - "- The token is shown once in the server output above\n", - "- If you need a new one, delete `/content/new-ui-prototype/studio/backend/auth.db` and restart\n", - "\n", - "---\n", - "\n", - "## šŸ“š Additional Resources\n", - "\n", - "- **GitHub Repository**: https://github.com/unslothai/new-ui-prototype/tree/nightly\n", - "- **Unsloth Documentation**: https://github.com/unslothai/unsloth\n", - "- **API Documentation**: Access `/docs` on your running instance\n", - "\n", - "---\n", - "\n", - "## šŸ’¾ Saving Your Work\n", - "\n", - "To save trained models and datasets:\n", - "\n", - "1. **Mount Google Drive:**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8543c470", - "metadata": {}, - "outputs": [], - "source": [ - "from google.colab import drive\n", - "drive.mount('/content/drive')\n", - "\n", - "# Create directory for outputs\n", - "!mkdir -p /content/drive/MyDrive/unsloth_studio_outputs" - ] - }, - { - "cell_type": "markdown", - "id": "f964762d", - "metadata": {}, - "source": [ - "2. **Copy outputs to Drive:**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e2a3c1c7", - "metadata": {}, - "outputs": [], - "source": [ - "# Copy trained models to Google Drive\n", - "!cp -r /content/new-ui-prototype/outputs/* /content/drive/MyDrive/unsloth_studio_outputs/\n", - "print(\"āœ… Outputs saved to Google Drive\")" - ] - }, - { - "cell_type": "markdown", - "id": "67fcb4db", - "metadata": {}, - "source": [ - "---\n", - "\n", - "## šŸŽÆ Quick Training Tips\n", - "\n", - "### Recommended Settings for Colab Free (T4 GPU)\n", - "\n", - "| Setting | Value | Note |\n", - "|---------|-------|------|\n", - "| Model | `unsloth/Qwen2.5-1.5B-Instruct` | Small but capable |\n", - "| Training Method | LoRA/QLoRA | Memory efficient |\n", - "| 4-bit Quantization | āœ… Enabled | Reduces memory usage |\n", - "| Batch Size | 2-4 | Depends on model size |\n", - "| Context Length | 2048 | Balance memory/performance |\n", - "| LoRA Rank | 16-64 | Higher = more capacity |\n", - "| Gradient Accumulation | 4 | Simulates larger batch |\n", - "\n", - "### Sample Datasets (Quick Start)\n", - "- `mlabonne/FineTome-100k` - General instruction following\n", - "- `HuggingFaceH4/no_robots` - Clean chat conversations\n", - "- `vicgalle/alpaca-gpt4` - High-quality instructions\n", - "\n", - "---\n", - "\n", - "**šŸŽ‰ Happy Fine-tuning!**" - ] } ], "metadata": { From 1530d1c16511a28c58c3f63beff3959863b5563d Mon Sep 17 00:00:00 2001 From: Leo Borcherding Date: Tue, 17 Feb 2026 05:07:46 -0600 Subject: [PATCH 3/9] fix: Add GitHub authentication cell for private repo - Add cell 1 for token input (getpass) - Update clone command to use token from environment - Now 3 cells: auth, setup, start --- Unsloth_Studio_Colab.ipynb | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/Unsloth_Studio_Colab.ipynb b/Unsloth_Studio_Colab.ipynb index b4ac1a5186..e5d4f7f058 100644 --- a/Unsloth_Studio_Colab.ipynb +++ b/Unsloth_Studio_Colab.ipynb @@ -11,8 +11,13 @@ "# Setup: Clone repo and build frontend\n", "# ===========================================\n", "\n", - "# Clone repository\n", - "!git clone https://github.com/unslothai/new-ui-prototype.git\n", + "# Clone repository with GitHub token\n", + "import os\n", + "github_token = os.environ.get('GITHUB_TOKEN', '')\n", + "if github_token:\n", + " !git clone https://{github_token}@github.com/unslothai/new-ui-prototype.git\n", + "else:\n", + " !git clone https://github.com/unslothai/new-ui-prototype.git\n", "%cd /content/new-ui-prototype\n", "\n", "# Install Node.js 20.x\n", @@ -34,6 +39,29 @@ "print(\"āœ… Python dependencies installed\")" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "26b0e164", + "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, From 2c5e0ce606584e1122cb222f79423d3a93b9b7e0 Mon Sep 17 00:00:00 2001 From: Leo Borcherding Date: Tue, 17 Feb 2026 12:54:07 -0600 Subject: [PATCH 4/9] Simplify notebook to use existing setup.sh script --- Unsloth_Studio_Colab.ipynb | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/Unsloth_Studio_Colab.ipynb b/Unsloth_Studio_Colab.ipynb index e5d4f7f058..4f4767aaac 100644 --- a/Unsloth_Studio_Colab.ipynb +++ b/Unsloth_Studio_Colab.ipynb @@ -8,7 +8,7 @@ "outputs": [], "source": [ "# ===========================================\n", - "# Setup: Clone repo and build frontend\n", + "# Setup: Clone repo and run setup\n", "# ===========================================\n", "\n", "# Clone repository with GitHub token\n", @@ -20,23 +20,9 @@ " !git clone https://github.com/unslothai/new-ui-prototype.git\n", "%cd /content/new-ui-prototype\n", "\n", - "# Install Node.js 20.x\n", - "!curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - > /dev/null 2>&1\n", - "!sudo apt-get install -y nodejs > /dev/null 2>&1\n", - "print(\"āœ… Node.js installed\")\n", - "\n", - "# Build frontend\n", - "%cd studio/frontend\n", - "!npm install --silent\n", - "!npm run build\n", - "print(\"āœ… Frontend built\")\n", - "\n", - "# Install Python dependencies\n", - "%cd /content/new-ui-prototype\n", - "!pip install -q unsloth\n", - "!pip install -q -r studio/backend/requirements.txt\n", - "!pip install -q huggingface_hub datasets python-jose[cryptography] passlib[bcrypt] python-multipart matplotlib pandas ujson\n", - "print(\"āœ… Python dependencies installed\")" + "# Run setup script\n", + "!chmod +x setup.sh\n", + "!./setup.sh" ] }, { From f48d81579786bc9407eddbbd7c31643fee088ded Mon Sep 17 00:00:00 2001 From: Leo Borcherding Date: Tue, 17 Feb 2026 13:01:16 -0600 Subject: [PATCH 5/9] Clone feature/colab-notebook branch for colab.py --- Unsloth_Studio_Colab.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Unsloth_Studio_Colab.ipynb b/Unsloth_Studio_Colab.ipynb index 4f4767aaac..bedaea85c0 100644 --- a/Unsloth_Studio_Colab.ipynb +++ b/Unsloth_Studio_Colab.ipynb @@ -15,9 +15,9 @@ "import os\n", "github_token = os.environ.get('GITHUB_TOKEN', '')\n", "if github_token:\n", - " !git clone https://{github_token}@github.com/unslothai/new-ui-prototype.git\n", + " !git clone -b feature/colab-notebook https://{github_token}@github.com/unslothai/new-ui-prototype.git\n", "else:\n", - " !git clone https://github.com/unslothai/new-ui-prototype.git\n", + " !git clone -b feature/colab-notebook https://github.com/unslothai/new-ui-prototype.git\n", "%cd /content/new-ui-prototype\n", "\n", "# Run setup script\n", From 057f3b96286fb610b658e7b54353f54267d1eae3 Mon Sep 17 00:00:00 2001 From: Leo Borcherding Date: Tue, 17 Feb 2026 13:27:05 -0600 Subject: [PATCH 6/9] Remove unnecessary if statement for token check --- Unsloth_Studio_Colab.ipynb | 48 +++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/Unsloth_Studio_Colab.ipynb b/Unsloth_Studio_Colab.ipynb index bedaea85c0..19e72c20d7 100644 --- a/Unsloth_Studio_Colab.ipynb +++ b/Unsloth_Studio_Colab.ipynb @@ -3,32 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27e68f91", - "metadata": {}, - "outputs": [], - "source": [ - "# ===========================================\n", - "# Setup: Clone repo and run setup\n", - "# ===========================================\n", - "\n", - "# Clone repository with GitHub token\n", - "import os\n", - "github_token = os.environ.get('GITHUB_TOKEN', '')\n", - "if github_token:\n", - " !git clone -b feature/colab-notebook https://{github_token}@github.com/unslothai/new-ui-prototype.git\n", - "else:\n", - " !git clone -b feature/colab-notebook https://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": "26b0e164", + "id": "f04a9b46", "metadata": {}, "outputs": [], "source": [ @@ -48,6 +23,27 @@ "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, From 25bf39c1e607715657f806d81ceb58b1257bb38d Mon Sep 17 00:00:00 2001 From: Leo Borcherding Date: Tue, 17 Feb 2026 13:57:45 -0600 Subject: [PATCH 7/9] Detect Colab environment and upgrade npm directly instead of using nvm --- setup.sh | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/setup.sh b/setup.sh index bb5da805a8..a88a9909ff 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..." From 7cac94c930beca1159296148b0f51433659c9044 Mon Sep 17 00:00:00 2001 From: Leo Borcherding Date: Tue, 17 Feb 2026 14:21:56 -0600 Subject: [PATCH 8/9] Skip venv creation in Colab, install packages directly --- setup.sh | 61 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/setup.sh b/setup.sh index a88a9909ff..98d878421f 100755 --- a/setup.sh +++ b/setup.sh @@ -149,18 +149,30 @@ fi BEST_VER=$("$BEST_PY" --version 2>&1 | awk '{print $2}') echo "āœ… Using $BEST_PY ($BEST_VER) — compatible (≤ 3.12.x)" -"$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 unsloth-zoo unsloth -echo " Installing studio dependencies..." -run_quiet "pip install extras" pip install typer fastapi uvicorn pydantic matplotlib pandas nest_asyncio "datasets==4.3.0" pyjwt easydict addict -echo "āœ… Python 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 unsloth-zoo unsloth + echo " Installing studio dependencies..." + run_quiet "pip install extras" pip install typer fastapi uvicorn pydantic matplotlib pandas nest_asyncio "datasets==4.3.0" pyjwt easydict addict + echo "āœ… Python dependencies installed" +else + # Local: create 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 unsloth-zoo unsloth + echo " Installing studio dependencies..." + run_quiet "pip install extras" pip install typer fastapi uvicorn pydantic matplotlib pandas nest_asyncio "datasets==4.3.0" pyjwt easydict addict + echo "āœ… Python dependencies installed" +fi -# ── 7. Add shell alias ── +# ── 7. 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" @@ -203,16 +215,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 "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" From 7818e3efc8a00b50fec5d86e1174c6137ecbd32f Mon Sep 17 00:00:00 2001 From: Leo Borcherding Date: Tue, 17 Feb 2026 15:29:29 -0600 Subject: [PATCH 9/9] Add GPU check as first cell in notebook --- Unsloth_Studio_Colab.ipynb | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Unsloth_Studio_Colab.ipynb b/Unsloth_Studio_Colab.ipynb index 19e72c20d7..fb2ca72604 100644 --- a/Unsloth_Studio_Colab.ipynb +++ b/Unsloth_Studio_Colab.ipynb @@ -1,5 +1,32 @@ { "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,