Repository navigation
[Model] Add HiDream-O1-Image support (text-to-image, editing, personalization, layout-bbox) - #5306
SOKOUDJOU-LEOPOLD wants to merge 19 commits into
Conversation
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
There was a problem hiding this comment.
Pull request overview
Adds first-class vLLM-Omni diffusion support for HiDream-O1-Image (Qwen3-VL–based Pixel-level Unified Transformer) including text-to-image, reference-image editing/personalization, and layout-bbox conditioning, plus integration into registry/docs/CI and a test+example suite.
Changes:
- Introduces a new HiDream-O1-Image model implementation (UiT backbone + diffusion pipeline) and registers it in the diffusion registry/post-processing map.
- Adds utilities for packed attention metadata, MRoPE position id construction, patchify/depatchify, and layout-bbox rendering.
- Adds unit + E2E tests, Buildkite coverage, docs updates, and offline/online runnable examples + a serving recipe.
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| vllm_omni/diffusion/registry.py | Registers HiDreamO1ImagePipeline and its post-process function. |
| vllm_omni/diffusion/models/hidream_o1_image/utils_hidream_o1.py | Adds core tensor/PIL utilities (masking, MRoPE indices, patch ops, layout-bbox helpers). |
| vllm_omni/diffusion/models/hidream_o1_image/qwen3_vl_uit_transformer.py | Implements the Qwen3-VL–based UiT backbone with vLLM-Omni attention + TP-capable linears. |
| vllm_omni/diffusion/models/hidream_o1_image/pipeline_hidream_o1_image.py | Implements denoising pipeline for T2I/editing/personalization/layout-bbox + weight loading and caching. |
| vllm_omni/diffusion/models/hidream_o1_image/init.py | Exposes lazy imports for pipeline and post-processing function. |
| tests/e2e/online_serving/test_hidream_o1_image.py | Adds L2/L3 online-serving smoke tests for T2I, editing, and multi-ref. |
| tests/e2e/online_serving/test_hidream_o1_image_expansion.py | Adds L4 nightly expansion matrix (cache_dit/tp/sp/cfg-parallel/hsdp/offload). |
| tests/e2e/offline_inference/test_hidream_o1_image.py | Adds offline inference smoke test. |
| tests/diffusion/models/hidream_o1_image/test_packed_sequence.py | Unit tests for packed sequence masking, MRoPE, patchify/depatchify, ref preprocessing. |
| tests/diffusion/models/hidream_o1_image/test_layout_bbox_utils.py | Unit tests for bbox parsing/rendering and ref/layout image generation. |
| tests/diffusion/models/hidream_o1_image/test_embed_cache.py | Unit tests for vision-embed caching contract. |
| tests/diffusion/models/hidream_o1_image/init.py | Package marker for hidream_o1_image diffusion tests. |
| recipes/HiDream/HiDream-O1-Image.md | Adds a full serving recipe and profiling methodology placeholders. |
| examples/online_serving/hidream_o1_image/run_server.sh | Adds a convenience script to start the server. |
| examples/online_serving/hidream_o1_image/run_curl_hidream_o1.sh | Adds a curl example for /v1/images/generations. |
| examples/online_serving/hidream_o1_image/README.md | Documents online-serving usage for HiDream-O1-Image. |
| examples/online_serving/hidream_o1_image/openai_client_hidream_o1.py | Adds a simple OpenAI-compatible Python client example. |
| examples/offline_inference/hidream_o1_image/text_to_image_hidream_o1.py | Adds an offline T2I driver with full/dev presets. |
| examples/offline_inference/hidream_o1_image/README.md | Documents offline scripts and arguments for T2I/edit/layout control. |
| examples/offline_inference/hidream_o1_image/layout_control_hidream_o1.py | Adds an offline layout-bbox controlled generation script. |
| examples/offline_inference/hidream_o1_image/image_edit_hidream_o1.py | Adds an offline editing/multi-ref personalization script. |
| docs/user_guide/diffusion/cpu_offload_diffusion.md | Documents CPU/layerwise offload support for the new pipeline. |
| docs/user_guide/diffusion_features.md | Updates diffusion feature support matrix to include HiDream-O1-Image. |
| docs/models/supported_models.md | Adds HiDream-O1-Image to supported models list. |
| .buildkite/cuda/test-ready.yml | Adds L2 test-ready step for HiDream-O1-Image serving. |
| .buildkite/cuda/test-nightly.yml | Adds nightly expansion test entry for HiDream-O1-Image. |
| .buildkite/cuda/test-merge.yml | Adds L3 test-merge step for HiDream-O1-Image serving. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| is_gen = token_types > 0 | ||
| for b in range(batch_size): | ||
| dense_mask[b, is_gen[b]] = True # gen rows attend to everything (incl. future gen positions) | ||
| row = is_gen[b] | ||
| idx = torch.nonzero(row, as_tuple=False).squeeze(-1) |
There was a problem hiding this comment.
Invalid.
It would add a redundant operation (dense_mask[b, ~gen] &= (~gen).unsqueeze(0)) that zeroes bits already guaranteed to be zero by construction. The docstring phrase "causal-only-over-text" is accurate as is, text tokens already only see text columns because the layout puts all gen tokens at the tail of every sequence.
| """HiDream-O1-Image pipeline — Phase 3 (text-to-image + image editing + | ||
| multi-reference personalization + layout-bbox conditioning, single GPU, | ||
| no Cache-DiT / TP / SP yet). | ||
|
|
There was a problem hiding this comment.
Valid.
The docstring was written at the end of Phase 3 and still says "single GPU, no Cache-DiT / TP / SP yet", but Phases 5–8 added all of those. It's now stale and misleading.
| ref_pixel_grid_thw = torch.tensor( | ||
| [[1, pil.size[1] // PATCH_SIZE, pil.size[0] // PATCH_SIZE] | ||
| for pil, n in zip(ref_pils, ref_image_lens) | ||
| for _ in [resize_pilimage(pil, ref_max)]], | ||
| dtype=torch.int64, | ||
| device=self.device, | ||
| ) | ||
| # Build per-ref pixel grid from actual resized dimensions. |
There was a problem hiding this comment.
Valid.
- Lines 388–394 build ref_pixel_grid_thw but with a subtle bug: it calls resize_pilimage(pil, ref_max) inside the comprehension expression (for _ in [resize_pilimage(...)]) but then reads pil.size (the original unresized dimensions), not the resized ones. This produces wrong grid values AND does a wasted resize call.
- Lines 396–401 immediately overwrite ref_pixel_grid_thw correctly: it resizes, captures pil_r.size, and uses rw, rh.
| "--model-type", | ||
| choices=["full", "dev"], | ||
| default="full", | ||
| help="full: 50 steps, guidance 5.0, shift 3.0. dev: 28 steps, guidance 0.0 (no CFG), shift 1.0.", |
| raw_layout = mm_data.get("layout_bboxes") | ||
| if raw_layout is not None and ref_pils is not None: | ||
| layout_data = load_layout_bboxes(raw_layout) if isinstance(raw_layout, str) else raw_layout | ||
| ref_pils = create_layout_reference_images(ref_pils, layout_data, width, height) |
There was a problem hiding this comment.
Valid.
load_layout_bboxes calls os.path.exists() on any user-supplied string in online serving a remote client controls multi_modal_data["layout_bboxes"], so they can probe server-local file existence and read arbitrary JSON files from disk.
To fix:
- use json.loads() directly, only accept inline JSON, never read files from user data.
- preload the file with load_layout_bboxes() before putting data in multi_modal_data, since that's operator-controlled code anyway.
Starting work on HiDream-O1-Image. This is the first piece: plain text-to-image generation, nothing else yet. This model works differently from everything else already in the repo. Instead of a VAE plus a separate transformer, it's one Qwen3-VL-based transformer that does the diffusion denoising directly on raw pixels. Text and image tokens get packed into a single sequence, and the tricky part is that attention has to be causal for the text but bidirectional for the image/timestep tokens, all within the same forward pass. I tested this for real on GPU against the actual HiDream-O1-Image-Dev checkpoint, not just unit tests: weight loading, the attention masking, the position ids, and the denoising loop (with and without classifier-free guidance) all produce correct images when run through the normal Omni() serving path. Editing, personalization, layout/skeleton conditioning, caching and multi-GPU support are not in this commit, those are next. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
…sonalization Extends the Phase 1 packed-sequence machinery with dual-pathway reference image conditioning for instruction-based editing (1 ref) and subject-driven personalization (N refs): - utils_hidream_o1: add adaptive_ref_max_size() and preprocess_ref_patches() for pixel-patch pathway preprocessing - qwen3_vl_uit_transformer: add _deepstack_process() static method to Qwen3VLTextModel; fix get_image_features() to extract from BaseModelOutputWithDeepstackFeatures; fix forward_generation() bug (removed erroneous torch.cat on already-merged image_embeds tensor) - pipeline_hidream_o1_image: add SupportImageInput protocol, _build_edit_sample() for dual-pathway conditioning (VLM semantic path + pixel-patch path), embed caching via mutable dict side-channel (_embed_storage) to call the vision tower exactly once per request, ref_patches concatenation in diffuse() - test_packed_sequence: add Phase 2 test classes (TestAdaptiveRefMaxSize, TestPreprocessRefPatches, TestEditSampleTokenTypes) - test_embed_cache: new file verifying vision tower is called exactly once regardless of num_inference_steps or CFG - image_edit_hidream_o1.py: example script for editing (1 ref) and personalization (2+ refs) All 23 CPU unit tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
Adds spatially-grounded layout conditioning via bounding boxes. The layout pipeline reuses Phase 2's ref-image path unchanged: bboxes are rendered into a synthetic layout image (black background, colored box outlines), and each reference image gets a matching-colored border. The augmented ref list is passed to _build_edit_sample() as regular token_type=2 conditioning tokens. - utils_hidream_o1: add load_layout_bboxes, parse_layout_bboxes, draw_bbox_layout, add_outer_border_keep_size, create_layout_reference_images and their helpers; accepts xxyy relative/percentage coords, three input formats (list, list-of-dicts, dict wrapper), auto-loads JSON strings/files - pipeline_hidream_o1_image: forward() extracts layout_bboxes from multi_modal_data and calls create_layout_reference_images before diffuse(); no changes needed to _build_edit_sample() or diffuse() - test_layout_bbox_utils: 24 CPU-only tests covering all new functions - layout_control_hidream_o1.py: example script with --layout-bboxes argument All 47 CPU unit tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
…sts, CI wiring - docs/models/supported_models.md: add HiDreamO1ImagePipeline row - recipes/HiDream/HiDream-O1-Image.md: new recipe with Dev/Full variants, offline t2i/editing/layout commands, online serving verification - examples/offline_inference/hidream_o1_image/README.md: updated to Phase 3, scripts table, editing/personalization/layout-bbox sections - examples/online_serving/hidream_o1_image/: run_server.sh, run_curl, Python client, README - tests/e2e/online_serving/test_hidream_o1_image.py: L2+L3 online tests (t2i baseline dual-marked core+advanced; editing/personalization advanced) - tests/e2e/offline_inference/test_hidream_o1_image.py: L3 offline smoke - tests/e2e/online_serving/test_hidream_o1_image_expansion.py: L4 skeleton - .buildkite/test-ready.yml: L2 step (core_model, H100) - .buildkite/test-merge.yml: L3 step (advanced_model and cuda, H100) - .buildkite/test-nightly.yml: add expansion file to Single-GPU shard Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
Add Cache-DiT support to HiDreamO1UiTModel via the standard
_cache_dit_adapter_config / layers-property pattern (no custom
CUSTOM_DIT_ENABLERS entry needed):
- _cache_dit_adapter_config: Pattern_0 blocks, has_separate_cfg=True
(cond/uncond are separate pipeline.forward() calls via CFGParallelMixin),
check_forward_pattern=False (block sig has attn_metadata kwargs)
- layers property: aliases self.language_model.layers so the framework's
direct-attribute block discovery works without nesting
- _layerwise_offload_blocks_attrs: ["language_model.layers"] (also used
by Phase 7 CPU offload)
- _is_transformer_block + _hsdp_shard_conditions: HSDP predicate bundled
here (trivial addition, consumed by Phase 6d)
docs: add HiDream-O1-Image row to diffusion_features.md ImageGen table
(Cache-DiT=✅, all other acceleration features pending later phases)
tests: extend L4 expansion parametrize with cache_dit server variant
Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
Replace plain nn.Linear q/k/v projections and MLP gate/up/down with TP-capable parallel linears (QKVParallelLinear, MergedColumnParallelLinear, RowParallelLinear) inside _build_attention_subclasses, so --tensor-parallel-size ≥ 2 shards correctly across heads and intermediate dimensions. Key changes: - qwen3_vl_uit_transformer.py: _build_attention_subclasses(quant_config) now captures quant_config via closure; Qwen3VLTextAttention replaces q/k/v/o plain linears with qkv_proj (QKVParallelLinear) and o_proj (RowParallelLinear, return_bias=False); new HiDreamO1TextMLP uses gate_up_proj (MergedColumnParallelLinear, return_bias=False) and down_proj (RowParallelLinear, return_bias=False). - pipeline_hidream_o1_image.py: load_weights gains stacked_params_mapping to route checkpoint's split q/k/v and gate/up weights into fused params. - diffusion_features.md: TP column → ✅ for HiDream-O1-Image. - test_hidream_o1_image_expansion.py: adds tp2 L4 row (2× H100, server_args --tensor-parallel-size 2). Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
Add _sp_plan to HiDreamO1UiTModel for hook-based SP: - Split inputs_embeds (dim 1) and position_ids (dim 2) before language_model.forward() so each SP rank processes its sequence shard - Gather final_layer2 output (dim 1) to reconstruct full sequence before returning to the pipeline attn_metadata is intentionally excluded from the plan: Ulysses AllToAll reconstructs the full sequence on each rank before local attention, so full_attn_spans global coordinates and the dense attn_mask remain correct without modification. Known limitation: SP with reference images (pixel_values != None) is not yet supported — deepstack visual_pos_masks would need per-rank coordinate adjustment. T2I usage is correct. Also: SP column → ✅ in diffusion_features.md, ulysses2 L4 row added. Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
6c — CFG-Parallel: HiDreamO1ImagePipeline already inherits CFGParallelMixin; CFGParallelMixin.predict_noise_maybe_with_cfg routes cond/uncond branches to separate ranks and all_gathers noise predictions. No code change needed. Note: under CFG-Parallel with reference images the uncond rank re-runs the vision tower each step (no cross-rank embed cache); functionally correct, a future optimisation could broadcast the precomputed embeds. 6d — HSDP: _hsdp_shard_conditions was already added in Phase 5 via _is_transformer_block; each Qwen3VLTextDecoderLayer is a shard unit. Both columns → ✅ in diffusion_features.md. L4 rows: cfg_parallel2 (2× H100, --cfg-parallel-size 2) and hsdp (1× H100, --use-hsdp). Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
Add SupportsComponentDiscovery to HiDreamO1ImagePipeline so the CPU-offload
framework can discover which submodules to offload:
_dit_modules = ["transformer"]
_encoder_modules = ["transformer.visual"] # nested dotted path, safe
_vae_modules = [] # no VAE; empty list is safe
_resident_modules = ["transformer.x_embedder",
"transformer.t_embedder1",
"transformer.final_layer2"]
Two novel cases verified against module_collector.py:
- _vae_modules=[]: offloader iterates `for vae in modules.vaes:` — zero
iterations over an empty list, no error.
- _encoder_modules=["transformer.visual"]: resolved via operator.attrgetter
which supports dotted paths per the SupportsComponentDiscovery docstring;
no special framework support needed.
Both --enable-cpu-offload (sequential) and --enable-layerwise-offload
(layerwise, using the existing _layerwise_offload_blocks_attrs on
HiDreamO1UiTModel) are now active.
Other changes:
- CPU Offload column → ✅ in diffusion_features.md
- HiDreamO1ImagePipeline row added to cpu_offload_diffusion.md
- cpu_offload and layerwise_offload L4 expansion rows added
Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
Add DiffusionPipelineProfilerMixin to HiDreamO1ImagePipeline: - Import and add to class MRO - Custom _PROFILER_TARGETS for the no-VAE architecture: transformer.get_image_features (one-time vision-tower cost), transformer.forward_generation (per-step cost), transformer.x_embedder.forward (patch embedding) - setup_diffusion_pipeline_profiler() wired at end of __init__, enabled via --enable-diffusion-pipeline-profiler flag recipes/HiDream/HiDream-O1-Image.md: add Performance section documenting the two-bucket cost model (one-time setup vs per-step forward_generation), benchmarking configs (baseline / Cache-DiT / TP-2 / Ulysses-2 / combined), profiling commands, and an empty benchmark table to fill from H100 runs. Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
…nings griffe (used by mkdocstrings) treats 'param_a / param_b:' as a single parameter name, emitting 4 warnings that fail the strict mkdocs build. Split into individual Args entries matching the actual function signature. Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
16d38ff to
391d004
Compare
Upstream main replaced the verbose kubernetes pod-spec for Qwen Image Edit with the shorter mirror_hardwares: h100_1. Align our new test blocks (HiDream-O1 Image, Longcat Image Edit, Longcat Image Test) with the same pattern to resolve the merge conflict. Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
Signed-off-by: SOKOUDJOU Leopold <109520753+SOKOUDJOU-LEOPOLD@users.noreply.github.com>
Remove stale 'single GPU, no Cache-DiT / TP / SP yet' claim written at Phase 3. The pipeline now supports Cache-DiT, TP, SP (Ulysses), CFG-Parallel, HSDP, and CPU offload added in Phases 5-8. Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
…d_edit_sample The first tensor construction used pre-resize pil.size dimensions and was immediately overwritten by the correct second construction that uses the actual resized pil_r.size dimensions. Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
The actual preset uses guidance_scale=1.0 (mathematically no-CFG); the help string incorrectly said 0.0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
…online serving In online serving, multi_modal_data is user-controlled. Using load_layout_bboxes() in the pipeline allowed remote clients to probe os.path.exists() and read arbitrary JSON files from the server filesystem. Fix: pipeline now uses json.loads() directly (inline JSON only). The offline script pre-loads file-path layouts via load_layout_bboxes() before passing parsed data into multi_modal_data. Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com>
a08c4af to
566ebad
Compare
ruff check: remove unused variables (n_refs, z, sigma, original_color), rename uppercase arg K→k in adaptive_ref_max_size, remove unused imports (json, math, os, tempfile, MagicMock/patch/call from unittest.mock), fix import ordering and UP037/UP035 style issues. ruff format: reformat 9 files to match project style (line breaks, trailing commas, slice spacing). Signed-off-by: Leopold Sokoudjou <leopoldgatsing@gmail.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Hi @hsliuustc0106 , Please can you review my PR? |
hsliuustc0106
left a comment
There was a problem hiding this comment.
Joint review with #5194. These are comment-only findings; no approval or change-request verdict.
| echo "Model: $MODEL" | ||
| echo "Port: $PORT" | ||
|
|
||
| vllm serve "$MODEL" --omni \ |
There was a problem hiding this comment.
[P1] Add automatic model-class resolution before relying on this command. The official checkpoint config declares Qwen3VLForConditionalGeneration, but the resolver returns that unregistered architecture while the registry contains HiDreamO1ImagePipeline, so vllm serve "$MODEL" --omni will fail unless the caller supplies --model-class-name. Please add the HiDream signature mapping to both resolver/enrichment paths; carrying #5194's diffusion detector alone is not sufficient because detection does not select the pipeline class.
| # Limitation: the reference-image path (pixel_values != None) uses | ||
| # visual_pos_masks for deepstack indexing into the sequence. Those | ||
| # positions may span multiple SP ranks, making gather/scatter logic | ||
| # non-trivial. For now SP is supported for text-to-image only; using |
There was a problem hiding this comment.
[P1] Guard this combination before advertising sequence parallelism for the full pipeline. With reference images, visual_pos_masks remains full-length while _sp_plan splits only inputs_embeds and position_ids; _deepstack_process() then indexes local hidden states with the full mask. Edit/personalization/layout with --ulysses-degree > 1 can therefore shape-error or produce incorrect deepstack embeddings. Please either implement mask-aware split/gather or reject this mode explicitly and add a reference-image SP test.
| -d '{ | ||
| "prompt": "A golden retriever running through a field of sunflowers at sunset", | ||
| "size": "1024x1024", | ||
| "num_inference_steps": 28, |
There was a problem hiding this comment.
[P1] Apply the documented Dev sampling defaults to this online path. The server defaults to the Dev variant and this request sends 28 steps, but omits guidance and shift; the pipeline consequently substitutes guidance 5.0 and shift 3.0, enabling CFG and using the full-model shift instead of the Dev preset (1.0/1.0). Please make defaults model-variant-aware or include and enforce the Dev parameters in the server example.
| "sigma": sigma, | ||
| "vinputs": neg_vinputs, | ||
| # CFG uncond branch reuses the same cached embeds (same refs) | ||
| "precomputed_image_embeds": cached_img_emb, |
There was a problem hiding this comment.
[P2] Refresh the vision-embedding cache before constructing the negative CFG branch. cached_img_emb and cached_ds_emb are captured before the positive branch populates embed_storage; the negative branch still receives those stale None values and _embed_storage=None, so reference-image CFG recomputes the vision tower on the first denoise step. Please refresh the cached values after the positive call and add an actual pipeline call-count test.
Omni ReviewBot: no human activity for 29 days@SOKOUDJOU-LEOPOLD this pull request has had no human commit, comment or review since 2026-08-10. Per repository policy it may be closed if it stays inactive. To keep it moving, any one of these is enough: push an update, reply to the open blocker, or post the current plan and timeline. |
Omni ReviewBot: no human activity for 44 days@SOKOUDJOU-LEOPOLD this pull request has had no human commit, comment or review since 2026-08-10. Please consider closing this PR if the work is no longer planned. The author or a maintainer decides whether to change the PR state. To keep it moving, any one of these is enough: push an update, reply to the open blocker, or post the current plan and timeline. |
Omni ReviewBot routing recordAssigned Strict on zcode (GLM-5.3-Flash) under experiment |
Omni ReviewBot attempt recordReview attempt ended as timeout (Strict attempt outlived its budget; falling back to direct/cursor/auto). |
vllm-omni-review-bot
left a comment
There was a problem hiding this comment.
Omni ReviewBot review
Changes since the previous review
- 4 new inline finding(s); 0 finding(s) below.
CI at
43db9cd47b29(2026-10-09T16:29:35.281736+00:00): required check(s) blocking:buildkite/vllm-omni(missing).
Note: The assigned review arm
strict/zcode/GLM-5.3-Flashcould not complete this review, so it was produced by the fallback armdirect/cursor/auto. It is excluded from the routing experiment.
Full review analysis
PR description
This PR adds HiDream-O1-Image (HiDreamO1ImagePipeline / HiDreamO1UiTModel): a Qwen3-VL pixel-space diffusion pipeline with no VAE, covering text-to-image, reference editing, multi-reference personalization, and layout-bbox conditioning. Denoising patches 32×32 pixels, swaps a <|tms_token|> embedding for the timestep, and registers a [-1, 1] to RGB post-process. Docs, examples, and Buildkite steps advertise Cache-DiT, TP, Ulysses, CFG-parallel, HSDP, and CPU/layerwise offload. The online serve script still does not select HiDreamO1ImagePipeline, and edit-plus-sequence-parallel plus the Dev sampling defaults are not actually enforced.
Change flow
flowchart LR
req["[EXISTING] OmniDiffusionRequest"]:::existing
reg["[CHANGED] diffusion registry"]:::changed
pipe["[NEW] HiDreamO1ImagePipeline"]:::new
uit["[NEW] HiDreamO1UiTModel"]:::new
rgb["[NEW] patch RGB post-process"]:::new
req --> reg --> pipe --> uit --> rgb
classDef existing fill:#e5e7eb,stroke:#6b7280,color:#111827
classDef changed fill:#fef3c7,stroke:#d97706,color:#451a03,stroke-width:2px
classDef new fill:#dcfce7,stroke:#16a34a,color:#052e16,stroke-width:2px
classDef removed fill:#fee2e2,stroke:#dc2626,color:#450a0a,stroke-width:2px
See inline comments below.
🤖 This review was generated by InferMatrix Copilot, an open-source repo-maintenance agent for PR review, CI debugging and issue triage. Try it on your own repo, and ⭐ star it if it helped!
| "attn_metadata": attn_metadata, | ||
| "vinput_mask": vinput_mask, | ||
| "pixel_values": pixel_values_vlm, | ||
| "image_grid_thw": image_grid_thw_all, |
There was a problem hiding this comment.
[P1] Edit vision tower receives target and ref grids
Evidence and suggested fix
_build_edit_sample returns pixel_values from the VLM processor (K reference images) but sets image_grid_thw to image_grid_thw_all, which concatenates those VLM grids with the target patch grid and the pixel-ref grids. forward_generation passes that tensor straight into get_image_features. Qwen3-VL splits pixel_values by the product of grid_thw, so the extra rows make the vision-tower length disagree with the image-token mask and masked_scatter. Every edit, personalization, and layout request can throw or scatter the wrong embeddings. Pass only image_grid_thw_vlm into the vision tower; keep the concatenated grid for get_rope_index_fix_point.
| tests/e2e/online_serving/test_krea2_expansion.py | ||
| tests/e2e/offline_inference/test_krea2_expansion.py | ||
| tests/e2e/offline_inference/test_sensenova_u1_expansion.py | ||
| tests/e2e/online_serving/test_hidream_o1_image_expansion.py |
There was a problem hiding this comment.
[P1] Two-GPU HiDream rows are only on the 1-GPU nightly shard
Evidence and suggested fix
test_hidream_o1_image_expansion.py is added only to the H100 single-GPU step (.buildkite/cuda/test-nightly.yml around line 213), whose command is -m "full_model and diffusion and H100 and not distributed_cuda" and mirror_hardwares: h100_1. The tp2, ulysses2, and cfg_parallel2 params request 2 GPUs. They are either deselected by not distributed_cuda or executed on one GPU. No h100_2 step lists this file (the multi-GPU steps cover Qwen-Image, layered, and Bagel only). The PR test plan's 2×H100 rows are not what nightly runs. Put those params on a distributed_cuda / h100_2 step.
| - pytest -s -v tests/e2e/online_serving/test_hidream_o1_image.py -m 'advanced_model and cuda' --run-level 'advanced_model' | ||
| mirror_hardwares: h100_1 | ||
|
|
||
| - label: "Diffusion · Longcat Image Edit Test" |
There was a problem hiding this comment.
[P2] Merge CI adds unrelated Longcat and SenseNova jobs
Evidence and suggested fix
The HiDream test-merge hunk also inserts "Diffusion · Longcat Image Edit Test" and "Diffusion · Longcat Image Test", whose sources are tests/e2e/online_serving/test_longcat_image*.py and vllm_omni/diffusion/models/longcat_image/. test-nightly.yml line 228 likewise adds tests/e2e/offline_inference/test_sensenova_u1_expansion.py. Neither model is part of HiDream-O1. Those jobs add H100 time and can fail this PR for unrelated tests. Drop them from this diff.
| self.cond_deepstack_image_embeds = cond_deepstack_image_embeds | ||
|
|
||
|
|
||
| class HiDreamO1UiTModel(nn.Module): |
There was a problem hiding this comment.
[P2] Non-test diff exceeds the size budget
Evidence and suggested fix
The supplied diff size is 2592 non-test added lines against a budget of 1000 (14 counted files, 13 exempt). The largest contributors are qwen3_vl_uit_transformer.py (694), pipeline_hidream_o1_image.py (679), and utils_hidream_o1.py (592). The PR body describes the new model and deferred pixel/benchmark follow-ups (#5304, #5305) but does not give a split plan or a concrete size exemption. Split the backbone, pipeline, and examples/CI into reviewable PRs, or state an explicit exemption for this new-model drop. Size is not itself a correctness defect.
Omni ReviewBot: finding feedback[p1] Reference-image sequence parallel is not rejected — See the review for details. If you are the PR author and disagree, react 👎 here; the maintainer will see your disagreement. |
Omni ReviewBot: finding feedback[p1] Two-GPU HiDream rows are only on the 1-GPU nightly shard — See the review for details. If you are the PR author and disagree, react 👎 here; the maintainer will see your disagreement. |
Omni ReviewBot: finding feedback[p1] Dev examples and nightly tests use full CFG defaults — See the review for details. If you are the PR author and disagree, react 👎 here; the maintainer will see your disagreement. |
Omni ReviewBot: finding feedback[p1] Online serve does not select the pipeline class — See the review for details. If you are the PR author and disagree, react 👎 here; the maintainer will see your disagreement. |
Omni ReviewBot: finding feedback[p1] CFG negative branch sees a stale vision cache — See the review for details. If you are the PR author and disagree, react 👎 here; the maintainer will see your disagreement. |
Omni ReviewBot: finding feedback[p1] Edit vision tower receives target and ref grids — See the review for details. If you are the PR author and disagree, react 👎 here; the maintainer will see your disagreement. |
|
@SOKOUDJOU-LEOPOLD Are you planning to continue work on this PR? Please reply by 2026-10-19 10:37:13 UTC. A yes or a brief plan is enough; code changes do not need to be finished within these three days. If you are waiting on a review or other maintainer action, please let us know. If there is no reply, new commit, or maintainer takeover by then, we will recheck and close this PR as inactive. It can be reopened when work resumes. |
|
Closing as inactive after the 3-day deadline with no author reply. Feel free to reopen anytime. 3 天期限已过且作者未回复,先关闭;随时可以 reopen。 |
Purpose
This PR adds support for HiDream-O1-Image (
HiDream-ai/HiDream-O1-ImageandHiDream-ai/HiDream-O1-Image-Dev), a ~8B-parameter image generation model from HiDream.ai. It supports text-to-image, instruction-based image editing, multi-reference subject personalization, and layout-bbox spatial conditioning — all in a single model with no VAE and no separate text encoder.Why
HiDream-O1-Image has an architecture that nothing in vLLM-Omni currently handles:
<|tms_token|>placeholder has its embedding swapped for the sinusoidal timestep embedding at every step.AttentionMetadata+full_attn_spans.fix_pointanchoring. Customget_rope_index_fix_pointpins reference image positions at a fixed offset to avoid RoPE extrapolation.What
New files —
vllm_omni/diffusion/models/hidream_o1_image/(4 files):qwen3_vl_uit_transformer.py—HiDreamO1UiTModelbackbone. Lazy-imports HF Qwen3-VL (gated ontransformers >= 4.57.1). Overrides attention/MLP projections with TP-capable parallel linears via_build_attention_subclasses(quant_config)closure. Carries_cache_dit_adapter_config,_sp_plan,_hsdp_shard_conditions,_layerwise_offload_blocks_attrs.pipeline_hidream_o1_image.py—HiDreamO1ImagePipelinedenoise loop. Handles T2I, editing, personalization, layout-bbox. ImplementsSupportsComponentDiscovery(empty_vae_modules, nested_encoder_modules = ["transformer.visual"]),DiffusionPipelineProfilerMixin,CFGParallelMixin.utils_hidream_o1.py— pure utilities:build_packed_attention_metadata(builds bothfull_attn_spansand denseattn_mask),get_rope_index_fix_point,patchify/depatchify, layout-bbox rendering.__init__.py— module exports.Parallelism — all wired in the model files above:
QKVParallelLinear(fused Q/K/V),MergedColumnParallelLinear(gate+up),RowParallelLinear(o_proj, down_proj).stacked_params_mappinginload_weights()remaps split checkpoint weights into fused params._sp_plansplitsinputs_embeds/position_idsbefore decoder stack, gathersfinal_layer2after.attn_metadataexcluded — Ulysses AllToAll restores full sequence before attention. T2I only; editing path documented as known SP limitation.CFGParallelMixin._hsdp_shard_conditions = [_is_transformer_block]— eachQwen3VLTextDecoderLayeris a shard unit._cache_dit_adapter_configwithForwardPattern.Pattern_0,has_separate_cfg=True,check_forward_pattern=False. Addedlayersproperty alias onHiDreamO1UiTModelpointing tolanguage_model.layersfor cache-dit auto-detection.SupportsComponentDiscovery._vae_modules = [](no VAE)._encoder_modules = ["transformer.visual"]— first nested encoder path in the codebase, resolved viaoperator.attrgetter._layerwise_offload_blocks_attrs = ["language_model.layers"]Integration:
vllm_omni/diffusion/registry.py— registered withget_hidream_o1_image_post_process_func([-1, 1]→ uint8, no VAE decode).docs/models/supported_models.md,docs/user_guide/diffusion_features.md,docs/user_guide/diffusion/cpu_offload_diffusion.md— rows added..buildkite/cuda/test-ready.yml,test-merge.yml,test-nightly.yml— CI entries added.examples/offline_inference/hidream_o1_image/,examples/online_serving/hidream_o1_image/— runnable examples with READMEs.recipes/HiDream/HiDream-O1-Image.md— full serving recipe with two-bucket performance methodology.Test Plan
Unit tests (47 tests, CPU-only,
pytest -m core_model):tests/diffusion/models/hidream_o1_image/test_packed_sequence.pybuild_packed_attention_metadatafor T2I, editing, multi-ref, layout-bbox. Verifiesfull_attn_spanscoordinates,attn_mask, token types, causal vs bidirectional pattern.tests/diffusion/models/hidream_o1_image/test_embed_cache.pytests/diffusion/models/hidream_o1_image/test_layout_bbox_utils.pyE2E offline (
tests/e2e/offline_inference/test_hidream_o1_image.py): full pipeline forward pass, output shape and pixel range verified.E2E online (
tests/e2e/online_serving/test_hidream_o1_image.py): T2I (Dev + Full variants), image editing, multi-reference personalization, layout-bbox.Nightly expansion (
tests/e2e/online_serving/test_hidream_o1_image_expansion.py): 8 parametrized server configs:defaultcache_dit--cache-backend cache_dittp2--tensor-parallel-size 2ulysses2--ulysses-degree 2cfg_parallel2--cfg-parallel-size 2hsdp--use-hsdpcpu_offload--enable-cpu-offloadlayerwise_offload--enable-layerwise-offloadvLLM Version: 0.23.0
vLLM-Omni Commit: 1ff2f22
Test Result
Unit tests (47): all passed on CPU.
E2E and expansion tests require H100 hardware, tracked through CI (test-merge and test-nightly pipelines).
Pixel-level output comparison against the HiDream reference repo is explicitly deferred and tracked in #5304 .
H100 benchmark numbers are explicitly deferred and tracked in #5305 .