Image Gen Dispatcher (cache + ledger + image_done)
The node that actually draws the stills — with a cache that never lies
- patched_ledger_json
- image_done
- report
Here's the moment the pack actually spends GPU on pictures. OTR_ImageGenDispatcher is the terminal C node for the image branch: it takes the image policy from OTR_ImageDirector and the per-object prompts from OTR_MetaBriefImagePromptGen, renders every still, stamps the ledger, and emits an image_done signal that lets the video branch start. It's the image-side equivalent of the voice dispatchers - and it's where the pack's cache philosophy pays off.
The mechanism that matters is content-addressed caching. For each image object, the dispatcher builds a cache key from (role, object_id, prompt_hash, seed, engine_id, engine_version). On a cache hit it reuses the existing image - no GPU spent. On a miss it generates, then writes the still content-addressed at output/otr/stills/{portrait_content_hash}.png, never overwriting. Change the prompt, seed, or engine and you get a new hash, new pixels, new file - and every downstream consumer that keys on that hash (like a 3D mesh cache) invalidates correctly. This is the anti-frustration design done right: rerunning an episode with identical inputs is nearly free.
There's a hard cap wired in too: fresh-mode renders are limited to min(fresh_cap, beat_budget) - never over-generate, per the director's plan. And it's fail-closed: if the selected engine isn't usable, it raises rather than silently swapping to a different model. No quiet Flux substitution, ever.
Inputs and outputs
Required: script_json (the frozen ledger - the dispatcher stamps ledger['images'] into it), image_policy_json (from ImageDirector), and image_prompts_json (the versioned {"objects":[...]} payload from MetaBriefImagePromptGen - portraits plus scene stills). Optional: gate_in (an ordering signal, e.g. audio_done) and episode_id - every still materializes into episodes/<episode_id>/stills/ plus a stills_manifest.json, and an unkeyed dispatch is loud.
Outputs: patched_ledger_json (the ledger with images stamped in), image_done (the gate that mirrors audio_done - wire it to OTR_ShotLock), and report.
Install and gotchas
Pack-level install (ComfyUI Manager → ComfyUI-OldTimeRadio, restart). The local image default is Z-Image-Turbo (Apache-2.0), which a fresh run needs on disk; heavier engines like flux_gen1 (BFL non-commercial) and ideogram4_local (non-commercial, 16 GB-class) are opt-in. If the console names a missing weight, that's the fail-closed contract - install it or pick the procedural path. The dispatcher takes a shared GPU-residency lease for local engines and re-probes NVML on release, so don't fight it for VRAM by running other GPU work in parallel on the same card mid-episode.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| script_json | STRING | {} | Frozen ledger JSON; the dispatcher stamps ledger['images'] into it. |
| image_policy_json | STRING | {} | OTR_ImageDirector policy (engine per role + granularity + seed). |
| image_prompts_json | STRING | {} | OTR_MetaBriefImagePromptGen output: the versioned {"objects":[...]} payload (portraits + scene stills). |
| gate_inopt | STRING | Optional ordering signal (e.g. audio_done); opaque STRING. | |
| episode_idopt | STRING | Episode id (still-spine ST-3/DS-3; wired in the saved json). Every still materializes into episodes/<episode_id>/stills/ + stills_manifest.json. Falls back to the ledger's episode_id; an unkeyed dispatch is LOUD. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| patched_ledger_json | STRING | — |
| image_done | STRING | — |
| report | STRING | — |