feat(model-manager): "Free GPU memory" button to unload ComfyUI models

ComfyUI caches the last model when RAM is plentiful (unified memory), so
memory doesn't drop after switching models even though models are being
swapped, not accumulated. Add a sidebar "Free GPU memory" button that
proxies ComfyUI's POST /free (unload_models + free_memory) via a new
/api/comfyui/free endpoint (COMFYUI_URL env). Verified it releases ~7GB.
README documents this plus the --disable-smart-memory auto-unload option.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 17:14:37 -04:00
parent c2b0f202bf
commit 399acabd58
7 changed files with 53 additions and 1 deletions
+5
View File
@@ -256,6 +256,11 @@ SparkyUI includes a **StabilityMatrix-style Model Manager** - a lightweight Fast
- **HuggingFace** - paste a `resolve` URL (works with gated repos via your token)
- **Settings** - store your **CivitAI API key** and **HuggingFace token** persistently
(saved to a SQLite DB under `./sparkyui-data`, never committed to git)
- **Free GPU memory** - a sidebar button that unloads all models from ComfyUI and releases
memory (proxies ComfyUI's `/free`). ComfyUI keeps the last model cached for fast reuse when
RAM is plentiful, so memory won't drop on its own after switching models - use this to
release it on demand. (For automatic unload after every generation, add
`--disable-smart-memory` to `COMFYUI_FLAGS`, at the cost of reloading each run.)
**How it works:**
- Runs as a FastAPI server in its own container (`python:3.12-slim`)
+2
View File
@@ -154,6 +154,8 @@ services:
# Ports used by the device-routing landing page (/start)
COMFYUI_PORT: "${COMFYUI_PORT:-8188}"
COMFYUIMINI_PORT: "${COMFYUIMINI_PORT:-3000}"
# Internal URL used to proxy the "Free GPU memory" action
COMFYUI_URL: "http://comfyui:8188"
volumes:
# Shared models dir - read-WRITE here so downloads land on the host.
+4
View File
@@ -19,6 +19,10 @@ DB_PATH = DATA_DIR / "manager.db"
COMFYUI_PORT = os.environ.get("COMFYUI_PORT", "8188")
COMFYUIMINI_PORT = os.environ.get("COMFYUIMINI_PORT", "3000")
# Internal URL of the ComfyUI container (reachable over the docker network),
# used to proxy the "free memory / unload models" action.
COMFYUI_URL = os.environ.get("COMFYUI_URL", "http://comfyui:8188")
# Image file types shown in the gallery.
IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".webp", ".gif", ".bmp"}
+15
View File
@@ -15,6 +15,7 @@ from pydantic import BaseModel
from . import db, downloader, registries
from .config import (
COMFYUI_PORT,
COMFYUI_URL,
COMFYUIMINI_PORT,
IMAGE_EXTS,
KEY_BY_FOLDER,
@@ -314,6 +315,20 @@ def ui_config() -> dict:
return {"comfyui_port": COMFYUI_PORT, "comfyuimini_port": COMFYUIMINI_PORT}
@app.post("/api/comfyui/free")
async def comfyui_free() -> dict:
"""Ask ComfyUI to unload all models and free GPU/RAM (proxies POST /free)."""
try:
async with httpx.AsyncClient(timeout=30) as client:
resp = await client.post(
f"{COMFYUI_URL}/free",
json={"unload_models": True, "free_memory": True})
resp.raise_for_status()
except Exception as exc: # noqa: BLE001 - report to UI
raise HTTPException(502, f"Could not reach ComfyUI: {exc}")
return {"ok": True}
@app.get("/start")
def start() -> FileResponse:
return FileResponse(STATIC_DIR / "start.html")
+18
View File
@@ -663,6 +663,24 @@ $("#saveSettings").addEventListener("click", async () => {
}
});
// ---- free GPU memory ------------------------------------------------------
$("#freeMemBtn").addEventListener("click", async () => {
const btn = $("#freeMemBtn");
const msg = $("#freeMemMsg");
btn.disabled = true;
msg.className = "form-msg"; msg.textContent = "Unloading…";
try {
await api("/api/comfyui/free", { method: "POST" });
msg.className = "form-msg ok"; msg.textContent = "Models unloaded.";
} catch (err) {
msg.className = "form-msg err"; msg.textContent = err.message;
} finally {
btn.disabled = false;
setTimeout(() => { msg.textContent = ""; }, 4000);
}
});
// ---- boot -----------------------------------------------------------------
(async function init() {
+6 -1
View File
@@ -33,7 +33,12 @@
<span class="nav-ico"></span> Settings
</button>
</nav>
<div class="sidebar-foot" id="modelsDir"></div>
<div class="sidebar-foot">
<button class="btn" id="freeMemBtn" title="Unload all models from ComfyUI and free GPU/RAM">
⏏ Free GPU memory
</button>
<span class="form-msg" id="freeMemMsg"></span>
</div>
</aside>
<main class="content">
+3
View File
@@ -58,7 +58,10 @@ nav { display: flex; flex-direction: column; gap: 4px; }
.sidebar-foot {
margin-top: auto; color: var(--text-dim); font-size: 11px;
word-break: break-all; padding: 8px 6px 0;
display: flex; flex-direction: column; gap: 6px;
}
.sidebar-foot .btn { width: 100%; font-size: 13px; }
.sidebar-foot .form-msg { font-size: 11px; text-align: center; }
/* ---- content ---- */
.content { flex: 1; overflow-y: auto; padding: 26px 32px; }