[Feature] Add HunyuanImage3 DiT grouped step batching (#4041)

Signed-off-by: TaffyOfficial <2324465096@qq.com>
Signed-off-by: zuiho <2324465096@qq.com>
Co-authored-by: TaffyOfficial <2324465096@qq.com>
Co-authored-by: Gao Han <hgaoaf@connect.ust.hk>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
This commit is contained in:
authored and GitHub committed 2026-06-15 20:36:41 +08:00
1 parent 11829ccd75
commit ae937320ff
7 files changed
+1337 -76

No files matched your search

File diff suppressed because it is too large. Load diff
+3 -2
View File
@@ -16,6 +16,7 @@ if TYPE_CHECKING:
import torch
from vllm_omni.diffusion.data import DiffusionOutput
from vllm_omni.diffusion.worker.input_batch import InputBatch
from vllm_omni.diffusion.worker.utils import DiffusionRequestState
@@ -56,8 +57,8 @@ class SupportsStepExecution(Protocol):
def prepare_encode(self, state: DiffusionRequestState, **kwargs: Any) -> DiffusionRequestState:
"""Prepare request-level inputs and return initialized state."""
def denoise_step(self, state: DiffusionRequestState, **kwargs: Any) -> torch.Tensor | None:
"""Run one denoise step."""
def denoise_step(self, input_batch: InputBatch, **kwargs: Any) -> torch.Tensor | None:
"""Run one denoise forward on the runner-assembled batch."""
def step_scheduler(self, state: DiffusionRequestState, noise_pred: torch.Tensor, **kwargs: Any) -> None:
"""Run one scheduler step."""
@@ -374,6 +374,13 @@ class DiffusionModelRunner(OmniConnectorModelRunnerMixin):
sampling=copy.deepcopy(req.sampling_params),
prompts=req.prompts,
)
state_req = copy.copy(req)
state_req.sampling_params = new_state.sampling
self.kv_transfer_manager.receive_multi_kv_cache_distributed(
state_req,
cfg_kv_collect_func=getattr(self.od_config, "cfg_kv_collect_func", None),
target_device=getattr(self.pipeline, "device", None),
)
self.state_cache[request_id] = new_state
resolved.append(new_state)
@@ -452,8 +459,8 @@ class DiffusionModelRunner(OmniConnectorModelRunnerMixin):
if not self.supports_step_mode():
raise ValueError("Current pipeline does not support step execution.")
# Stepwise mode only supports the basic state-driven denoise path for now.
# Request-mode extras such as cache backends, KV transfer, editing inputs,
# and similar features are not supported here yet.
# Request-mode extras such as cache backends, editing inputs, and
# similar features are not supported here yet.
if self.od_config.cache_backend not in (None, "none"):
raise ValueError("Step mode does not support cache_backend yet.")
+13 -5
View File
@@ -12,7 +12,7 @@ step, and step outputs are scattered back into request states by
from __future__ import annotations
from collections.abc import Sequence
from dataclasses import dataclass
from dataclasses import dataclass, field
import numpy as np
import torch
@@ -496,7 +496,7 @@ def _prepare_prompt_embeds(
*,
embeds_out: torch.Tensor | None = None,
mask_out: torch.Tensor | None = None,
) -> tuple[torch.Tensor, torch.Tensor | None]:
) -> tuple[torch.Tensor | None, torch.Tensor | None]:
prompt_embeds, prompt_embeds_mask = _prepare_padded_prompt_fields(
states,
embeds_attr="prompt_embeds",
@@ -505,8 +505,6 @@ def _prepare_prompt_embeds(
embeds_out=embeds_out,
mask_out=mask_out,
)
if prompt_embeds is None:
raise ValueError("All requests must have `prompt_embeds` initialized.")
return prompt_embeds, prompt_embeds_mask
@@ -581,6 +579,10 @@ class InputBatch:
source of truth. ``InputBatch`` only assembles a contiguous view for the
current step and refreshes dynamic fields in-place when composition is
unchanged.
``states`` is a narrow escape hatch for Hunyuan-style state-driven
pipelines that need request-private KV/cache metadata during denoise.
Other pipelines should continue to use the standard batch fields.
"""
request_ids: list[str]
@@ -591,7 +593,7 @@ class InputBatch:
latents: torch.Tensor
timesteps: torch.Tensor
prompt_embeds: torch.Tensor
prompt_embeds: torch.Tensor | None
prompt_embeds_mask: torch.Tensor | None
negative_prompt_embeds: torch.Tensor | None
negative_prompt_embeds_mask: torch.Tensor | None
@@ -604,6 +606,7 @@ class InputBatch:
img_shapes: list | None = None
txt_seq_lens: list[int] | None = None
negative_txt_seq_lens: list[int] | None = None
states: Sequence[DiffusionRequestState] = field(default_factory=tuple)
def __post_init__(self) -> None:
if len(self.request_ids) != int(self.idx_mapping.numel()):
@@ -648,6 +651,9 @@ class InputBatch:
self,
selected_states: Sequence[DiffusionRequestState],
) -> None:
# Same-composition cache hits reuse static prompt fields; request ids
# must keep the same encoded prompt metadata for the batch lifetime.
self.states = tuple(selected_states)
self._refresh_dynamic_fields(selected_states)
def _rebuild(
@@ -662,6 +668,7 @@ class InputBatch:
self.num_reqs_after_padding = len(request_ids)
self.idx_mapping = idx_mapping
self.idx_mapping_np = idx_mapping_np
self.states = tuple(selected_states)
self.latents = _prepare_latents(selected_states, out=self.latents)
self.timesteps = _prepare_timesteps(selected_states, out=self.timesteps)
self._refresh_static_fields(selected_states)
@@ -718,6 +725,7 @@ class InputBatch:
selected_states,
"negative_txt_seq_lens",
),
states=tuple(selected_states),
)