| |
| """Fix Colab โ use cloudflared for tunnel (free, no auth) + verify server starts.""" |
| import subprocess, os, json |
|
|
| TOKEN = "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" |
| subprocess.run(["git", "clone", f"https://{TOKEN}@github.com/ticketguy/littlefig.git", "/app/littlefig"], check=True) |
| os.chdir("/app/littlefig") |
| subprocess.run(["git", "config", "user.name", "0xticketguy"], check=True) |
| subprocess.run(["git", "config", "user.email", "0xticketguy@harboria.dev"], check=True) |
|
|
| colab = { |
| "nbformat": 4, |
| "nbformat_minor": 0, |
| "metadata": { |
| "colab": {"provenance": [], "gpuType": "T4"}, |
| "kernelspec": {"name": "python3", "display_name": "Python 3"}, |
| "accelerator": "GPU" |
| }, |
| "cells": [ |
| {"cell_type": "markdown", "metadata": {}, "source": [ |
| "# ๐ Little Fig Studio\n", |
| "\n", |
| "**Train any LLM on any hardware.** Select your model, configure, launch.\n", |
| "\n", |
| "| Feature | Result |\n", |
| "|---|---|\n", |
| "| Quantization | Beats NF4 on 156/156 layers |\n", |
| "| GPU Speed | 7ร faster than BnB NF4 |\n", |
| "| CPU Training | 1.1B model in 400MB RAM |\n", |
| "| Optimizer | FigMeZO (โ18.6%, original research) |\n", |
| "\n", |
| "**Run all cells below โ**" |
| ]}, |
| {"cell_type": "code", "metadata": {}, "source": [ |
| "# Step 1: Install\n", |
| "!git clone https://github.com/ticketguy/littlefig.git /content/littlefig --quiet 2>/dev/null || (cd /content/littlefig && git pull --quiet)\n", |
| "!cd /content/littlefig && pip install -q -e \".[train]\"\n", |
| "!pip install -q uvicorn fastapi python-multipart websockets\n", |
| "\n", |
| "# Download cloudflared (free tunnel, no signup)\n", |
| "!wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -O /usr/local/bin/cloudflared\n", |
| "!chmod +x /usr/local/bin/cloudflared\n", |
| "\n", |
| "import torch, sys\n", |
| "sys.path.insert(0, '/content/littlefig/src')\n", |
| "print(f'\\nโ
Installed | PyTorch {torch.__version__} | GPU: {torch.cuda.get_device_name() if torch.cuda.is_available() else \"CPU\"}')" |
| ], "execution_count": None, "outputs": []}, |
| {"cell_type": "code", "metadata": {}, "source": [ |
| "# Step 2: Launch Little Fig Studio\n", |
| "import subprocess, time, threading, re\n", |
| "\n", |
| "# Start the web server\n", |
| "server = subprocess.Popen(\n", |
| " ['python', '-m', 'uvicorn', 'little_fig.web.server:app',\n", |
| " '--host', '0.0.0.0', '--port', '8888'],\n", |
| " cwd='/content/littlefig/src',\n", |
| " stdout=subprocess.PIPE, stderr=subprocess.PIPE\n", |
| ")\n", |
| "time.sleep(3)\n", |
| "\n", |
| "# Check if server is running\n", |
| "import urllib.request\n", |
| "try:\n", |
| " urllib.request.urlopen('http://localhost:8888', timeout=2)\n", |
| " print('โ
Server running on port 8888')\n", |
| "except:\n", |
| " # Server might return non-200 but still be alive\n", |
| " print('โ ๏ธ Server may not have started correctly. Check errors below:')\n", |
| " try:\n", |
| " err = server.stderr.read(1000).decode()\n", |
| " print(err[:500])\n", |
| " except:\n", |
| " pass\n", |
| "\n", |
| "# Start cloudflared tunnel (free, no account needed)\n", |
| "tunnel = subprocess.Popen(\n", |
| " ['cloudflared', 'tunnel', '--url', 'http://localhost:8888'],\n", |
| " stdout=subprocess.PIPE, stderr=subprocess.PIPE\n", |
| ")\n", |
| "time.sleep(5)\n", |
| "\n", |
| "# Read tunnel URL from stderr\n", |
| "tunnel_url = None\n", |
| "try:\n", |
| " output = tunnel.stderr.read(4096).decode()\n", |
| " urls = re.findall(r'https://[a-z0-9-]+\\.trycloudflare\\.com', output)\n", |
| " if urls:\n", |
| " tunnel_url = urls[0]\n", |
| "except:\n", |
| " pass\n", |
| "\n", |
| "if tunnel_url:\n", |
| " print(f'\\n๐ Little Fig Studio is LIVE!')\n", |
| " print(f'\\n ๐ {tunnel_url}')\n", |
| " print(f'\\n Open the link above in your browser.')\n", |
| " print(f' Select model โ Configure โ Train')\n", |
| "else:\n", |
| " print('\\nโ ๏ธ Tunnel failed. Using iframe fallback...')\n", |
| " from IPython.display import IFrame, display\n", |
| " # Try Colab proxy\n", |
| " from google.colab.output import eval_js\n", |
| " proxy_url = eval_js(\"google.colab.kernel.proxyPort(8888)\")\n", |
| " print(f' Try: {proxy_url}')\n", |
| " display(IFrame(src=str(proxy_url), width='100%', height=700))\n", |
| "\n", |
| "print('\\n Keep this cell running to keep the server alive.')" |
| ], "execution_count": None, "outputs": []}, |
| {"cell_type": "markdown", "metadata": {}, "source": [ |
| "---\n", |
| "## Alternative: Python API (no UI)\n", |
| "\n", |
| "Change `MODEL` to any HuggingFace model." |
| ]}, |
| {"cell_type": "code", "metadata": {}, "source": [ |
| "from little_fig.engine import FigModel, FigTrainer, FigTrainingConfig\n", |
| "\n", |
| "# === YOUR MODEL HERE ===\n", |
| "MODEL = 'TinyLlama/TinyLlama-1.1B-Chat-v1.0'\n", |
| "# MODEL = 'google/gemma-3-4b-it'\n", |
| "# MODEL = 'Qwen/Qwen2.5-1.5B'\n", |
| "# MODEL = 'microsoft/phi-2'\n", |
| "\n", |
| "model = FigModel.from_pretrained(MODEL, lora_r=16, lora_alpha=32, shared_codebook=True)\n", |
| "print(f'โ
{MODEL} loaded | Trainable: {sum(p.numel() for p in model.parameters() if p.requires_grad):,}')" |
| ], "execution_count": None, "outputs": []}, |
| {"cell_type": "code", "metadata": {}, "source": [ |
| "# Train\n", |
| "config = FigTrainingConfig(\n", |
| " num_epochs=1,\n", |
| " learning_rate=2e-4,\n", |
| " max_seq_length=256,\n", |
| " batch_size=2,\n", |
| " gradient_accumulation_steps=4,\n", |
| " logging_steps=5,\n", |
| " use_packing=True,\n", |
| ")\n", |
| "trainer = FigTrainer(model, config)\n", |
| "trainer.load_dataset('tatsu-lab/alpaca', max_samples=200)\n", |
| "trainer.train()\n", |
| "model.save_adapter('./my_adapter')\n", |
| "print('\\nโ
Training complete! Adapter saved.')" |
| ], "execution_count": None, "outputs": []}, |
| {"cell_type": "markdown", "metadata": {}, "source": [ |
| "---\n", |
| "*0xticketguy / Harboria Labs | AGPL-3.0*" |
| ]} |
| ] |
| } |
|
|
| with open("Little_Fig_Colab.ipynb", "w") as f: |
| json.dump(colab, f, indent=2) |
|
|
| subprocess.run(["git", "add", "-A"], check=True) |
| subprocess.run(["git", "commit", "-m", |
| "Fix Colab: cloudflared tunnel (free, no signup) + fallback to Colab proxy\n\n" |
| "- Uses cloudflared (Cloudflare's free tunnel) instead of ngrok\n" |
| "- No account needed, no auth token\n" |
| "- Falls back to google.colab.output.eval_js proxy if tunnel fails\n" |
| "- Checks if server is actually running before tunneling\n" |
| "- Shows errors if server fails to start"], check=True) |
| subprocess.run(["git", "push", "origin", "main"], check=True) |
| print("โ
Colab fixed with cloudflared tunnel") |
|
|