diff --git a/README.md b/README.md index 757696323..51f1d8fff 100644 --- a/README.md +++ b/README.md @@ -4,261 +4,232 @@ Your GPU, as something an agent can drive. -diffusers-workflow wraps the [Hugging Face Diffusers library](https://github.com/huggingface/diffusers) -in an engine that runs image, video and audio generation as jobs, and puts -two front ends on it: an **MCP server**, so Claude Code (or any MCP client) -can author, run and inspect generations; and a **web UI** for doing the same -by hand. A CLI sits underneath for when you want neither. +diffusers-workflow runs [Hugging Face Diffusers](https://github.com/huggingface/diffusers) +image, video and audio generation as jobs on your own GPU. **An agent does the work.** +Claude Code, or any MCP client, picks or writes the workflow, checks what it will +cost, runs it, and looks at what came back. **You steer from the browser.** Every +workflow, run and output the agent made is in the web UI, where you can change a +variable, run it again, compare takes and keep the one you like. **Python 3.12-3.14 | CUDA (NVIDIA) | MPS (Apple Silicon) | CPU** - - The workflow browser: every workflow as a card with its description, output kinds, and variables + + A workspace's overview in the web UI: recent outputs from an agent-made musical trailer, its jobs, and the workflows the agent wrote ## Getting started -**1. Install.** The script picks the right torch build for your platform, -creates a virtual environment and installs everything, MCP server included. +**1. Install.** The script picks the right torch build for your platform, creates a +virtual environment and installs everything, MCP server included. ```bash -# Linux / macOS -bash ./install.sh -source ./activate - -# Windows -.\install.ps1 -.\venv\scripts\activate +bash ./install.sh && source ./activate # Linux / macOS +.\install.ps1; .\venv\scripts\activate # Windows ``` -`python -m dw.test` runs a small built-in workflow end to end - it downloads -SD 1.5 (a few GB) and generates one image on whichever accelerator was found. - -**Or run it in Docker** (Linux with an NVIDIA GPU). The image -[`dkackman/diffusers-workflow`](https://hub.docker.com/r/dkackman/diffusers-workflow) -has torch, the engine and the web UI built in, and it shares the model cache -and workspace with a native install on the same box. diffusers itself lives -on a volume, so it can follow GitHub main. That makes step 2 a container: +**2. Start the engine.** Leave it running; the agent and the browser both talk to it. ```bash -bash docker/init-env.sh -docker compose -f docker/compose.yaml up -d --build -docker compose -f docker/compose.yaml logs -f # wait for the server to start -docker compose -f docker/compose.yaml exec dw python /opt/dw/entrypoint.py login-url # the login link +dw-serve # web UI on http://127.0.0.1:8765 ``` -Setup, data sharing and updates are in [docs/DOCKER.md](docs/DOCKER.md). - -**2. Start the engine.** Leave it running; everything else talks to it. - -```bash -dw-serve -# diffusers-workflow server on http://127.0.0.1:8765 -``` - -That address is the web UI. Open it and run `templates/text-to-image` — a -small, ungated model, so the first generation needs no Hugging Face login and -downloads only a few GB. - -**3. Connect Claude Code.** Register the MCP server with the absolute path to -`dw-mcp` in the venv you just made (the relative path is the one setup detail -that reliably goes wrong): +**3. Connect your agent.** Register the MCP server with the absolute path to `dw-mcp` +in the venv you just made (a relative path is the setup detail that most often goes +wrong). Then add the [dw plugin](plugins/dw/README.md): ```bash claude mcp add dw -- "$(pwd)/venv/bin/dw-mcp" ``` -Then, optionally, the [dw plugin](plugins/dw/README.md) — one skill per model -family that knows which workflow fits a request and the rules that bite: - ``` /plugin marketplace add dkackman/diffusers-workflow /plugin install dw@diffusers-workflow ``` -Most of the shipped workflows (Flux, LTX-2, MiniMax...) use **gated** models. -Request access on the model's Hugging Face page, then `hf auth login` -once; without it the run fails partway through with a 401/403 from the Hub. - -> **GPU on another machine?** Start the engine there with `--mcp` and connect -> over HTTP — nothing to install on the laptop: -> -> ```bash -> # on the GPU box -> dw-serve --host 0.0.0.0 --token "$DW_API_TOKEN" --mcp --workspace ~/studio -> -> # on your laptop -> claude mcp add --transport http dw http://gpu-box:8765/mcp \ -> --header "Authorization: Bearer $DW_API_TOKEN" -> ``` -> -> The server's own **Server** page composes that line for the address you -> pick. End to end: [Remote GPU server](docs/REMOTE.md). - -## Drive it from an agent - -Then just ask. The agent has 65 tools covering the whole surface — the -workflow catalog, the real diffusers pipeline signatures, the job queue, the -gallery, the model cache: +Most of the shipped models (Flux, LTX, MiniMax, ...) are **gated**. Request access on +the model's Hugging Face page and run `hf auth login` once. Without it a run fails +partway through with a 401/403 from the Hub. `templates/text-to-image` uses a small, +ungated model, so it makes a good first run. -![Claude Code driving the dw MCP server: creating a workspace, authoring a script, and generating from it](docs/img/claude-authoring.png) +
+GPU on another machine, or in Docker? -Generation is the long pass, and the agent stays with it — queuing each shot, -waiting it out, and reporting what came back: - -![The same session hours later: shots rendering one at a time, roughly 30 minutes each, with the agent reporting progress between them](docs/img/claude-generating.png) - -What a session looks like: - -- **"What can this box run, and what do I already have?"** — `get_server_info` - for the accelerator and workspace, `list_workflows` for the catalog with each - entry's shape, cost and variables, `list_models` for what is already in the - hub cache. The agent knows the device before it proposes anything CUDA-only. -- **"Take my Flux workflow, swap in the portrait LoRA, render four at 1024."** - — `get_workflow`, `get_pipeline_signature` to check the arguments exist, - `validate_workflow` (free: schema *and* signature checking, no model loads), - `save_workflow`, `run_workflow`. That last one refuses until the agent passes - `acknowledged_cost=true`, so it has to tell you what it is about to spend. -- **"How's it going?"** — `wait_for_job` blocks for a bounded interval instead - of polling; `get_output_image` brings the result back into the conversation - so the agent can look at what it made. -- **"That third frame is the one — keep it and seed the video pass from it."** - — `keep_output` promotes the file into the asset library under a name you - pick, and the next workflow references `asset:hero-frame.png`. - -Everything that costs real GPU time or real disk (`run_workflow`, `rerun_job`, -`enhance_prompt`, `download_model`, `delete_model`, `update_diffusers`, -`delete_workspace`) refuses until it is explicitly acknowledged, so an agent -cannot quietly burn an hour of GPU or delete 40GB of weights. - -One server holds several **workspaces** — each with its own workflows, assets -and outputs — so two agents, or an agent and you in the browser, share the GPU -without saving over each other. An agent calls `use_workspace` once and the -rest of the session lands there. - -**Feedback from a session.** At the end of a working session, ask the agent -what got in its way: bugs, gaps, misleading skill text, tools it reached for -and couldn't find. Have it file each one as an issue on -`dkackman/diffusers-workflow` with the `field-report` label, for example: - -> File each bug or gap you hit as an issue on dkackman/diffusers-workflow -> with the label `field-report`. - -The label marks a report as coming from real use, not from the automated test -loop. The agent loop (see [Agent Loop](docs/AGENT_LOOP.md)) picks the report up -like any other issue when you filed it yourself; a report filed under any -other GitHub login is parked for the maintainer to review first (relabelled -`owner:don` + `status:needs-approval`), since the loop must not act -unattended on third-party text in a public repo, and is only handed to the -loop, or not, after that review. The label is also what feature planning -reads as evidence of demand. - -The complete tool reference, client configuration for other MCP hosts, and the -troubleshooting table: [MCP Server](docs/MCP.md). Workspaces in depth: -[Workspaces](docs/WORKSPACES.md). - -## The web UI - -Everything the engine does, in a browser, backed by the same persistent GPU -worker — models stay loaded between runs. - -**An editor built from the real pipeline signatures.** Forms and argument -autocomplete are generated by introspecting diffusers itself, so every knob a -pipeline exposes is there with its documentation. Validation catches schema -errors *and* argument typos before any model loads. - -![The editor: introspection-driven forms beside live JSON in Monaco](docs/img/ui-editor.png) - -**A gallery where every image is a recipe.** Outputs carry their full workflow -and seed; *open as workflow* drops any image back into the editor, ready to -reproduce or riff on. *Keep as asset* promotes a generated file into the asset -library for later workflows to build on. - -![The gallery with generated images and videos](docs/img/ui-gallery.jpg) - -**A prompt library** stores a prompt once and lets any workflow reference it, -with an *Enhance with AI* panel that expands an idea into a full prompt using -a local language model. **A model manager** inventories the Hugging Face hub -cache — sizes, last use, free space — and downloads or deletes models with -live progress. - -![The model manager listing cached models with sizes](docs/img/ui-models.png) - -Jobs queue, stream progress live per denoising step, cancel cooperatively and -persist to a searchable history. See [Server & Web UI](docs/SERVER.md) for the -pages and the HTTP API. - -## The command line - -`dw.run` is a thin client of `dw.serve`: it queues a job over HTTP, prints -its progress, and reports the run directory the server wrote to - start the -server first. +Start the engine on the GPU box with `--mcp` and connect over HTTP. There is nothing +to install on the laptop: ```bash -python -m dw.serve -python -m dw.run workflows/templates/text-to-image.json -python -m dw.run workflows/templates/text-to-image.json prompt="a cat" num_images_per_prompt=4 -python -m dw.validate workflows/models/flux-dev.json +# on the GPU box +dw-serve --host 0.0.0.0 --token "$DW_API_TOKEN" --mcp --workspace ~/studio + +# on your laptop +claude mcp add --transport http dw http://gpu-box:8765/mcp \ + --header "Authorization: Bearer $DW_API_TOKEN" ``` -`dw.run` takes `WORKFLOW [name=value ...] [--server URL] [--workspace NAME] -[--token TOKEN]` - `--server` defaults to `DW_MCP_URL`, else -`http://127.0.0.1:8765`; `--token` to `DW_API_TOKEN`. Where the output lands, -which workspace's `prompts:`/`asset:` references resolve, and the output -layout are all `dw.serve` flags now (`--workspace`, `--output-dir`, -`--prompt-dir`, `--asset-dir`, `--output-layout` on the server) - `dw.run` -only says which server and which of its workspaces to run in. - -## What's underneath - -Every front end reads and writes the same thing: a JSON document of named -steps, each a diffusers pipeline or a utility task, whose arguments reference -variables, earlier steps' outputs, stored prompts and assets rather than -hard-coded values. That is what makes text-to-image chain into image-to-video, -and what makes a generated image reopen as the exact recipe that produced it. -[workflows/](workflows/) is a corpus of runnable examples across model -families; the [Workflow Guide](docs/WORKFLOW_GUIDE.md) is the reference when -you do want to write one. - -Because a workflow reaches any diffusers pipeline or quantization backend by -dynamic import, loading one can execute arbitrary Python. Treat a workflow -file from someone else the way you'd treat a `.py` script — see +The server's **Status** page writes that line for you. The whole setup is in +[Remote GPU server](docs/REMOTE.md). + +On Linux with an NVIDIA GPU, the +[`dkackman/diffusers-workflow`](https://hub.docker.com/r/dkackman/diffusers-workflow) +image runs the server with torch, the engine and the web UI built in. It shares the +model cache and workspace with a native install on the same box. See +[Docker](docs/DOCKER.md). + +
+ +## Ask the agent + +Describe what you want. The agent has 65 tools covering the workflow catalog, the +real diffusers pipeline signatures, the job queue, the gallery and the model cache, +and it stays with a long render until it finishes: + +![Claude Code driving the dw MCP server: creating a workspace, authoring a script, and generating from it](docs/img/claude-authoring.png) + +A session sounds like this: + +- **"Make a 30-second musical trailer: four men in boater hats, a giant eye, a + score under it."** The agent picks a shape from the catalog (`list_workflows`), + breaks the brief into shots, writes each prompt to the prompt library, and builds + the workflow that renders the shots and cuts them to the score. +- **"What will that cost?"** `validate_workflow` checks the JSON against the schema + *and* the real pipeline signatures, with no model loads, and quotes an estimate in + GPU minutes from this box's own history. Anything that spends GPU time or disk + refuses to run until the agent passes `acknowledged_cost`, so it has to tell you + what it is about to spend first. +- **"How's it going?"** `wait_for_job` waits out a render without polling. Then + `get_output_frames`, `get_output_image` and `get_output_audio` bring the result + back into the conversation, so the agent judges what it made against what you + asked for. +- **"Shot three is the one. Keep it and start the next scene from it."** + `keep_output` turns the file into a named asset, and the next workflow references + it as `asset:hero-frame.png`. + +The [dw plugin](plugins/dw/README.md) adds what a general-purpose agent can't know +on its own: one skill per model family (MiniMax H3 and Music 3, LTX-2.5, Kandinsky 6, +FLUX 3 Klein) that picks the template for a request and states the rules that bite. +Cross-cutting skills cover script-to-video, series with a consistent cast, +storyboards and animatics, and a shared camera-and-look vocabulary. + +![The same session hours later: shots rendering one at a time, with the agent reporting progress between them](docs/img/claude-generating.png) + +One server holds several **workspaces**, each with its own workflows, assets and +outputs. Two agents, or an agent and you in the browser, can share one GPU without +saving over each other's work. + +The tool reference, setup for other MCP hosts and the troubleshooting table are in +[MCP Server](docs/MCP.md). + +## Then tweak it in the browser + +The web UI shows everything the agent made and lets you take over at any point. It +runs on the same persistent GPU worker, so models stay loaded between runs. + +**A workflow the agent wrote is a form you can edit.** The workflow's description, +recent outputs and variables are on one page. Change a prompt, a frame count or a +LoRA scale and press **Run**. + +![A workflow page: the agent's description of the trailer, its recent shots, and its variables ready to edit and run](docs/img/ui-workflow.jpg) + +**Every job is a recipe.** A job keeps the exact workflow it ran and draws it as a +graph of how data flows between steps. Click a step to see what it produced. The +run's warnings sit above the graph, so a problem the agent missed, such as an +audible level jump at a cut, is in front of you with the argument that fixes it. +**Run again** repeats the job, and **New seed** gives you another take. Progress +streams live for each denoising step, and a cancel stops the job cleanly at the +next step. + +![A finished cut-and-score job: a warning about a level jump at a cut, then the workflow as a data-flow graph from the edit and soundtrack through resampling, mixing and normalizing to the final film](docs/img/ui-job.png) + +**The gallery** groups outputs by the workflow that made them, with final and +intermediate files kept apart. Any image reopens as the workflow that produced it. +*Keep as asset* adds a file to the asset library for later workflows to use. + + + + The gallery: a poster workflow's four takes and the frames grabbed from the trailer's shots + + +**The editor works on the agent's JSON.** You can view the workflow as a form, as +JSON, side by side, or as a flow graph. The forms and autocomplete come from the +real diffusers pipeline signatures, so every argument a pipeline takes is there with +its documentation. **Validate** catches schema errors and argument typos before any +model loads. + +![The editor in split view: variables as a form beside the workflow JSON](docs/img/ui-editor.png) + +There is also a **prompt library**, with an *Enhance with AI* panel that expands an +idea into a full prompt using a local language model. The **model manager** shows +the Hugging Face cache (size, last use, free disk) and downloads or deletes models +with live progress. + +![The model manager: every cached model with its size and when it was last used](docs/img/ui-models.png) + +The pages are described in [Server & Web UI](docs/SERVER.md). The UI and the agent +both use the server's HTTP API, which you can browse at `/docs` on a running server +(see [The HTTP API](docs/SERVER.md#the-http-api)). + +## Underneath + +Every front end reads and writes the same thing: a JSON workflow of named steps, +each a diffusers pipeline or a utility task. Its arguments are references to +variables, earlier steps' results, stored prompts and assets, not hard-coded +values. That is how text-to-image chains into image-to-video, and how a generated +image reopens as the recipe that made it. You rarely need to write one by hand. When +you do, the [Workflow Guide](docs/WORKFLOW_GUIDE.md) is the reference, the +[JSON Schema](dw/workflow_schema.json) is the exact structure, and +[workflows/](workflows/) has runnable examples for each model family. There is also +a command-line client, `python -m dw.run workflow.json prompt="a cat"`, which queues +a job on a running server (see [Server & Web UI](docs/SERVER.md#the-command-line)). + +The engine also covers: + +- **Quantization:** BitsAndBytes, TorchAO, GGUF, SDNQ and optimum-quanto, set per + component. +- **Acceleration:** FirstBlockCache, FasterCache, MagCache, TaylorSeerCache, + `torch.compile` and attention backends. +- **Adapters and prompts:** LoRA, IP-Adapter and A1111-style prompt weighting. +- **Long video and caching:** long-video chaining with audio-driven length, and + step-output caching, so re-running a fixed-seed workflow finishes instantly. +- **Utility tasks:** upscaling, face restoration, segmentation, captioning, frame + interpolation, audio slicing, cut-and-score assembly and more. + +A workflow can reach any diffusers pipeline or quantization backend by dynamic +import, so loading one can execute arbitrary Python. Treat a workflow file from +someone else the way you'd treat a `.py` script. See [Trust model](docs/SECURITY.md#trust-model). -Under the hood the engine also handles: quantization (BitsAndBytes, TorchAO, -GGUF, SDNQ, optimum-quanto); inference acceleration -(FirstBlockCache, FasterCache, MagCache, TaylorSeerCache); LoRA and IP-Adapter; -A1111-style prompt weighting; long-video chaining with audio-driven length; -step-output caching, so re-running a fixed-seed workflow finishes instantly; -and utility tasks for upscaling, face restoration, segmentation, captioning, -frame interpolation and more. +**Field reports.** At the end of a session, ask the agent what got in its way, and +have it file each bug or gap as an issue on `dkackman/diffusers-workflow` with the +`field-report` label. The label marks a report as coming from real use. A report +from anyone but the maintainer is reviewed before the [agent loop](docs/AGENT_LOOP.md) +acts on it. ## Documentation -### Guides - -- [MCP Server](docs/MCP.md) — The agent tool surface (Claude Code, Claude Desktop) -- [Server & Web UI](docs/SERVER.md) — The web UI, jobs API, and introspection service -- [Remote GPU server](docs/REMOTE.md) — Using the server, UI and MCP from another machine -- [Docker](docs/DOCKER.md) — The server in a container on a Linux NVIDIA box, sharing data with a native install -- [Workspaces](docs/WORKSPACES.md) — Where your content lives, run directories, and several workspaces on one server -- [Workflow Guide](docs/WORKFLOW_GUIDE.md) — JSON structure, variables, steps, data flow -- [Quantization](docs/QUANTIZATION.md) — BitsAndBytes, TorchAO, GGUF, SDNQ -- [Inference Acceleration](docs/ACCELERATION.md) — torch.compile, FirstBlockCache, MagCache, TaylorSeer -- [Fast on 24GB](docs/RECIPES_24GB.md) — Recommended speed/memory configurations per model family -- [LoRA](docs/LORAS.md) — Loading and stacking LoRA adapters -- [IP-Adapter](docs/IP_ADAPTER.md) — Image-prompt conditioning -- [Prompt Weighting](docs/PROMPT_WEIGHTING.md) — A1111-style syntax -- [Prompt References](docs/WORKFLOW_GUIDE.md#prompt-references) — The stored prompt library and `prompt:` references -- [Tasks](docs/TASKS.md) — Image processing, ControlNet preprocessors, utilities - -### Reference - -- [Worker Guide](docs/WORKER_GUIDE.md) — GPU persistence and troubleshooting -- [Dependencies](docs/DEPENDENCIES.md) — Installation details -- [Security](docs/SECURITY.md) — Security model -- [Testing](docs/TESTING.md) — Running the test suite -- [Releasing](docs/RELEASING.md) — Cutting a release from a version tag -- [Agent Loop](docs/AGENT_LOOP.md) — What the automated implementer/tester ticket labels mean, for anyone joining an Issue -- [Attributions](docs/ATTRIBUTIONS.md) — Code included from other projects, and the projects whose ideas dw reimplemented +**Using it** + +- [MCP Server](docs/MCP.md) - the agent's tool surface (Claude Code, Claude Desktop, other MCP hosts) +- [dw plugin](plugins/dw/README.md) - the model-family and composition skills +- [Server & Web UI](docs/SERVER.md) - the web UI, the command line, and the [HTTP API](docs/SERVER.md#the-http-api) with its OpenAPI spec +- [Workspaces](docs/WORKSPACES.md) - where your content lives, run directories, several workspaces on one server +- [Remote GPU server](docs/REMOTE.md) - the server, UI and MCP from another machine +- [Docker](docs/DOCKER.md) - the server in a container on a Linux NVIDIA box + +**Workflows in depth** + +- [Workflow Guide](docs/WORKFLOW_GUIDE.md) - JSON structure, variables, steps, data flow, references +- [Tasks](docs/TASKS.md) - image, video and audio utility tasks, ControlNet preprocessors +- [Quantization](docs/QUANTIZATION.md) - BitsAndBytes, TorchAO, GGUF, SDNQ +- [Inference Acceleration](docs/ACCELERATION.md) - caches, `torch.compile`, attention backends, MPS +- [Fast on 24GB](docs/RECIPES_24GB.md) - tested speed/memory settings per model family +- [LoRA](docs/LORAS.md), [IP-Adapter](docs/IP_ADAPTER.md), [Prompt Weighting](docs/PROMPT_WEIGHTING.md) + +**Reference** + +- [Architecture](docs/ARCHITECTURE.md) - each concept, the module that owns it, and the check that enforces it +- [Worker Guide](docs/WORKER_GUIDE.md) - the persistent GPU worker and troubleshooting +- [Security](docs/SECURITY.md) - validators and the trust gate ([quick reference](docs/SECURITY_QUICKREF.md)) +- [Dependencies](docs/DEPENDENCIES.md), [Testing](docs/TESTING.md), [Releasing](docs/RELEASING.md) +- [Agent Loop](docs/AGENT_LOOP.md) - what the automated implementer/tester labels mean +- [Attributions](docs/ATTRIBUTIONS.md) - code from other projects, and the projects whose ideas dw reimplemented diff --git a/docs/ACCELERATION.md b/docs/ACCELERATION.md index e1db74441..437ffd864 100644 --- a/docs/ACCELERATION.md +++ b/docs/ACCELERATION.md @@ -1,49 +1,28 @@ # Inference Acceleration -Speed up generation by caching intermediate computations and skipping redundant transformer steps. Diffusers' built-in caching (`first_block`, `mag` and the others below) provides it. Beyond caching, `torch.compile`, attention backend selection, layerwise casting, and device-level settings (TF32, cuDNN) also affect throughput - see below. Memory offloading trades speed for VRAM and is covered in depth in [WORKFLOW_GUIDE.md](WORKFLOW_GUIDE.md#memory-offloading). - -For ready-made configurations that combine these levers per model family, see [RECIPES_24GB.md](RECIPES_24GB.md). +The levers that make a generation faster: diffusers' step caches, attention backends, `torch.compile`, layerwise casting, device settings (TF32, cuDNN) and offloading. Offloading trades speed for VRAM; its syntax is in [WORKFLOW_GUIDE.md](WORKFLOW_GUIDE.md#memory-offloading). Tested combinations per model family are in [RECIPES_24GB.md](RECIPES_24GB.md); quantization is in [QUANTIZATION.md](QUANTIZATION.md). ## Diffusers Built-in Cache -Applied at pipeline load time via the `cache` configuration. Hooks auto-reset between runs. +A `cache` block in the pipeline `configuration` attaches one of diffusers' cache hooks to the pipeline's `transformer` at load time. The hooks reset between runs. `cache` may also be a whole-object `variable:` reference; one that resolves to `null` means no cache ([WORKFLOW_GUIDE.md](WORKFLOW_GUIDE.md#cache-acceleration)). Every MiniMax-H3 template takes a `cache` argument, `null` by default and wired to the H3 step only. -`validate_workflow` warns (it never refuses) about two cache settings that cost a render to find out: +`validate_workflow` warns, and never refuses, about cache settings that cannot pay off (`dw/acceleration_warnings.py`): -- **`cache_short_schedule`: a threshold cache on a short schedule.** `first_block` and `mag` decide each step's skip on a residual threshold, and the consecutive steps of a short schedule differ too much for it: under 20 `num_inference_steps` such a cache will likely skip nothing and still perturbs the result. On MiniMax-H3 a `first_block` cache at `0.05` skipped nothing at 12 steps and took 22% off at 30 ([FirstBlockCache](#firstblockcache)). Drop the cache or run 20 steps or more. A turbo schedule (9 steps or fewer) never benefits. `taylorseer` and `faster` skip on a fixed interval and `text_kv` skips no step, so the rule does not apply to them. -- **`taylorseer_state_unestimated`: TaylorSeer's state is not in the VRAM estimate.** Said whenever `taylorseer` is set - see [TaylorSeerCache](#taylorseercache). +- **`cache_short_schedule`**: a `first_block` or `mag` cache under 20 `num_inference_steps`. Both decide each step's skip on a residual threshold, and the consecutive steps of a short schedule differ too much for it, so the cache likely skips nothing and still perturbs the result. On MiniMax-H3 a `first_block` cache at `0.05` skipped nothing at 12 steps or on a turbo schedule, and took 22% off at 30. Drop the cache or run 20 steps or more. `taylorseer` and `faster` skip on a fixed interval and `text_kv` skips no step, so the rule does not apply to them. +- **`cache_unsupported`**: the pipeline class has no `transformer` (a UNet pipeline such as SD 1.5), or its transformer class has no `enable_cache()` (Z-Image's). The step runs uncached and the job warns that the cache was not applied. +- **`taylorseer_state_unestimated`**: said whenever `taylorseer` is set - see [TaylorSeerCache](#taylorseercache). +- **`compile_fullgraph_with_cache`**: see [torch.compile](#torchcompile). ### Models diffusers has not registered -`first_block`, `mag` and `layer_skip` look a model's transformer block class up in -diffusers' own registry and raise when it is absent, which is how a model that supports -`enable_cache()` ends up with no usable cache. [cache_blocks.json](../dw/cache_blocks.json) -fills those gaps in, registering the missing block metadata on demand; entries become -redundant, not wrong, once diffusers registers the same class upstream. MiniMax-H3, -LTX-2 and Qwen-Image-2.1 are listed there today. - -Qwen-Image-2.1's pipeline keeps a key/value cache of its own (`use_kv_cache`, on by -default) that drops the text tokens from the sequence after the first step. `first_block` -and `mag` compare one step's residual against the previous one, so the sequence length -changing under them fails the second step; pass `"use_kv_cache": false` in the step's -`arguments` to cache it. - -A transformer that does not support `enable_cache()` at all - Z-Image's, for one - takes -no cache of any type. The step still runs, uncached, and the job carries a warning that the -cache was not applied. - -LTX-2's block needs one thing more than the metadata diffusers defines. It returns two -streams - video and audio - and diffusers reads the second one back out of a forward -argument named literally `encoder_hidden_states`, the only two-stream shape it registers -upstream (text beside image). LTX-2's blocks take an `encoder_hidden_states` of their -own, the text conditioning, so the fixed name reads the wrong tensor and feeds the text -embeddings back as the audio stream on every skipped block. The entry names the argument -its second stream actually comes from (`encoder_hidden_states_argument_name`), which is -what makes caching correct there rather than merely quiet. +`first_block` and `mag` look the transformer's block class up in diffusers' block registry and raise when it is absent, so a model that supports `enable_cache()` can still have no usable cache. [cache_blocks.json](../dw/cache_blocks.json) registers the missing block metadata on demand (`dw/cache_blocks.py`); an entry becomes redundant, not wrong, once diffusers registers the class itself. It lists MiniMax-H3, LTX-2 and Qwen-Image-2.1. + +- **LTX-2**: its blocks return video and audio streams, and diffusers reads the second stream back from a forward argument named `encoder_hidden_states`. LTX-2's own `encoder_hidden_states` is the text conditioning, so a skipped block would feed the text embeddings back as audio. The entry's `encoder_hidden_states_argument_name` names the argument the second stream actually comes from. +- **Qwen-Image-2.1**: the pipeline's own key/value cache (`use_kv_cache`, on by default) drops the text tokens from the sequence after the first step, and the changed sequence length fails `first_block` and `mag` on the second step. Pass `"use_kv_cache": false` in the step's `arguments` to cache it. ### FirstBlockCache -Simplest and broadest support. Compares first-block residuals to decide whether to skip remaining blocks. +Compares the first block's residual between steps to decide whether to skip the remaining blocks. The broadest support. ```json "configuration": { @@ -55,13 +34,13 @@ Simplest and broadest support. Compares first-block residuals to decide whether } ``` -Higher threshold = more speedup, more quality loss. Start with `0.05` and increase to taste. On MiniMax-H3 (RTX 3090, 1344x768, 4 references) `0.05` took 22% off a 30-step base-model job and nothing off a 12-step or turbo one, which it never skips; `torch.compile` there cost 289 s on the first pass for ~1% per pass, `text_kv` did nothing on Ref2VA, and `taylorseer` ran out of memory. Every MiniMax-H3 template takes a `cache` argument, `null` (off) by default and wired to the H3 step only: pass `{"type": "first_block", "threshold": 0.05}` rather than editing the JSON, and only at ~20 or more base steps, never with the turbo LoRAs. +`threshold` defaults to `0.05`. Higher skips more and costs more quality. On MiniMax-H3 (RTX 3090, 1344x768, 4 references) `0.05` took 22% off a 30-step base-model job and nothing off a 12-step or turbo one. On H3 `torch.compile` cost 289 s on the first pass for about 1% per pass, `text_kv` did nothing on Ref2VA, and `taylorseer` ran out of memory. On an H3 template pass `cache={"type": "first_block", "threshold": 0.05}`, at 20 or more base steps and never with a turbo LoRA. **Example:** [step-caching.json](../workflows/templates/step-caching.json) ### MagCache -Magnitude-based caching with error accumulation. Requires `num_inference_steps` to match the pipeline arguments, and `mag_ratios` — the per-step magnitude ratios, which are checkpoint-dependent: +Magnitude-based skipping with error accumulation: ```json "cache": { @@ -76,29 +55,33 @@ Magnitude-based caching with error accumulation. Requires `num_inference_steps` | Property | Default | Description | | -------- | ------- | ----------- | -| `mag_ratios` | required | Preset name or explicit per-step ratio array — see below | +| `mag_ratios` | required unless `calibrate` | Preset name or per-step ratio array - see below | | `threshold` | 0.06 | Accumulated error threshold for skipping | -| `num_inference_steps` | required | The transformer forwards the call runs: the pipeline's `num_inference_steps`, except where the scheduler runs fewer (MiniMax-H3 runs one fewer) | +| `num_inference_steps` | 28 (diffusers) | The transformer forwards the call runs: the pipeline's `num_inference_steps`, except where the scheduler runs fewer (MiniMax-H3 runs one fewer). Set it to match the call | | `max_skip_steps` | 3 | Max consecutive steps to skip | | `retention_ratio` | 0.2 | Fraction of initial steps where skipping is disabled | -| `calibrate` | false | Measure ratios for a new model instead of skipping — see below | +| `calibrate` | false | Measure ratios for a new model instead of skipping - see below | #### Supplying `mag_ratios` -MagCache needs to know how each denoising step's output magnitude typically behaves for *your* checkpoint, so unlike the other cache types it cannot run on defaults alone. Give it either: +The ratios describe how each step's output magnitude behaves for one checkpoint, so MagCache cannot run on defaults alone. Give either: -- **A preset name** — `"mag_ratios": "flux"` resolves to the ratios diffusers ships for Flux. Any preset a later diffusers release adds is usable by name without a change here. -- **An explicit array** — `"mag_ratios": [1.0, 0.98, 0.96, ...]`. The array is interpolated automatically when its length differs from `num_inference_steps`, so ratios measured at one step count can be reused at another. +- **A preset name**: `"mag_ratios": "flux"` resolves to diffusers' `FLUX_MAG_RATIOS`. A preset a later diffusers adds (`_MAG_RATIOS`) works by name with no change here; an unknown name fails with the list of presets. +- **An array**: `"mag_ratios": [1.0, 0.98, 0.96, ...]`. An array whose length differs from `num_inference_steps` is interpolated. -For a model with no preset, run once with `"calibrate": true`. Calibration skips nothing. At the end of the run the measured ratios appear as a job warning and a `log` event (`get_job`'s `warnings`, `get_job_events`, `export_job`): `MagCache calibration (N steps): [ ... ] — paste into mag_ratios`. While calibrating, the run's own `num_inference_steps` is the step count, so the cache's can be left at its default (a mismatch is reported as a warning). The cache attaches to a pipeline's `transformer`, and only one that supports `enable_cache()`: a UNet pipeline such as SD 1.5 has no transformer, and Z-Image's transformer has no cache support, so both get a warning that the cache was not applied and no calibration. Paste that array into `mag_ratios` and drop the `calibrate` flag for subsequent runs. `N` is the forwards the run actually made, which is not always the call's `num_inference_steps`: MiniMax-H3's scheduler turns N sigmas into N - 1 evaluations, so a 30-step H3 call reports 29 ratios. Set the cache's `num_inference_steps` to that `N` when you paste them, so each ratio lines up with its forward. A pipeline that runs classifier-free guidance as two transformer passes in their own `cond` and `uncond` cache contexts (Qwen-Image-2.1 with `true_cfg_scale` above 1 and a `negative_prompt`, or the other Qwen-Image pipelines) keeps a separate state for each, so it emits **two** lists, each labeled `[cond]` or `[uncond]`; use the `cond` one, since `mag_ratios` takes one array. A guidance-distilled pipeline such as Flux, or one that batches both passes into one forward emits one unlabeled list. +For a model with no preset, run once with `"calibrate": true`: ```json "cache": { "type": "mag", "calibrate": true, "num_inference_steps": 28 } ``` +Calibration skips nothing. At the end of the run the ratios appear as a job warning and a `log` event (`get_job`'s `warnings`, `get_job_events`, `export_job`): `MagCache calibration (N steps): [ ... ] — paste into mag_ratios`. While calibrating, the run's own `num_inference_steps` is used, and a mismatch with the cache's is reported as a warning. Paste the array into `mag_ratios`, drop `calibrate`, and set the cache's `num_inference_steps` to the reported `N`, which counts the forwards actually made: MiniMax-H3's scheduler turns N sigmas into N - 1 evaluations, so a 30-step H3 call reports 29 ratios. + +A pipeline that runs classifier-free guidance as two passes in separate `cond` and `uncond` cache contexts (Qwen-Image-2.1 with `true_cfg_scale` above 1 and a `negative_prompt`, or the other Qwen-Image pipelines) emits two lists, labeled `[cond]` and `[uncond]`; use the `cond` one. A guidance-distilled pipeline such as Flux, or one that batches both passes into one forward, emits one unlabeled list. A pipeline the cache cannot attach to (see `cache_unsupported` above) reports nothing. + ### TaylorSeerCache -Taylor series approximation of cached outputs: +Approximates skipped outputs with a Taylor series: ```json "cache": { @@ -111,13 +94,13 @@ Taylor series approximation of cached outputs: | Property | Default | Description | | -------- | ------- | ----------- | | `cache_interval` | 5 | Full computation every N steps | -| `max_order` | 1 | Taylor series order (higher = better approximation, and more state kept on the GPU) | +| `max_order` | 1 | Taylor series order (higher = better approximation, more state on the GPU) | -TaylorSeer keeps per-block state on the GPU that no `vram_estimate` charges, so a job that fits without the cache can run out of memory with it. On MiniMax-H3 at 1344x768, 30 steps and `max_order` 1, a run that `validate_workflow` had passed ran out of memory 2 min in on a 24 GB card. Validate says so as `taylorseer_state_unestimated` whenever `taylorseer` is set, with the job's estimate and the card's capacity when both are known; the state itself is on top of that figure, and is not sized. +TaylorSeer keeps per-block state on the GPU that no `vram_estimate` charges, so a job that fits without it can run out of memory with it. On MiniMax-H3 at 1344x768, 30 steps and `max_order` 1, a job `validate_workflow` had passed ran out of memory 2 min in on a 24 GB card. Validate warns `taylorseer_state_unestimated`, with the job's estimate and the card's capacity when both are known; the state comes on top of that figure and is not sized. ### FasterCache -Experimental, video-oriented. Uses FFT frequency decomposition: +Experimental and video-oriented (CogVideoX, for example); uses FFT frequency decomposition. dw wires diffusers' `current_timestep_callback` to the pipeline, so no other parameter is needed for basic use: ```json "cache": { @@ -125,16 +108,13 @@ Experimental, video-oriented. Uses FFT frequency decomposition: } ``` -Best for video models like CogVideoX. No additional parameters needed for basic use. - | Property | Default | Description | | -------- | ------- | ----------- | -| `is_guidance_distilled` | false | Set `true` for a guidance-distilled model (FLUX.1-dev, FLUX.1-Krea-dev), where `guidance_scale` is an embedding and the transformer sees a batch of 1. FasterCache otherwise assumes a cond/uncond batch of 2 and fails at the first step with `tuple index out of range` (#895). Validate cannot tell a distilled model from a CFG one, so it does not check this | +| `is_guidance_distilled` | inferred | `true` for a guidance-distilled model (FLUX.1-dev, FLUX.1-Krea-dev), where `guidance_scale` is an embedding and the transformer sees a batch of 1. Unset, dw sets it `true` when the transformer's config has `guidance_embeds: true`, else diffusers' `false`. Wrong, FasterCache assumes a cond/uncond batch of 2 and fails at the first step with `tuple index out of range` (#895). Validate does not check it | ### TextKVCache -Caches the transformer's key/value projections of the (unchanging) text -embeddings across denoising steps, recomputing only what the latents need: +Caches the transformer's key/value projections of the unchanging text embeddings across steps. No parameters. ```json "cache": { @@ -142,15 +122,15 @@ embeddings across denoising steps, recomputing only what the latents need: } ``` -No parameters. - ### Reading the skip counts -A step that set a `cache` reports `timing.cache` on its manifest entry (fields in [WORKSPACES.md](WORKSPACES.md#step-timing)). `hit_rate` is the fraction of denoiser forwards whose remaining blocks were skipped. It counts forwards, not steps: classifier-free guidance runs two per step, so a 30-step run is 60 forwards. Only `first_block` and `mag` are counted; `taylorseer`, `faster` and `text_kv` report null counts. A cache with zero skips can still change the output, so a 0-skip run is not guaranteed bit-identical to an uncached one. With `first_block` the head block always runs, and enabling any cache wraps the blocks in hooks that recompute the residual (the tail's output minus the head's, in the transformer's dtype), which changes the order and precision of operations; the MiniMax-H3 matrix run P2 saw a changed output with nothing skipped. Judge quality against an uncached run, not against `skipped == 0`. A threshold sweep (#875) reads `hit_rate` against quality. +A step with a `cache` reports `timing.cache` on its manifest entry (fields in [WORKSPACES.md](WORKSPACES.md#step-timing)). `hit_rate` is the fraction of denoiser forwards whose remaining blocks were skipped. It counts forwards, not steps: classifier-free guidance runs two per step, so a 30-step run is 60 forwards. Only `first_block` and `mag` are counted; `taylorseer`, `faster` and `text_kv` report null counts. + +Zero skips is not an uncached run. With `first_block` the head block always runs, and any cache wraps the blocks in hooks that recompute the residual in the transformer's dtype, which changes the order and precision of operations; the MiniMax-H3 matrix run P2 changed output with nothing skipped. Judge quality against an uncached run (a `null` cache), not against `skipped == 0`. A threshold sweep (#875) reads `hit_rate` against quality. ## Attention Backends -Select the attention implementation diffusers uses for the duration of each pipeline call, via a context manager wrapped around `pipeline(...)`: +`attention_backend` in the pipeline `configuration` selects diffusers' attention implementation for the duration of each pipeline call, via a context manager around `pipeline(...)`: ```json "configuration": { @@ -159,33 +139,48 @@ Select the attention implementation diffusers uses for the duration of each pipe } ``` -Common values: `"flash"`, `"flash_hub"`, `"sage"`, `"sage_hub"`, `"native"`, `"flex"`. The full set is diffusers' `AttentionBackendName` enum - availability depends on what's installed (`flash-attn`, `sageattention`, etc.) and the platform. `_hub`-suffixed backends are fetched from the Hugging Face Hub kernel registry on first use, which needs the `kernels` package installed (`pip install kernels`) - it is not a dw dependency. The only backend a bundled workflow sets is `sparse`, the default on every MiniMax-H3 template, and it needs no `kernels` install (comfy-kitchen is a core dependency), so each bundled workflow runs on a plain install. diffusers also refuses a Hub kernel whose publisher it does not list as trusted - `sage_hub` among them, since SageAttention 2 comes from `SageAttention/sage-attention` - until `DIFFUSERS_TRUST_REMOTE_KERNELS=true`, which allows remote code from any such publisher. On an RTX 3090 `sage_hub` measured about 30% faster per MiniMax-H3 step (13.8 s against 19.2 s at 960x544x124, 54.7 s against 81.2 s at 345 frames); the local `sage` backend needs `sageattention>=2.1.1`, which PyPI does not carry. +A component can pin its backend instead, persistently, via `set_attention_backend`: + +```json +"configuration": { + "components": { + "transformer": { "attention_backend": "flash_hub" } + } +} +``` + +Pin it on a compiled component: the per-call context manager switches implementations under the compiled graph and forces a recompile on every run. Set at both levels, the component's own wins. + +Values are diffusers' `AttentionBackendName` enum, plus dw's [`sparse`](#sparse-minimax-h3). Common ones: `native`, `flex`, `flash`, `flash_hub`, `sage`, `sage_hub`. Availability depends on the installed packages and the platform: + +- `_hub` backends are fetched from the Hugging Face Hub kernel registry on first use through the `kernels` package, which dw installs. +- diffusers refuses a Hub kernel from a publisher it does not list as trusted - `sage_hub` among them (SageAttention 2 from `SageAttention/sage-attention`) - until `DIFFUSERS_TRUST_REMOTE_KERNELS=true` is set in the server's environment (`.env`). That allows remote code from any such publisher. +- The local `sage` backend needs `sageattention>=2.1.1`, which PyPI does not carry. +- The flash, flash-3, sage and xformers families need CUDA, `_hub` variants included. On a server whose device is not CUDA, `validate_workflow` warns `attention_backend_unavailable`, naming the backend and the device: the job would fail at load. A `native`-family backend never warns, and a backend diffusers adds later draws no warning until dw's list (`CUDA_ONLY_ATTENTION_BACKENDS`) is updated. + +On an RTX 3090 `sage_hub` measured about 30% faster per MiniMax-H3 step than `native` (13.8 s against 19.2 s at 960x544x124, 54.7 s against 81.2 s at 345 frames). LTX-2.5 cannot use sage: its transformer passes an attention mask, which sage refuses ("`attn_mask` is not supported for sage attention", measured 2026-10-10), so the LTX templates expose no backend. -Every MiniMax-H3 template in [templates/minimax/](../workflows/templates/minimax/) -exposes this as a variable: `attention_backend`, pinned on the H3 step's transformer -(`components.transformer`, or `components.transformer_ref` for a ref2va workflow) and -`"sparse"` by default (with `sparse_tau`, below). That covers the shots, chains and episodes - `dialogue-short`, -`shots-batch`, `music-video` and `storyboard` among them - and only their H3 steps: a -Z-Image still or Music3 song beside an H3 shot keeps the process default. Pass -`attention_backend=sage_hub` (or `native`) to override the default; `sage_hub` needs a box with the `kernels` package and -`DIFFUSERS_TRUST_REMOTE_KERNELS=true` in its `.env`; on MPS the default falls back to dense, but a CUDA-only kernel such as `sage_hub` fails there at load (validation warns, below). LTX-2.5 cannot use it: its transformer -passes an attention mask, and sage attention refuses one ("`attn_mask` is not supported -for sage attention", measured 2026-10-10), so the LTX templates expose no backend. +Either form stays on what it names. diffusers' `set_attention_backend` also makes its backend the process-wide default, which the persistent worker would keep for every later model; dw restores the default after pinning, so an H3 `sage_hub` pin cannot reach a Music3 or LTX job that follows. The server-wide default is `DIFFUSERS_ATTN_BACKEND` in `.env`; leave it `native`, since it reaches every model and sage cannot serve all of them. ### `sparse` (MiniMax-H3) -`attention_backend: "sparse"` runs H3's attention through comfy-kitchen's block-sparse kernel: each query tile attends only to the key tiles that matter, so the cost of the long video-token sequence falls. It applies to the H3 transformer only, at pipeline or component level, and is the default on every H3 template (#931). Set at both levels, the component's own `attention_backend` wins, as it does for every backend: a pipeline-level `sparse` skips a component pinned to `sage_hub` (or anything else) and warns `attention_backend 'sparse' at pipeline level skips '', which pins ''`. When it does install, the run logs `Using attention backend: sparse (tau ) on `, or `(topk )` with top-k routing. `sparse_tau` (number, minimum 0, default 0.5; anything else, such as a `variable:` that resolves to a string or a bool, fails the step rather than running dense) sets how much of the attention mass a tile may drop; 0 keeps the most. Observed at Ref2VA 1344x768, seed 42: tau 1.0 ran 42.1 s/forward against 44.8 s at 0.5, and by Don's eye 1.0 gave smoother skin while 0.5 matched dense (the table below). That is one shape and one seed, not a rule that higher is always faster or smoother. +`attention_backend: "sparse"` runs H3's attention through comfy-kitchen's block-sparse kernel (`dw/sparse_attention.py`, #931): each query tile attends only to the key tiles that matter, so the cost of the long video-token sequence falls. Text, references and audio stay dense. comfy-kitchen is a core dependency, so it needs no `kernels` package or `DIFFUSERS_TRUST_REMOTE_KERNELS`. The only backend a bundled workflow sets is `sparse`. -`sparse_topk` (a number strictly between 0 and 1; the H3 templates set 0.15, and a workflow that sets none routes by `sparse_tau`) switches the kernel to top-k routing: each query tile keeps that fraction of key tiles and drops the rest outright, with `sparse_tau` ignored. It is the selection lightx2v's Turbo-SLA LoRAs were trained against (85% sparsity, so `0.15`). Measured on `video-with-audio-768p`, seed 42 (#932): top-k 0.15 ran 21.1 s/forward against 23.6 s at tau 0.5, about 11% faster, and Don saw no difference on that clip, with either the Turbo-SLA LoRA or the default v1.2 turbo (so dw does not adopt the SLA LoRA). That clip has no faces, so it was checked on the H3 quality ramp's 4-reference sitcom shot at seeds 42, 7 and 1234 against tau 0.5. Don judged top-k equal or better: on the turbo L7 rung tau showed blue ghosting at seed 7 and slight smearing at all three, and on the 30-step base-model L5 rung the skin held. Top-k 0.15 is therefore the H3 templates' default; pass `sparse_topk=null` to route by `sparse_tau` instead. +Every H3 template in [templates/minimax/](../workflows/templates/minimax/) takes it as variables, pinned on the H3 step's transformer (`components.transformer`, or `components.transformer_ref` for a ref2va workflow): `attention_backend` (default `"sparse"`), `sparse_tau` (0.5) and `sparse_topk` (0.15). That covers the shots, chains and episodes (`dialogue-short`, `shots-batch`, `music-video`, `storyboard` among them), and only their H3 steps: a Z-Image still or Music3 song beside an H3 shot keeps the process default. Pass `attention_backend=sage_hub` (or `native`) to override. + +- **Where it applies**: the H3 transformer, at pipeline or component level. On any other component, or a pipeline with no H3 transformer, it runs dense with a warning. A pipeline-level `sparse` skips a component pinned to another backend and warns `attention_backend 'sparse' at pipeline level skips '', which pins ''`. When it installs, the run logs `Using attention backend: sparse (tau ) on `, or `(topk )`. +- **`sparse_tau`**: number >= 0, default 0.5. How much of the attention mass a tile may drop: higher is sparser and faster, 0 keeps the most. Anything else, such as a `variable:` that resolves to a string or a bool, fails the step rather than running dense. +- **`sparse_topk`**: a number strictly between 0 and 1, unset by default outside the templates. Switches to top-k routing: each query tile keeps that fraction of key tiles and drops the rest, and `sparse_tau` is ignored. It is the selection lightx2v's Turbo-SLA LoRAs were trained against (85% sparsity, so `0.15`). Pass `sparse_topk=null` to an H3 template to route by `sparse_tau`. +- Both are runtime settings: changing one reuses the loaded pipeline. + +**Top-k 0.15 against tau 0.5** (#932). On `video-with-audio-768p`, seed 42, top-k ran 21.1 s/forward against 23.6 s at tau 0.5, about 11% faster, with no visible difference under the Turbo-SLA LoRA or the default v1.2 turbo (so dw does not adopt the SLA LoRA). On the H3 quality ramp's 4-reference sitcom shot, seeds 42, 7 and 1234, Don judged top-k equal or better: on the turbo L7 rung tau showed blue ghosting at seed 7 and slight smearing at all three, and on the 30-step base-model L5 rung the skin held. | rung (minutes per job, seeds 42 / 7 / 1234) | tau 0.5 | top-k 0.15 | |---|---|---| | L7: ref2v 4-step turbo, int8 | 6.5 / 6.5 / 6.5 | 6.1 / 6.2 / 5.8 | | L5: base model, int8, 30 steps | 26.8 / 26.6 / 26.4 | 23.9 / 25.5 / 23.9 | -**What the default buys depends on what it replaces.** Against `native` attention, which is what an H3 template ran before #931 unless `attention_backend` was set, sparse is 2.2-4.4x faster per step. Against `sage_hub`, the gain is much smaller: 6-16% depending on the shape (the second table). So sparse's main win is for a run that set no backend, and it needs no `kernels` package or `DIFFUSERS_TRUST_REMOTE_KERNELS`. A box already on `sage_hub` gains a little, at equal quality: on the H3 skill's quality-ramp rungs, Don judged faces, skin and draft-to-final staging equal under both backends across three seeds (#931 stage 3). - -Measured on an RTX 3090, seed 42, s per transformer forward (one per denoise step), lower is faster: +**Sparse against `native` and `sage_hub`.** Against `native`, which an H3 template ran before #931 unless `attention_backend` was set, sparse is 2.2-4.4x faster per step. Against `sage_hub` the gain is 6-16% depending on the shape, at equal quality: on the H3 skill's quality-ramp rungs Don judged faces, skin and draft-to-final staging equal under both across three seeds (#931 stage 3). RTX 3090, seed 42, s per transformer forward (one per denoise step): | shape | native | `sage_hub` | sparse | sparse vs native | sparse vs `sage_hub` | |---|---|---|---|---|---| @@ -194,9 +189,9 @@ Measured on an RTX 3090, seed 42, s per transformer forward (one per denoise ste | Ref2VA 1344x768, 8-step ref2v turbo, tau 1.0 | 108.4 | - | 42.1 | 2.58x | - | | Ref2VA 1344x768, 8-step ref2v turbo, tau 0.5 (the default) | 108.4 | 51.0 | 44.1-44.8 | 2.42x | 1.16x | -Wall time, same order: 150 -> 95 s, 457 -> 158 s, 965 -> 430 s, and for the last row 965 s native, 489 s `sage_hub`, 442-450 s sparse. +Wall time, same order: 150 -> 95 s, 457 -> 158 s, 965 -> 430 s, and for the last row 965 s native, 489 s `sage_hub`, 442-450 s sparse. Peak VRAM did not change. By eye, tau 0.5 matched dense and tau 1.0 gave smoother skin; that is one shape and one seed, not a rule that higher tau is always faster or smoother. The native and tau 1.0 figures come from the #931 prototype; the `sage_hub` figure and the 44.1 s come from the engine on lem after the deploy, with a newer diffusers (jobs 94c04976d21d and 593131ca713f), so the sage comparison is between those two jobs only. -Against `sage_hub` on the H3 skill's quality-ramp rungs (ep 6 cold open, 4 references, 1344x768, 124 frames; minutes per job, seeds 42 / 7 / 1234, all on the same diffusers): +On the quality-ramp rungs (ep 6 cold open, 4 references, 1344x768, 124 frames; minutes per job, seeds 42 / 7 / 1234, same diffusers): | rung | `sage_hub` | sparse | sparse vs `sage_hub` | |---|---|---|---| @@ -205,27 +200,11 @@ Against `sage_hub` on the H3 skill's quality-ramp rungs (ep 6 cold open, 4 refer | B3: base model, int8, 12 steps | 13.0 / 12.9 / 12.9 | 12.3 / 12.0 / 12.1 | 1.06-1.07x | | L5: base model, int8, 30 steps | 29.7 / 29.7 / 29.7 | 26.8 / 26.6 / 26.4 | 1.11-1.12x | -The `first_block` cache on `sage_hub` (23.1 min at L5's settings) is still faster than sparse alone there; sparse with that cache is not measured. The native and tau 1.0 figures come from the #931 prototype. The `sage_hub` figure and the 44.1 s come from the engine on lem after the deploy, with a newer diffusers (jobs 94c04976d21d and 593131ca713f), so the sage comparison is between those two jobs only. The T2VA rows have no `sage_hub` run. Peak VRAM did not change. Judged by eye, tau 0.5 matched dense; tau 1.0 was smoother in skin. +The `first_block` cache on `sage_hub` (23.1 min at L5's settings) is still faster than sparse alone there; sparse with that cache is not measured. -It needs CUDA with compute capability 8.0 or higher. Anywhere else (MPS, CPU, an older card, an unsupported dtype or head size) the run goes dense instead of failing, and says so twice: `validate_workflow` warns `sparse_attention_fallback` when the server's device is not CUDA, and the run reports `