Signed-off-by: zhangbin.327 <zhangbin.327@bytedance.com> Signed-off-by: Jack47 <scsvip@gmail.com> Co-authored-by: zhangbin.327 <zhangbin.327@bytedance.com> Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
232 lines
7.4 KiB
Python
232 lines
7.4 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import AsyncIterator, Sequence
|
|
from typing import Any
|
|
|
|
import pytest
|
|
|
|
from vllm_omni.experimental.ar_diffusion.consumer import ARDiffusionOmniTickConsumer
|
|
from vllm_omni.experimental.ar_diffusion.session import (
|
|
ARDiffusionSession,
|
|
ARDiffusionSessionEvent,
|
|
)
|
|
from vllm_omni.experimental.ar_diffusion.tick_protocol import (
|
|
ARDiffusionChunkMetadata,
|
|
ARDiffusionControlInput,
|
|
ARDiffusionTickRequest,
|
|
)
|
|
from vllm_omni.inputs.data import (
|
|
OmniDiffusionSamplingParams,
|
|
OmniPromptType,
|
|
OmniSamplingParams,
|
|
)
|
|
from vllm_omni.outputs import OmniRequestOutput
|
|
|
|
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
|
|
|
|
|
class FakeStagePool:
|
|
def __init__(self, num_replicas: int = 1) -> None:
|
|
self.num_replicas = num_replicas
|
|
|
|
|
|
class FakeEngine:
|
|
def __init__(self, num_replicas: int = 1) -> None:
|
|
self.stage_pools = [FakeStagePool(num_replicas)]
|
|
|
|
|
|
class FakeGenerateClient:
|
|
def __init__(
|
|
self,
|
|
*,
|
|
include_metadata: bool = True,
|
|
applied_event_ids: list[int] | None = None,
|
|
output_error: str | None = None,
|
|
num_replicas: int = 1,
|
|
) -> None:
|
|
self.engine = FakeEngine(num_replicas)
|
|
self.include_metadata = include_metadata
|
|
self.applied_event_ids = applied_event_ids
|
|
self.output_error = output_error
|
|
self.calls: list[dict[str, Any]] = []
|
|
|
|
async def generate(
|
|
self,
|
|
prompt: OmniPromptType,
|
|
*,
|
|
request_id: str,
|
|
sampling_params_list: Sequence[OmniSamplingParams],
|
|
output_modalities: list[str] | None = None,
|
|
) -> AsyncIterator[OmniRequestOutput]:
|
|
self.calls.append(
|
|
{
|
|
"prompt": prompt,
|
|
"request_id": request_id,
|
|
"sampling_params_list": sampling_params_list,
|
|
"output_modalities": output_modalities,
|
|
}
|
|
)
|
|
params = sampling_params_list[0]
|
|
assert isinstance(params, OmniDiffusionSamplingParams)
|
|
tick = ARDiffusionTickRequest.from_extra_args(params.extra_args)
|
|
assert tick is not None
|
|
chunk_metadata = ARDiffusionChunkMetadata.from_tick(tick).to_dict()
|
|
if self.applied_event_ids is not None:
|
|
chunk_metadata["applied_event_ids"] = self.applied_event_ids
|
|
metadata = {"metadata": {"ar_diffusion": chunk_metadata}} if self.include_metadata else {}
|
|
yield OmniRequestOutput(
|
|
request_id=request_id,
|
|
stage_id=0,
|
|
final_output_type="video",
|
|
images=["chunk"], # type: ignore[list-item]
|
|
_multimodal_output=metadata,
|
|
finished=True,
|
|
error=self.output_error,
|
|
)
|
|
|
|
|
|
class FakeLifecycle:
|
|
async def reset_session(self, session_id: str) -> None:
|
|
pass
|
|
|
|
async def close_session(self, session_id: str) -> None:
|
|
pass
|
|
|
|
|
|
def make_tick() -> ARDiffusionTickRequest:
|
|
return ARDiffusionTickRequest(
|
|
session_id="world-1",
|
|
request_id="world-1-chunk-3",
|
|
chunk_index=3,
|
|
applied_event_ids=(7, 8),
|
|
prompt="turn left",
|
|
controls=(
|
|
ARDiffusionControlInput(
|
|
track="camera",
|
|
schema="example.camera.v1",
|
|
data={"samples": [{"x": 1.0}]},
|
|
),
|
|
),
|
|
)
|
|
|
|
|
|
def test_consumer_rejects_replicated_ar_diffusion_stage() -> None:
|
|
client = FakeGenerateClient(num_replicas=2)
|
|
|
|
with pytest.raises(
|
|
ValueError,
|
|
match=r"require exactly one replica.*num_replicas=2",
|
|
):
|
|
ARDiffusionOmniTickConsumer(
|
|
client,
|
|
prompt_provider=lambda tick: tick.prompt or "",
|
|
sampling_params_list=[OmniDiffusionSamplingParams()],
|
|
diffusion_stage_id=0,
|
|
)
|
|
|
|
assert client.calls == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_consumer_submits_typed_tick_and_returns_standard_output() -> None:
|
|
client = FakeGenerateClient()
|
|
template = OmniDiffusionSamplingParams(
|
|
output_type="latent",
|
|
extra_args={"deployment_option": "kept"},
|
|
)
|
|
consumer = ARDiffusionOmniTickConsumer(
|
|
client,
|
|
prompt_provider=lambda tick: {
|
|
"prompt": tick.prompt,
|
|
"multi_modal_data": {"image": "initial-image"},
|
|
},
|
|
sampling_params_list=[template],
|
|
diffusion_stage_id=0,
|
|
output_modalities=["video"],
|
|
)
|
|
tick = make_tick()
|
|
|
|
output = await consumer.execute_tick(tick)
|
|
|
|
assert output.images == ["chunk"]
|
|
assert consumer.chunk_metadata(output) == ARDiffusionChunkMetadata.from_tick(tick)
|
|
assert len(client.calls) == 1
|
|
call = client.calls[0]
|
|
assert call["request_id"] == tick.request_id
|
|
assert call["output_modalities"] == ["video"]
|
|
assert call["prompt"] == {
|
|
"prompt": "turn left",
|
|
"multi_modal_data": {"image": "initial-image"},
|
|
}
|
|
submitted_params = call["sampling_params_list"][0]
|
|
assert submitted_params.extra_args["deployment_option"] == "kept"
|
|
assert ARDiffusionTickRequest.from_extra_args(submitted_params.extra_args) == tick
|
|
assert "ar_diffusion_tick" not in template.extra_args
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_consumer_rejects_missing_chunk_metadata() -> None:
|
|
consumer = ARDiffusionOmniTickConsumer(
|
|
FakeGenerateClient(include_metadata=False),
|
|
prompt_provider=lambda tick: tick.prompt or "",
|
|
sampling_params_list=[OmniDiffusionSamplingParams()],
|
|
diffusion_stage_id=0,
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="multimodal_output.metadata"):
|
|
await consumer.execute_tick(make_tick())
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_consumer_rejects_non_monotonic_applied_event_metadata() -> None:
|
|
consumer = ARDiffusionOmniTickConsumer(
|
|
FakeGenerateClient(applied_event_ids=[8, 7]),
|
|
prompt_provider=lambda tick: tick.prompt or "",
|
|
sampling_params_list=[OmniDiffusionSamplingParams()],
|
|
diffusion_stage_id=0,
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="unique and strictly increasing"):
|
|
await consumer.execute_tick(make_tick())
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_consumer_preserves_engine_failure_before_metadata_validation() -> None:
|
|
consumer = ARDiffusionOmniTickConsumer(
|
|
FakeGenerateClient(include_metadata=False, output_error="model failed"),
|
|
prompt_provider=lambda tick: tick.prompt or "",
|
|
sampling_params_list=[OmniDiffusionSamplingParams()],
|
|
diffusion_stage_id=0,
|
|
)
|
|
|
|
with pytest.raises(RuntimeError, match="model failed"):
|
|
await consumer.execute_tick(make_tick())
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_session_commits_only_after_standard_engine_output_metadata() -> None:
|
|
client = FakeGenerateClient()
|
|
consumer = ARDiffusionOmniTickConsumer(
|
|
client,
|
|
prompt_provider=lambda tick: tick.prompt or "",
|
|
sampling_params_list=[OmniDiffusionSamplingParams()],
|
|
diffusion_stage_id=0,
|
|
)
|
|
session = ARDiffusionSession(
|
|
"world-1",
|
|
tick_consumer=consumer,
|
|
lifecycle=FakeLifecycle(),
|
|
max_pending_events=4,
|
|
request_id_factory=lambda session_id, chunk_index: f"{session_id}-chunk-{chunk_index}",
|
|
)
|
|
await session.accept_event(ARDiffusionSessionEvent(event_id=7, prompt="turn left"))
|
|
|
|
output = await session.next_chunk()
|
|
|
|
assert output.request_id == "world-1-chunk-0"
|
|
assert session.chunk_index == 1
|
|
assert session.pending_event_count == 0
|
|
assert client.calls[0]["request_id"] == "world-1-chunk-0"
|