Give the 720p HunyuanVideo repack its own family and free offload hooks on cancel

The trusted-repo allowlist admitted the 720p t2v repack while the only
Hunyuan family entry carried 480p presets, so a 720p load silently
defaulted to 832x480. It now resolves a dedicated entry whose repo-id
alias outranks the generic token (same guider config, verified: both
repos ship guidance 6.0).

A scheduler-wrapped cancel unwinds pipe call by exception and skips the
pipeline's end-of-call maybe_free_model_hooks, leaving onloaded offload
modules on the GPU until the next request; generate() now frees them
before surfacing the cancelled sentinel.
This commit is contained in:
Daniel Han 2026-07-05 05:30:14 +00:00
commit b96820a0d2
4 changed files with 59 additions and 0 deletions

View file

@ -1238,6 +1238,17 @@ class VideoBackend:
with torch.inference_mode(), progress_ctx:
output = pipe(**kwargs)
except _VideoGenerationCancelled:
# This cancel unwinds pipe.__call__ by exception (the scheduler
# wrapper has no cooperative _interrupt), skipping the pipeline's
# end-of-call maybe_free_model_hooks(); under model/group offload
# the currently-onloaded modules would otherwise stay on the GPU
# until the next request touches them.
free_hooks = getattr(pipe, "maybe_free_model_hooks", None)
if callable(free_hooks):
try:
free_hooks()
except Exception: # noqa: BLE001 -- cleanup is best-effort
pass
raise RuntimeError(VIDEO_CANCELLED_MSG) from None
if cancel.is_set():
raise RuntimeError(VIDEO_CANCELLED_MSG)

View file

@ -237,6 +237,30 @@ _FAMILIES: tuple[VideoFamily, ...] = (
# VAE (4.7 -> 2.4); the Qwen2.5-VL TE is stored bf16 (14.0) plus ByT5 0.8.
bf16_components_gb = (16.6, 14.8, 2.4),
),
# The 720p t2v repack: same architecture, pipeline quirks, guider config
# (guidance 6.0) and shard footprint as the 480p entry above; only the
# trained resolution class differs. Kept as its OWN family so a 720p load
# defaults to 720p-class sizes instead of silently rendering at 832x480.
# The repo-id alias is the full path segment, so it out-lengths (and thus
# outranks) the generic "hunyuanvideo-1.5" token for this repo only.
VideoFamily(
name = "hunyuanvideo-1.5-720p",
pipeline_class = "HunyuanVideo15Pipeline",
transformer_class = "HunyuanVideo15Transformer3DModel",
base_repo = "hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-720p_t2v",
aliases = ("hunyuanvideo-1.5-diffusers-720p_t2v", "hv15-720p"),
has_audio = False,
guidance_via_guider = True,
default_steps = 50,
default_guidance = 6.0,
default_num_frames = 121,
default_fps = 24,
frame_step = 4,
resolution_multiple = 16,
# 720p-class presets: landscape, vertical, square (all /16).
resolution_presets = ((1280, 720), (720, 1280), (960, 960)),
bf16_components_gb = (16.6, 14.8, 2.4),
),
)

View file

@ -303,6 +303,10 @@ class _FakeHV15Pipe:
self.components = {"transformer": self.transformer, "vae": self.vae}
self.moved_to = None
self.last_kwargs = None
self.hooks_freed = 0
def maybe_free_model_hooks(self):
self.hooks_freed += 1
def to(self, device):
self.moved_to = device
@ -718,6 +722,10 @@ def test_hv15_cancel_unwinds_scheduler_loop(fake_runtime):
assert pipe.scheduler.calls == 1
# The wrapper must restore scheduler.step even on the exception path.
assert pipe.scheduler.step.__func__ is _FakeHV15Scheduler.step
# The exception unwound pipe.__call__ before its own end-of-call cleanup, so
# generate() must have freed the offload hooks itself (VRAM would otherwise
# stay onloaded until the next request).
assert pipe.hooks_freed == 1
def test_singleton():
@ -1129,3 +1137,18 @@ def test_base_download_files_ltx23_keeps_only_shared_components():
assert not any(
n.startswith(("vae/", "connectors/", "latent_upsampler/", "transformer/")) for n in names
)
def test_hv15_720p_repo_gets_720p_family_defaults():
# The 720p repack is trusted, but it must resolve its OWN family entry: the
# generic hunyuanvideo-1.5 entry would default generation to 832x480.
from core.inference.video_families import detect_video_family
fam = detect_video_family("hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-720p_t2v")
assert fam is not None and fam.name == "hunyuanvideo-1.5-720p"
assert fam.resolution_presets[0] == (1280, 720)
assert fam.base_repo.endswith("720p_t2v")
# The 480p repo keeps the original entry.
fam480 = detect_video_family("hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-480p_t2v")
assert fam480 is not None and fam480.name == "hunyuanvideo-1.5"
assert fam480.resolution_presets[0] == (832, 480)

View file

@ -151,6 +151,7 @@ def test_supported_names():
"wan2.2-ti2v-5b",
"wan2.2-t2v-a14b",
"hunyuanvideo-1.5",
"hunyuanvideo-1.5-720p",
)