From 4a1a9ec5a761e24eb9dfac97a0580e8f04d07fd5 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 29 Jun 2026 19:07:40 -0400 Subject: [PATCH] feat(agent): GPU load readout + live worker-count tuning (#114) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Control UI gains what the operator asked for: - GPU load (nvidia-smi): util %, VRAM used/total + bar, temp — so you can see how hard the card is working while you're at the desktop. - Worker count is now a live − / + control (POST /concurrency), not just an env: the worker is a pool of independent slots (shared model, so slots add concurrent inference, not N× VRAM). Dial up for speed, down to free the card. Replaces pause/resume with Start/Stop + the worker dial. - Graceful release on stop / pool-shrink: a slot hands its still-leased jobs back via client.release() so they're re-picked immediately (pairs with the server recovery sweep). Not CI-tested (agent/ outside CI) — verified by running. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa --- agent/fc_agent/app.py | 76 ++++++++++++++--------- agent/fc_agent/client.py | 13 ++++ agent/fc_agent/config.py | 4 +- agent/fc_agent/gpu.py | 30 +++++++++ agent/fc_agent/worker.py | 127 +++++++++++++++++++++++---------------- 5 files changed, 168 insertions(+), 82 deletions(-) create mode 100644 agent/fc_agent/gpu.py diff --git a/agent/fc_agent/app.py b/agent/fc_agent/app.py index 8329297..f93b9fe 100644 --- a/agent/fc_agent/app.py +++ b/agent/fc_agent/app.py @@ -1,13 +1,14 @@ """FastAPI control surface for the agent (served on localhost). -Start / pause / resume / stop the worker, set nothing else here (config is env), -and watch progress + the server-side queue. The container exposes this on a -localhost port; stopping the worker frees the GPU. +Start / stop the worker pool, tune the worker count live (trades desktop +responsiveness for throughput), and watch GPU load + progress + the server-side +queue. Config is env-seeded; the worker count is adjustable here on the fly. """ -from fastapi import FastAPI +from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse, JSONResponse from .config import Config +from .gpu import read_gpu from .worker import Worker cfg = Config.from_env() @@ -26,29 +27,25 @@ def start(): return JSONResponse(worker.status()) -@app.post("/pause") -def pause(): - worker.pause() - return JSONResponse(worker.status()) - - -@app.post("/resume") -def resume(): - worker.resume() - return JSONResponse(worker.status()) - - @app.post("/stop") def stop(): worker.stop() return JSONResponse(worker.status()) +@app.post("/concurrency") +async def concurrency(request: Request): + body = await request.json() + worker.set_concurrency(int(body.get("value", 1))) + return JSONResponse(worker.status()) + + @app.get("/status") def status(): s = worker.status() s["fc_url"] = cfg.fc_url s["configured"] = bool(cfg.token) + s["gpu"] = read_gpu() try: s["queue"] = worker.client.queue_status() except Exception: @@ -59,35 +56,54 @@ def status(): _PAGE = """ FabledCurator GPU agent

FabledCurator GPU agent

FC: · token

-

+

- - -

-

- idle
state
+

+
+ workers + + 1 + + (more = faster + more GPU) +
+
+ stopped
state
+ 0
active now
0
processed
0
errors
-
current image
-

+
+
GPU — …
+