Files
vllm-omni/vllm_omni/diffusion/sched/interface.py
T
e49fbd8a2d [Feat] DiffusionEngine Support async batch infer (#2729)
Signed-off-by: Semmer <semmer@live.cn>
Signed-off-by: jader <yjader@foxmail.com>
Signed-off-by: asukaqaq-s <1311722138@qq.com>
Co-authored-by: asukaqaq-s <1311722138@qq.com>
Co-authored-by: jader <yjader@foxmail.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
Co-authored-by: Gao Han <hgaoaf@connect.ust.hk>
2026-05-06 09:47:13 +12:00

207 lines
6.5 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from __future__ import annotations
import enum
import uuid
from abc import ABC, abstractmethod
from dataclasses import dataclass
from functools import cached_property
from typing import TYPE_CHECKING
from vllm.logger import init_logger
from vllm_omni.diffusion.data import OmniDiffusionConfig
from vllm_omni.diffusion.request import OmniDiffusionRequest
if TYPE_CHECKING:
from vllm_omni.diffusion.worker.utils import RunnerOutput
logger = init_logger(__name__)
class DiffusionRequestStatus(enum.IntEnum):
"""Request status tracked by diffusion scheduler."""
WAITING = enum.auto()
RUNNING = enum.auto()
PREEMPTED = enum.auto()
# if any status is after or equal to FINISHED_COMPLETED, it is considered finished
FINISHED_COMPLETED = enum.auto()
FINISHED_ABORTED = enum.auto()
FINISHED_ERROR = enum.auto()
@staticmethod
def is_finished(status: DiffusionRequestStatus) -> bool:
return status >= DiffusionRequestStatus.FINISHED_COMPLETED
@dataclass(frozen=True, eq=True)
class SamplingParamsKey:
"""Batch-compatibility key derived from ``OmniDiffusionSamplingParams``.
Only requests with the same key can be batched together.
Fields not included here are treated as request-local and do not
participate in the current homogeneous batching policy.
"""
# Spatial / temporal shape.
height: int | None = None
width: int | None = None
num_frames: int = 1
resolution: int | str | None = None
fps: int | None = None
frame_rate: float | None = None
boundary_ratio: float | None = None
# CFG / guidance.
do_classifier_free_guidance: bool = False
guidance_scale: float = 0.0
guidance_scale_provided: bool = False
guidance_scale_2: float | None = None
guidance_rescale: float = 0.0
true_cfg_scale: float | None = None
cfg_normalize: bool = False
@dataclass
class DiffusionRequestState:
"""Scheduler-owned state for one queued OmniDiffusionRequest."""
# Unique scheduler-owned request ID.
# NOTE: This identifies one OmniDiffusionRequest, which may contain multiple request_ids.
# TODO: Align this with OmniDiffusionRequest.request_ids once scheduler batching is supported.
sched_req_id: str
req: OmniDiffusionRequest
sampling_params_key: SamplingParamsKey | None = None
status: DiffusionRequestStatus = DiffusionRequestStatus.WAITING
error: str | None = None
def is_finished(self) -> bool:
return DiffusionRequestStatus.is_finished(self.status)
@dataclass
class NewRequestData:
"""Full request payload for a newly scheduled diffusion request."""
sched_req_id: str
req: OmniDiffusionRequest
@classmethod
def from_state(cls, state: DiffusionRequestState) -> NewRequestData:
return cls(sched_req_id=state.sched_req_id, req=state.req)
@dataclass
class CachedRequestData:
"""Cached diffusion requests that only need their scheduler ids resent."""
sched_req_ids: list[str]
@classmethod
def make_empty(cls) -> CachedRequestData:
return cls(sched_req_ids=[])
@dataclass
class DiffusionSchedulerOutput:
"""Output of a single scheduling cycle."""
step_id: int # global step index
scheduled_new_reqs: list[NewRequestData]
scheduled_cached_reqs: CachedRequestData
finished_req_ids: set[str]
num_running_reqs: int
num_waiting_reqs: int
@cached_property
def scheduled_req_ids(self) -> list[str]:
"""
All scheduled request ids in this cycle, including both new and cached ones.
NOTE:
This id is generated and owned by the scheduler,
and may be different from the OmniDiffusionRequest.request_ids.
"""
return [
*(req.sched_req_id for req in self.scheduled_new_reqs),
*self.scheduled_cached_reqs.sched_req_ids,
]
@property
def num_scheduled_reqs(self) -> int:
return len(self.scheduled_req_ids)
@property
def is_empty(self) -> bool:
return self.num_scheduled_reqs == 0
class SchedulerInterface(ABC):
"""Abstract lifecycle contract for diffusion schedulers."""
def _make_sched_req_id(self, request: OmniDiffusionRequest) -> str:
"""
Generate a unique scheduler request ID for the given request.
The default implementation uses the first request_id from the request if available,
otherwise generates a random one.
"""
if request.request_ids:
base = request.request_ids[0]
else:
logger.warning("Request has no request_ids, generating a random one. Request: %s", request)
base = f"req_{uuid.uuid4().hex[:8]}"
sched_req_id = base
suffix = 1
while self.get_request_state(sched_req_id) is not None:
sched_req_id = f"{base}#{suffix}"
suffix += 1
return sched_req_id
@abstractmethod
def initialize(self, od_config: OmniDiffusionConfig) -> None:
"""Initialize or reset scheduler state."""
@abstractmethod
def add_request(self, request: OmniDiffusionRequest) -> str:
"""Add a request and return the scheduler-owned request id."""
@abstractmethod
def schedule(self) -> DiffusionSchedulerOutput:
"""Run one scheduling cycle."""
@abstractmethod
def update_from_output(self, sched_output: DiffusionSchedulerOutput, output: RunnerOutput) -> set[str]:
"""Update scheduler state from executor output."""
@abstractmethod
def get_request_state(self, sched_req_id: str) -> DiffusionRequestState | None:
"""Return request state if present."""
@abstractmethod
def has_requests(self) -> bool:
"""Return whether the scheduler still owns runnable requests."""
@abstractmethod
def get_sched_req_id(self, request_id: str) -> str | None:
"""Resolve a public request_id to the active scheduler request id."""
@abstractmethod
def pop_request_state(self, sched_req_id: str) -> DiffusionRequestState | None:
"""Remove and return request state if present."""
@abstractmethod
def preempt_request(self, sched_req_id: str) -> bool:
"""Preempt a running request back to waiting."""
@abstractmethod
def finish_requests(self, sched_req_ids: str | list[str], status: DiffusionRequestStatus) -> None:
"""Mark one or more requests finished."""
@abstractmethod
def close(self) -> None:
"""Release scheduler-owned state."""