Files
vllm-omni/vllm_omni/diffusion/sched/step_scheduler.py
T
Wu JIAZHENandjader 3fd4a4dc27 [Feat] Support step-boundary abort in diffusion (#1769)
Signed-off-by: jader <yjader@foxmail.com>
Signed-off-by: asukaqaq-s <1311722138@qq.com>
Co-authored-by: jader <yjader@foxmail.com>
2026-04-01 17:25:02 +08:00

130 lines
4.8 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any
from vllm.logger import init_logger
from vllm_omni.diffusion.request import OmniDiffusionRequest
from vllm_omni.diffusion.sched.base_scheduler import _BaseScheduler
from vllm_omni.diffusion.sched.interface import (
DiffusionRequestStatus,
DiffusionSchedulerOutput,
)
if TYPE_CHECKING:
from vllm_omni.diffusion.worker.utils import RunnerOutput
logger = init_logger(__name__)
@dataclass
class _StepProgress:
current_step: int
total_steps: int
class StepScheduler(_BaseScheduler):
"""Placeholder scheduler that advances a request one denoise step per update."""
def __init__(self) -> None:
super().__init__()
self._request_progress: dict[str, _StepProgress] = {}
def _reset_scheduler_state(self) -> None:
self._request_progress.clear()
def add_request(self, request: OmniDiffusionRequest) -> str:
sched_req_id = self._make_sched_req_id(request)
total_steps = self._get_total_steps(request)
if total_steps <= 0:
raise ValueError(f"Diffusion request {sched_req_id} must have positive total_steps, got {total_steps}")
current_step = request.sampling_params.step_index or 0
if current_step < 0 or current_step >= total_steps:
raise ValueError(
f"Diffusion request {sched_req_id} has invalid initial step_index {current_step} "
f"for total_steps={total_steps}"
)
request.sampling_params.step_index = current_step
sched_req_id = self._add_request_with_sched_req_id(sched_req_id, request)
self._request_progress[sched_req_id] = _StepProgress(current_step=current_step, total_steps=total_steps)
logger.debug(
"StepScheduler add_request: %s (step=%d/%d, waiting=%d)",
sched_req_id,
current_step,
total_steps,
len(self._waiting),
)
return sched_req_id
def schedule(self) -> DiffusionSchedulerOutput:
return super().schedule()
def update_from_output(self, sched_output: DiffusionSchedulerOutput, output: RunnerOutput) -> set[str]:
scheduled_req_ids = sched_output.scheduled_req_ids
if not scheduled_req_ids:
return set()
terminal_statuses: dict[str, DiffusionRequestStatus] = {}
terminal_errors: dict[str, str | None] = {}
output_error = output.result.error if output.result is not None else None
for sched_req_id in scheduled_req_ids:
state = self._request_states.get(sched_req_id)
progress = self._request_progress.get(sched_req_id)
if state is None or progress is None or state.is_finished():
continue
if output_error is not None:
terminal_statuses[sched_req_id] = DiffusionRequestStatus.FINISHED_ERROR
terminal_errors[sched_req_id] = output_error
continue
if output.step_index is None:
logger.warning(
"Received RunnerOutput with no step_index for request %s, treating as error",
sched_req_id,
)
terminal_statuses[sched_req_id] = DiffusionRequestStatus.FINISHED_ERROR
terminal_errors[sched_req_id] = "Missing step_index in RunnerOutput"
continue
# We assume that the decoding stage is executed immediately after the denoising stage completes.
progress.current_step = output.step_index
state.req.sampling_params.step_index = output.step_index
if output.finished:
terminal_statuses[sched_req_id] = DiffusionRequestStatus.FINISHED_COMPLETED
terminal_errors[sched_req_id] = None
else:
state.error = None
return self._finalize_update_from_output(sched_output, terminal_statuses, terminal_errors)
def _pop_extra_request_state(self, sched_req_id: str) -> None:
self._request_progress.pop(sched_req_id, None)
def _get_total_steps(self, request: OmniDiffusionRequest) -> int:
sampling = request.sampling_params
if sampling.timesteps is not None:
return self._sequence_length(sampling.timesteps)
if sampling.sigmas is not None:
return len(sampling.sigmas)
return int(sampling.num_inference_steps)
@staticmethod
def _sequence_length(values: Any) -> int:
ndim = getattr(values, "ndim", None)
if ndim == 0:
return 1
shape = getattr(values, "shape", None)
if shape is not None:
return int(shape[0])
return len(values)