diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 56 |
1 files changed, 40 insertions, 16 deletions
@@ -4,19 +4,30 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What this is -Local SDXL-base image generation on an Intel Arc B580 (12GB) under Slackware. Three files: -`imggen` (host bash wrapper) → `docker run` → `generate.py` (in-container, XPU). README.md carries -the full rationale and VRAM measurements; read it before changing behavior. +Local SDXL image generation on an Intel Arc B580 (12GB) under Slackware. Five source files: +`imggen` (host bash wrapper: subcommands + daemon client + one-shot fallback) → `docker compose` +→ either `server.py` (persistent daemon, in-container, XPU) or `generate.py` (one-shot CLI, model +registry + resolve_model + build_pipe + generate), plus `compose.yaml` (compose service) and +`.env.example` (documents the gitignored `.env`). There is also `test_generate.py` (pure-logic +unit tests). README.md carries the full rationale and VRAM measurements; read it before changing +behavior. ## Build and run ``` -docker build -t imggen:local . # image tag is hardcoded as imggen:local in the wrapper -cp imggen ~/bin/imggen && chmod +x ~/bin/imggen -imggen "a prompt" --steps 30 --seed 42 -o out.png -n "blurry" +docker build -t imggen:local . # image tag is hardcoded as imggen:local in compose.yaml +cp .env.example .env # set real IMGGEN_OUT / IMGGEN_CACHE / IMGGEN_UID / IMGGEN_GID +./imggen start # start daemon, load default model, hold VRAM +./imggen "a prompt" --steps 30 --seed 42 -o out.png -n "blurry" +./imggen stop # free VRAM ``` -No test suite, no lint config. Verification is running `imggen` end to end and checking the PNG. +The wrapper lives in the repo and `cd`s to `$IMGGEN_DIR` (default `$HOME/Programming/GIT/imggen`) +so it can find `compose.yaml` and `.env`. It is not copied to `~/bin`; symlink it onto PATH if +convenient, it still operates on the repo via `IMGGEN_DIR`. + +`test_generate.py` covers pure logic (model registry, resolve_model) with no GPU needed. Beyond +that, no lint config; verification is running `imggen` end to end and checking the PNG. ## Architecture constraints (the whole point of the project) @@ -24,11 +35,18 @@ No test suite, no lint config. Verification is running `imggen` end to end and c the VAE to fp32. The fix is the fp16-fix VAE (`madebyollin/sdxl-vae-fp16-fix`, hardcoded in `generate.py`). This one swap is why the pipeline fits. Do not remove it. Peak VRAM in the working config is 9.07 GB. -- **Scope is deliberately bounded:** SDXL base, single 1024x1024 image, 25 steps, no refiner, - no Flux, no batching. Refiner/Flux/heavy work is explicitly out of scope and belongs on a - RunPod endpoint, not here. Don't add them "to be helpful." +- **Scope is deliberately bounded:** SDXL-architecture models, single 1024x1024 image, 25 steps, + no refiner, no Flux, no batching. The persistent daemon (`server.py`) is IN SCOPE: it holds one + model resident in VRAM for fast local burst-iteration, and that does not change the limits. + Refiner/Flux/batching/heavy work stays explicitly out of scope and belongs on a RunPod endpoint, + not here. Don't add them "to be helpful." - **XPU only.** `build_pipe()` fails loud (`sys.exit(2)`) if `torch.xpu` is unavailable rather than falling back to CPU (which would be minutes per image). Keep that behavior. +- **The model registry (`MODELS` in `generate.py`) is the source of truth.** Three keys: `sdxl` + (stabilityai/stable-diffusion-xl-base-1.0, default), `realvis` (SG161222/RealVisXL_V5.0), `pony` + (kitty7779/ponyDiffusionV6XL, an ungated diffusers-format mirror; the original AstraliteHeart + repo is gated). All three share the fp16-fix VAE and fit 12GB. The daemon holds only ONE model + at a time and swaps on demand; it never holds two co-resident (would exceed 12GB). ## Version pinning is load-bearing @@ -40,9 +58,15 @@ keep the old Dockerfile in git to revert. ## Container boundaries -- `imggen` bind-mounts host `$IMGGEN_OUT` (default `~/imggen-out`) → `/out`, and - `$IMGGEN_CACHE` (default `~/.cache/imggen-models`) → `/models`. `HF_HOME=/models` so the - ~7.5GB weights download once. Output paths in `generate.py` are all under `/out`. -- All wrapper args pass straight through to `generate.py` argparse. Adding a CLI flag means - editing `generate.py` only; the wrapper needs no change. -- GPU passthrough is `--device /dev/dri`. +- `docker compose` (via `compose.yaml`) replaces raw `docker run`. The image tag is still + `imggen:local`. The service runs as the HOST user (`user: "${IMGGEN_UID:-1000}:${IMGGEN_GID:-100}"` + plus `group_add: video` for `/dev/dri` access), so generated PNGs are host-owned, not root. +- Real host paths live in the gitignored `.env` (copy `.env.example` to create it), NOT in + hardcoded wrapper defaults. `.env` sets `IMGGEN_OUT` → `/out` and `IMGGEN_CACHE` → `/models` + (bind mounts), plus optionally `IMGGEN_PORT` (daemon port, default 8765, localhost-only), + `IMGGEN_MODEL` (default sdxl), `IMGGEN_UID`/`IMGGEN_GID`. `HF_HOME=/models` so the ~7.5GB + weights download once. Output paths in `generate.py`/`server.py` are all under `/out`. +- Daemon args (`generate` endpoint) and one-shot CLI args (`generate.py` argparse) are kept in + sync by hand in the wrapper; adding a flag means updating `generate.py`, `server.py`, and the + wrapper's arg parsing together. +- GPU passthrough is `--device /dev/dri` (via compose `devices:`). |
