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
This commit is contained in:
parent
7b8220598e
commit
17df5bf3bf
2 changed files with 674 additions and 0 deletions
584
Unsloth_Studio_Colab.ipynb
Normal file
584
Unsloth_Studio_Colab.ipynb
Normal file
|
|
@ -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": [
|
||||
"<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": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
90
studio/backend/colab.py
Normal file
90
studio/backend/colab.py
Normal file
|
|
@ -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"""
|
||||
<div style="padding: 20px; background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
|
||||
border-radius: 12px; margin: 10px 0; font-family: system-ui, -apple-system, sans-serif;">
|
||||
<h2 style="color: white; margin: 0 0 12px 0; font-size: 24px;">
|
||||
🦥 Unsloth Studio is Ready!
|
||||
</h2>
|
||||
<a href="{url}" target="_blank"
|
||||
style="display: inline-block; padding: 14px 28px; background: white; color: #16a34a;
|
||||
text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 16px;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
|
||||
🚀 Open Unsloth Studio
|
||||
</a>
|
||||
<p style="color: rgba(255,255,255,0.9); margin: 16px 0 0 0; font-size: 13px;
|
||||
word-break: break-all; font-family: monospace;">
|
||||
{url}
|
||||
</p>
|
||||
</div>
|
||||
"""
|
||||
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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue