[feat] Streaming diffusion video generation output (#3737)

Signed-off-by: Huang, Zeyu <11222265+fhfuih@users.noreply.github.com>
Signed-off-by: Zeyu Huang | 黃澤宇 <11222265+fhfuih@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
authored and GitHub committed 2026-06-23 09:57:53 +00:00
1 parent b84c99c122
commit 3901ad8165
42 files changed
+4322 -65

No files matched your search

+100 -3
View File
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import asyncio
import queue
import threading
from types import SimpleNamespace
@@ -238,6 +239,35 @@ class TestRequestScheduler:
assert finished == {req_id}
assert self.scheduler.get_request_state(req_id).status == DiffusionRequestStatus.FINISHED_COMPLETED
def test_streaming_output_keeps_request_running_until_final_chunk(self) -> None:
scheduler = StepScheduler()
scheduler.initialize(SimpleNamespace())
req_id = scheduler.add_request(_make_request("stream"))
sched_output = scheduler.schedule()
chunk = RunnerOutput(
request_id=req_id,
step_index=1,
finished=False,
result=DiffusionOutput(output="chunk-0", finished=False, chunk_index=0, total_chunks=2),
)
finished = scheduler.update_from_output(sched_output, chunk)
assert finished == set()
assert scheduler.get_request_state(req_id).status == DiffusionRequestStatus.RUNNING
assert scheduler.has_requests() is True
final_chunk = RunnerOutput(
request_id=req_id,
step_index=2,
finished=True,
result=DiffusionOutput(output="chunk-1", finished=True, chunk_index=1, total_chunks=2),
)
finished = scheduler.update_from_output(sched_output, final_chunk)
assert finished == {req_id}
assert scheduler.get_request_state(req_id).status == DiffusionRequestStatus.FINISHED_COMPLETED
def test_fifo_single_request_scheduling(self) -> None:
req_id_a = self.scheduler.add_request(_make_request("a"))
req_id_b = self.scheduler.add_request(_make_request("b"))
@@ -369,6 +399,7 @@ class TestRequestScheduler:
class TestDiffusionEngine:
def test_add_req_and_wait_for_response_single_path(self, mocker: MockerFixture) -> None:
engine = DiffusionEngine.__new__(DiffusionEngine)
engine.od_config = SimpleNamespace(streaming_output=False)
engine.scheduler = RequestScheduler()
engine.scheduler.initialize(SimpleNamespace())
engine._rpc_lock = threading.RLock()
@@ -391,6 +422,7 @@ class TestDiffusionEngine:
scheduler = _StubScheduler(request, runner_output)
engine = DiffusionEngine.__new__(DiffusionEngine)
engine.od_config = SimpleNamespace(streaming_output=False)
engine.scheduler = scheduler
engine._rpc_lock = threading.RLock()
engine._cv = threading.Condition(engine._rpc_lock)
@@ -410,7 +442,7 @@ class TestDiffusionEngine:
) -> None:
request = _make_request("init")
scheduler = _StubScheduler(request, DiffusionOutput(output=None))
od_config = SimpleNamespace(model_class_name="mock_model")
od_config = SimpleNamespace(model_class_name="mock_model", streaming_output=False)
fake_executor_cls = mocker.Mock(return_value=mocker.Mock())
monkeypatch.setattr(
@@ -472,7 +504,7 @@ class TestDiffusionEngine:
def test_finalize_finished_request_returns_aborted_output(self) -> None:
engine = DiffusionEngine.__new__(DiffusionEngine)
engine.scheduler = RequestScheduler()
engine.scheduler = StepScheduler()
engine.scheduler.initialize(SimpleNamespace())
req_id = engine.scheduler.add_request(_make_request("req-finalize"))
@@ -483,12 +515,77 @@ class TestDiffusionEngine:
assert output.aborted is True
assert output.abort_message == "Request req-finalize aborted."
@pytest.mark.asyncio
async def test_streaming_runner_output_notifies_each_chunk(self) -> None:
engine = DiffusionEngine.__new__(DiffusionEngine)
engine.scheduler = StepScheduler()
engine.scheduler.initialize(SimpleNamespace())
engine._rpc_lock = threading.RLock()
engine._cv = threading.Condition(engine._rpc_lock)
engine._out_queue_streaming = {}
engine.main_loop = asyncio.get_running_loop()
req_id = engine.scheduler.add_request(_make_request("stream-engine"))
queue: asyncio.Queue[DiffusionOutput] = asyncio.Queue()
engine._out_queue_streaming[req_id] = queue
sched_output = engine.scheduler.schedule()
chunk = RunnerOutput(
request_id=req_id,
step_index=1,
finished=False,
result=DiffusionOutput(output="chunk-0", finished=False, chunk_index=0, total_chunks=2),
)
finished_req_ids = engine.scheduler.update_from_output(sched_output, chunk)
engine._handle_step_streaming_runner_output(finished_req_ids, sched_output.scheduled_request_ids, chunk)
notified_chunk = await asyncio.wait_for(queue.get(), timeout=1)
assert notified_chunk.output == "chunk-0"
assert notified_chunk.finished is False
assert engine.scheduler.get_request_state(req_id).status == DiffusionRequestStatus.RUNNING
final_chunk = RunnerOutput(
request_id=req_id,
step_index=2,
finished=True,
result=DiffusionOutput(output="chunk-1", finished=True, chunk_index=1, total_chunks=2),
)
finished_req_ids = engine.scheduler.update_from_output(sched_output, final_chunk)
engine._handle_step_streaming_runner_output(finished_req_ids, sched_output.scheduled_request_ids, final_chunk)
notified_final = await asyncio.wait_for(queue.get(), timeout=1)
assert notified_final.output == "chunk-1"
assert notified_final.finished is True
assert engine.scheduler.get_request_state(req_id) is None
@pytest.mark.asyncio
async def test_finished_streaming_request_without_runner_output_notifies_waiter(self) -> None:
engine = DiffusionEngine.__new__(DiffusionEngine)
engine.scheduler = RequestScheduler()
engine.scheduler.initialize(SimpleNamespace())
engine._rpc_lock = threading.RLock()
engine._cv = threading.Condition(engine._rpc_lock)
engine._out_queue_streaming = {}
engine.main_loop = asyncio.get_running_loop()
req_id = engine.scheduler.add_request(_make_request("stream-abort"))
queue: asyncio.Queue[DiffusionOutput] = asyncio.Queue()
engine._out_queue_streaming[req_id] = queue
engine.scheduler.finish_requests(req_id, DiffusionRequestStatus.FINISHED_ABORTED)
engine._handle_empty_streaming_requests({req_id})
output = await asyncio.wait_for(queue.get(), timeout=1)
assert output.aborted is True
assert output.finished is True
assert engine.scheduler.get_request_state(req_id) is None
def test_initializes_step_scheduler_when_step_execution_enabled(
self,
monkeypatch: pytest.MonkeyPatch,
mocker: MockerFixture,
) -> None:
od_config = SimpleNamespace(model_class_name="mock_model")
od_config = SimpleNamespace(model_class_name="mock_model", streaming_output=False)
od_config.step_execution = True
fake_executor = mocker.Mock()
fake_executor_cls = mocker.Mock(return_value=fake_executor)