Spaces:
Running on Zero
Running on Zero
| title: LiveWan | |
| emoji: ποΈ | |
| colorFrom: blue | |
| colorTo: indigo | |
| sdk: gradio | |
| sdk_version: 6.24.0 | |
| app_file: app.py | |
| python_version: "3.10" | |
| short_description: Streaming text-to-video you can steer mid-stream | |
| startup_duration_timeout: 1h | |
| pinned: false | |
| license: apache-2.0 | |
| models: | |
| - JonathanColetti/LiveWan | |
| - Wan-AI/Wan2.1-T2V-1.3B | |
| tags: | |
| - text-to-video | |
| - streaming | |
| - real-time | |
| - wan2.1 | |
| # LiveWan | |
| Streaming, steerable text-to-video from a 1.3B student distilled out of | |
| Wan2.1-T2V-14B with SF-DMD. It generates video continuously rather than as a fixed | |
| clip β 750 ms of 640Γ368 at a time, extended block by block β and the text | |
| conditioning can be swapped mid-stream without clearing the K/V cache, so the scene | |
| continues instead of cutting. | |
| - Code: https://github.com/JonathanColetti/LiveWan | |
| - Weights and data: https://huggingface.co/JonathanColetti/LiveWan | |
| ## What this Space runs | |
| `app.py` drives the project's own serving engine, `wanstreamer.serve.engine.Engine` | |
| β the same code path `livewan-serve` runs locally. `wanstreamer/` and | |
| `wan21_patches/` here are copies of the GitHub repo's; the Wan2.1 reference code is | |
| cloned and patched at startup exactly as `setup.sh` does. | |
| **Steering is a schedule, not a button.** This is the one real difference from the | |
| local demo. A GPU worker is forked per request and cannot be steered from outside | |
| while it runs, so the swap is given up front β "switch to this at t = N seconds" β | |
| rather than clicked mid-stream. The swap itself is the live one: `Engine.steer` | |
| replaces the cross-attention conditioning and leaves the K/V cache in place. | |
| Steers can come from the 96-prompt bank or from free text. umt5-xxl is downloaded | |
| and loaded at module scope, so ZeroGPU packs it with the rest of the weights instead | |
| of making every cold worker pay for a lazy 11 GB load; it is most of why this Space | |
| pulls 29 GB at startup. Free text is not numerically comparable to the bank, though | |
| β umt5 embeddings differ slightly by hardware, so the same string encoded on the | |
| training box is a slightly different tensor. | |
| `torch.compile` of the VAE decoder is off, since it cannot run in a ZeroGPU worker, | |
| and that is what keeps this Space below real time. Measured here: 386 ms to generate | |
| a block and 631 ms to decode it, against a 750 ms budget β **0.74Γ real time**. | |
| Decoding is the whole of the gap; locally, with the decoder compiled, it takes | |
| 343 ms and the same checkpoint sustains about 2.7Γ real time. | |
| Two more things this port had to work around, both specific to running inside a | |
| forked ZeroGPU worker: | |
| - **Block generation is forced under `torch.no_grad()`.** `generate_block` has no | |
| `no_grad` of its own and relies on `requires_grad_(False)` set at load time. The | |
| weights are loaded in the main process, packed to disk and streamed into the | |
| worker, and they arrive with `requires_grad` back to `True` β so every layer of | |
| every forward was being retained and a single block peaked at 44.2 GB. | |
| - **A CUDA OOM inside a worker does not say so.** PyTorch builds its OOM message by | |
| asking NVML which processes hold memory; NVML is not available there, so the run | |
| dies on `NVML_SUCCESS == r INTERNAL ASSERT FAILED` instead. | |