[refactor]: remove VideoGenerator.generate_video
VideoGenerator.generate(request) is the one inference entry point. The generate_video keyword call, its mapping from flat keywords to a request (legacy_generate_call_to_request and its helpers), and the tests of that mapping are removed. Comments and docs that named generate_video describe generate and request extensions instead. scripts/ltx2_sr_alignment.py keeps its generate_video call, because its reference run imports the separate FastVideo-internal package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d6ba5ced94
commit
d728a0dc5f
@@ -42,7 +42,7 @@ Near-term OSS note:
|
||||
- `apps/dreamverse/dreamverse/main.py`: websocket endpoint, request handling,
|
||||
session state machine, rewrite orchestration, REST routes, and stream relay
|
||||
- `apps/dreamverse/dreamverse/gpu_pool.py`: GPU worker processes, warmup, model
|
||||
loading, and `generate_video()` calls through FastVideo
|
||||
loading, and `generate()` calls through FastVideo
|
||||
- `apps/dreamverse/dreamverse/prompt_enhancer.py`: prompt enhancement, rollout
|
||||
rewrite execution, provider selection, and timeout/fallback behavior
|
||||
- `apps/dreamverse/dreamverse/rewrite_prompt_payload.py`: canonical rewrite request payload
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_dsg" value="generator.generate_video()" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#9673a6;endArrow=classic;fontSize=10;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="do_step" target="generator" edge="1">
|
||||
<mxCell id="e_dsg" value="generator.generate()" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#9673a6;endArrow=classic;fontSize=10;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="do_step" target="generator" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_dscache" value="read / write" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#d6b656;endArrow=classic;startArrow=classic;fontSize=10;exitX=0;exitY=0.8;exitDx=0;exitDy=0;entryX=1;entryY=0.2;entryDx=0;entryDy=0;" parent="1" source="do_step" target="caches" edge="1">
|
||||
@@ -534,7 +534,7 @@
|
||||
<mxPoint x="1040" y="1610" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dm11a" value="10a. worker runs:
VideoGenerationWorker.generate_step()
 (ltx2_generation.py:380)
 → generator.generate_video()
 → updates ContinuationState
then stream_fmp4() (av_streaming.py:121)
 → ffmpeg (rawvideo+wav → fmp4)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe0b2;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
|
||||
<mxCell id="dm11a" value="10a. worker runs:
VideoGenerationWorker.generate_step()
 (ltx2_generation.py:380)
 → generator.generate()
 → updates ContinuationState
then stream_fmp4() (av_streaming.py:121)
 → ffmpeg (rawvideo+wav → fmp4)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe0b2;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
|
||||
<mxGeometry x="955" y="1640" width="180" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="dm11" value="10b. resp_q.put(MediaInit / MediaChunk / MediaComplete / StepComplete)" style="endArrow=classic;html=1;strokeColor=#b85450;fontSize=10;labelBackgroundColor=#ffffff;" parent="1" edge="1">
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
@@ -67,7 +67,7 @@ test.describe('preset prompt generation', () => {
|
||||
// on a B200 plus encode/transfer time. The "Continuation flipped
|
||||
// to Generating + Leave button rendered" pair above is the proof
|
||||
// the integration works: FE → /readyz → /curated-presets → WS
|
||||
// /ws → BE → GPU pool → VideoGenerator.generate_video, all green.
|
||||
// /ws → BE → GPU pool → VideoGenerator.generate, all green.
|
||||
const video = page.locator('video').first();
|
||||
await expect(video).toHaveCount(1);
|
||||
});
|
||||
|
||||
@@ -112,7 +112,7 @@ per-metric policy with direction, percent threshold, absolute threshold, and a
|
||||
|
||||
`test_inference_performance.py` temporarily sets `FASTVIDEO_STAGE_LOGGING=1`
|
||||
while it runs so pipeline stage execution times are available in
|
||||
`generate_video(...).logging_info`. Stage logs use pipeline-unique keys such as
|
||||
`generate(...).logging_info`. Stage logs use pipeline-unique keys such as
|
||||
`prompt_encoding_stage` so duplicate stage classes do not collide. For
|
||||
`PipelineStage` entries, shared component stage bases emit a stable
|
||||
`component_metric`: text encoding stages map to `text_encoder_time_s`,
|
||||
|
||||
@@ -192,9 +192,7 @@ def main() -> None:
|
||||
last = _generate()
|
||||
torch.cuda.synchronize()
|
||||
wall = time.perf_counter() - t0
|
||||
# generate_video returns a plain dict; attribute access would always
|
||||
# fall back to wall time.
|
||||
gen_t = last.get("generation_time") if isinstance(last, dict) else None
|
||||
gen_t = last.generation_time
|
||||
denoise_times.append(gen_t if gen_t is not None else wall)
|
||||
print(f"[qad] {tag} run {i + 1}/{runs}: {wall:.2f}s wall "
|
||||
f"(gen {denoise_times[-1]:.2f}s)")
|
||||
|
||||
@@ -47,8 +47,8 @@ def main() -> None:
|
||||
|
||||
def _run(tag: str) -> float:
|
||||
save = tag == "measured"
|
||||
# Modern typed-request API (generate_video is deprecated). Same
|
||||
# prompt/seed/shapes both runs so the compiled graph is reused.
|
||||
# Typed request API. Same prompt/seed/shapes both runs so the
|
||||
# compiled graph is reused.
|
||||
request: dict = {
|
||||
"prompt": PROMPT,
|
||||
"sampling": {
|
||||
|
||||
@@ -23,10 +23,6 @@ from fastvideo.api.schema import (
|
||||
ContinuationState,
|
||||
GenerationRequest,
|
||||
GeneratorConfig,
|
||||
InputConfig,
|
||||
OutputConfig,
|
||||
RequestRuntimeConfig,
|
||||
SamplingConfig,
|
||||
)
|
||||
from fastvideo.api.sampling_param import SamplingParam
|
||||
from fastvideo.fastvideo_args import FastVideoArgs
|
||||
@@ -34,16 +30,8 @@ from fastvideo.pipelines.basic.ltx2.stage_overrides import (
|
||||
refine_preset_override_fields,
|
||||
refine_stage_override_fields,
|
||||
)
|
||||
from fastvideo.utils import shallow_asdict
|
||||
|
||||
_INPUT_FIELD_NAMES = {field.name for field in fields(InputConfig)}
|
||||
_SAMPLING_FIELD_NAMES = {field.name for field in fields(SamplingConfig)}
|
||||
_RUNTIME_FIELD_NAMES = {field.name for field in fields(RequestRuntimeConfig)}
|
||||
_OUTPUT_FIELD_NAMES = {field.name for field in fields(OutputConfig)}
|
||||
_MISSING = object()
|
||||
_LEGACY_REQUEST_ALIASES = {
|
||||
"neg_prompt": "negative_prompt",
|
||||
}
|
||||
REQUEST_BATCH_EXTRA_PASSTHROUGH_FIELDS = (
|
||||
"ltx2_audio_latents",
|
||||
"ltx2_audio_clean_latent",
|
||||
@@ -198,34 +186,6 @@ def normalize_generation_request(request: GenerationRequest | Mapping[str, Any],
|
||||
return normalized
|
||||
|
||||
|
||||
def legacy_generate_call_to_request(
|
||||
prompt: str | None,
|
||||
sampling_param: SamplingParam | None,
|
||||
*,
|
||||
mouse_cond: Any | None = None,
|
||||
keyboard_cond: Any | None = None,
|
||||
grid_sizes: Any | None = None,
|
||||
legacy_kwargs: Mapping[str, Any] | None = None,
|
||||
) -> GenerationRequest:
|
||||
raw = _sampling_param_to_request_raw(sampling_param)
|
||||
if prompt is not None:
|
||||
raw["prompt"] = prompt
|
||||
|
||||
for key, value in (legacy_kwargs or {}).items():
|
||||
_apply_request_field(raw, key, value)
|
||||
|
||||
if mouse_cond is not None:
|
||||
raw.setdefault("inputs", {})["mouse_cond"] = mouse_cond
|
||||
if keyboard_cond is not None:
|
||||
raw.setdefault("inputs", {})["keyboard_cond"] = keyboard_cond
|
||||
if grid_sizes is not None:
|
||||
raw.setdefault("inputs", {})["grid_sizes"] = grid_sizes
|
||||
|
||||
normalized = parse_config(GenerationRequest, raw)
|
||||
bind_generation_request_raw(normalized, raw)
|
||||
return normalized
|
||||
|
||||
|
||||
def request_to_sampling_param(
|
||||
request: GenerationRequest,
|
||||
*,
|
||||
@@ -354,42 +314,6 @@ def _compile_config_to_torch_kwargs(compile_config: CompileConfig, ) -> dict[str
|
||||
return out
|
||||
|
||||
|
||||
def _sampling_param_to_request_raw(sampling_param: SamplingParam | None, ) -> dict[str, Any]:
|
||||
if sampling_param is None:
|
||||
return {}
|
||||
|
||||
raw: dict[str, Any] = {}
|
||||
for key, value in shallow_asdict(sampling_param).items():
|
||||
if key == "prompt":
|
||||
continue
|
||||
_apply_request_field(raw, key, deepcopy(value))
|
||||
return raw
|
||||
|
||||
|
||||
def _apply_request_field(
|
||||
raw: dict[str, Any],
|
||||
key: str,
|
||||
value: Any,
|
||||
) -> None:
|
||||
key = _LEGACY_REQUEST_ALIASES.get(key, key)
|
||||
if key == "negative_prompt":
|
||||
raw["negative_prompt"] = value
|
||||
return
|
||||
if key in _INPUT_FIELD_NAMES:
|
||||
raw.setdefault("inputs", {})[key] = value
|
||||
return
|
||||
if key in _SAMPLING_FIELD_NAMES:
|
||||
raw.setdefault("sampling", {})[key] = value
|
||||
return
|
||||
if key in _RUNTIME_FIELD_NAMES:
|
||||
raw.setdefault("runtime", {})[key] = value
|
||||
return
|
||||
if key in _OUTPUT_FIELD_NAMES:
|
||||
raw.setdefault("output", {})[key] = value
|
||||
return
|
||||
raw.setdefault("extensions", {})[key] = value
|
||||
|
||||
|
||||
def request_to_batch_extra(request: GenerationRequest) -> dict[str, Any]:
|
||||
"""Extract typed-request extensions consumed through ``ForwardBatch.extra``."""
|
||||
return {
|
||||
@@ -587,7 +511,6 @@ __all__ = [
|
||||
"explicit_request_updates",
|
||||
"generator_config_to_fastvideo_args",
|
||||
"legacy_from_pretrained_to_config",
|
||||
"legacy_generate_call_to_request",
|
||||
"load_generator_config_from_file",
|
||||
"normalize_generation_request",
|
||||
"normalize_generator_config",
|
||||
|
||||
@@ -140,7 +140,7 @@ class SamplingParam:
|
||||
ltx2_stg_blocks_audio: list[int] = field(default_factory=lambda: [29])
|
||||
|
||||
# LTX-2 image / video / continuation conditioning. These flow from
|
||||
# generate_video(...) kwargs through ``sampling_param.update(kwargs)``
|
||||
# request ``extensions`` through ``sampling_param.update(...)``
|
||||
# onto the ForwardBatch fields of the same name. ``ltx2_image_crf``
|
||||
# gates the conditioning-image H.264 re-encode; the streaming
|
||||
# session controller passes ``ltx2_image_crf=0.0`` because it
|
||||
|
||||
@@ -19,7 +19,7 @@ backend differs from the Wan-tuned ``video_sparse_attn``:
|
||||
- Non-video *queries* are always dense. Non-video *keys* are either
|
||||
always-selected for every query ("exempt", default) or compete in
|
||||
top-k under a FLOP-matched budget ("compete") — the ablation axis,
|
||||
switched per request via ``generate_video(..., vsa_mode=...)``
|
||||
switched per request via the request extension ``vsa_mode``
|
||||
(default: exempt). Per-request scheduling knobs
|
||||
(``vsa_dense_first_n_steps``, ``vsa_dense_layers``) let mixed schedules
|
||||
run the diffuse steps/layers dense while pushing the rest harder.
|
||||
|
||||
@@ -32,7 +32,6 @@ from fastvideo.api.compat import (
|
||||
expand_request_prompt_batch,
|
||||
generator_config_to_fastvideo_args,
|
||||
legacy_from_pretrained_to_config,
|
||||
legacy_generate_call_to_request,
|
||||
load_generator_config_from_file,
|
||||
normalize_generation_request,
|
||||
normalize_generator_config,
|
||||
@@ -401,80 +400,6 @@ class VideoGenerator:
|
||||
output=OutputConfig(save_video=False, return_frames=False),
|
||||
)
|
||||
|
||||
def generate_video(
|
||||
self,
|
||||
prompt: str | None = None,
|
||||
sampling_param: SamplingParam | None = None,
|
||||
# Action control inputs (Matrix-Game)
|
||||
mouse_cond: torch.Tensor | None = None,
|
||||
keyboard_cond: torch.Tensor | None = None,
|
||||
grid_sizes: tuple[int, int, int] | list[int] | torch.Tensor
|
||||
| None = None,
|
||||
**kwargs,
|
||||
) -> dict[str, Any] | list[dict[str, Any]]:
|
||||
"""
|
||||
Generate a video based on the given prompt.
|
||||
|
||||
Args:
|
||||
prompt: The prompt to use for generation (optional if prompt_txt is provided)
|
||||
negative_prompt: The negative prompt to use (overrides the one in fastvideo_args)
|
||||
output_path: Path to save the video (overrides the one in fastvideo_args)
|
||||
prompt_path: Path to prompt file
|
||||
save_video: Whether to save the video to disk
|
||||
return_frames: Whether to include raw frames in the result dict
|
||||
num_inference_steps: Number of denoising steps (overrides fastvideo_args)
|
||||
guidance_scale: Classifier-free guidance scale (overrides fastvideo_args)
|
||||
num_frames: Number of frames to generate (overrides fastvideo_args)
|
||||
height: Height of generated video (overrides fastvideo_args)
|
||||
width: Width of generated video (overrides fastvideo_args)
|
||||
fps: Frames per second for saved video (overrides fastvideo_args)
|
||||
seed: Random seed for generation (overrides fastvideo_args)
|
||||
callback: Callback function called after each step
|
||||
callback_steps: Number of steps between each callback
|
||||
|
||||
Returns:
|
||||
A metadata dictionary for single-prompt generation, or a list of
|
||||
metadata dictionaries for prompt-file batch generation.
|
||||
"""
|
||||
log_queue = kwargs.pop("log_queue", None)
|
||||
warnings.warn(
|
||||
"VideoGenerator.generate_video(...) is deprecated; use "
|
||||
"VideoGenerator.generate(request=...) instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
if log_queue:
|
||||
self.executor.set_log_queue(log_queue)
|
||||
|
||||
try:
|
||||
extra_overrides: dict[str, Any] = {}
|
||||
for _ek in _BATCH_EXTRA_PASSTHROUGH_KEYS:
|
||||
if _ek in kwargs:
|
||||
extra_overrides[_ek] = kwargs.pop(_ek)
|
||||
|
||||
request = legacy_generate_call_to_request(
|
||||
prompt,
|
||||
sampling_param,
|
||||
mouse_cond=mouse_cond,
|
||||
keyboard_cond=keyboard_cond,
|
||||
grid_sizes=grid_sizes,
|
||||
legacy_kwargs=kwargs,
|
||||
)
|
||||
|
||||
resolved_sampling_param = request_to_sampling_param(
|
||||
request,
|
||||
model_path=self.fastvideo_args.model_path,
|
||||
)
|
||||
return self._generate_video_impl(
|
||||
prompt=request.prompt,
|
||||
sampling_param=resolved_sampling_param,
|
||||
fastvideo_args=self.fastvideo_args,
|
||||
**extra_overrides,
|
||||
)
|
||||
finally:
|
||||
if log_queue:
|
||||
self.executor.clear_log_queue()
|
||||
|
||||
def _generate_request_impl(
|
||||
self,
|
||||
request: GenerationRequest,
|
||||
@@ -530,7 +455,7 @@ class VideoGenerator:
|
||||
fastvideo_args: FastVideoArgs | None = None,
|
||||
**kwargs,
|
||||
) -> dict[str, Any] | list[np.ndarray] | list[dict[str, Any]]:
|
||||
"""Internal implementation of generate_video."""
|
||||
"""Run one prompt, or each prompt of a prompt file, through the pipeline with ``sampling_param``."""
|
||||
if fastvideo_args is None:
|
||||
fastvideo_args = self.fastvideo_args
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
A :class:`PromptDataset` is an iterable of *sample dicts* describing the
|
||||
prompts and conditions for a benchmark. Each sample is a plain dict —
|
||||
no dataclass, no schema enforcement — that flows directly into both
|
||||
generation (``VideoGenerator.generate_video(**sample)``) and scoring
|
||||
generation (a ``VideoGenerator.generate`` request) and scoring
|
||||
(``Evaluator.evaluate(**eval_kwargs)``). The runner picks well-known
|
||||
keys (``prompt``, ``n_samples``, ``dimensions``, ``auxiliary_info``,
|
||||
...) and passes the rest through.
|
||||
@@ -11,7 +11,6 @@ keys (``prompt``, ``n_samples``, ``dimensions``, ``auxiliary_info``,
|
||||
This matches the surrounding FastVideo style:
|
||||
|
||||
* :class:`fastvideo.dataset.validation_dataset.ValidationDataset` yields dicts.
|
||||
* :meth:`fastvideo.VideoGenerator.generate_video` consumes ``**kwargs``.
|
||||
* :meth:`fastvideo.eval.Evaluator.evaluate` consumes ``**kwargs``.
|
||||
|
||||
To add a new benchmark:
|
||||
|
||||
@@ -154,7 +154,7 @@ class MiniMaxH3DenoisingStage(PipelineStage):
|
||||
if vsa_metadata_builder is not None:
|
||||
vsa_patch_size = fastvideo_args.pipeline_config.dit_config.patch_size
|
||||
vsa_prefix_segments = _h3_vsa_prefix_segments(layout, vsa_patch_size)
|
||||
# Per-request knobs (sweeps flip these between generate_video calls
|
||||
# Per-request knobs (sweeps flip these between generate calls
|
||||
# without respawning workers); mode None defers to the env default.
|
||||
vsa_mode = batch.extra.get("vsa_mode", "exempt")
|
||||
if vsa_mode not in ("exempt", "compete"):
|
||||
|
||||
@@ -62,7 +62,7 @@ def _disable_tf32_for_stable_audio() -> None:
|
||||
class StableAudioPipeline(ComposedPipelineBase):
|
||||
"""Stable Audio Open 1.0 pipeline.
|
||||
|
||||
Mode is kwargs-driven on `generate_video()`:
|
||||
Mode is driven by the request `extensions` passed to `generate()`:
|
||||
|
||||
* Text-to-audio (default) -- `prompt=...`, `audio_end_in_s=...`
|
||||
* Audio-to-audio variation -- add `init_audio=ref` (and optionally
|
||||
|
||||
@@ -63,7 +63,7 @@ class StableAudioDecodingStage(PipelineStage):
|
||||
# Raw tensor for parity tests.
|
||||
batch.extra["decoded_audio"] = decoded.detach().cpu()
|
||||
|
||||
# `VideoGenerator.generate_video` is video-shaped (asserts
|
||||
# `VideoGenerator.generate` is video-shaped (asserts
|
||||
# `output_batch.output is not None`); fill with a placeholder of
|
||||
# the expected `[B, 3, num_frames, H, W]` shape — the real audio
|
||||
# is on `batch.extra` above. Keep this compatibility shim until
|
||||
|
||||
@@ -13,8 +13,8 @@ invocation closed stdin before the writer thread had pushed every
|
||||
frame, surfacing as ``BrokenPipeError`` in the streaming server.
|
||||
|
||||
These tests pin two contracts:
|
||||
1. ``_BATCH_EXTRA_PASSTHROUGH_KEYS`` lists the exact set of kwargs
|
||||
pulled out of ``generate_video(**kwargs)`` for ``batch.extra``.
|
||||
1. ``_BATCH_EXTRA_PASSTHROUGH_KEYS`` lists the exact set of request
|
||||
extensions that go to ``batch.extra``.
|
||||
2. ``SamplingParam.update()`` raises ``ValueError`` on unknown keys.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -13,10 +13,7 @@ This test makes sure:
|
||||
|
||||
1. The public typed API can represent everything Dreamverse currently
|
||||
passes at init time (``legacy_from_pretrained_to_config``).
|
||||
2. The request-path Dreamverse uses (``generator.generate_video(**kwargs)``
|
||||
with per-segment flags) round-trips through the typed ``GenerationRequest``
|
||||
without reintroducing private-only fields at the public boundary.
|
||||
3. Private-only Dreamverse fields that don't belong on the public
|
||||
2. Private-only Dreamverse fields that don't belong on the public
|
||||
surface either go to ``pipeline.experimental`` / ``request.extensions``
|
||||
(the documented escape hatch) or raise explicitly, rather than
|
||||
silently becoming part of the public compatibility promise.
|
||||
@@ -36,7 +33,6 @@ from fastvideo.api import (
|
||||
)
|
||||
from fastvideo.api.compat import (
|
||||
legacy_from_pretrained_to_config,
|
||||
legacy_generate_call_to_request,
|
||||
normalize_generation_request,
|
||||
)
|
||||
|
||||
@@ -151,35 +147,6 @@ class TestDreamverseRequestShape:
|
||||
"""The per-segment Dreamverse request path mirrors OpenAI's shape
|
||||
plus a few LTX-2 knobs. All of them must have a typed home."""
|
||||
|
||||
def test_basic_request_fields_round_trip(self):
|
||||
# The request path calls legacy_generate_call_to_request with a
|
||||
# prompt + legacy kwargs; verify the typed shape carries them.
|
||||
legacy_kwargs = {
|
||||
"num_frames": 121,
|
||||
"height": 1024,
|
||||
"width": 1536,
|
||||
"num_inference_steps": 8,
|
||||
"guidance_scale": 1.0,
|
||||
"seed": 42,
|
||||
"fps": 24,
|
||||
"negative_prompt": "blurry",
|
||||
}
|
||||
request = legacy_generate_call_to_request(
|
||||
prompt="a fox running",
|
||||
sampling_param=None,
|
||||
legacy_kwargs=legacy_kwargs,
|
||||
)
|
||||
request = normalize_generation_request(request)
|
||||
assert request.prompt == "a fox running"
|
||||
assert request.negative_prompt == "blurry"
|
||||
assert request.sampling.num_frames == 121
|
||||
assert request.sampling.height == 1024
|
||||
assert request.sampling.width == 1536
|
||||
assert request.sampling.num_inference_steps == 8
|
||||
assert request.sampling.guidance_scale == 1.0
|
||||
assert request.sampling.seed == 42
|
||||
assert request.sampling.fps == 24
|
||||
|
||||
def test_return_state_reaches_output_config(self):
|
||||
"""PR 7 added ``output.return_state`` — must survive the legacy
|
||||
translation path so Dreamverse callers can opt in."""
|
||||
|
||||
@@ -897,79 +897,6 @@ def test_generate_removes_deleted_loaded_stage_overrides(monkeypatch, tmp_path):
|
||||
assert captured["sampling_param"].t_thresh == 0.5
|
||||
|
||||
|
||||
def test_generate_video_legacy_call_uses_legacy_impl(monkeypatch):
|
||||
generator = _new_runtime_video_generator()
|
||||
captured = {}
|
||||
|
||||
def fake_generate_video_impl(
|
||||
prompt=None,
|
||||
sampling_param=None,
|
||||
mouse_cond=None,
|
||||
keyboard_cond=None,
|
||||
grid_sizes=None,
|
||||
**kwargs,
|
||||
):
|
||||
captured["prompt"] = prompt
|
||||
captured["sampling_param"] = sampling_param
|
||||
captured["mouse_cond"] = mouse_cond
|
||||
captured["keyboard_cond"] = keyboard_cond
|
||||
captured["grid_sizes"] = grid_sizes
|
||||
captured["kwargs"] = kwargs
|
||||
return {"prompts": prompt, "video_path": "outputs/test.mp4"}
|
||||
|
||||
monkeypatch.setattr(generator, "_generate_video_impl", fake_generate_video_impl)
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
result = generator.generate_video(
|
||||
prompt="legacy prompt",
|
||||
num_frames=49,
|
||||
output_path="outputs/legacy",
|
||||
save_video=False,
|
||||
log_queue="queue-token",
|
||||
)
|
||||
|
||||
assert captured["prompt"] == "legacy prompt"
|
||||
assert captured["sampling_param"].num_frames == 49
|
||||
assert captured["sampling_param"].output_path == "outputs/legacy"
|
||||
assert captured["sampling_param"].save_video is False
|
||||
assert result["video_path"] == "outputs/test.mp4"
|
||||
|
||||
|
||||
def test_generate_video_legacy_call_routes_compat_kwargs(monkeypatch):
|
||||
generator = _new_runtime_video_generator()
|
||||
generator.fastvideo_args.pipeline_config = SimpleNamespace(embedded_cfg_scale=1.0)
|
||||
captured = {}
|
||||
|
||||
def fake_generate_video_impl(
|
||||
prompt=None,
|
||||
sampling_param=None,
|
||||
mouse_cond=None,
|
||||
keyboard_cond=None,
|
||||
grid_sizes=None,
|
||||
**kwargs,
|
||||
):
|
||||
captured["prompt"] = prompt
|
||||
captured["sampling_param"] = sampling_param
|
||||
captured["kwargs"] = kwargs
|
||||
return {"prompts": prompt, "video_path": "outputs/test.mp4"}
|
||||
|
||||
monkeypatch.setattr(generator, "_generate_video_impl", fake_generate_video_impl)
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
result = generator.generate_video(
|
||||
prompt="legacy prompt",
|
||||
neg_prompt="custom negative",
|
||||
embedded_cfg_scale=7.5,
|
||||
)
|
||||
|
||||
assert captured["prompt"] == "legacy prompt"
|
||||
assert captured["sampling_param"].negative_prompt == "custom negative"
|
||||
assert captured["sampling_param"].embedded_cfg_scale == 7.5
|
||||
assert captured["kwargs"]["fastvideo_args"] is generator.fastvideo_args
|
||||
assert generator.fastvideo_args.pipeline_config.embedded_cfg_scale == 1.0
|
||||
assert result["video_path"] == "outputs/test.mp4"
|
||||
|
||||
|
||||
def test_generate_batch_prompt_file_returns_typed_results(tmp_path, monkeypatch):
|
||||
generator = _new_runtime_video_generator()
|
||||
_patch_sampling_param_from_pretrained(monkeypatch)
|
||||
|
||||
@@ -110,12 +110,12 @@ class Executor(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def set_log_queue(self, log_queue: Queue | None) -> None:
|
||||
"""Forward worker logs to the given queue. Call before generate_video."""
|
||||
"""Forward worker logs to the given queue. Call before generate."""
|
||||
self.collective_rpc("set_log_queue", kwargs={"log_queue": log_queue})
|
||||
|
||||
@abstractmethod
|
||||
def clear_log_queue(self) -> None:
|
||||
"""Stop forwarding worker logs to the queue. Call after generate_video."""
|
||||
"""Stop forwarding worker logs to the queue. Call after generate."""
|
||||
self.collective_rpc("clear_log_queue")
|
||||
|
||||
@abstractmethod
|
||||
|
||||
@@ -283,11 +283,11 @@ class MultiprocExecutor(Executor):
|
||||
raise RuntimeError(f"Worker {i} failed to merge LoRA weights")
|
||||
|
||||
def set_log_queue(self, log_queue: Queue | None) -> None:
|
||||
"""Forward worker logs to the given queue. Call before generate_video."""
|
||||
"""Forward worker logs to the given queue. Call before generate."""
|
||||
self.collective_rpc("set_log_queue", kwargs={"log_queue": log_queue})
|
||||
|
||||
def clear_log_queue(self) -> None:
|
||||
"""Stop forwarding worker logs to the queue. Call after generate_video."""
|
||||
"""Stop forwarding worker logs to the queue. Call after generate."""
|
||||
self.collective_rpc("clear_log_queue")
|
||||
|
||||
def collective_rpc(self,
|
||||
|
||||
@@ -682,20 +682,23 @@ def main():
|
||||
print(" 1. Test basic generation:")
|
||||
print(" from fastvideo import VideoGenerator")
|
||||
print(f" generator = VideoGenerator.from_pretrained('{output_dir}')")
|
||||
print(" video = generator.generate_video(")
|
||||
print(" prompt='A cat playing piano',")
|
||||
print(" num_inference_steps=50")
|
||||
print(" )")
|
||||
print(" video = generator.generate({")
|
||||
print(" 'prompt': 'A cat playing piano',")
|
||||
print(" 'sampling': {'num_inference_steps': 50},")
|
||||
print(" })")
|
||||
print()
|
||||
if (output_dir / "lora" / "distilled").exists():
|
||||
print(" 2. Test distilled generation (16 steps with LoRA):")
|
||||
print(f" generator = VideoGenerator.from_pretrained('{output_dir}',")
|
||||
print(f" lora_path='{output_dir}/lora/distilled',")
|
||||
print(" lora_nickname='distilled')")
|
||||
print(" video = generator.generate_video(")
|
||||
print(" prompt='A cat playing piano',")
|
||||
print(" num_inference_steps=16,")
|
||||
print(" guidance_scale=1.0)")
|
||||
print(" generator = VideoGenerator.from_config({")
|
||||
print(f" 'model_path': '{output_dir}',")
|
||||
print(" 'pipeline': {'components': {")
|
||||
print(f" 'lora_path': '{output_dir}/lora/distilled',")
|
||||
print(" 'lora_nickname': 'distilled'}},")
|
||||
print(" })")
|
||||
print(" video = generator.generate({")
|
||||
print(" 'prompt': 'A cat playing piano',")
|
||||
print(" 'sampling': {'num_inference_steps': 16, 'guidance_scale': 1.0},")
|
||||
print(" })")
|
||||
print()
|
||||
print()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user