From a7da439f8797a19d54e97dd0ca827b2feda908db Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Fri, 30 Oct 2026 11:39:42 -0700 Subject: [PATCH] [bugfix]: document the NVFP4 decoder through the commands that actually parse it `fastvideo generate` takes a run config plus dotted overrides and never reads FastVideoArgs.add_cli_args, so the documented `--h3-vae-nvfp4-decoder` flag failed before the model loaded. The flag is removed; the guide uses `--generator.pipeline.experimental.h3_vae_nvfp4_decoder`, and a test drives that override through the generate parser to FastVideoArgs. Also drops two narrating test docstrings. --- docs/inference/open_h3_regenerate_2k.md | 13 +++++----- fastvideo/fastvideo_args.py | 7 ------ .../pipelines/test_open_h3_regenerate_2k.py | 25 +++++++++++++------ .../vaes/test_minimax_h3_nvfp4_decoder.py | 1 - 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/docs/inference/open_h3_regenerate_2k.md b/docs/inference/open_h3_regenerate_2k.md index b7beffbb9..d12550272 100644 --- a/docs/inference/open_h3_regenerate_2k.md +++ b/docs/inference/open_h3_regenerate_2k.md @@ -107,16 +107,17 @@ hf download FastVideo/FastH3-NVFP4-VAE-Decoder --include 'quality-36L/decoder-nv 'fast-8L/decoder-nvfp4.safetensors' --local-dir fasth3-nvfp4-vae ``` -Select it through the surface you drive the pipeline with; the field, the CLI flag and the environment variable all -set `fastvideo_args.h3_vae_nvfp4_decoder`, and the serve-config field wins over the environment. +Select it through the surface you drive the pipeline with. The config field (also a dotted override of +`fastvideo generate`), the example's `--vae-decoder` and the environment variable all set +`fastvideo_args.h3_vae_nvfp4_decoder`, and the config field wins over the environment. ```bash # example script python examples/inference/basic/basic_open_h3_regenerate_2k.py --video draft.mp4 --prompt "..." \ --vae-decoder fasth3-nvfp4-vae/quality-36L/decoder-nvfp4.safetensors -# `fastvideo generate` and the serve config use the same field -fastvideo generate --model-path Efficient-Large-Model/Open-H3-Regenerate-2K --video draft.mp4 --prompt "..." \ - --h3-vae-nvfp4-decoder fasth3-nvfp4-vae/quality-36L/decoder-nvfp4.safetensors +# `fastvideo generate`: a run config, with the decoder as a dotted override +fastvideo generate --config regen2k_run.yaml \ + --generator.pipeline.experimental.h3_vae_nvfp4_decoder fasth3-nvfp4-vae/quality-36L/decoder-nvfp4.safetensors ``` ```yaml @@ -126,7 +127,7 @@ generator: h3_vae_nvfp4_decoder: ./fasth3-nvfp4-vae/quality-36L/decoder-nvfp4.safetensors ``` -`FASTVIDEO_H3_VAE_NVFP4_DECODER` stays as the fallback when neither the serve config nor the CLI sets the field. The +`FASTVIDEO_H3_VAE_NVFP4_DECODER` stays as the fallback when the config does not set the field. The swap happens in the VAE loader, so the dense decoder never reaches the GPU: it is replaced on the CPU by the exported checkpoint (2.0 GiB of packed weights instead of 9.7 GiB of FP32), moved to the device, and decoded under bf16 autocast. The decoder keeps the pipeline's tiling (at 2560x1440: 8x13 tiles of 256 px with 64 px overlap) and the fused Triton diff --git a/fastvideo/fastvideo_args.py b/fastvideo/fastvideo_args.py index 79e377d45..818834e18 100644 --- a/fastvideo/fastvideo_args.py +++ b/fastvideo/fastvideo_args.py @@ -881,13 +881,6 @@ class FastVideoArgs: default=FastVideoArgs.taeh3_chunk_size, help="TAEH3 latent frames per execution chunk.", ) - parser.add_argument( - "--h3-vae-nvfp4-decoder", - type=str, - default=FastVideoArgs.h3_vae_nvfp4_decoder, - help="Exported NVFP4 MiniMax-H3 decoder (decoder-nvfp4.safetensors) that replaces the VAE decoder at " - "load. Blackwell only. Overrides FASTVIDEO_H3_VAE_NVFP4_DECODER.", - ) parser.add_argument( "--vae-parallel-decode", action=StoreBoolean, diff --git a/fastvideo/tests/pipelines/test_open_h3_regenerate_2k.py b/fastvideo/tests/pipelines/test_open_h3_regenerate_2k.py index 0b60d57d2..357c64968 100644 --- a/fastvideo/tests/pipelines/test_open_h3_regenerate_2k.py +++ b/fastvideo/tests/pipelines/test_open_h3_regenerate_2k.py @@ -298,7 +298,6 @@ def test_registry_resolves_the_release_to_the_regenerate_pipeline(tmp_path): def test_serve_config_forwards_the_nvfp4_decoder_option(monkeypatch): - """`generator.pipeline.experimental.h3_vae_nvfp4_decoder` reaches FastVideoArgs through the serve path.""" from fastvideo import fastvideo_args as fva from fastvideo.api import ComponentConfig, EngineConfig, GeneratorConfig, PipelineSelection from fastvideo.api.compat import generator_config_to_fastvideo_args @@ -327,11 +326,23 @@ def test_serve_config_forwards_the_nvfp4_decoder_option(monkeypatch): assert captured["draft_size"] == "768p" -def test_cli_flag_sets_the_nvfp4_decoder_option(): - from fastvideo.fastvideo_args import FastVideoArgs +def test_generate_dotted_override_sets_the_nvfp4_decoder_option(tmp_path): + from fastvideo.api.compat import generator_config_to_fastvideo_args + from fastvideo.entrypoints.cli.generate import GenerateSubcommand, build_generate_run_config from fastvideo.utils import FlexibleArgumentParser - args = FastVideoArgs.add_cli_args(FlexibleArgumentParser()).parse_args( - ["--h3-vae-nvfp4-decoder", "/tmp/decoder-nvfp4.safetensors"]) - assert args.h3_vae_nvfp4_decoder == "/tmp/decoder-nvfp4.safetensors" - assert FastVideoArgs.add_cli_args(FlexibleArgumentParser()).parse_args([]).h3_vae_nvfp4_decoder is None + config_path = tmp_path / "run.yaml" + config_path.write_text("generator:\n model_path: Efficient-Large-Model/Open-H3-Regenerate-2K\n" + "request:\n prompt: hello\n", encoding="utf-8") + parser = FlexibleArgumentParser() + GenerateSubcommand().subparser_init(parser.add_subparsers(dest="subparser")) + args, unknown = parser.parse_known_args([ + "generate", "--config", str(config_path), "--generator.pipeline.experimental.h3_vae_nvfp4_decoder", + "/tmp/decoder-nvfp4.safetensors" + ]) + + config = build_generate_run_config(args, unknown) + + assert config.generator.pipeline.experimental["h3_vae_nvfp4_decoder"] == "/tmp/decoder-nvfp4.safetensors" + assert generator_config_to_fastvideo_args(config.generator).h3_vae_nvfp4_decoder == "/tmp/decoder-nvfp4.safetensors" + diff --git a/fastvideo/tests/vaes/test_minimax_h3_nvfp4_decoder.py b/fastvideo/tests/vaes/test_minimax_h3_nvfp4_decoder.py index f701b7fe9..99b41ec89 100644 --- a/fastvideo/tests/vaes/test_minimax_h3_nvfp4_decoder.py +++ b/fastvideo/tests/vaes/test_minimax_h3_nvfp4_decoder.py @@ -502,7 +502,6 @@ def _load_h3_vae(vae_dir, cpu: bool = True, decoder: str | None = None, pipeline def test_open_h3_regenerate_2k_vae_load_applies_the_exported_decoder(tmp_path): - """Regenerate-2K loads its ``base_model/vae`` through the same swap, so the option applies there too.""" from fastvideo.configs.pipelines.open_h3_regenerate_2k import OpenH3Regenerate2KPipelineConfig _write_vae_dir(tmp_path / "vae", decoder_layers=5)