[Core][Frontend] Support request-level batching for diffusion pipelines (#4079)

Signed-off-by: jader <yjader@foxmail.com>
Co-authored-by: Samit <285365963@qq.com>
This commit is contained in:
JiangJie ZhangandSamit authored and GitHub committed 2026-06-29 18:09:19 +02:00
1 parent 504698dec8
commit c45ac74d43
123 files changed
+4160 -2233

No files matched your search

@@ -349,12 +349,14 @@ class YourModelPipeline(nn.Module):
- def __call__(
+ def forward(
self,
+ req: OmniDiffusionRequest, # ← Add request parameter here
+ req: DiffusionRequestBatch, # ← Add request-batch parameter here
- ):
+ ) -> DiffusionOutput: # ← Add return type
+ ) -> list[DiffusionOutput]: # ← Add return type
```
[`OmniDiffusionRequest`](https://docs.vllm.ai/projects/vllm-omni/en/latest/api/vllm_omni/diffusion/request/#vllm_omni.diffusion.request.OmniDiffusionRequest) is a dataclass that contains the **prompts** and **sampling parameters** [`OmniDiffusionSamplingParams`](https://docs.vllm.ai/projects/vllm-omni/en/latest/api/vllm_omni/inputs/data/#vllm_omni.inputs.data.OmniDiffusionSamplingParams) for the diffusion pipeline execution. It also contains a request_id for other components to trace this request and its outputs.
[`OmniDiffusionRequest`](https://docs.vllm.ai/projects/vllm-omni/en/latest/api/vllm_omni/diffusion/request/#vllm_omni.diffusion.request.OmniDiffusionRequest) is a dataclass that contains one **prompt** and the **sampling parameters** [`OmniDiffusionSamplingParams`](https://docs.vllm.ai/projects/vllm-omni/en/latest/api/vllm_omni/inputs/data/#vllm_omni.inputs.data.OmniDiffusionSamplingParams) for one logical diffusion request. It also contains a request_id for other components to trace this request and its outputs. Before pipeline execution, the runner wraps one or more independent requests into `DiffusionRequestBatch`.
[`DiffusionRequestBatch`](https://docs.vllm.ai/projects/vllm-omni/en/latest/api/vllm_omni/diffusion/worker/request_batch/#vllm_omni.diffusion.worker.request_batch.DiffusionRequestBatch) exposes compatibility properties such as `prompts`, `sampling_params`, and `request_id`. Pipelines that can execute the whole request batch in one forward pass should set `supports_request_batch = True`; other pipelines still receive a single-request batch and return a one-element output list.
See some parameters in `OmniDiffusionSamplingParams` as follows:
@@ -367,19 +369,18 @@ See some parameters in `OmniDiffusionSamplingParams` as follows:
**Extract parameters from request:**
```python
from vllm_omni.diffusion.request import OmniDiffusionRequest
from vllm_omni.diffusion.data import DiffusionOutput
from vllm_omni.diffusion.worker.request_batch import DiffusionRequestBatch
def forward(
self,
req: OmniDiffusionRequest,
) -> DiffusionOutput:
# Extract prompts from request
if req.prompts is not None:
prompt = [
p if isinstance(p, str) else (p.get("prompt") or "")
for p in req.prompts
]
req: DiffusionRequestBatch,
) -> list[DiffusionOutput]:
# Extract prompts from the request batch
prompts = [
p if isinstance(p, str) else (p.get("prompt") or "")
for p in req.prompts
]
# Extract sampling parameters
sampling_params = req.sampling_params
@@ -388,14 +389,16 @@ def forward(
height = sampling_params.height or (self.default_sample_size * self.vae_scale_factor)
width = sampling_params.width or (self.default_sample_size * self.vae_scale_factor)
# For image editing pipelines, extract images from multi_modal_data
if hasattr(req, 'multi_modal_data') and req.multi_modal_data:
input_images = req.multi_modal_data.get('image', [])
# For image editing pipelines, extract media from each prompt dict
input_images = []
for p in req.prompts:
multi_modal_data = p.get("multi_modal_data", {}) if isinstance(p, dict) else {}
input_images.append(multi_modal_data.get("image"))
# ... rest of generation logic
```
For an image editing model, an example `OmniDiffusionRequest` is like:
For an image editing model, the request `prompt` can be a dict like:
```python
{
"prompt": "turn this cat to a dog",
@@ -472,12 +475,12 @@ def get_your_model_pre_process_func(
def pre_process_func(
request: OmniDiffusionRequest,
):
for i, prompt in enumerate(request.prompts):
multi_modal_data = prompt.get("multi_modal_data", {}) if not isinstance(prompt, str) else None
raw_image = multi_modal_data.get("image", None) if multi_modal_data is not None else None
# image pre-processing
# after pre-processing, update the request attributes
...
prompt = request.prompt
multi_modal_data = prompt.get("multi_modal_data", {}) if not isinstance(prompt, str) else None
raw_image = multi_modal_data.get("image", None) if multi_modal_data is not None else None
# image pre-processing
# after pre-processing, update the request attributes
...
return request
return pre_process_func
@@ -923,11 +926,11 @@ When implementing a new pipeline, avoid putting all logic inside a single functi
For example:
```
def forward(self, req: OmniDiffusionRequest):
def forward(self, req: DiffusionRequestBatch) -> list[DiffusionOutput]:
prompt_embeds = self.encode_prompt(req)
latents = self.diffuse(prompt_embeds, req)
images = self.vae.decode(latents)
return DiffusionOutput(output=images)
return [DiffusionOutput(output=images)]
```
This allows the timing utility to measure each stage (e.g., encode_prompt, diffuse, vae.decode) separately and helps identify performance bottlenecks more easily.