Signed-off-by: Huang, Zeyu <11222265+fhfuih@users.noreply.github.com> Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
184 lines
6.1 KiB
Python
184 lines
6.1 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
from contextlib import contextmanager
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
import torch
|
|
from torch import nn
|
|
|
|
from vllm_omni.diffusion.models.wan2_2.pipeline_wan2_2 import Wan22Pipeline
|
|
from vllm_omni.diffusion.worker.request_batch import DiffusionRequestBatch
|
|
|
|
pytestmark = [pytest.mark.core_model, pytest.mark.cpu, pytest.mark.diffusion]
|
|
|
|
|
|
class _StubTransformer(nn.Module):
|
|
@property
|
|
def dtype(self) -> torch.dtype:
|
|
return torch.float32
|
|
|
|
|
|
class _StubTextEncoder(nn.Module):
|
|
@property
|
|
def dtype(self) -> torch.dtype:
|
|
return torch.float32
|
|
|
|
|
|
class _StubScheduler:
|
|
def __init__(self, timesteps: list[int]) -> None:
|
|
self.timesteps = torch.tensor(timesteps, dtype=torch.int64)
|
|
self.config = SimpleNamespace(num_train_timesteps=1000)
|
|
self.set_timesteps_calls: list[tuple[int, torch.device]] = []
|
|
|
|
def set_timesteps(self, num_steps: int, device: torch.device) -> None:
|
|
self.set_timesteps_calls.append((num_steps, device))
|
|
|
|
|
|
@contextmanager
|
|
def _noop_progress_bar(*args, **kwargs):
|
|
del args, kwargs
|
|
|
|
class _Bar:
|
|
def update(self) -> None:
|
|
return None
|
|
|
|
yield _Bar()
|
|
|
|
|
|
def _stub_encode_prompt(
|
|
prompt,
|
|
negative_prompt=None,
|
|
do_classifier_free_guidance=True,
|
|
num_videos_per_prompt=1,
|
|
max_sequence_length=512,
|
|
device=None,
|
|
dtype=None,
|
|
):
|
|
del negative_prompt, do_classifier_free_guidance, device, dtype
|
|
batch_size = 1 if isinstance(prompt, str) else len(prompt)
|
|
n = batch_size * num_videos_per_prompt
|
|
hidden_size = 8
|
|
prompt_embeds = torch.zeros(n, max_sequence_length, hidden_size)
|
|
return prompt_embeds, None
|
|
|
|
|
|
def _make_pipeline() -> Wan22Pipeline:
|
|
pipeline = object.__new__(Wan22Pipeline)
|
|
nn.Module.__init__(pipeline)
|
|
pipeline.device = torch.device("cpu")
|
|
pipeline.transformer = _StubTransformer()
|
|
pipeline.transformer_2 = None
|
|
pipeline.text_encoder = _StubTextEncoder()
|
|
pipeline.transformer_config = SimpleNamespace(patch_size=(1, 2, 2), in_channels=4, out_channels=4)
|
|
pipeline.scheduler = _StubScheduler([9, 5])
|
|
pipeline.od_config = SimpleNamespace(flow_shift=5.0)
|
|
pipeline._sample_solver = "unipc"
|
|
pipeline._flow_shift = 5.0
|
|
pipeline.vae_scale_factor_temporal = 4
|
|
pipeline.vae_scale_factor_spatial = 8
|
|
pipeline.boundary_ratio = 0.875
|
|
pipeline.expand_timesteps = False
|
|
pipeline._guidance_scale = None
|
|
pipeline._guidance_scale_2 = None
|
|
pipeline._num_timesteps = None
|
|
pipeline._current_timestep = None
|
|
pipeline.check_inputs = lambda **kwargs: None
|
|
pipeline.encode_prompt = _stub_encode_prompt # type: ignore[method-assign]
|
|
pipeline.prepare_latents = lambda **kwargs: torch.zeros((1, 4, 1, 8, 8), dtype=torch.float32)
|
|
pipeline.progress_bar = _noop_progress_bar
|
|
return pipeline
|
|
|
|
|
|
def test_forward_delegates_denoising_to_diffuse() -> None:
|
|
pipeline = _make_pipeline()
|
|
captured: dict[str, object] = {}
|
|
|
|
def _fake_diffuse(**kwargs):
|
|
captured.update(kwargs)
|
|
return kwargs["latents"] + 1
|
|
|
|
pipeline.diffuse = _fake_diffuse # type: ignore[method-assign]
|
|
|
|
mock_req = SimpleNamespace(
|
|
prompt="prompt",
|
|
request_id="test-req",
|
|
sampling_params=SimpleNamespace(
|
|
height=None,
|
|
width=None,
|
|
num_frames=1,
|
|
num_inference_steps=2,
|
|
guidance_scale_provided=True,
|
|
guidance_scale=1.0,
|
|
guidance_scale_2=None,
|
|
boundary_ratio=None,
|
|
generator=None,
|
|
seed=None,
|
|
num_outputs_per_prompt=1,
|
|
max_sequence_length=32,
|
|
latents=None,
|
|
output_type="latent",
|
|
extra_args={},
|
|
),
|
|
)
|
|
batch = DiffusionRequestBatch(requests=[mock_req])
|
|
|
|
output = pipeline.forward(batch)
|
|
|
|
assert torch.equal(output.output, torch.ones((1, 4, 1, 8, 8)))
|
|
assert torch.equal(captured["prompt_embeds"], torch.zeros(1, 32, 8))
|
|
assert torch.equal(captured["timesteps"], pipeline.scheduler.timesteps)
|
|
assert captured["guidance_low"] == 1.0
|
|
assert captured["guidance_high"] == 1.0
|
|
assert captured["boundary_timestep"] == pytest.approx(875.0)
|
|
assert captured["latent_condition"] is None
|
|
assert captured["first_frame_mask"] is None
|
|
assert pipeline.scheduler.set_timesteps_calls == [(2, torch.device("cpu"))]
|
|
|
|
|
|
def test_diffuse_runs_prediction_and_scheduler_for_each_timestep() -> None:
|
|
pipeline = _make_pipeline()
|
|
latents = torch.zeros((1, 1, 1, 2, 2), dtype=torch.float32)
|
|
timesteps = torch.tensor([7, 3], dtype=torch.int64)
|
|
prompt_embeds = torch.randn(1, 8)
|
|
|
|
predict_calls: list[dict[str, object]] = []
|
|
scheduler_calls: list[tuple[float, int, float, bool]] = []
|
|
|
|
def _fake_predict_noise_maybe_with_cfg(**kwargs):
|
|
predict_calls.append(kwargs)
|
|
timestep = kwargs["positive_kwargs"]["timestep"]
|
|
assert isinstance(timestep, torch.Tensor)
|
|
return torch.full_like(latents, float(timestep[0].item()))
|
|
|
|
def _fake_scheduler_step_maybe_with_cfg(noise_pred, t, current_latents, do_true_cfg):
|
|
scheduler_calls.append(
|
|
(float(noise_pred[0, 0, 0, 0, 0]), int(t.item()), float(current_latents.sum()), do_true_cfg)
|
|
)
|
|
return current_latents + noise_pred
|
|
|
|
pipeline.predict_noise_maybe_with_cfg = _fake_predict_noise_maybe_with_cfg # type: ignore[method-assign]
|
|
pipeline.scheduler_step_maybe_with_cfg = _fake_scheduler_step_maybe_with_cfg # type: ignore[method-assign]
|
|
|
|
result = pipeline.diffuse(
|
|
latents=latents,
|
|
timesteps=timesteps,
|
|
prompt_embeds=prompt_embeds,
|
|
negative_prompt_embeds=None,
|
|
guidance_low=1.0,
|
|
guidance_high=2.0,
|
|
boundary_timestep=5.0,
|
|
dtype=torch.float32,
|
|
attention_kwargs={},
|
|
)
|
|
|
|
assert len(predict_calls) == 2
|
|
assert predict_calls[0]["true_cfg_scale"] == 1.0
|
|
assert predict_calls[1]["true_cfg_scale"] == 2.0
|
|
assert scheduler_calls == [
|
|
(7.0, 7, 0.0, False),
|
|
(3.0, 3, 28.0, False),
|
|
]
|
|
assert torch.equal(result, torch.full_like(latents, 10.0))
|