|
| 1 | +# `fastvideo/pipelines/basic/magi_human/` — daVinci-MagiHuman |
| 2 | + |
| 3 | +**Generated:** 2026-05-07 |
| 4 | + |
| 5 | +Single-stream joint audio-visual generative pipeline. 4 variants × 2 input modes |
| 6 | +(T2V / TI2V) = 8 entrypoints. The DiT denoises video and audio latents in one |
| 7 | +unified token sequence — no cross-attention, channel-major token packing. |
| 8 | + |
| 9 | +If you are touching this pipeline, **read the parity invariants and cross-refs |
| 10 | +sections below before editing any file in the manifest**. |
| 11 | + |
| 12 | +## Manifest |
| 13 | + |
| 14 | +| File | Role | |
| 15 | +|------|------| |
| 16 | +| `magi_human_pipeline.py` | Composed pipeline class. `load_modules` lazy-loads four shared upstream components (Wan 2.2 VAE, T5-Gemma, Stable Audio VAE, scheduler). | |
| 17 | +| `pipeline_configs.py` | Per-variant `PipelineConfig` dataclasses (`base`, `distill`, `sr_540p`, `sr_1080p`). | |
| 18 | +| `presets.py` | Preset registry per variant — entry point that `fastvideo/registry.py` imports. | |
| 19 | +| `__init__.py` | SPDX header only; no public exports beyond what `presets.py` registers. | |
| 20 | +| `stages/audio_decoding.py` | Decodes audio latents through the lazy Stable Audio VAE wrapper. | |
| 21 | +| `stages/denoising.py` | Joint AV denoising loop (32-step FlowUniPC with CFG=2 base, 8-step CFG=1 distill). | |
| 22 | +| `stages/latent_preparation.py` | Channel-major video token packing + audio interleave + reference-image masking. **Carries the channel-major packing invariant — see Parity Invariants.** | |
| 23 | +| `stages/reference_image.py` | TI2V reference-image conditioning. | |
| 24 | +| `stages/sr_denoising.py` | SR DiT denoising loop with cfg-trick guidance tensor. SR-1080p uses block-sparse video→video local-window attention on 32 of 40 SR DiT layers via a 3-block SDPA accumulator. | |
| 25 | +| `stages/sr_latent_preparation.py` | Trilinear-up of base latent + ZeroSNR noise + audio mix for the SR pass. | |
| 26 | +| `stages/__init__.py` | Re-exports stage classes. | |
| 27 | +| `JOURNAL.md` | 14-wave port-state journal; root cause writeups for the parity invariants below. | |
| 28 | +| `AGENTS.md` | This file. | |
| 29 | + |
| 30 | +External coordinates of related files (read these too if you change wiring): |
| 31 | + |
| 32 | +| Path | Role | |
| 33 | +|------|------| |
| 34 | +| `fastvideo/models/dits/magi_human.py` | DiT architecture port. | |
| 35 | +| `fastvideo/configs/models/dits/magi_human.py` | `MagiHumanVideoConfig` arch dataclass. | |
| 36 | +| `fastvideo/models/encoders/t5gemma.py` | T5-Gemma 9B UL2 text encoder port. | |
| 37 | +| `fastvideo/configs/models/encoders/t5gemma.py` | `T5GemmaEncoderConfig`. | |
| 38 | +| `fastvideo/models/vaes/sa_audio.py` | Lazy `OobleckVAE` wrapper (shared with `pipelines/basic/stable_audio/`). **Not new — pre-existed on main.** | |
| 39 | +| `fastvideo/models/vaes/oobleck.py` | `OobleckVAE` itself (pre-existing). | |
| 40 | +| `fastvideo/models/loader/component_loader.py` | `sr_transformer` module type alias added here. | |
| 41 | +| `scripts/checkpoint_conversion/convert_magi_human_to_diffusers.py` | Reference → FastVideo state-dict converter. **Carries the `_FP32_KEEP_SUFFIXES` invariant — see Parity Invariants.** | |
| 42 | +| `scripts/checkpoint_conversion/push_magi_human_to_hf.py` | Push converted weights to umbrella HF repo. | |
| 43 | +| `tests/local_tests/magi_human/` | 14-test parity battery (skipped in CI; GPU-gated). | |
| 44 | +| `tests/local_tests/helpers/magi_human_upstream.py` | Upstream daVinci-MagiHuman reference loader. | |
| 45 | +| `examples/inference/basic/basic_magi_human*.py` | 8 user-facing example scripts (one per variant × mode). | |
| 46 | +| `fastvideo/tests/ssim/test_magi_human_similarity.py` | CI-eligible SSIM regression. | |
| 47 | + |
| 48 | +## Parity Invariants |
| 49 | + |
| 50 | +These are **load-bearing**. Each one was a multi-wave bug hunt in the original |
| 51 | +port. Breaking any of them re-introduces a known production-only regression. |
| 52 | +See the matching lesson under `.agents/lessons/`. |
| 53 | + |
| 54 | +### 1. Channel-major video token packing |
| 55 | + |
| 56 | +Video tokens must be packed **`(C pT pH pW)`** — channel-major. |
| 57 | + |
| 58 | +```python |
| 59 | +# CORRECT |
| 60 | +einops.rearrange(x, "b c (T pT) (H pH) (W pW) -> b (T H W) (C pT pH pW)", ...) |
| 61 | +# WRONG — produced pure noise in production E2E despite passing pipeline parity |
| 62 | +einops.rearrange(x, "b c (T pT) (H pH) (W pW) -> b (T H W) (pT pH pW C)", ...) |
| 63 | +``` |
| 64 | + |
| 65 | +Upstream `UnfoldNd` packs channel-major. Mismatching this passes the FastVideo |
| 66 | +self-parity test (both sides use FastVideo's packer) but breaks against the |
| 67 | +official reference, which only surfaces in production E2E. Lives in |
| 68 | +`stages/latent_preparation.py` — search for `_img2tokens`. |
| 69 | + |
| 70 | +→ `.agents/lessons/2026-05-07_silent-channel-major-packing-bugs.md` |
| 71 | + |
| 72 | +### 2. DiT dtype boundary discipline |
| 73 | + |
| 74 | +Across DiT block boundaries, the residual stream **stays fp32**. SDPA inputs |
| 75 | +are cast to bf16 inside the attention call. Post-attention output is upcast to |
| 76 | +fp32 before the per-head gating multiply. **There is no block-boundary cast.** |
| 77 | + |
| 78 | +These four rules are cumulative — relaxing any one re-introduces measurable |
| 79 | +parity drift (worst case `diff_max=0.5`, best case `diff_max≈1e-3` which is |
| 80 | +still not bit-exact). |
| 81 | + |
| 82 | +→ `.agents/lessons/2026-05-07_dit-dtype-boundary-with-flash-attn.md` |
| 83 | + |
| 84 | +### 3. Conversion `_FP32_KEEP_SUFFIXES` allowlist |
| 85 | + |
| 86 | +`scripts/checkpoint_conversion/convert_magi_human_to_diffusers.py` runs with |
| 87 | +`--cast-bf16` by default. The allowlist of fp32-keep suffixes prevents a |
| 88 | +specific set of 8 tensors from being downcast. The base checkpoint and the |
| 89 | +FastVideo `final_linear`/adapter modules require these in fp32. The distill |
| 90 | +DiT was the canary: its parity went from `diff_mean=0.114` (silently wrong) |
| 91 | +to bit-exact when the allowlist was fixed. |
| 92 | + |
| 93 | +If you add or rename DiT modules, check that any fp32-required tensors are |
| 94 | +covered by `_FP32_KEEP_SUFFIXES` and re-run `test_magi_human_distill_parity`. |
| 95 | + |
| 96 | +→ `.agents/lessons/2026-05-07_conversion-cast-bf16-suffix-allowlist.md` (lands in 7/8) |
| 97 | + |
| 98 | +### 4. Umbrella HF repo layout |
| 99 | + |
| 100 | +User code is a single string per variant: |
| 101 | + |
| 102 | +```python |
| 103 | +VideoGenerator.from_pretrained("FastVideo/MagiHuman-Diffusers/base") |
| 104 | +``` |
| 105 | + |
| 106 | +`fastvideo/utils.py:maybe_download_model` recognises the 3-segment |
| 107 | +`org/repo/subfolder` form and downloads only that subtree (not the full |
| 108 | +~75 GB repo). The umbrella repo: |
| 109 | + |
| 110 | +``` |
| 111 | +FastVideo/MagiHuman-Diffusers/ |
| 112 | +├── base/{model_index.json, transformer/, scheduler/} |
| 113 | +├── distill/{model_index.json, transformer/, scheduler/} |
| 114 | +├── sr_540p/{model_index.json, transformer/, sr_transformer/, scheduler/} |
| 115 | +└── sr_1080p/{model_index.json, transformer/, sr_transformer/, scheduler/} |
| 116 | +``` |
| 117 | + |
| 118 | +Note: `vae/`, `text_encoder/`, `audio_vae/` subfolders are **deliberately |
| 119 | +absent**. Those four shared components are lazy-loaded by |
| 120 | +`MagiHumanPipeline.load_modules` from their canonical upstream repos |
| 121 | +(`Wan-AI/Wan2.2-TI2V-5B`, `google/t5gemma-9b-9b-ul2`, |
| 122 | +`stabilityai/stable-audio-open-1.0`). This relies on |
| 123 | +`fastvideo/utils.py:verify_model_config_and_directory` honoring |
| 124 | +`model_index.json` declarations. |
| 125 | + |
| 126 | +## Cross-Refs (If you change X, re-run Y) |
| 127 | + |
| 128 | +| If you touch... | Re-run at minimum | |
| 129 | +|---|---| |
| 130 | +| `stages/latent_preparation.py` (any token packing) | `test_magi_human_pipeline_parity` (base T2V) **and** one of the SR-540p/1080p tests **and** `examples/inference/basic/basic_magi_human.py` (E2E mp4 hash) | |
| 131 | +| `stages/denoising.py` or `stages/sr_denoising.py` | All four pipeline-parity tests (`{base, ti2v, sr540p, sr1080p}_pipeline_parity`) | |
| 132 | +| `fastvideo/models/dits/magi_human.py` (any layer) | `test_magi_human_parity` and `test_magi_human_distill_parity` (DiT-level) **before** the pipeline tests | |
| 133 | +| Anything dtype-related in the DiT | All DiT parity tests + verify the residual-stream dtype invariant manually with a layer-by-layer hook trace (see `fastvideo/hooks/activation_trace.py`) | |
| 134 | +| `scripts/checkpoint_conversion/convert_magi_human_to_diffusers.py` | Convert the distill checkpoint and run `test_magi_human_distill_parity` (it's the canary for `_FP32_KEEP_SUFFIXES`) | |
| 135 | +| `fastvideo/utils.py:maybe_download_model` (3-segment detector) | Smoke-load every existing 2-segment HF id in `registry.py` plus one MagiHuman variant | |
| 136 | +| `magi_human_pipeline.py:load_modules` | `test_magi_human_pipeline_smoke` + one full E2E `examples/inference/basic/basic_magi_human.py` | |
| 137 | +| `presets.py` or `pipeline_configs.py` | Both DiT parity tests (preset wiring leaks into module construction) | |
| 138 | + |
| 139 | +## Run Book |
| 140 | + |
| 141 | +```bash |
| 142 | +# Setup once |
| 143 | +export HF_TOKEN=hf_... # any of HF_TOKEN / HUGGINGFACE_HUB_TOKEN / HF_API_KEY works |
| 144 | +# Accept terms at: |
| 145 | +# - https://huggingface.co/GAIR/daVinci-MagiHuman |
| 146 | +# - https://huggingface.co/google/t5gemma-9b-9b-ul2 |
| 147 | +# - https://huggingface.co/stabilityai/stable-audio-open-1.0 |
| 148 | +# - https://huggingface.co/Wan-AI/Wan2.2-TI2V-5B |
| 149 | + |
| 150 | +# Full parity battery (~233s on a single H100, GPU-gated) |
| 151 | +pytest tests/local_tests/magi_human/ -v -s |
| 152 | + |
| 153 | +# E2E mp4 generation (any variant) |
| 154 | +python examples/inference/basic/basic_magi_human.py |
| 155 | + |
| 156 | +# CI-eligible SSIM regression |
| 157 | +pytest fastvideo/tests/ssim/test_magi_human_similarity.py -v -s |
| 158 | +``` |
| 159 | + |
| 160 | +The base T2V mp4 hash should be `dcf5f2bf6534c7c0d91e7353e42b23db` — stable |
| 161 | +across all 43 commits of the original port. |
| 162 | + |
| 163 | +## Open Questions |
| 164 | + |
| 165 | +- **OQ-7** — Wan VAE decode shows a max diff of ~8e-4 against the diffusers |
| 166 | + reference (not bit-exact). Root cause is a known fp32 op-order drift in the |
| 167 | + Wan 2.2 VAE itself, not MagiHuman wiring. Tracked in `JOURNAL.md`. Documented |
| 168 | + here so the test author knows the tolerance is intentional, not a regression. |
| 169 | + |
| 170 | +## Provenance |
| 171 | + |
| 172 | +This pipeline was decomposed from a single 9,812-line PR |
| 173 | +([#1280](https://github.com/hao-ai-lab/FastVideo/pull/1280), |
| 174 | +`will/magi` @ `4e1603634d27c8e1b5c4cc5d9387f046547f5c49`) into a stack of |
| 175 | +focused PRs: |
| 176 | + |
| 177 | +| Step | PR | Branch | Scope | |
| 178 | +|---|---|---|---| |
| 179 | +| Prereq A | [#1293](https://github.com/hao-ai-lab/FastVideo/pull/1293) | `will/activation-trace` | Generic activation-tracing infra | |
| 180 | +| Prereq B | [#1294](https://github.com/hao-ai-lab/FastVideo/pull/1294) | `will/loader-infra` | Loader umbrella-repo + optional component dirs | |
| 181 | +| 1/8 | [#1295](https://github.com/hao-ai-lab/FastVideo/pull/1295) | `will/magi-01-housekeeping` | gitignore, codespell, skills index | |
| 182 | +| 2/8 | [#1296](https://github.com/hao-ai-lab/FastVideo/pull/1296) | `will/magi-02-t5gemma` | T5-Gemma encoder + parity test | |
| 183 | +| 3/8 | [#1297](https://github.com/hao-ai-lab/FastVideo/pull/1297) | `will/magi-03-dit` | DiT + parity tests | |
| 184 | +| 4/8 | [#1298](https://github.com/hao-ai-lab/FastVideo/pull/1298) | `will/magi-04a-stages` | Pipeline stages + sr_transformer alias | |
| 185 | +| 5/8 | [#1299](https://github.com/hao-ai-lab/FastVideo/pull/1299) | `will/magi-04b-orchestrator` | Pipeline orchestrator + parity battery | |
| 186 | +| 6/8 | this PR | `will/magi-04c-provenance` | This AGENTS.md, JOURNAL.md, lessons, parent AGENTS.md hook | |
| 187 | +| 7/8 | (next) | `will/magi-05-conversion` | Checkpoint conversion + 3rd lesson | |
| 188 | +| 8/8 | (last) | `will/magi-06-activate` | Registry + examples + SSIM + codebase-map | |
| 189 | + |
| 190 | +Provenance section will be finalized in 8/8 with the actual PR numbers for 7/8 and 8/8. |
0 commit comments