diff --git a/CLAUDE.md b/CLAUDE.md index 3a6e311c..bcdc072a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -282,7 +282,23 @@ same reason - default setup cannot load a pack. the workspace) so the free pre-flight covers the part the caller wrote. A workflow that declares no variables takes no arguments at all - those were dropped in silence, since `Workflow.run` only substitutes when a `variables` - block exists + block exists. A valid `POST /api/validate` answer also carries `plan` + (`dw/plan.py`): the fingerprint of the work, step and list counts, + `downloads_required` and a cost `estimate` with its `basis` - the number an + agent quotes, with `basis` saying whether it was measured for this list + (`catalog`/`per_entry`) or extrapolated over one the caller resized + (`derived`); `plan: null` when it could not be built, never a changed + verdict. `acknowledged_cost` on `POST /api/jobs` / `rerun` takes `true` + (recorded) or the plan's `{fingerprint, minutes, downloads}` (checked - 409 + with the current plan when the fingerprint or the required downloads + changed; `minutes` never compared), and the job records `acknowledged: + none | boolean | bound`. `cached_steps` is the worker's answer to a + `probe_cache` command (`Workflow.cache_hits`, which shares + `_prepare_definition` / `_cache_lookup` with `run` so the two cannot drift). + The web UI reads the fields only: the editor lists the plan under a valid + verdict (`describePlan`, `ui/src/lib/plan.ts`), and a job queued `bound` + says so on the job page and in the jobs list; the UI itself sends no + acknowledgement - **A failed run still reports what it wrote** — the worker carries its partial manifest on the error and cancelled messages as well as on success, and the "Previous result not found" error names the steps that ran even after @@ -319,8 +335,10 @@ same reason - default setup cannot load a pack. job page sections results under `final/` / `intermediate/` headings (or whatever the step named) (`sectionBySubfolder`, `ui/src/lib/results.ts`), unchanged for a run that chose none. `file_base_name` may not contain a - separator - - it is a name, not a path. + separator - it is a name, not a path - and it *replaces* the derived + `-.` base rather than prefixing it (#100), so + two steps in one subfolder that set the same one collide onto + `output_file_path`'s `-2` counter. Every `workflows/templates/**` file with two or more saving steps marks each one `final`/`intermediate` (`tests/test_template_subfolders.py` pins the rule; `dw/workflows/` builtins stay unmarked - a role is the parent's to assign). That moved diff --git a/docs/ACCELERATION.md b/docs/ACCELERATION.md index ef6801ea..6c907d21 100644 --- a/docs/ACCELERATION.md +++ b/docs/ACCELERATION.md @@ -296,6 +296,24 @@ Three older per-component knobs, set in the pipeline `configuration` beside **Example:** [flux-dev.json](../workflows/models/flux-dev.json) (`"offload": "model"`), [z-image.json](../workflows/models/z-image.json) (`"offload": "sequential"`), [video-with-audio.json](../workflows/templates/minimax/video-with-audio.json) (`group_offload` per component), [reference-to-video.json](../workflows/templates/minimax/reference-to-video.json) (`group_offload` for the transformer, `on_demand` for the VAEs) +## Reading Memory While Offloading + +A workflow that offloads keeps its weights in host memory by design, so the +card can sit near-empty through a generation and the VRAM figures alone say +nothing about what a run holds or fails to release. `get_memory` (MCP) and +`GET /api/memory` report both: `gpu_*` is the card, `host_memory_rss_mb` is +what the worker process holds and `host_memory_peak_rss_mb` the most it has +ever held, beside the machine's `host_memory_total_mb` / +`host_memory_available_mb`. + +`host_pinned_reserved_mb` / `host_pinned_allocated_mb`, where the platform +reports them, are torch's pinned-host cache - the staging buffers group +offloading moves weights through. They are part of `host_memory_rss_mb` and +invisible in every `gpu_*` figure, so a worker that has released every model +and still holds gigabytes is usually holding these; they are returned when +the worker switches to a different workflow (#98). A host field is absent, +rather than null, on a platform that cannot measure it. + ## TF32 and cuDNN Device-level settings, read once at startup from `~/.diffusers_helper/settings.json`: diff --git a/docs/MCP.md b/docs/MCP.md index a54b4c64..7a68fb04 100644 --- a/docs/MCP.md +++ b/docs/MCP.md @@ -211,10 +211,10 @@ when no single workflow covers it. | Tool | Arguments | Purpose | | --- | --- | --- | | `list_guides()` | — | List the documentation the engine serves: each guide's name, what it covers, and its section headings. The index is the routing table - match a request's shape against a heading rather than guessing | -| `get_guide(name, section=None)` | `name`, `section` | Get one guide whole, or one section of it. Prefer a section: a guide runs to thousands of lines. Section names match loosely, so a heading copied approximately still resolves | -| `list_workflows(shape=None, traits=None, configures=None, include_models=False)` | `shape`, `traits`, `configures`, `include_models` | List stored workflows. Always the server's compact view: each entry carries `summary`, `shape`, `traits`, `cost`, `kinds`, `variable_names`, `lists`, and `configures` only when set - `get_workflow` has the full description and definition. `lists`, present for a list-driven workflow, names the fields an entry of each list takes, the steps over it and the default's length; `cost` may carry `per_entry`, the measured cost of one entry so a run over a different-length list can be priced from it. `shape` keeps one of `image`, `image-set`, `image-edit`, `shot`, `sequence`, `audio`, `text`, `utility`; `traits` is comma-separated and every one listed must match (`has-audio`, `chained`, `image-conditioned`, `identity-referenced`, `needs-input-media`, `composes-workflows`); an unknown value in either is a 400 listing the vocabulary. Templates only by default - `configures=