Files
FabledCurator/agent
bvandeusen f01b59f390
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 17s
CI / backend-lint-and-test (push) Successful in 27s
CI / integration (push) Successful in 3m24s
fix(agent): py3.10 startup crash + submit-path retry; pin agent ruff to py310
The agent container (CUDA base, Python 3.10) crashed on startup with
`NameError: name 'Config' is not defined` — an earlier `ruff --fix` unquoted the
`from_env(cls) -> Config` self-reference, which is safe on CI's Python 3.14
(PEP 649 lazy annotations) but is evaluated at class-definition time on 3.10.
CI lint/compile run on 3.14, so it slipped through.

- config.py: `from __future__ import annotations` so the self-referential
  annotation is a string, never evaluated — works on 3.10 and every version.
- agent/ruff.toml: pin the agent to `target-version = "py310"` (its real runtime)
  and inherit the root rules. Ruff now flags exactly this class as F821, so CI's
  lint lane catches it instead of shipping a broken image. (CI otherwise lints on
  3.14, masking 3.10 issues.)
- client.py: submit path now retries in-place. A dedicated session with a
  urllib3 Retry (connect/read/status, 0.5s backoff, 500/502/503/504, POST) so a
  momentary blip after the GPU work is done doesn't discard it and force a full
  re-download + recompute elsewhere. A duplicate submit after a lost response is
  a harmless 409 no-op. Lease/fetch keep the plain session + loop-level backoff.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
2026-06-30 22:00:10 -04:00
..

FabledCurator GPU agent

A desktop-GPU worker that embeds characters (CCIP) + figure crops for FabledCurator. It talks to FC only over HTTP — it leases jobs, fetches image pixels, runs the models on your GPU, and posts results back. Your FC database and Redis stay private; the agent never touches them.

You run it when you want a burst and stop it to reclaim the card.

0. Host prerequisite — NVIDIA Container Toolkit

Docker needs the toolkit to hand the GPU to a container (else: "could not select device driver nvidia with capabilities gpu"). On Arch/CachyOS:

sudo pacman -S nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
# verify:
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi

1. Get a token

In FC: Settings → Tagging → GPU agent → Generate token (or Rotate). Copy it.

2. Pull (CI publishes it alongside the web/ml images)

docker pull git.fabledsword.com/bvandeusen/fabledcurator-agent:latest

Local build for development instead: docker build -t fc-gpu-agent agent/

3. Run (on the machine with the GPU)

docker run --rm --gpus all -p 8770:8770 \
  -e FC_URL=http://curator.traefik.internal \
  -e FC_TOKEN=<paste-the-token> \
  -v fc-agent-models:/models \
  git.fabledsword.com/bvandeusen/fabledcurator-agent:latest

Then open http://localhost:8770 — the control page. Click Start to begin draining the queue; Pause/Stop to yield the GPU. The -v fc-agent-models volume caches the downloaded ONNX models so restarts are fast.

Kick off a backfill from FC (GPU agent card → Queue character embedding), then watch the queue counts on the control page (or FC's card) drain.

Config (env)

var default meaning
FC_URL http://localhost:8000 FC base URL
FC_TOKEN the bearer token (required)
AGENT_ID desktop-agent identifies this agent's leases
BATCH_SIZE 4 jobs leased per round (still processed one at a time)
CCIP_MODEL imgutils default CCIP model name
DETECTOR_LEVEL m person-detector size: n < s < m < x
POLL_IDLE_SECONDS 10 wait between empty leases

⚠️ Verify on first run

This part can't be CI-tested (no GPU/models in CI), so confirm against your installed dghs-imgutils (pip show dghs-imgutils) — see fc_agent/models.py:

  • imgutils.detect.detect_person(image, level=...) returns [((x0,y0,x1,y1), label, score), ...].
  • imgutils.metrics.ccip_extract_feature(image, model=...) returns a vector (768-d for caformer). If you want the F1-0.94 variant, set CCIP_MODEL=ccip-caformer_b36-24 (verify the exact string in imgutils).

If FC's matcher under/over-fires, tune the cosine threshold in backend/app/services/ml/ccip.py (DEFAULT_SIM_THRESHOLD) and use GET /api/ccip/overview + /api/ccip/images/<id> to spot-check.

CPU fallback

Swap onnxruntime-gpuonnxruntime in requirements.txt and drop --gpus all to grind it slowly on the server instead. Same agent, no card.