VideoGenerator.from_pretrained(model_path, config) takes the same nested
settings as from_config; the 24 flat keywords, from_pretrained_kwargs_to_config,
FROM_PRETRAINED_KWARGS, and every flat_field in the schema are deleted.
nvfp4_fa4 is the typed field engine.attention.nvfp4_fa4, applied by a
resolution step. 65 call sites and the docs use the nested form; the three
kwargs golden cases are config cases with byte-identical results.
Every value is decided during resolution; no runtime code calls
with_override. The device offload policy (unified memory, MPS, layerwise
conflicts, lazy module load) runs as resolution steps in the main process
(fastvideo/api/device_policy.py), and the worker runs on the config it
receives. The LTX-2 refine defaults from model_index.json and the MiniMax-H3
schedule from fastvideo_inference.json are resolution steps
(fastvideo/api/checkpoint_defaults.py) that read local checkpoints or download
only those files; the H3 pipeline validates the schedule against the loaded
schedulers but no longer writes it. The preprocessing entry point passes the
downloaded local path as run state instead of a config override. Teacher and
critic models load with override_transformer_cls_name as a load argument.
The test isolation turns the device policy off, as it blocks downloads, so
that the goldens do not depend on the machine.
Against the 3f6893a0 goldens, every field still matches except boundary_ratio
and ltx2_vae_tiling (DESIGN section 9) and the fields that no longer exist.
The launcher and trainer YAML verifications report 0 unexplained differences.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
83 lines
2.6 KiB
Python
83 lines
2.6 KiB
Python
"""Generate one LTX2 video and score its audio track.
|
|
|
|
LTX2 produces video + audio and muxes both into the output mp4. The
|
|
eval suite reads the audio track straight out of that mp4 (librosa
|
|
decodes via ffmpeg under the hood), so we just point the audio
|
|
metrics at the same path.
|
|
|
|
Only the reference-free audio metrics run here — they're the ones
|
|
that make sense for a single one-shot generation:
|
|
|
|
* ``audio.clap_score`` — CLAP text↔audio cosine similarity
|
|
* ``audio.audiobox_aesthetics`` — AudioBox 4-axis quality score
|
|
|
|
The other audio metrics need extra inputs we don't have for a
|
|
one-shot run: ``audio.frechet_distance`` and ``audio.kl_divergence``
|
|
need a reference audio, ``audio.wer`` needs a ground-truth transcript.
|
|
|
|
Install: ``uv pip install -e .[eval-audio]`` covers both metrics here
|
|
(and the rest of the audio suite).
|
|
"""
|
|
import torch
|
|
|
|
from fastvideo import VideoGenerator
|
|
from fastvideo.eval import create_evaluator
|
|
|
|
PROMPT = ("A warm sunny backyard. The camera starts in a tight cinematic close-up "
|
|
"of a woman and a man in their 30s, facing each other with serious "
|
|
"expressions. The woman, emotional and dramatic, says softly, \"That's "
|
|
"it... Dad's lost it. And we've lost Dad.\" The man exhales, slightly "
|
|
"annoyed: \"Stop being so dramatic, Jess.\" A beat. He glances aside, "
|
|
"then mutters defensively, \"He's just having fun.\"")
|
|
|
|
METRICS = [
|
|
"audio.clap_score",
|
|
"audio.audiobox_aesthetics",
|
|
]
|
|
|
|
|
|
def main() -> None:
|
|
generator = VideoGenerator.from_pretrained(
|
|
"Davids048/LTX2-Base-Diffusers",
|
|
{
|
|
"engine": {
|
|
"num_gpus": 1,
|
|
},
|
|
},
|
|
)
|
|
|
|
output_path = "outputs_video/ltx2_audio_eval/output.mp4"
|
|
generator.generate({
|
|
"prompt": PROMPT,
|
|
"sampling": {
|
|
"num_frames": 121,
|
|
"height": 1088,
|
|
"width": 1920,
|
|
},
|
|
"output": {
|
|
"output_path": output_path,
|
|
"save_video": True,
|
|
},
|
|
})
|
|
generator.shutdown()
|
|
torch.cuda.empty_cache()
|
|
|
|
print(f"\n[eval] building evaluator: {METRICS}")
|
|
evaluator = create_evaluator(metrics=METRICS)
|
|
results = evaluator.evaluate(audio=output_path, text_prompt=PROMPT)
|
|
|
|
print("\n=== Audio scores ===")
|
|
for name in METRICS:
|
|
r = results[name]
|
|
if r.score is None:
|
|
print(f" {name}: SKIPPED ({r.details.get('skipped', 'no score')})")
|
|
else:
|
|
print(f" {name}: {r.score:.4f}")
|
|
if r.details:
|
|
for k, v in r.details.items():
|
|
print(f" {k}: {v}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|