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.
This commit is contained in:
Leo Borcherding 2026-02-17 05:03:59 -06:00
commit bebb45d847

View file

@ -50,528 +50,6 @@
"from colab import start\n",
"start()"
]
},
{
"cell_type": "markdown",
"id": "c98b28eb",
"metadata": {},
"source": [
"<div align=\"center\">\n",
" <img src=\"https://raw.githubusercontent.com/unslothai/unsloth/main/images/unsloth%20logo%20white%20text.png\" width=\"400\"/>\n",
"</div>\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": {