Serve real AI models from Kaggle's free tier (2× Tesla T4) and reach them from anywhere through cloudflared tunnels. This repo is the single source of truth: thin Kaggle notebooks clone it at session start, all logic lives in the .py modules, and nothing assumes a previous session ever existed. Three stacks: LLMs via llama.cpp (harness.py + model_registry.py), text-to-image via diffusers (image_models.py), video via headless ComfyUI (comfy_bootstrap.py), plus small side-task helpers (tasks.py).
run_studio.ipynb (the UI) or run_model.ipynb (code cells) from this repo.https://…trycloudflare.com URL — that's your server, from any device.stop() (or comfy.stop()), then end the session so you don't burn GPU quota.| Notebook | Stack | Open it when you want |
|---|---|---|
run_studio.ipynb | all three | the point-and-click studio: launch/chat/image/video tabs from one link |
run_model.ipynb | llama.cpp LLMs | the gradio-free code path: an OpenAI-compatible API + built-in chat UI |
Image and video from code (no studio): from image_models import install, load, generate and import comfy_bootstrap as comfy; comfy.install(); comfy.fetch_stack("ltx-2.3"); comfy.start() in any notebook that clones this repo — the module docstrings carry the full recipes.
Model keys are the full Hugging Face repo id (author/name) — the same string you'd see on the model page, so there's no short alias to decode. Registry entries are defaults; override anything per-call:
run("bartowski/Qwen_Qwen3.6-35B-A3B-GGUF", MODELS,
quant="Q3_K_M", # switch quant BY NAME -- resolved to the real filename for you
ctx=16384, # any registry field works: ngl, tensor_split, n_cpu_moe, extra_args...
api_key="secret")
list_quants("bartowski/Qwen_Qwen3.6-35B-A3B-GGUF", MODELS) # every gguf in the repo, with sizes
Unknown quant names fail fast listing what the repo actually offers, and a size check warns before you download something that won't fit (12GB single-T4 / 26GB dual). Edit model_registry.py only to change a default; a proven override deserves promotion into the registry.
Models that aren't in the registry at all: detect_entry("author/AnyModel-GGUF") builds an entry automatically (best-fitting quant + single/dual-GPU placement from real file sizes) — same thing the studio's "import from hugging face" box does, which also prints a paste-ready registry line.
<url>/v1.run_studio.ipynb (or from control_panel import launch_panel; launch_panel(auth=("user","pass"))): four tabs — Launch (llm + quant picker with sizes, launch/stop/status with per-GPU VRAM, cache harvest), Chat (streams from the running model, system-prompt box, card-recommended sampling applied automatically), Image (diffusers spread across both T4s, prompt → png in the browser), Video (boots ComfyUI, shows its GUI url, ships stack workflow JSONs into its workflow browser). Every tab imports new models straight from HF. The share link is public — always set auth (the studio notebook generates a random password and prints it).comfy.queue_workflow(...).trycloudflare.com URLs are publicly reachable by anyone who has them. For LLMs always pass run(..., api_key="something") — the server then requires Authorization: Bearer something. For the Gradio panel always pass auth=. ComfyUI has no auth: treat that URL as a secret and stop the tunnel when done.
One-time, at the end of a session that built things:
harvest_cache() in a cell (or press Harvest cache in the studio) — it stages the built binaries, cloudflared, and this session's ggufs into /kaggle/working with the right names, watching the ~19.5GB quota.llama-server-*/cloudflared; CACHE_DATASET_DIR exists only to pin one when several are attached. Binaries are copied out and re-chmodded (dataset mounts drop the executable bit), cached ggufs are used directly.Quick tunnels mint a new trycloudflare.com URL every session, so anything you point at the API breaks daily. Free fix: Cloudflare dashboard → Zero Trust → Networks → Tunnels → create one, set its public hostname to http://localhost:8080, then add two Kaggle secrets: CF_TUNNEL_TOKEN (the tunnel token — required) and CF_TUNNEL_HOSTNAME (your hostname, so printed URLs are real). Export them like HF_TOKEN and the harness runs your named tunnel instead — same URL every session. Non-8080 ports (ComfyUI) still get quick tunnels.
Every process logs to a file — read the tail, the answer is usually right there:
| Component | Log |
|---|---|
| llama-server | /kaggle/tmp/llama-server.log |
| cloudflared tunnel | /kaggle/tmp/cloudflared.log |
| ComfyUI | /kaggle/tmp/comfyui.log |
| transcribe()'s vllm | /kaggle/tmp/vllm-transcribe.log |
!tail -50 /kaggle/tmp/llama-server.log in a cell. Failed health checks and crashed launches already include the tail in the raised error. Out of disk? Everything big must be under /kaggle/tmp (~60GB ephemeral), never /kaggle/working (~20GB, persists).
| Model | Caveat |
|---|---|
| FLUX.1-dev | Non-commercial license; gated — accept on the model page, needs HF_TOKEN |
| Ideogram 4 (incl. fal instant/fast) | Non-commercial + gated — accept license on the model page, needs HF_TOKEN (Kaggle secret) |
| stable-fast-3d / stable-point-aware-3d | Stability community license + gated — accept on the model pages |
| ltx-10eros / sulphur-2 stacks | Community finetunes aimed at explicit content — NSFW output violates Kaggle ToS |
| Bonsai 27B (ternary + 1-bit) | Needs the PrismML llama.cpp fork (auto-built); published perf is H100 — unverified on T4 kernels, treat first boot as a smoke test |
Abliterated / uncensored entries (-abl) | Reduced refusals — review outputs; you are responsible for what you generate |
| Everything | NSFW generation violates Kaggle ToS — don't; it risks your account |
HF_TOKEN comes from Kaggle Secrets (Add-ons → Secrets) — never hardcode tokens in notebooks.
73 commits
Python
96.1%
Jupyter Notebook
3.9%
Serve real AI models from Kaggle's free tier (2× Tesla T4) and reach them from anywhere through cloudflared tunnels. This repo is the single source of truth: thin Kaggle notebooks clone it at session start, all logic lives in the .py modules, and nothing assumes a previous session ever existed. Three stacks: LLMs via llama.cpp (harness.py + model_registry.py), text-to-image via diffusers (image_models.py), video via headless ComfyUI (comfy_bootstrap.py), plus small side-task helpers (tasks.py).
run_studio.ipynb (the UI) or run_model.ipynb (code cells) from this repo.https://…trycloudflare.com URL — that's your server, from any device.stop() (or comfy.stop()), then end the session so you don't burn GPU quota.| Notebook | Stack | Open it when you want |
|---|---|---|
run_studio.ipynb | all three | the point-and-click studio: launch/chat/image/video tabs from one link |
run_model.ipynb | llama.cpp LLMs | the gradio-free code path: an OpenAI-compatible API + built-in chat UI |
Image and video from code (no studio): from image_models import install, load, generate and import comfy_bootstrap as comfy; comfy.install(); comfy.fetch_stack("ltx-2.3"); comfy.start() in any notebook that clones this repo — the module docstrings carry the full recipes.
Model keys are the full Hugging Face repo id (author/name) — the same string you'd see on the model page, so there's no short alias to decode. Registry entries are defaults; override anything per-call:
run("bartowski/Qwen_Qwen3.6-35B-A3B-GGUF", MODELS,
quant="Q3_K_M", # switch quant BY NAME -- resolved to the real filename for you
ctx=16384, # any registry field works: ngl, tensor_split, n_cpu_moe, extra_args...
api_key="secret")
list_quants("bartowski/Qwen_Qwen3.6-35B-A3B-GGUF", MODELS) # every gguf in the repo, with sizes
Unknown quant names fail fast listing what the repo actually offers, and a size check warns before you download something that won't fit (12GB single-T4 / 26GB dual). Edit model_registry.py only to change a default; a proven override deserves promotion into the registry.
Models that aren't in the registry at all: detect_entry("author/AnyModel-GGUF") builds an entry automatically (best-fitting quant + single/dual-GPU placement from real file sizes) — same thing the studio's "import from hugging face" box does, which also prints a paste-ready registry line.
<url>/v1.run_studio.ipynb (or from control_panel import launch_panel; launch_panel(auth=("user","pass"))): four tabs — Launch (llm + quant picker with sizes, launch/stop/status with per-GPU VRAM, cache harvest), Chat (streams from the running model, system-prompt box, card-recommended sampling applied automatically), Image (diffusers spread across both T4s, prompt → png in the browser), Video (boots ComfyUI, shows its GUI url, ships stack workflow JSONs into its workflow browser). Every tab imports new models straight from HF. The share link is public — always set auth (the studio notebook generates a random password and prints it).comfy.queue_workflow(...).trycloudflare.com URLs are publicly reachable by anyone who has them. For LLMs always pass run(..., api_key="something") — the server then requires Authorization: Bearer something. For the Gradio panel always pass auth=. ComfyUI has no auth: treat that URL as a secret and stop the tunnel when done.
One-time, at the end of a session that built things:
harvest_cache() in a cell (or press Harvest cache in the studio) — it stages the built binaries, cloudflared, and this session's ggufs into /kaggle/working with the right names, watching the ~19.5GB quota.llama-server-*/cloudflared; CACHE_DATASET_DIR exists only to pin one when several are attached. Binaries are copied out and re-chmodded (dataset mounts drop the executable bit), cached ggufs are used directly.Quick tunnels mint a new trycloudflare.com URL every session, so anything you point at the API breaks daily. Free fix: Cloudflare dashboard → Zero Trust → Networks → Tunnels → create one, set its public hostname to http://localhost:8080, then add two Kaggle secrets: CF_TUNNEL_TOKEN (the tunnel token — required) and CF_TUNNEL_HOSTNAME (your hostname, so printed URLs are real). Export them like HF_TOKEN and the harness runs your named tunnel instead — same URL every session. Non-8080 ports (ComfyUI) still get quick tunnels.
Every process logs to a file — read the tail, the answer is usually right there:
| Component | Log |
|---|---|
| llama-server | /kaggle/tmp/llama-server.log |
| cloudflared tunnel | /kaggle/tmp/cloudflared.log |
| ComfyUI | /kaggle/tmp/comfyui.log |
| transcribe()'s vllm | /kaggle/tmp/vllm-transcribe.log |
!tail -50 /kaggle/tmp/llama-server.log in a cell. Failed health checks and crashed launches already include the tail in the raised error. Out of disk? Everything big must be under /kaggle/tmp (~60GB ephemeral), never /kaggle/working (~20GB, persists).
| Model | Caveat |
|---|---|
| FLUX.1-dev | Non-commercial license; gated — accept on the model page, needs HF_TOKEN |
| Ideogram 4 (incl. fal instant/fast) | Non-commercial + gated — accept license on the model page, needs HF_TOKEN (Kaggle secret) |
| stable-fast-3d / stable-point-aware-3d | Stability community license + gated — accept on the model pages |
| ltx-10eros / sulphur-2 stacks | Community finetunes aimed at explicit content — NSFW output violates Kaggle ToS |
| Bonsai 27B (ternary + 1-bit) | Needs the PrismML llama.cpp fork (auto-built); published perf is H100 — unverified on T4 kernels, treat first boot as a smoke test |
Abliterated / uncensored entries (-abl) | Reduced refusals — review outputs; you are responsible for what you generate |
| Everything | NSFW generation violates Kaggle ToS — don't; it risks your account |
HF_TOKEN comes from Kaggle Secrets (Add-ons → Secrets) — never hardcode tokens in notebooks.
73 commits
Python
96.1%
Jupyter Notebook
3.9%