UNPKG

comfyui-mcp

Version:

Local-first, agent-native control plane for ComfyUI — MCP server + autonomous sidebar agent that drives your live graph in natural language on ANY LLM: Claude/ChatGPT/Gemini on your subscription (no API key), free local models via Ollama (fully offline),

102 lines • 5.21 kB
/** Poll interval between /system_stats reads after /free (#2050). */ export const VRAM_SETTLE_INTERVAL_MS = 250; /** Do not treat an unchanged first reading as settled — CUDA may not have started releasing yet. */ export const VRAM_SETTLE_MIN_MS = 1_000; /** * Hard cap so a card that never plateaus still returns a number. * * #2704 raised this from 5s. An RTX 5090 on cudaMallocAsync took ~8s for a * ~29 GB unload to become visible in /system_stats, so 5s could not cover it. * The cap is only ever REACHED when a `baseline` was supplied and the release * never landed — a card that releases promptly returns as soon as it plateaus, * so this costs nothing on hardware that already worked. */ export const VRAM_SETTLE_TIMEOUT_MS = 12_000; /** * "This card is holding memory" — free/total at or below this ratio, on a card * big enough for the ratio to mean anything. Mirrors the pin thresholds * `panel-tools.ts` already applies to the same counters (#1895), so callers * decide whether to wait for a release using one definition of occupied. */ export const VRAM_OCCUPIED_FREE_RATIO = 0.2; export const VRAM_OCCUPIED_MIN_TOTAL_BYTES = 1024 * 1024 * 1024; function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * /free answers when ComfyUI drops model refs; CUDA/driver release can lag. * Poll `read` until `signatureOf` stops changing (or the cap) so the value * matches a follow-up get_system_stats (action:"stats") (#2050). * * A failed or null read returns null rather than the previous sample: /free * may have released memory between the two, so returning the prior sample * would report a known-stale VRAM figure as if it were current. * * #2704 — WHY A `baseline` IS THE LOAD-BEARING PART, and not the cap. * Waiting for the counters to "stop changing" cannot distinguish a plateau * from a release that has not started, because both are perfectly still. On * the reported RTX 5090 the torch pool released in ~400ms while the DRIVER * number stayed frozen at its pre-/free value for ~8s; the torch movement * satisfied the change-then-plateau test, so the loop returned the frozen * driver number after ~780ms and never came close to the 5s cap. Raising the * cap alone changes nothing — measured: both 5s and 12s returned the same * stale 2805 MB at ~775ms. Comparing against a pre-mutation sample is what * turns the unanswerable "is this still?" into the answerable "has anything * been released yet?". */ export async function settleUntilStable(read, signatureOf, options = {}) { const baseline = options.baseline ?? null; const progressOf = options.progressOf ?? ((value) => new Map([["", signatureOf(value)]])); const confirmable = options.confirmable ?? true; const started = Date.now(); const deadline = started + VRAM_SETTLE_TIMEOUT_MS; let lastSig = null; // Identities still sitting on their pre-mutation value. Emptying is LATCHED // per identity: a unit that moves has demonstrably released, even if a later // sample happens to land back on the baseline value. const unmoved = new Set(baseline ? baseline.keys() : []); for (;;) { let current; try { current = await read(); } catch { return { value: null, settled: false }; } if (current == null) return { value: null, settled: false }; const sig = signatureOf(current); const elapsed = Date.now() - started; if (baseline !== null && unmoved.size > 0) { const now = progressOf(current); for (const id of [...unmoved]) { const seen = now.get(id); // A device that VANISHED from the sample proves nothing — absence is // not a release. Leave it unmoved and let the cap expire into an // honest "unconfirmed" rather than certifying a value we cannot see. if (seen !== undefined && seen !== baseline.get(id)) unmoved.delete(id); } } const stable = lastSig !== null && sig === lastSig; lastSig = sig; // With a baseline, stillness counts only AFTER every unit has released. // Without one there is nothing available to prove, so a plateau past the // min wait is accepted — and `confirmable` says whether that plateau may be // reported as a measured result. The old rule let ANY change shorten the // min wait, which is how the torch pool moving returned a frozen driver // number at ~780ms. const releaseObserved = baseline === null || unmoved.size === 0; if (stable && releaseObserved && elapsed >= VRAM_SETTLE_MIN_MS) { return { value: current, settled: baseline !== null ? true : confirmable }; } if (elapsed >= VRAM_SETTLE_TIMEOUT_MS) { return { value: current, settled: false }; } const wait = Math.min(VRAM_SETTLE_INTERVAL_MS, Math.max(0, deadline - Date.now())); if (wait <= 0) return { value: current, settled: false }; await sleep(wait); } } //# sourceMappingURL=vram-settle.js.map