Compare commits

...
Author SHA1 Message Date
Hongsheng Liu 4241081a67 Merge branch 'main' into docs/add-new-committers 2026-05-20 12:24:22 +08:00
hsliuustc0106andCursor 28773fbd7e Fix cache owner to @SamitHuang in workload division
Correct yongxiang mapping in the diffusion cache ownership list.

Signed-off-by: hsliuustc0106 <liuhongsheng4@huawei.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 02:02:42 +00:00
hsliuustc0106andCursor 38c92ab0fe Fix yongxiang GitHub handle in diffusion workload division
Use @samixxx for cache ownership in the governance page.

Signed-off-by: hsliuustc0106 <liuhongsheng4@huawei.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 01:57:04 +00:00
Y. Fisherandzzh 29179599fc [Feat] Enable VAE parallel in HunyuanImage3 (#3091)
Signed-off-by: KexiongYu <yukexiong1@huawei.com>
Signed-off-by: zzh <943967662@qq.com>
Co-authored-by: zzh <943967662@qq.com>
2026-05-20 09:55:33 +08:00
hsliuustc0106andCursor a9b6f71abf Add diffusion workload division to governance page
Document diffusion area ownership for runtime, optimization, and docs
based on maintainer team alignment. Update committer area descriptions
for @RuixiangMa, @wtomin, @ZJY0516, and @yuanheng-zhao.

Signed-off-by: hsliuustc0106 <liuhongsheng4@huawei.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 01:53:11 +00:00
wangyuandHongsheng Liu 4e82192fd6 [CI] optional --assert-baseline and update perf JSON baselines (#3695)
Signed-off-by: wangyu <410167048@qq.com>
Signed-off-by: wangyu <53896905+yenuo26@users.noreply.github.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-20 09:15:53 +08:00
hsliuustc0106andCursor e140c224b7 Add new committers to governance page
Add @RuixiangMa and @yuanheng-zhao as active committers in the diffusion area.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: hsliuustc0106 <liuhongsheng4@huawei.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 00:19:32 +00:00
WeiQing Chen a3d4ed809d [Quantization][tools] Add diffusion quantization output comparison tool (#3175)
Signed-off-by: david6666666 <530634352@qq.com>
Signed-off-by: David Chen <530634352@qq.com>
2026-05-20 00:14:08 +08:00
dengyunyang 3c58868c9a [BugFix] fix mult cli timeout with get kv (#3741)
Signed-off-by: dengyunyang <584797741@qq.com>
2026-05-19 14:22:27 +00:00
Shin da53618793 [Recipe] Qwen/Qwen-Image-Edit (#3684)
Signed-off-by: yixiaoer <shin@yixiaoer.sg>
2026-05-19 19:56:38 +08:00
Schatten 18186db216 [Cleanup] Remove unused build_base_engine_args after #1115 (#3720)
Signed-off-by: Schatten <czhengt@qq.com>
2026-05-19 19:23:45 +08:00
LuandSYLAR 14e5baceaf [Qwen-Image] Drop unused vision tower from text encoder (#3608)
Signed-off-by: lulugoodcoder <luludachiever@gmail.com>
Co-authored-by: SYLAR <125541396+lishunyang12@users.noreply.github.com>
2026-05-19 19:19:58 +08:00
wangyu 2af2a50e0e [CI] improve Buildkite testcase statistics reports (#3543)
Signed-off-by: wangyu <410167048@qq.com>
2026-05-19 15:22:02 +08:00
wangyu bd83ac9b4b [CI] invalid_param reliability suite and weekly http_invalid jobs (#3652)
Signed-off-by: wangyu <410167048@qq.com>
2026-05-19 14:28:47 +08:00
chickeytonandherotai214 e277feacaf [large-scale-serving] Integrate OmniCoordinator into stage engine pipeline (#3569)
Signed-off-by: chickeyton <ngton2014@gmail.com>
Signed-off-by: herotai214 <herotai214@gmail.com>
Co-authored-by: herotai214 <herotai214@gmail.com>
2026-05-19 12:08:20 +08:00
JiaHong ca9fd0b71c Reject non-positive Flux2 Klein inference steps (#3717)
Signed-off-by: MmMaiIIi <2360655509@qq.com>
2026-05-19 11:52:41 +08:00
JiaHongandWeiQing Chen 3ac739817f [Bugfix] Reject empty prompts in Flux2 Klein diffusion pipeline (#3711)
Signed-off-by: MmMaiIIi <2360655509@qq.com>
Co-authored-by: WeiQing Chen <40507679+david6666666@users.noreply.github.com>
2026-05-19 11:30:54 +08:00
bjf-frzandbjfwhite 1fa734419e [Refactor]Rename diffusion benchmark backend to endpoint (#3137)
Signed-off-by: bjf-frz <frz123db@gmail.com>
Signed-off-by: bjfwhite <baijingfan1@huawei.com>
Co-authored-by: bjfwhite <baijingfan1@huawei.com>
2026-05-19 11:30:33 +08:00
fan2956 2c6b1bb0c0 [Bugfix] Fix hunyuanimage3 dit quant storageshape mismatch error (#3694)
Signed-off-by: fan2956 <zhoufan53@huawei.com>
2026-05-19 10:24:27 +08:00
Daniel Huang e2ed1c4574 Disable sampler kernel for XPU test (#3718)
Signed-off-by: Daniel Huang <daniel1.huang@intel.com>
2026-05-19 02:19:16 +00:00
Rustam Khadipash 89f8819525 [Feature] Add support for Pipeline Parallel and integrate it into Wan 2.2 (#2322)
Signed-off-by: Rustam Khadipash <16683750+hadipash@users.noreply.github.com>
2026-05-19 09:42:17 +08:00
Chendi.Xue 475a4002b0 [XPU] set flash_attn as default diffusion attn backend and fix k_len for cross_attn (#3525)
Signed-off-by: Chendi Xue <chendi.xue@intel.com>
2026-05-18 23:58:15 +00:00
Sy03andamy-why-3459 ab59673f21 [Bugfix][Qwen3-Omni] Handle short Code2Wav chunk outputs (#3687)
Signed-off-by: Sy03 <1370724210@qq.com>
Co-authored-by: amy-why-3459 <wuhaiyan17@huawei.com>
2026-05-18 18:59:23 +00:00
lyj-jjjandCursor 821286794f [HY-Imgae3.0] support hunyuan image3 dit fa-fp8 on npu (#3540)
Signed-off-by: lyj-jjj <liuyingjun5@huawei.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-18 09:35:30 -07:00
amy-why-3459 309e5c38c6 [BugFix][CI]Fixing occasional CI failures (#3623)
Signed-off-by: amy-why-3459 <wuhaiyan17@huawei.com>
2026-05-18 21:25:37 +08:00
Ding Zuhaoandnussejzz f4115bd771 [Bugfix] Fix SenseNova U1 broken import after SupportsModuleOffload (#3691)
Signed-off-by: nussejzz <nussejzz@users.noreply.github.com>
Co-authored-by: nussejzz <nussejzz@users.noreply.github.com>
2026-05-18 21:12:46 +08:00
Lancer dbc589dbca [Bugfix] fix diffusion quantization benchmarking for Omni outputs (#3653)
Signed-off-by: Lancer <maruixiang6688@gmail.com>
2026-05-18 17:23:40 +08:00
Yueqian Lin 990566aef1 [Bugfix][TTS] Drop meaningless TTFT from speech-endpoint benchmarks (#3674)
Signed-off-by: Yueqian Lin <linyueqian@outlook.com>
2026-05-18 17:18:18 +08:00
Chendi.Xue 6d37e77fb2 [XPU] update dockerfile and CI to 0.21.0 (#3675)
Signed-off-by: Chendi Xue <chendi.xue@intel.com>
2026-05-18 03:06:56 +00:00
wuhangandHongsheng Liu 4ba8e14981 Fix diffusion engine cleanup lifecycle (#3494)
Signed-off-by: wuhang <wuhang6@huawei.com>
Signed-off-by: Hongsheng Liu <liuhongsheng4@huawei.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-18 08:54:57 +08:00
c99df1ebd9 [TTS][Perf] Optimize Qwen3-TTS high-concurrency serving (#3662)
Signed-off-by: Sy03 <1370724210@qq.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
Co-authored-by: SYLAR <125541396+lishunyang12@users.noreply.github.com>
2026-05-17 16:58:59 -04:00
bjf-frz 0a395f9de1 [SKILL]Add diffusion perf skill (#3461)
Signed-off-by: bjf-frz <frz123db@gmail.com>
2026-05-18 00:59:46 +08:00
Hongsheng Liuanddeepseek-v4-pro c0e132d973 [Doc] Reorganize available recipes into a table (#3671)
Signed-off-by: hsliu <liuhongsheng4@huawei.com>
Co-authored-by: deepseek-v4-pro <noreply@anthropic.com>
2026-05-18 00:02:34 +08:00
Hongsheng LiuandClaude Opus 4.7 471ddfe025 [Doc] Simplify template example subtitle (#3669)
Signed-off-by: hsliu <liuhongsheng4@huawei.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 23:36:46 +08:00
8cfc9179e6 Fix reasoning_parser crash: reconstruct StructuredOutputsConfig from dict (#2845)
Signed-off-by: Mike_Qiu <qiudayu.qdy@antgroup.com>
Co-authored-by: Mike_Qiu <qiudayu.qdy@antgroup.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-17 23:30:24 +08:00
0da9ffdb0d Fix output finish reason issue for audio chunk in stream mode (#2849)
Signed-off-by: Mike_Qiu <qiudayu.qdy@antgroup.com>
Co-authored-by: Mike_Qiu <qiudayu.qdy@antgroup.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-17 23:05:34 +08:00
4e88053750 [BugFix][HunyuanImage3] Set MRoPE dynamic_arg_dims so graph mode can compile (#3630)
Signed-off-by: TaffyOfficial <2324465096@qq.com>
Co-authored-by: TaffyOfficial <2324465096@qq.com>
Co-authored-by: Codex <codex@openai.com>
2026-05-17 22:42:59 +08:00
ReidandSYLAR 768943b879 [Frontend]Handle audio generate engine errors consistently (#3316)
Signed-off-by: reidliu41 <reid201711@gmail.com>
Co-authored-by: SYLAR <125541396+lishunyang12@users.noreply.github.com>
2026-05-17 14:10:26 +00:00
Yuanheng Zhao 220db62b3f [Bugfix] Adapt LTX-2 connector arg with diffusers 0.38.0 (#3661)
Signed-off-by: Yuanheng Zhao <jonathan.zhaoyh@gmail.com>
2026-05-17 21:58:44 +08:00
Kevin H. LuuandClaude Opus 4.6 5549b7f44a [CI/Build] Enable twine upload to PyPI (#3667)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-17 19:02:28 +08:00
Kevin H. LuuandClaude Opus 4.7 bc26cad19a [CI/Build] Unify release pipeline with NIGHTLY=1 option, add x86_64/aarch64 image builds (#3428)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 18:40:14 +08:00
9c5e35f485 [Config Refactor] Support Recursive Merging for Engine Args (#3009)
Signed-off-by: Alex Brooks <albrooks@redhat.com>
Signed-off-by: Hongsheng Liu <liuhongsheng4@huawei.com>
Co-authored-by: SYLAR <125541396+lishunyang12@users.noreply.github.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-17 07:33:03 +08:00
Yuanheng Zhao a64ebf103b [Refactor] Migrate and clean up TTS configs: CosyVoice3, OmniVoice, VoxCPM (#3338)
Signed-off-by: yuanheng <jonathan.zhaoyh@gmail.com>
Signed-off-by: Yuanheng Zhao <jonathan.zhaoyh@gmail.com>
2026-05-17 07:32:34 +08:00
TJian c08959ee04 [ROCm] [CI] [Bugfix] Upgrade vllm version to v0.21.0 and ROCm 7.2.2 (#3659)
Signed-off-by: tjtanaa <tunjian.tan@embeddedllm.com>
2026-05-17 03:15:59 +12:00
rongfu.leng c5ac295e3c [Feat] Add helios support cache dit (#3470)
Signed-off-by: rongfu.leng <lenronfu@gmail.com>
2026-05-16 21:11:34 +08:00
Zeng ChuangandHongsheng Liu ea35a0cc4a [Bugfix]update process name for dit stage (#3602)
Signed-off-by: zengchuang <zengchuang3@huawei.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-16 20:51:31 +08:00
wuhangandHongsheng Liu 0f4853ff86 [Bugfix] Support diffusion worker dead detect when use inline engine (#3214)
Signed-off-by: wuhang <wuhang6@huawei.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-16 20:50:29 +08:00
WeiQing Chen b5e163cfca [CI][Accuracy] Add Qwen-Image-2512 Qwen-Image-Edit-2511 pixel accuracy tests (#3502)
Signed-off-by: david6666666 <530634352@qq.com>
2026-05-16 20:08:51 +08:00
dengyunyangandHongsheng Liu d647e7e4cf [Hunyuanimage 3.0] hunyuan accuracy test (#3655)
Signed-off-by: dengyunyang <584797741@qq.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-16 23:35:05 +12:00
33220b1e39 [BugFix] Finish async_chunk requests without pad-token injection (#3613)
Signed-off-by: Nick Cao <ncao@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-16 23:34:04 +12:00
+20 eb4e60ee64 [Rebase] Rebase to vllm v0.21.0 (#3530)
Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
Signed-off-by: Zhou Taichang <tzhouam@connect.ust.hk>
Signed-off-by: NumberWan <wantszkin2003@gmail.com>
Signed-off-by: Divyansh Singhvi <divyanshsinghvi@gmail.com>
Signed-off-by: dsinghvi <divyanshsinghvi@gmail.com>
Signed-off-by: Dnoob <dxpouo@gmail.com>
Signed-off-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com>
Signed-off-by: knlnguyen1802 <knlnguyen1802@gmail.com>
Signed-off-by: rein yang <ruiruyang2@gmail.com>
Signed-off-by: Nick Cao <ncao@redhat.com>
Signed-off-by: zhumingjue <zhumingjue@huawei.com>
Signed-off-by: Ricardo Noriega De Soto <rnoriega@redhat.com>
Signed-off-by: lyj-jjj <liuyingjun5@huawei.com>
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
Signed-off-by: wangyu <410167048@qq.com>
Signed-off-by: weizhoublue <weizhoublue@github.com>
Signed-off-by: weizhou.lan@daocloud.io <weizhou.lan@daocloud.io>
Signed-off-by: dengyunyang <584797741@qq.com>
Signed-off-by: 汪志鹏 <wangzhipeng628@gmail.com>
Signed-off-by: David Chen <530634352@qq.com>
Signed-off-by: Jie Liu <33612777+keeper-jie@users.noreply.github.com>
Signed-off-by: Yueqian Lin <linyueqian@outlook.com>
Signed-off-by: princepride <wangzhipeng628@gmail.com>
Signed-off-by: natureofnature <wzliu@connect.hku.hk>
Signed-off-by: bjf-frz <frz123db@gmail.com>
Signed-off-by: amy-why-3459 <wuhaiyan17@huawei.com>
Signed-off-by: KexiongYu <yukexiong1@huawei.com>
Signed-off-by: Huang, Zeyu <11222265+fhfuih@users.noreply.github.com>
Signed-off-by: Zeyu Huang | 黃澤宇 <11222265+fhfuih@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: NumberWan <wantszkin2003@gmail.com>
Co-authored-by: dsinghvi <divyanshsinghvi@gmail.com>
Co-authored-by: Dnoob <dxpouo@gmail.com>
Co-authored-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
Co-authored-by: knlnguyen1802 <knlnguyen1802@gmail.com>
Co-authored-by: Samit <285365963@qq.com>
Co-authored-by: rein yang <73573651+R2-Y@users.noreply.github.com>
Co-authored-by: Nick Cao <ncao@redhat.com>
Co-authored-by: zhumingjue138 <zhumingjue@huawei.com>
Co-authored-by: Ricardo Noriega <rnoriega@redhat.com>
Co-authored-by: lyj-jjj <liuyingjun5@huawei.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: gcanlin <canlinguosdu@gmail.com>
Co-authored-by: wangyu <53896905+yenuo26@users.noreply.github.com>
Co-authored-by: weizhoublue <45163302+weizhoublue@users.noreply.github.com>
Co-authored-by: weizhoublue <weizhoublue@github.com>
Co-authored-by: dengyunyang <584797741@qq.com>
Co-authored-by: 汪志鹏 <wangzhipeng628@gmail.com>
Co-authored-by: WeiQing Chen <40507679+david6666666@users.noreply.github.com>
Co-authored-by: Jie Liu <33612777+keeper-jie@users.noreply.github.com>
Co-authored-by: Yueqian Lin <linyueqian@outlook.com>
Co-authored-by: NATURE <wzliu@connect.hku.hk>
Co-authored-by: bjf-frz <frz123db@gmail.com>
Co-authored-by: amy-why-3459 <wuhaiyan17@huawei.com>
Co-authored-by: Y. Fisher <yukexiong1@huawei.com>
Co-authored-by: Zeyu Huang | 黃澤宇 <11222265+fhfuih@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-16 18:18:43 +08:00
Yueqian LinandHongsheng Liu 5e1986206f [CI] Replace c=128 perf cell with c=16; loosen new-cell baselines (#3637)
Signed-off-by: Yueqian Lin <linyueqian@outlook.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-15 23:35:44 +08:00
amy-why-3459 d1c65bdffa [BugFix] fix ci (#3650)
Signed-off-by: amy-why-3459 <wuhaiyan17@huawei.com>
2026-05-15 22:50:03 +08:00
Zeyu Huang | 黃澤宇andCopilot Autofix powered by AI d18168ccbb [bugfix] Fix diffusers backend input bug after #2913 (#3644)
Signed-off-by: Huang, Zeyu <11222265+fhfuih@users.noreply.github.com>
Signed-off-by: Zeyu Huang | 黃澤宇 <11222265+fhfuih@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 09:55:09 +00:00
Y. Fisher 779bf3118b [Bugfix] fix compatibility of _hunyuan_image3_unpack_packed_topk between vllm / vllm ascend (#3640)
Signed-off-by: KexiongYu <yukexiong1@huawei.com>
2026-05-15 17:35:22 +08:00
amy-why-3459 e7ee5de09f [BugFix] Fix the issue of thinker requests being preempted, causing shape mismatch. (#3147)
Signed-off-by: amy-why-3459 <wuhaiyan17@huawei.com>
2026-05-15 17:07:42 +08:00
bjf-frz 440c718d2a [Bugfix]Fix multimodal cache routing for AR replicas (#3605)
Signed-off-by: bjf-frz <frz123db@gmail.com>
2026-05-15 16:42:18 +08:00
NATUREandHongsheng Liu 82a0b3a467 [2/5] [core]refactor communication layer: PR 2 of 5 Qwen3 Omni non async (#2677)
Signed-off-by: natureofnature <wzliu@connect.hku.hk>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-15 14:14:08 +08:00
汪志鹏 c7178d89bb [Bugfix] UnspecifiedOmniPlatform.get_device_count returns 0 instead o… (#3636)
Signed-off-by: princepride <wangzhipeng628@gmail.com>
2026-05-15 05:28:40 +00:00
wangyu fdb0efea94 [CI] add cuda marker to Diffusion X2V function pytest (#3625)
Signed-off-by: wangyu <410167048@qq.com>
2026-05-15 11:50:22 +08:00
Jie LiuandYueqian Lin 90f5b3c3a1 Update streaming_speech_client.py to solve Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice voice problem (#3380)
Signed-off-by: Jie Liu <33612777+keeper-jie@users.noreply.github.com>
Signed-off-by: Yueqian Lin <linyueqian@outlook.com>
Co-authored-by: Yueqian Lin <linyueqian@outlook.com>
2026-05-14 20:43:05 -07:00
lyj-jjjandCursor bbc00f9f86 [BugFix] fix(omni): isolate diffusion KV-cache dtype from vLLM --kv-cache-dtype #3585 (#3596)
Signed-off-by: lyj-jjj <liuyingjun5@huawei.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-15 11:22:59 +08:00
WeiQing Chen adb2291c27 Update WeChat group QR code (#3624)
Signed-off-by: David Chen <530634352@qq.com>
2026-05-15 09:38:47 +08:00
Canlin Guo 4f13b871f9 [CI] Add Qwen3-TTS tests for ready tag (#3600)
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
2026-05-14 20:37:51 +08:00
dengyunyangand汪志鹏 94254e015f [BugFix] fix shm connector (#3583)
Signed-off-by: dengyunyang <584797741@qq.com>
Signed-off-by: 汪志鹏 <wangzhipeng628@gmail.com>
Co-authored-by: 汪志鹏 <wangzhipeng628@gmail.com>
2026-05-14 20:07:12 +08:00
bjf-frzandCanlin Guo f7161b07d0 [Bugfix]Allow HunyuanImage3 AR sampler batching (#3590)
Signed-off-by: bjf-frz <frz123db@gmail.com>
Co-authored-by: Canlin Guo <canlinguosdu@gmail.com>
2026-05-14 09:27:59 +00:00
Hongsheng Liuandhsliu_ustc c0b7509f07 update v0.20.0 readme (#3594)
Signed-off-by: hsliu_ustc <hsliu_ustc@noreply.gitcode.com>
Co-authored-by: hsliu_ustc <hsliu_ustc@noreply.gitcode.com>
2026-05-14 17:23:55 +08:00
3f63aaf982 [Feature] HunyuanImage-3.0 IT2I: multi-image input + prompt API cleanup (#3444)
Signed-off-by: TaffyOfficial <2324465096@qq.com>
Signed-off-by: TaffyOfficial <wu15922848573@outlook.com>
Signed-off-by: skf1999 <13234016272@163.com>
Signed-off-by: zuiho <2324465096@qq.com>
Signed-off-by: Claude Code <noreply@anthropic.com>
Signed-off-by: zuiho <wu15922848573@outlook.com>
Signed-off-by: TaffyOfficial <2587297563@qq.com>
Co-authored-by: TaffyOfficial <2324465096@qq.com>
Co-authored-by: TaffyOfficial <wu15922848573@outlook.com>
Co-authored-by: skf1999 <13234016272@163.com>
2026-05-14 16:58:40 +08:00
Yueqian Lin c4f859bf56 [CI] Harden Qwen3-TTS perf nightly: enable Base voice_clone, add c=64/128, 2-GPU split (#3491)
Signed-off-by: Yueqian Lin <linyueqian@outlook.com>
2026-05-14 14:26:59 +08:00
wuhang 0d9d57acd9 [Entrypoint][Refactor] Make field type hint more concrete (#3139)
Signed-off-by: wuhang <wuhang6@huawei.com>
2026-05-14 11:25:55 +08:00
Y. Fisher 51b4b1131e [Bugfix]: Fix online serving failure when using deploy config (#3537)
Signed-off-by: KexiongYu <yukexiong1@huawei.com>
Signed-off-by: Y. Fisher <yukexiong1@huawei.com>
2026-05-14 11:17:15 +08:00
zhumingjue138 e818dba016 [Test] Add stability tests for HunyuanImage-3-Instruct (#3504)
Signed-off-by: zhumingjue <zhumingjue@huawei.com>
2026-05-14 10:47:14 +08:00
Alex Brooks 754d2e52fc [BugFix] Refresh TeaCache when num_inference_steps=None (#2240)
Signed-off-by: Alex Brooks <albrooks@redhat.com>
2026-05-13 20:20:18 +00:00
vraitiandClaude Opus 4.6 9de9d1f7b5 [Model] Add TP-aware MistralEncoder for FLUX.2-dev TP (#2465)
Signed-off-by: vraiti <vraiti@redhat.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 12:33:55 -07:00
dengyunyang efd955674b [Bugfix][HunyuanImage3.0] Fix KV reuse compatibility in SP scenarios (#3546)
Signed-off-by: dengyunyang <584797741@qq.com>
2026-05-13 22:40:18 +08:00
Y. Fisher 4d3eed152a [Feat][Config] Support additional_config for diffusion worker (#3020)
Signed-off-by: KexiongYu <yukexiong1@huawei.com>
Signed-off-by: Y. Fisher <yukexiong1@huawei.com>
2026-05-13 14:22:43 +00:00
Zeng Chuang 16a84b29d5 [Bugfix] Add bot_task option of think_recaption for hunyuanimage3 it2i (#3551)
Signed-off-by: zengchuang <zengchuang3@huawei.com>
2026-05-13 22:10:35 +08:00
TJian b9cb57b631 [ROCm] Bugfix wan22 (#3463)
Signed-off-by: tjtanaa <tunjian.tan@embeddedllm.com>
2026-05-13 20:50:57 +08:00
amy-why-3459 2e8e3057bc [skip ci][Tests] Splitting Qwen3-omni's performance test cases (#3501)
Signed-off-by: amy-why-3459 <wuhaiyan17@huawei.com>
2026-05-13 17:54:21 +08:00
hxhhhlalalaandhyh_hh a715abd447 [NPU][Quant] Add W8A8 MXFP8 online/offline quantization support for Wan2.2 T2V / I2V / TI2V inference on Ascend NPU (#3140)
Signed-off-by: hyh_hh <huyinghong1@huawei.com>
Co-authored-by: hyh_hh <huyinghong1@huawei.com>
2026-05-13 17:14:42 +08:00
weizhoublueandweizhoublue b6bdc5997f Fix: NPU AR model runner prefix cache key flattening (#3568)
Signed-off-by: weizhoublue <weizhoublue@github.com>
Signed-off-by: weizhou.lan@daocloud.io <weizhou.lan@daocloud.io>
Co-authored-by: weizhoublue <weizhoublue@github.com>
2026-05-13 08:22:48 +00:00
knlnguyen1802andSamit 631251a1f8 [Bugfix, rl] Diffusion worker SIGKILL under Ray actor (exitcode -9) (#3533)
Signed-off-by: knlnguyen1802 <knlnguyen1802@gmail.com>
Co-authored-by: Samit <285365963@qq.com>
2026-05-13 15:31:48 +08:00
wangyu b6f29ee614 [CI][Bugfix] skip fp8 Z-Image quality gate (#3531) and add torchdiffeq dev extra (#3563)
Signed-off-by: wangyu <410167048@qq.com>
2026-05-13 14:25:56 +08:00
Canlin Guo 0ab1d30056 [CI][Test] Add NPU nightly tests (#3480)
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
2026-05-13 16:58:17 +12:00
56ca7dd612 support online FP8 quantization for FA on NPU #2236 (#2640)
Signed-off-by: lyj-jjj <liuyingjun5@huawei.com>
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: gcanlin <canlinguosdu@gmail.com>
2026-05-13 11:46:00 +08:00
Ricardo Noriega 83bbe39d39 Bump diffusers minimum version to >=0.38.0 (#3349)
Signed-off-by: Ricardo Noriega De Soto <rnoriega@redhat.com>
2026-05-13 10:25:02 +08:00
Nick CaoandClaude Opus 4.6 5313cf6d48 [Bugfix] Fix omni processing test for non-multimodal talker stage (#3559)
Signed-off-by: Nick Cao <ncao@redhat.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 10:08:23 +08:00
zhumingjue138 c167b9d691 [Bugfix] Fix the issue where the qwen3-omni model long-term stability test sometimes gets stuck without sending requests. (#3468)
Signed-off-by: zhumingjue <zhumingjue@huawei.com>
2026-05-13 09:15:20 +08:00
Nick CaoandClaude dca369d448 [Perf] Remove dead audio_tower and visual from Qwen2.5-Omni talker stage (#3425)
Signed-off-by: Nick Cao <ncao@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-12 23:24:13 +08:00
rein yang f4b28f2398 [CI] update daily omni min accuracy (#3536)
Signed-off-by: rein yang <ruiruyang2@gmail.com>
2026-05-12 22:10:10 +08:00
knlnguyen1802andSamit aa1184d737 [bugfix, rl] Fix race condition bug on async running for diffusion model (#3379)
Signed-off-by: knlnguyen1802 <knlnguyen1802@gmail.com>
Co-authored-by: Samit <285365963@qq.com>
2026-05-12 14:55:30 +08:00
d7ea5d5979 [New Model] Add support for tencent/Covo-Audio-Chat (#2293)
Signed-off-by: Dnoob <dxpouo@gmail.com>
Signed-off-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com>
Co-authored-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-12 14:39:58 +08:00
dsinghvi e40076e872 [Refactor] msgspec standardisation for data entry key names and improved type checks (#3149)
Signed-off-by: Divyansh Singhvi <divyanshsinghvi@gmail.com>
Signed-off-by: dsinghvi <divyanshsinghvi@gmail.com>
2026-05-12 14:30:13 +08:00
NumberWan 9a60e11e3d [Nightly CI] Remove TP case (#3534)
Signed-off-by: NumberWan <wantszkin2003@gmail.com>
2026-05-12 05:59:04 +00:00
Saad Al-TohamyandGao Han fe72d078ca [FIX] Ensure extra_params are correctly merged into sampling params in _create_diffusion_speech() (#3320)
Signed-off-by: saadaltohamy <saad_altohamy@yahoo.com>
Co-authored-by: Gao Han <hgaoaf@connect.ust.hk>
2026-05-12 01:10:04 -04:00
dengyunyangandHongsheng Liu 0d91fbbbb7 [Bugfix] Align the AR and DiT prompt formatting across both online and offline modes. (#3516)
Signed-off-by: dengyunyang <584797741@qq.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-12 12:13:30 +08:00
amy-why-3459 ce621be29b [BugFix] Modify the splicing method of streaming audio output. (#3438)
Signed-off-by: amy-why-3459 <wuhaiyan17@huawei.com>
2026-05-12 11:56:10 +08:00
bjf-frz dd5626e607 [Recipes]update Wan2.2-I2V gpu part (#3271)
Signed-off-by: bjf-frz <frz123db@gmail.com>
2026-05-12 10:44:20 +08:00
wangyu ac5fbed6c0 [CI][Bugfix] Improve e2e latency logging, update response classes to include detailed latency documentation and add startup time logging (#3246)
Signed-off-by: wangyu <410167048@qq.com>
Signed-off-by: [Your Name] <your.email@example.com>
2026-05-12 10:23:56 +08:00
Lancer 955fcff828 [Chore] explicit .float() conversion in Helios's optimized_scale function (#3529)
Signed-off-by: Lancer <maruixiang6688@gmail.com>
2026-05-12 02:08:28 +00:00
ChenWenjing 4bca522f01 [bugfix][ci] avoid Whisper transcript deduplication in realtime audio test (#3417)
Signed-off-by: CHEN <116010019@link.cuhk.edu.cn>
2026-05-11 17:09:10 +00:00
Nick CaoandClaude bd4ede391b [Perf] Remove dead audio_tower and visual from Qwen3-Omni talker stage (#3296)
Signed-off-by: Nick Cao <ncao@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-11 16:04:56 +00:00
Junhong Liu 6be59f7d19 [Fix] Fix RMSNorm inductor KeyError under HSDP + torch.compile (#3460)
Signed-off-by: Junhong Liu <98734602+LJH-LBJ@users.noreply.github.com>
2026-05-11 22:56:54 +08:00
Y. Fisher a33e2eb588 [Config] Add HunyuanImage3 deploy configs (#3172)
Signed-off-by: KexiongYu <yukexiong1@huawei.com>
Signed-off-by: Y. Fisher <yukexiong1@huawei.com>
2026-05-11 22:49:19 +08:00
汪志鹏 c9a8556c24 [New Model]: Add sensenova u1 support (#3319)
Signed-off-by: princepride <wangzhipeng628@gmail.com>
Signed-off-by: 汪志鹏 <wangzhipeng628@gmail.com>
2026-05-11 22:19:00 +08:00
wangyu 2cdffcea6b [CI] skip failing diffusion and accuracy cases (#3432, #3256, #3257, #3488) (#3507)
Signed-off-by: wangyu <410167048@qq.com>
2026-05-11 21:56:08 +08:00
wuhang 3f27ffbd4d [Misc] Clean logs for image gen task (#3414)
Signed-off-by: wuhang <wuhang6@huawei.com>
2026-05-11 12:16:05 +00:00
dengyunyang 3bf4f2850c [Bug][Hunyuanimage 3.0] fix different AR encode behavior between online and offline (#3500)
Signed-off-by: dengyunyang <584797741@qq.com>
2026-05-11 11:34:58 +00:00
Canlin Guo 5e263b6929 [BugFix] Rename attention_config to diffusion_attention_config (#3489)
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
2026-05-11 11:27:35 +08:00
e1088026fa [Performance] Improve MiMo-Audio tokenizer decoding performance (#2183)
Signed-off-by: 齐保元 <qibaoyuan@xiaomi.com>
Co-authored-by: Jialong Liu <88185941+Galleons2029@users.noreply.github.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-11 10:05:46 +08:00
Canlin GuoandHongsheng Liu ef7f2f1cd2 [Docs] Refactor the attention backend docs/skill (#3475)
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-11 09:13:07 +08:00
TJian 67e0e10c85 [ROCm] [CI] Add the same skip ci logic as CUDA CI (#3482)
Signed-off-by: tjtanaa <tunjian.tan@embeddedllm.com>
2026-05-11 07:53:27 +08:00
Sy03 2c73cf3522 [Perf] Fix Qwen3-TTS latency regression (#3485)
Signed-off-by: Sy03 <1370724210@qq.com>
2026-05-10 23:07:38 +00:00
dengyunyang 857356d5b7 [Feature] hunyuanimage support flash attn (#2981)
Signed-off-by: dengyunyang <584797741@qq.com>
2026-05-10 22:08:24 +08:00
Canlin GuoandHongsheng Liu 11c4c7f0ff [Diffusion][Attention] Support per-role attention backend via CLI (#2681)
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
Signed-off-by: Hongsheng Liu <liuhongsheng4@huawei.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-10 11:59:05 +08:00
wangyuandHongsheng Liu 26d481fc84 [CI] Remove VLLM_TEST_CLEAN_GPU_MEMORY to avoid environment variable pollution that causes unnecessary GPU detection, thereby slowing down test case execution. (#3446)
Signed-off-by: wangyu <410167048@qq.com>
Signed-off-by: [Your Name] <your.email@example.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-10 07:00:29 +08:00
wuhang 77480215f5 [CI][Nightly] Shard nightly Diffusion X2I H100 lanes and centralize shard definitions (#3455)
Signed-off-by: wuhang <wuhang6@huawei.com>
2026-05-09 21:13:18 +08:00
baonudesifeizhaiandroG0d c4a0990044 (Phase 1)Add ModelOpt FP8 auto-detect support for diffusion checkpoints #2709 (#2913)
Signed-off-by: roG0d <rodgarcas98@gmail.com>
Signed-off-by: roG0d <baonudesifeizhai@gmail.com>
Signed-off-by: baonudesifeizhai <85092850+baonudesifeizhai@users.noreply.github.com>
Co-authored-by: roG0d <rodgarcas98@gmail.com>
2026-05-09 16:55:05 +08:00
wangyu 40a07e0d80 [CI] Refine nightly pytest command in Omni · Function Test with H100 to avoid duplicate testing. (#3459)
Signed-off-by: wangyu <410167048@qq.com>
2026-05-09 16:17:57 +08:00
zhumingjue138 0e81ef2870 [CI] Update merge condition to skip L3 merges during weekly test and update doc (#3197)
Signed-off-by: zhumingjue <zhumingjue@huawei.com>
2026-05-09 16:17:43 +08:00
Yueqian Lin ac69cbd27e [Test] Restore tts mark and omni_runner_function fixture for Voxtral TTS (#3462)
Signed-off-by: Yueqian Lin <linyueqian@outlook.com>
2026-05-09 15:22:43 +08:00
Wallbreazzzandhouzechen d460673647 Fix NPU code predictor device mismatch in concurrent mode (#3453)
Co-authored-by: houzechen <h00875519@china.huawei.com>
2026-05-09 14:58:56 +08:00
f6e3dece09 [Feature] Add FP8 quantization for Voxtral TTS (#3036)
Signed-off-by: akshatvishu <akshatnayak197@gmail.com>
Signed-off-by: Chen-Yo Sun <chenyo.sun@mistral.ai>
Signed-off-by: akshatvishu <33392262+akshatvishu@users.noreply.github.com>
Co-authored-by: Chen-Yo Sun <chenyo.sun@mistral.ai>
Co-authored-by: SYLAR <125541396+lishunyang12@users.noreply.github.com>
2026-05-08 22:40:36 -04:00
Lancer de3a291710 [Bugfix] fix OmniGen2 offload and dtype mismatch (#2560)
Signed-off-by: Lancer <maruixiang6688@gmail.com>
Signed-off-by: Lancer <402430575@qq.com>
2026-05-09 09:13:37 +08:00
Dan c481ccee2b [Perf] Optimize VoxCPM2 first-request latency via startup warmup (#3424)
Signed-off-by: Dan250124 <416947747@qq.com>
2026-05-09 06:44:19 +08:00
rongfu.leng b4ab37da22 [Bugfix] Qwen-Image use teachche serve will crash (#3450)
Signed-off-by: rongfu.leng <lenronfu@gmail.com>
2026-05-09 06:41:59 +08:00
Yueqian Lin c2a624bec4 [Bugfix][StableAudio] Pass model_class_name to Omni() and declare audio class attrs (#3405)
Signed-off-by: Yueqian Lin <linyueqian@outlook.com>
2026-05-09 06:40:43 +08:00
aca4b7d65c Add Qwen3 TTS Model recipe (#3130)
Signed-off-by: Chonghao Zhang <chzhang2021@gmail.com>
Signed-off-by: chzhang2021 <chzhang2021@gmail.com>
Signed-off-by: Chonghao Zhang <chonghaoz@meta.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
Co-authored-by: Chonghao Zhang <chonghaoz@meta.com>
2026-05-08 13:08:39 -07:00
Nick CaoandClaude 65bc968465 [Refactor] Rename SupportsModuleOffload to SupportsComponentDiscovery (#3354)
Signed-off-by: Nick Cao <ncao@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-08 14:16:49 +00:00
Ayush AgarwalandHongsheng Liu b968373c88 enhancement: extend to dmd2 to image generation + add flux, qwen image pipelines (#2974)
Signed-off-by: ayushag <ayushag@nvidia.com>
Signed-off-by: Ayush Agarwal <ayushag@nvidia.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-05-08 21:37:57 +08:00
skf 039a09a8e1 [Feature] online HunyuanImage-3.0 IT2I (image editing) support (#3410)
Signed-off-by: skf1999 <13234016272@163.com>
2026-05-08 21:33:53 +08:00
zdobaanddaixinning c83cd45069 [Feat] Add Sequence Parallelism (USP) support for HunyuanVideo 1.5 transformer (#2444)
Signed-off-by: daixinning <daixinning@163.com>
Co-authored-by: daixinning <daixinning@163.com>
2026-05-08 21:20:03 +08:00
boatmanandsphinxkkkbc f8624db93a [BugFix]Fix default stage config path in voxcpm2 (#3447)
Signed-off-by: sphinxkkkbc <binchengkang8@gmail.com>
Co-authored-by: sphinxkkkbc <binchengkang8@gmail.com>
2026-05-08 21:03:47 +08:00
TaffyOfficialandTaffyOfficial 07fd6afb4b [Test][HunyuanImage3] Add e2e offline I2T smoke test (#3332)
Signed-off-by: TaffyOfficial <2324465096@qq.com>
Co-authored-by: TaffyOfficial <2324465096@qq.com>
2026-05-08 20:55:23 +08:00
dengyunyang 5b61e7f1f1 [Feature][Hunyuan image 3.0] AR + DIT with kv reuse. (#3346)
Signed-off-by: dengyunyang <584797741@qq.com>
2026-05-08 20:51:46 +08:00
Alex BrooksandGao Han ce8a7dfd2d [Bugfix] Fix Dtype Crashes in SD3 (#2526)
Signed-off-by: Alex Brooks <albrooks@redhat.com>
Co-authored-by: Gao Han <hgaoaf@connect.ust.hk>
2026-05-08 16:05:38 +08:00
Phi-C 50fd3a3f85 [Bugfix] Fix the issue where the seed parameter does not take effect when using the OpenAI Python client (#3436)
Signed-off-by: Phi-C <chenxjhit@163.com>
2026-05-08 07:22:32 +00:00
lsyyyyyand汪志鹏 32663f21d5 [Feat] support hsdp for Bagel (#3150)
Signed-off-by: siyuan.lei <siyuanlei37@gmail.com>
Signed-off-by: lsyyyyy <siyuanlei37@gmail.com>
Signed-off-by: 汪志鹏 <wangzhipeng628@gmail.com>
Co-authored-by: 汪志鹏 <wangzhipeng628@gmail.com>
2026-05-08 15:20:42 +08:00
ea4cf77f56 [Hardware] Extend diffusion engine plugin extensibility for out-of-tree hardware backends (#3239)
Signed-off-by: Yuchen Jiang <yucjiang@amazon.com>
Co-authored-by: Yuchen Jiang <yucjiang@amazon.com>
Co-authored-by: Canlin Guo <canlinguosdu@gmail.com>
2026-05-08 14:29:15 +08:00
wangyuandHuang, Zeyu 6f2ad7b403 [Test] Unify L2/L3 test layout, Buildkite steps, and test helpers (#2556)
Signed-off-by: wangyu <410167048@qq.com>
Signed-off-by: Huang, Zeyu <11222265+fhfuih@users.noreply.github.com>
Signed-off-by: wangyu <53896905+yenuo26@users.noreply.github.com>
Co-authored-by: Huang, Zeyu <11222265+fhfuih@users.noreply.github.com>
2026-05-08 14:12:23 +08:00
WeiQing Chen b85833eeec Update CODEOWNERS feature reviewers (#3378)
Signed-off-by: David Chen <530634352@qq.com>
2026-05-08 12:21:29 +08:00
Chenguang Zheng eeb7e698c9 [Clean] Remove multi-replica Bagel CI and related docs/configs (#3407)
Signed-off-by: Chenguang ZHENG <645327136@qq.com>
2026-05-08 09:28:52 +08:00
Nick Cao e6466cf0e6 [Refactor] Replace and ban a few torch.cuda functions in favor of torch.accelerator replacements. (#3365)
Signed-off-by: Nick Cao <ncao@redhat.com>
2026-05-07 23:23:59 +08:00
Gao Han 54277a8dd0 [chore] Update command to download dataset from huggingface-cli to hf (#3403)
Signed-off-by: Gao Han <hgaoaf@connect.ust.hk>
2026-05-07 16:16:36 +08:00
623 changed files with 47301 additions and 12352 deletions

No files matched your search

+105 -24
View File
@@ -56,6 +56,88 @@ check_run_all_label() {
fi fi
} }
is_docs_only_change() {
local file_path
local has_any=0
while IFS= read -r file_path; do
[[ -z "${file_path}" ]] && continue
has_any=1
if [[ "${file_path}" == docs/* ]]; then
continue
fi
if [[ "${file_path}" == *.md ]]; then
continue
fi
if [[ "${file_path}" == "mkdocs.yml" ]]; then
continue
fi
return 1
done
[[ "${has_any}" -eq 1 ]]
}
resolve_skip_ci() {
local is_pr_build=0
local files
local base_branch base_ref
if [[ "${BUILDKITE_PULL_REQUEST:-false}" != "false" && -n "${BUILDKITE_PULL_REQUEST:-}" ]]; then
is_pr_build=1
fi
if [[ "${is_pr_build}" -eq 1 ]]; then
base_branch="${BUILDKITE_PULL_REQUEST_BASE_BRANCH:-main}"
if ! git rev-parse --verify "origin/${base_branch}" >/dev/null 2>&1; then
echo "resolve_skip_ci: origin/${base_branch} not found locally; trying fetch" >&2
git fetch --depth=200 origin "${base_branch}" >/dev/null 2>&1 || true
fi
base_ref=""
if git rev-parse --verify "origin/${base_branch}" >/dev/null 2>&1; then
base_ref="origin/${base_branch}"
elif git rev-parse --verify "${base_branch}" >/dev/null 2>&1; then
base_ref="${base_branch}"
else
echo "resolve_skip_ci: cannot resolve PR base ${base_branch}; skip-ci=0" >&2
echo -n 0
return 0
fi
if ! files="$(git diff --name-only "${base_ref}...${BUILDKITE_COMMIT}" 2>/dev/null)"; then
echo "resolve_skip_ci: failed to compute PR changed files; skip-ci=0" >&2
echo -n 0
return 0
fi
elif [[ "${BUILDKITE_BRANCH:-}" == "main" ]]; then
if ! git rev-parse --verify "${BUILDKITE_COMMIT}^" >/dev/null 2>&1; then
echo "resolve_skip_ci: commit has no parent on main; skip-ci=0" >&2
echo -n 0
return 0
fi
if ! files="$(git diff --name-only "${BUILDKITE_COMMIT}^..${BUILDKITE_COMMIT}" 2>/dev/null)"; then
echo "resolve_skip_ci: failed to compute main changed files; skip-ci=0" >&2
echo -n 0
return 0
fi
else
echo "resolve_skip_ci: not PR/main build; skip-ci=0" >&2
echo -n 0
return 0
fi
if is_docs_only_change <<< "${files}"; then
echo "resolve_skip_ci: docs-only change detected; skip-ci=1" >&2
echo -n 1
return 0
fi
echo "resolve_skip_ci: non-doc changes detected; skip-ci=0" >&2
echo -n 0
}
if [[ -z "${COV_ENABLED:-}" ]]; then if [[ -z "${COV_ENABLED:-}" ]]; then
COV_ENABLED=0 COV_ENABLED=0
fi fi
@@ -91,8 +173,24 @@ upload_pipeline() {
cd .buildkite cd .buildkite
# Select test definition file: merge suite for main, ready suite for PRs # Select test definition file: merge suite for main, ready suite for PRs.
if [[ $BUILDKITE_BRANCH == "main" ]]; then # For debugging, DEBUG_TEST_YAML can override the selection — accepts
# "merge" or "ready" (case-insensitive).
if [[ -n "${DEBUG_TEST_YAML:-}" ]]; then
case "${DEBUG_TEST_YAML,,}" in
merge)
TEST_YAML="test-amd-merge.yml"
;;
ready)
TEST_YAML="test-amd-ready.yaml"
;;
*)
echo "ERROR: DEBUG_TEST_YAML must be 'merge' or 'ready', got '$DEBUG_TEST_YAML'" >&2
exit 1
;;
esac
echo "DEBUG_TEST_YAML override: using $TEST_YAML"
elif [[ $BUILDKITE_BRANCH == "main" ]]; then
TEST_YAML="test-amd-merge.yml" TEST_YAML="test-amd-merge.yml"
else else
TEST_YAML="test-amd-ready.yaml" TEST_YAML="test-amd-ready.yaml"
@@ -140,30 +238,13 @@ fi
# Early exit start: skip pipeline if conditions are met # Early exit start: skip pipeline if conditions are met
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# skip pipeline if all changed files are under docs/ # Match CUDA Buildkite skip-ci behavior for docs/markdown-only changes.
if [[ "${DOCS_ONLY_DISABLE}" != "1" ]]; then if [[ "${DOCS_ONLY_DISABLE}" != "1" ]]; then
if [[ -n "${file_diff:-}" ]]; then SKIP_CI="$(resolve_skip_ci)"
docs_only=1 if [[ "${SKIP_CI}" == "1" ]]; then
# Robust iteration over newline-separated file_diff echo "[docs-only] Docs/markdown-only changes detected. Exiting before pipeline upload."
while IFS= read -r f; do exit 0
[[ -z "$f" ]] && continue
# **Policy:** only skip if *every* path starts with docs/
if [[ "$f" != docs/* ]]; then
docs_only=0
break
fi
done < <(printf '%s\n' "$file_diff" | tr ' ' '\n' | tr -d '\r')
if [[ "$docs_only" -eq 1 ]]; then
buildkite-agent annotate ":memo: CI skipped — docs/** only changes detected
\`\`\`
${file_diff}
\`\`\`" --style "info" || true
echo "[docs-only] All changes are under docs/. Exiting before pipeline upload."
exit 0
fi fi
fi
fi fi
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
-20
View File
@@ -1,20 +0,0 @@
steps:
- label: "Build and upload wheel"
key: "build-wheel"
agents:
queue: cpu_queue_release
commands:
- "curl -LsSf https://astral.sh/uv/install.sh | sh"
- 'export PATH="$HOME/.local/bin:$PATH"'
- "uv venv --python=3.12 && source .venv/bin/activate"
- "uv pip install --upgrade build"
- "python3 -m build"
- "bash .buildkite/scripts/upload-nightly-wheels.sh"
- label: "Generate and upload wheel indices"
depends_on: "build-wheel"
allow_dependency_failure: true
agents:
queue: small_cpu_queue_release
commands:
- "bash .buildkite/scripts/generate-and-upload-nightly-index.sh"
+1 -1
View File
@@ -10,7 +10,7 @@ steps:
DOCKER_BUILDKIT: "1" DOCKER_BUILDKIT: "1"
# Buildkite will automatically replace this with the actual commit hash # Buildkite will automatically replace this with the actual commit hash
VLLM_IMAGE_TAG: "${BUILDKITE_COMMIT}" VLLM_IMAGE_TAG: "${BUILDKITE_COMMIT}"
VLLM_VERSION: "v0.20.0" VLLM_VERSION: "v0.21.0"
priority: 100 priority: 100
timeout_in_minutes: 60 timeout_in_minutes: 60
soft_fail: false soft_fail: false
+110
View File
@@ -0,0 +1,110 @@
# steps:
# - label: ":buildkit: Build and Push NPU Test Image - A3"
# key: image-build-a3
# agents:
# queue: "ascend-a3"
# resource_class: "npu-2"
# plugins:
# - kubernetes:
# metadata:
# annotations:
# vault.hashicorp.com/agent-init-first: "true"
# vault.hashicorp.com/agent-inject: "true"
# vault.hashicorp.com/agent-inject-perms-ca.pem: "0400"
# vault.hashicorp.com/agent-inject-perms-cert.pem: "0400"
# vault.hashicorp.com/agent-inject-perms-config.json: "0400"
# vault.hashicorp.com/agent-inject-perms-key.pem: "0400"
# vault.hashicorp.com/agent-inject-secret-ca.pem: internal/data/ascend/buildkitd
# vault.hashicorp.com/agent-inject-secret-cert.pem: internal/data/ascend/buildkitd
# vault.hashicorp.com/agent-inject-secret-config.json: internal/data/ascend/buildkitd
# vault.hashicorp.com/agent-inject-secret-key.pem: internal/data/ascend/buildkitd
# vault.hashicorp.com/agent-inject-template-ca.pem: "{{- with secret \"internal/data/ascend/buildkitd\" -}}\n{{ .Data.data.RootCA }}\n{{- end }}"
# vault.hashicorp.com/agent-inject-template-cert.pem: "{{- with secret \"internal/data/ascend/buildkitd\" -}}\n{{ .Data.data.ClientCaCert }}\n{{- end }}"
# vault.hashicorp.com/agent-inject-template-config.json: "{{- with secret \"internal/data/ascend/buildkitd\" -}}\n{{ .Data.data.dockerConfig }}\n{{- end }}"
# vault.hashicorp.com/agent-inject-template-key.pem: "{{- with secret \"internal/data/ascend/buildkitd\" -}}\n{{ .Data.data.ClientCaKey }}\n{{- end }}"
# vault.hashicorp.com/agent-pre-populate-only: "true"
# vault.hashicorp.com/agent-run-as-group: "1000"
# vault.hashicorp.com/agent-run-as-user: "1000"
# vault.hashicorp.com/agent-service-account-token-volume-name: token-vol
# vault.hashicorp.com/role: ascend-gha-runners
# vault.hashicorp.com/secret-volume-path: /home/user/.docker/
# vault.hashicorp.com/tls-skip-verify: "true"
# podSpecPatch:
# volumes:
# - name: token-vol
# projected:
# defaultMode: 420
# sources:
# - serviceAccountToken:
# audience: api
# expirationSeconds: 600
# path: token
# env:
# VLLM_IMAGE_TAG: "${BUILDKITE_COMMIT}"
# IMAGE_NAME: "vllm-omni-ci-npu"
# IMAGE_REGISTRY: "swr.cn-southwest-2.myhuaweicloud.com/modelfoundry"
# BUILDKITD_ADDR: "tcp://buildkitd-service.buildkitd:1234"
# command: |
# set -ex
# echo "--- Building and pushing NPU Test Image"
# echo "Image: $${IMAGE_REGISTRY}/$${IMAGE_NAME}:$${VLLM_IMAGE_TAG}"
# echo "buildkitd address: $${BUILDKITD_ADDR}"
# git config --global url."https://gh-proxy.test.osinfra.cn/https://github.com/".insteadOf "https://github.com/"
# if ! command -v buildctl &> /dev/null; then
# echo "Installing buildctl..."
# mkdir -p /tmp/buildkit
# BUILDKIT_VERSION="v0.29.0"
# wget -q "https://gh-proxy.test.osinfra.cn/https://github.com/moby/buildkit/releases/download/$${BUILDKIT_VERSION}/buildkit-$${BUILDKIT_VERSION}.linux-arm64.tar.gz" -O /tmp/buildkit.tar.gz
# tar -xzf /tmp/buildkit.tar.gz -C /tmp/buildkit
# cp /tmp/buildkit/bin/buildctl /usr/local/bin/
# fi
# export DOCKER_CONFIG=/home/user/.docker
# buildctl \
# --addr="$${BUILDKITD_ADDR}" \
# --tlscacert=/home/user/.docker/ca.pem \
# --tlscert=/home/user/.docker/cert.pem \
# --tlskey=/home/user/.docker/key.pem \
# build \
# --frontend dockerfile.v0 \
# --local context=. \
# --local dockerfile=./docker \
# --opt filename=Dockerfile.npu.ci.a3 \
# --secret id=dockerconfig,src=/home/user/.docker/config.json \
# --output type=image,name=$${IMAGE_REGISTRY}/$${IMAGE_NAME}:$${VLLM_IMAGE_TAG},push=true \
# --progress=plain
# echo "--- Image pushed successfully"
# echo "$${IMAGE_REGISTRY}/$${IMAGE_NAME}:$${VLLM_IMAGE_TAG}"
# # - label: "Upload Ready Pipeline - A3"
# # depends_on: image-build-a3
# # key: upload-ready-pipeline-a3
# # if: build.branch != "main" && build.pull_request.labels includes "npu-test"
# # agents:
# # queue: "ascend-a3"
# # resource_class: "npu-2"
# # command: |
# # buildkite-agent pipeline upload .buildkite/test-npu-ready.yaml
# # L4 Test — main+NIGHTLY=1 (scheduled), or PR with specific label (e.g. add label then Rebuild)
# - label: "Upload Nightly Pipeline"
# depends_on: image-build
# key: upload-nightly-pipeline
# if: >-
# (build.branch == "main" && build.env("NIGHTLY") == "1") ||
# (build.branch != "main" && (
# build.pull_request.labels includes "nightly-test" ||
# build.pull_request.labels includes "omni-test" ||
# build.pull_request.labels includes "tts-test" ||
# build.pull_request.labels includes "diffusion-x2iat-test" ||
# build.pull_request.labels includes "diffusion-x2v-test"
# ))
# commands:
# - buildkite-agent pipeline upload .buildkite/test-npu-nightly.yaml
# agents:
# queue: "ascend-a3"
# resource_class: "npu-2"
+112
View File
@@ -0,0 +1,112 @@
steps:
- label: ":buildkit: Build and Push NPU Test Image - A2"
key: image-build
agents:
queue: "ascend-a2b3"
resource_class: "npu-1"
plugins:
- kubernetes:
metadata:
annotations:
vault.hashicorp.com/agent-init-first: "true"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-perms-ca.pem: "0400"
vault.hashicorp.com/agent-inject-perms-cert.pem: "0400"
vault.hashicorp.com/agent-inject-perms-config.json: "0400"
vault.hashicorp.com/agent-inject-perms-key.pem: "0400"
vault.hashicorp.com/agent-inject-secret-ca.pem: internal/data/ascend/buildkitd
vault.hashicorp.com/agent-inject-secret-cert.pem: internal/data/ascend/buildkitd
vault.hashicorp.com/agent-inject-secret-config.json: internal/data/ascend/buildkitd
vault.hashicorp.com/agent-inject-secret-key.pem: internal/data/ascend/buildkitd
vault.hashicorp.com/agent-inject-template-ca.pem: "{{- with secret \"internal/data/ascend/buildkitd\" -}}\n{{ .Data.data.RootCA }}\n{{- end }}"
vault.hashicorp.com/agent-inject-template-cert.pem: "{{- with secret \"internal/data/ascend/buildkitd\" -}}\n{{ .Data.data.ClientCaCert }}\n{{- end }}"
vault.hashicorp.com/agent-inject-template-config.json: "{{- with secret \"internal/data/ascend/buildkitd\" -}}\n{{ .Data.data.dockerConfig }}\n{{- end }}"
vault.hashicorp.com/agent-inject-template-key.pem: "{{- with secret \"internal/data/ascend/buildkitd\" -}}\n{{ .Data.data.ClientCaKey }}\n{{- end }}"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/agent-run-as-group: "1000"
vault.hashicorp.com/agent-run-as-user: "1000"
vault.hashicorp.com/agent-service-account-token-volume-name: token-vol
vault.hashicorp.com/role: ascend-gha-runners
vault.hashicorp.com/secret-volume-path: /home/user/.docker/
vault.hashicorp.com/tls-skip-verify: "true"
podSpecPatch:
volumes:
- name: token-vol
projected:
defaultMode: 420
sources:
- serviceAccountToken:
audience: api
expirationSeconds: 600
path: token
env:
VLLM_IMAGE_TAG: "${BUILDKITE_COMMIT}"
IMAGE_NAME: "vllm-omni-ci-npu"
IMAGE_REGISTRY: "swr.cn-southwest-2.myhuaweicloud.com/modelfoundry"
BUILDKITD_ADDR: "tcp://buildkitd-service.buildkitd:1234"
command: |
set -ex
echo "--- Building and pushing NPU Test Image"
echo "Image: $${IMAGE_REGISTRY}/$${IMAGE_NAME}:$${VLLM_IMAGE_TAG}"
echo "buildkitd address: $${BUILDKITD_ADDR}"
if ! command -v buildctl &> /dev/null; then
echo "Installing buildctl..."
mkdir -p /tmp/buildkit
BUILDKIT_VERSION="v0.29.0"
wget -q "https://github.com/moby/buildkit/releases/download/$${BUILDKIT_VERSION}/buildkit-$${BUILDKIT_VERSION}.linux-arm64.tar.gz" -O /tmp/buildkit.tar.gz
tar -xzf /tmp/buildkit.tar.gz -C /tmp/buildkit
cp /tmp/buildkit/bin/buildctl /usr/local/bin/
fi
export DOCKER_CONFIG=/home/user/.docker
buildctl \
--addr="$${BUILDKITD_ADDR}" \
--tlscacert=/home/user/.docker/ca.pem \
--tlscert=/home/user/.docker/cert.pem \
--tlskey=/home/user/.docker/key.pem \
build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=./docker \
--opt filename=Dockerfile.npu.ci \
--secret id=dockerconfig,src=/home/user/.docker/config.json \
--output type=image,name=$${IMAGE_REGISTRY}/$${IMAGE_NAME}:$${VLLM_IMAGE_TAG},push=true \
--progress=plain
echo "--- Image pushed successfully"
echo "$${IMAGE_REGISTRY}/$${IMAGE_NAME}:$${VLLM_IMAGE_TAG}"
- label: "Upload Ready Pipeline"
depends_on: image-build
key: upload-ready-pipeline
if: >-
build.branch != "main" && (
build.pull_request.labels includes "npu-test" ||
build.pull_request.labels includes "ready"
)
agents:
queue: "ascend-a2b3"
resource_class: "npu-1"
command: |
buildkite-agent pipeline upload .buildkite/test-npu-ready.yaml
# L4 Test — main+NIGHTLY=1 (scheduled), or PR with specific label (e.g. add label then Rebuild)
- label: "Upload Nightly Pipeline"
depends_on: image-build
key: upload-nightly-pipeline
if: >-
(build.branch == "main" && build.env("NIGHTLY") == "1") ||
(build.branch != "main" && (
build.pull_request.labels includes "nightly-test" ||
build.pull_request.labels includes "omni-test" ||
build.pull_request.labels includes "tts-test" ||
build.pull_request.labels includes "diffusion-x2iat-test" ||
build.pull_request.labels includes "diffusion-x2v-test"
))
commands:
- buildkite-agent pipeline upload .buildkite/test-npu-nightly.yaml
agents:
queue: "ascend-a2b3"
resource_class: "npu-1"
+38
View File
@@ -0,0 +1,38 @@
steps:
- label: ":docker: Build image"
key: image-build
commands:
- "aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/q9t5s3a7"
- "docker build --progress=plain --file docker/Dockerfile.ci -t vllm-omni-ci ."
- "docker tag vllm-omni-ci public.ecr.aws/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT"
- "docker push public.ecr.aws/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT"
agents:
queue: "cpu_queue_premerge"
# L2 Test
- label: "Upload Ready Pipeline"
depends_on: image-build
key: upload-ready-pipeline
commands:
- buildkite-agent pipeline upload .buildkite/test-ready.yml
agents:
queue: "cpu_queue_premerge"
# L3 Test
- label: "Upload Merge Pipeline"
depends_on: image-build
key: upload-merge-pipeline
commands:
- buildkite-agent pipeline upload .buildkite/test-merge.yml
agents:
queue: "cpu_queue_premerge"
# L4 Test
- label: "Upload Nightly Pipeline"
depends_on: image-build
key: upload-nightly-pipeline
if: build.env("NIGHTLY") == "1"
commands:
- buildkite-agent pipeline upload .buildkite/test-nightly.yml
agents:
queue: "cpu_queue_premerge"
+103 -31
View File
@@ -1,38 +1,110 @@
steps: steps:
- label: ":docker: Build image" - input: "Provide Release version here"
key: image-build id: input-release-version
commands: fields:
- "aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/q9t5s3a7" - text: "What is the release version?"
- "docker build --progress=plain --file docker/Dockerfile.ci -t vllm-omni-ci ." key: release-version
- "docker tag vllm-omni-ci public.ecr.aws/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT"
- "docker push public.ecr.aws/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT"
agents:
queue: "cpu_queue_premerge"
# L2 Test - label: "Build and upload wheel"
- label: "Upload Ready Pipeline" depends_on: ~
depends_on: image-build key: "build-wheel"
key: upload-ready-pipeline
commands:
- buildkite-agent pipeline upload .buildkite/test-ready.yml
agents: agents:
queue: "cpu_queue_premerge" queue: cpu_queue_release
commands:
- "curl -LsSf https://astral.sh/uv/install.sh | sh"
- 'export PATH="$HOME/.local/bin:$PATH"'
- "uv venv --python=3.12 && source .venv/bin/activate"
- "uv pip install --upgrade build"
- "python3 -m build"
- "bash .buildkite/scripts/upload-nightly-wheels.sh"
# L3 Test - label: "Generate and upload wheel indices"
- label: "Upload Merge Pipeline" depends_on: "build-wheel"
depends_on: image-build allow_dependency_failure: true
key: upload-merge-pipeline
commands:
- buildkite-agent pipeline upload .buildkite/test-merge.yml
agents: agents:
queue: "cpu_queue_premerge" queue: small_cpu_queue_release
commands:
- "bash .buildkite/scripts/generate-and-upload-nightly-index.sh"
# L4 Test - block: "Upload release wheels to PyPI"
- label: "Upload Nightly Pipeline" depends_on: "build-wheel"
depends_on: image-build key: block-upload-release-wheels
key: upload-nightly-pipeline
if: build.env("NIGHTLY") == "1" - label: "Upload release wheels to PyPI"
commands: depends_on:
- buildkite-agent pipeline upload .buildkite/test-nightly.yml - block-upload-release-wheels
- input-release-version
key: upload-release-wheels
agents: agents:
queue: "cpu_queue_premerge" queue: small_cpu_queue_release
commands:
- "bash .buildkite/scripts/publish-release-wheels-pypi.sh"
- block: "Unblock to build release Docker images"
depends_on: ~
key: block-build-release-images
- group: "Build release Docker images"
key: "build-release-images"
depends_on: block-build-release-images
allow_dependency_failure: true
steps:
- label: "Build release image - x86_64"
key: build-release-image-x86
depends_on: ~
agents:
queue: cpu_queue_release
commands:
- "aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/q9t5s3a7"
- |
DOCKER_BUILDKIT=1 docker build \
--build-arg max_jobs=16 \
--tag public.ecr.aws/q9t5s3a7/vllm-omni-release-repo:$BUILDKITE_COMMIT-$(uname -m) \
--progress plain \
-f docker/Dockerfile.cuda .
- "docker push public.ecr.aws/q9t5s3a7/vllm-omni-release-repo:$BUILDKITE_COMMIT-$(uname -m)"
# re-tag to default image tag and push, just in case arm64 build fails
- "docker tag public.ecr.aws/q9t5s3a7/vllm-omni-release-repo:$BUILDKITE_COMMIT-$(uname -m) public.ecr.aws/q9t5s3a7/vllm-omni-release-repo:$BUILDKITE_COMMIT"
- "docker push public.ecr.aws/q9t5s3a7/vllm-omni-release-repo:$BUILDKITE_COMMIT"
env:
DOCKER_BUILDKIT: "1"
- label: "Build release image - aarch64"
key: build-release-image-arm64
depends_on: ~
agents:
queue: arm64_cpu_queue_release
commands:
- "aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/q9t5s3a7"
- |
DOCKER_BUILDKIT=1 docker build \
--build-arg max_jobs=16 \
--tag public.ecr.aws/q9t5s3a7/vllm-omni-release-repo:$BUILDKITE_COMMIT-$(uname -m) \
--progress plain \
-f docker/Dockerfile.cuda .
- "docker push public.ecr.aws/q9t5s3a7/vllm-omni-release-repo:$BUILDKITE_COMMIT-$(uname -m)"
env:
DOCKER_BUILDKIT: "1"
- block: "Publish release images to DockerHub"
key: block-publish-release-images
depends_on:
- build-release-image-x86
- build-release-image-arm64
- input-release-version
- label: "Publish release images to DockerHub"
depends_on:
- block-publish-release-images
key: publish-release-images-dockerhub
agents:
queue: small_cpu_queue_release
commands:
- "bash .buildkite/scripts/publish-release-images.sh"
plugins:
- docker-login#v3.0.0:
username: vllmbot
password-env: DOCKERHUB_TOKEN
env:
DOCKER_BUILDKIT: "1"
DOCKERHUB_USERNAME: "vllmbot"
@@ -52,5 +52,6 @@ time timeout -k 30 30m docker run \
pip install tblib==3.1.0 pip install tblib==3.1.0
cd /workspace/vllm-omni cd /workspace/vllm-omni
pytest -v -s -m "core_model and xpu and B60" pytest -v -s -m "core_model and xpu and B60"
export VLLM_XPU_USE_SAMPLER_KERNEL=0 # NOTE: Remove this after vLLM v0.21.1 is merged. Fixes Qwen2-5 omni-expansion tests.
pytest -v -s -m "advanced_model and xpu and B60" pytest -v -s -m "advanced_model and xpu and B60"
' '
+50
View File
@@ -0,0 +1,50 @@
#!/bin/bash
# SPDX-License-Identifier: Apache-2.0
#
# Publish release Docker images from ECR to DockerHub.
# Pulls per-arch images, tags with `latest` and versioned tags, pushes them,
# then creates and pushes a multi-arch manifest.
set -euo pipefail
RELEASE_VERSION=$(buildkite-agent meta-data get release-version --default "" | sed 's/^v//')
if [ -z "${RELEASE_VERSION}" ]; then
echo "ERROR: release-version metadata not set"
exit 1
fi
COMMIT="$BUILDKITE_COMMIT"
DOCKERHUB_REPO="vllm/vllm-omni"
ECR_REPO="public.ecr.aws/q9t5s3a7/vllm-omni-release-repo"
echo "========================================"
echo "Publishing release images v${RELEASE_VERSION}"
echo " Commit: ${COMMIT}"
echo "========================================"
# Login to ECR to pull staging images
aws ecr-public get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin public.ecr.aws/q9t5s3a7
docker pull "${ECR_REPO}:${COMMIT}-x86_64"
docker pull "${ECR_REPO}:${COMMIT}-aarch64"
docker tag "${ECR_REPO}:${COMMIT}-x86_64" "${DOCKERHUB_REPO}:latest-x86_64"
docker tag "${ECR_REPO}:${COMMIT}-x86_64" "${DOCKERHUB_REPO}:v${RELEASE_VERSION}-x86_64"
docker push "${DOCKERHUB_REPO}:latest-x86_64"
docker push "${DOCKERHUB_REPO}:v${RELEASE_VERSION}-x86_64"
docker tag "${ECR_REPO}:${COMMIT}-aarch64" "${DOCKERHUB_REPO}:latest-aarch64"
docker tag "${ECR_REPO}:${COMMIT}-aarch64" "${DOCKERHUB_REPO}:v${RELEASE_VERSION}-aarch64"
docker push "${DOCKERHUB_REPO}:latest-aarch64"
docker push "${DOCKERHUB_REPO}:v${RELEASE_VERSION}-aarch64"
docker manifest rm "${DOCKERHUB_REPO}:latest" || true
docker manifest rm "${DOCKERHUB_REPO}:v${RELEASE_VERSION}" || true
docker manifest create "${DOCKERHUB_REPO}:latest" "${DOCKERHUB_REPO}:latest-x86_64" "${DOCKERHUB_REPO}:latest-aarch64"
docker manifest create "${DOCKERHUB_REPO}:v${RELEASE_VERSION}" "${DOCKERHUB_REPO}:v${RELEASE_VERSION}-x86_64" "${DOCKERHUB_REPO}:v${RELEASE_VERSION}-aarch64"
docker manifest push "${DOCKERHUB_REPO}:latest"
docker manifest push "${DOCKERHUB_REPO}:v${RELEASE_VERSION}"
echo ""
echo "Successfully published release images for v${RELEASE_VERSION}"
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: Apache-2.0
set -e
BUCKET="vllm-wheels"
SUBPATH="omni/$BUILDKITE_COMMIT"
S3_COMMIT_PREFIX="s3://$BUCKET/$SUBPATH/"
RELEASE_VERSION=$(buildkite-agent meta-data get release-version | sed 's/^v//')
if [[ -z "$RELEASE_VERSION" ]]; then
echo "[FATAL] release-version metadata not set."
exit 1
fi
echo "Release version: $RELEASE_VERSION"
if [[ -z "$PYPI_TOKEN" ]]; then
echo "[FATAL] PYPI_TOKEN is not set."
exit 1
else
export TWINE_USERNAME="__token__"
export TWINE_PASSWORD="$PYPI_TOKEN"
fi
set -x
if ! command -v uv &> /dev/null; then
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
fi
uv venv --python 3.12 /tmp/vllm-omni-release-env
source /tmp/vllm-omni-release-env/bin/activate
uv pip install twine
DIST_DIR=/tmp/vllm-omni-release-dist
mkdir -p "$DIST_DIR"
echo "Downloading wheels from S3:"
aws s3 ls "$S3_COMMIT_PREFIX"
aws s3 cp --recursive --exclude "*" --include "vllm_omni-${RELEASE_VERSION}*.whl" --exclude "*dev*" "$S3_COMMIT_PREFIX" "$DIST_DIR"
ls -la "$DIST_DIR"
PYPI_WHEEL_FILES=$(find "$DIST_DIR" -name "vllm_omni-${RELEASE_VERSION}*.whl" -not -name "*+*")
if [[ -z "$PYPI_WHEEL_FILES" ]]; then
echo "[FATAL] No wheels found for version ${RELEASE_VERSION}"
exit 1
fi
python3 -m twine check $PYPI_WHEEL_FILES
python3 -m twine upload --non-interactive --verbose $PYPI_WHEEL_FILES
echo "Wheels uploaded to PyPI"
@@ -1,10 +1,18 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Evaluate docs-only skip-ci and upload continuation steps from the same `.buildkite/pipeline.yml` # Evaluate docs-only skip-ci and upload continuation steps from the given pipeline YAML
# (YAML document after the first `---`). Buildkite `if` is evaluated at upload time. # (YAML document after the first `---`). Buildkite `if` is evaluated at upload time.
#
# Usage: upload_pipeline_with_skip_ci.sh [pipeline_yaml]
# pipeline_yaml: path relative to repo root or absolute (default: .buildkite/pipeline.yml)
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
PIPELINE_YML="${ROOT}/.buildkite/pipeline.yml" PIPELINE_ARG="${1:-.buildkite/pipeline.yml}"
if [[ "${PIPELINE_ARG}" = /* ]]; then
PIPELINE_YML="${PIPELINE_ARG}"
else
PIPELINE_YML="${ROOT}/${PIPELINE_ARG}"
fi
# Prints a single digit to stdout: 1 = skip image CI, 0 = run. Logs go to stderr. # Prints a single digit to stdout: 1 = skip image CI, 0 = run. Logs go to stderr.
is_docs_only_change() { is_docs_only_change() {
@@ -21,7 +29,7 @@ is_docs_only_change() {
if [[ "${file_path}" == *.md ]]; then if [[ "${file_path}" == *.md ]]; then
continue continue
fi fi
if [[ "${file_path}" == "mkdocs.yaml" ]]; then if [[ "${file_path}" == "mkdocs.yml" ]]; then
continue continue
fi fi
return 1 return 1
@@ -104,12 +112,13 @@ import pathlib
path = pathlib.Path(os.environ["PIPELINE_YML"]) path = pathlib.Path(os.environ["PIPELINE_YML"])
text = path.read_text(encoding="utf-8") text = path.read_text(encoding="utf-8")
sep = "\n---\n" sep = "\n---\n"
if sep not in text: # Two supported layouts:
raise SystemExit( # - multi-doc: doc 1 = bootstrap (loaded by Buildkite), doc 2 (after `---`) = uploaded steps.
"upload_pipeline_with_skip_ci: .buildkite/pipeline.yml must contain a '\\n---\\n' separator " # - single-doc: whole file is the uploaded steps; caller (UI init step) is the bootstrap.
"(document 1 = bootstrap, document 2 = uploaded steps)" if sep in text:
) _, continuation = text.split(sep, 1)
_, continuation = text.split(sep, 1) else:
continuation = text
skip = os.environ.get("SKIP_CI") == "1" skip = os.environ.get("SKIP_CI") == "1"
# When docs-only skip-ci: skip default CI image, but still build for L4 nightly (PR label nightly-test or # When docs-only skip-ci: skip default CI image, but still build for L4 nightly (PR label nightly-test or
@@ -126,7 +135,7 @@ if skip:
else: else:
rep = "'true'" rep = "'true'"
ready_rep = "'build.branch != \"main\" && build.pull_request.labels includes \"ready\"'" ready_rep = "'build.branch != \"main\" && build.pull_request.labels includes \"ready\"'"
merge_rep = "'(build.branch == \"main\" && build.env(\"NIGHTLY\") != \"1\") || (build.branch != \"main\" && build.pull_request.labels includes \"merge-test\")'" merge_rep = "'(build.branch == \"main\" && build.env(\"NIGHTLY\") != \"1\" && build.env(\"WEEKLY\") != \"1\") || (build.branch != \"main\" && build.pull_request.labels includes \"merge-test\")'"
rendered = ( rendered = (
continuation continuation
.replace("__IMAGE_BUILD_IF__", rep) .replace("__IMAGE_BUILD_IF__", rep)
+29 -33
View File
@@ -9,16 +9,7 @@ steps:
- export VLLM_ROCM_USE_AITER=0 - export VLLM_ROCM_USE_AITER=0
# ignore test_teacache_extractors.py because it use rocm gemm kernel from vLLM # ignore test_teacache_extractors.py because it use rocm gemm kernel from vLLM
# that is not supported on CPU # that is not supported on CPU
- "timeout 20m pytest -v -s -m 'core_model and cpu' --ignore=tests/diffusion/cache/test_teacache_extractors.py --cov=vllm_omni --cov-branch --cov-report=term-missing --cov-report=html --cov-report=xml" - "timeout 60m pytest -v -s -m 'core_model and cpu' --ignore=tests/diffusion/cache/test_teacache_extractors.py --cov=vllm_omni --cov-report=term-missing:skip-covered --cov-report=xml"
- label: "Diffusion Model Test"
agent_pool: mi325_2
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: Blocking
commands:
- export GPU_ARCHS=gfx942
- timeout 20m pytest -s -v tests/e2e/offline_inference/test_t2i_model.py -m "advanced_model and diffusion" --run-level "advanced_model"
- label: "Diffusion Images API LoRA E2E" - label: "Diffusion Images API LoRA E2E"
agent_pool: mi325_1 agent_pool: mi325_1
@@ -26,7 +17,7 @@ steps:
mirror_hardwares: [amdproduction] mirror_hardwares: [amdproduction]
grade: Blocking grade: Blocking
commands: commands:
- timeout 20m pytest -s -v tests/e2e/online_serving/test_images_generations_lora.py - timeout 20m pytest -s -v tests/diffusion/lora/test_images_generations_lora.py
- label: "Diffusion Model CPU offloading Test" - label: "Diffusion Model CPU offloading Test"
agent_pool: mi325_1 agent_pool: mi325_1
@@ -39,9 +30,9 @@ steps:
- | - |
timeout 20m bash -c ' timeout 20m bash -c '
set +e set +e
pytest -s -v tests/e2e/offline_inference/test_diffusion_cpu_offload.py pytest -s -v tests/diffusion/offloader/test_diffusion_cpu_offload.py
EXIT1=\$? EXIT1=\$?
pytest -s -v tests/e2e/offline_inference/test_diffusion_layerwise_offload.py pytest -s -v tests/diffusion/offloader/test_diffusion_layerwise_offload.py
EXIT2=\$? EXIT2=\$?
exit \$((EXIT1 | EXIT2)) exit \$((EXIT1 | EXIT2))
' '
@@ -76,7 +67,7 @@ steps:
commands: commands:
- export VLLM_LOGGING_LEVEL=DEBUG - export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn - export VLLM_WORKER_MULTIPROC_METHOD=spawn
- timeout 20m pytest -s -v tests/e2e/offline_inference/test_sequence_parallel.py - timeout 20m pytest -s -v tests/diffusion/distributed/test_sequence_parallel.py
- timeout 20m pytest -s -v tests/diffusion/distributed/test_ulysses_uaa_perf.py - timeout 20m pytest -s -v tests/diffusion/distributed/test_ulysses_uaa_perf.py
# merge-only tests # merge-only tests
@@ -86,7 +77,15 @@ steps:
mirror_hardwares: [amdproduction] mirror_hardwares: [amdproduction]
grade: Blocking grade: Blocking
commands: commands:
- pytest -s -v tests/e2e/offline_inference/test_zimage_parallelism.py - pytest -s -v tests/diffusion/distributed/test_zimage_parallelism.py
- label: "Diffusion Model Test"
agent_pool: mi325_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: Blocking
commands:
- timeout 30m pytest -s -v tests/e2e/offline_inference/test_z_image.py
- label: "Diffusion GPU Worker Test" - label: "Diffusion GPU Worker Test"
agent_pool: mi325_2 agent_pool: mi325_2
@@ -124,7 +123,6 @@ steps:
grade: Blocking grade: Blocking
commands: commands:
- export VLLM_WORKER_MULTIPROC_METHOD=spawn - export VLLM_WORKER_MULTIPROC_METHOD=spawn
- export VLLM_TEST_CLEAN_GPU_MEMORY=1
- pytest -s -v tests/e2e/offline_inference/test_qwen3_omni.py tests/e2e/online_serving/test_qwen3_omni.py tests/e2e/online_serving/test_mimo_audio.py -m "advanced_model" --run-level "advanced_model" - pytest -s -v tests/e2e/offline_inference/test_qwen3_omni.py tests/e2e/online_serving/test_qwen3_omni.py tests/e2e/online_serving/test_mimo_audio.py -m "advanced_model" --run-level "advanced_model"
- label: "Qwen3-TTS CustomVoice E2E Test" - label: "Qwen3-TTS CustomVoice E2E Test"
@@ -155,17 +153,6 @@ steps:
pytest -s -v tests/e2e/online_serving/test_qwen3_tts_base.py tests/e2e/offline_inference/test_qwen3_tts_base.py -m "advanced_model" --run-level "advanced_model" pytest -s -v tests/e2e/online_serving/test_qwen3_tts_base.py tests/e2e/offline_inference/test_qwen3_tts_base.py -m "advanced_model" --run-level "advanced_model"
' '
- label: "Diffusion Image Edit Test"
agent_pool: mi325_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: Blocking
commands:
- export GPU_ARCHS=gfx942
- export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn
- timeout 20m pytest -s -v tests/e2e/online_serving/test_image_gen_edit.py
# TODO: Bagel test on ROCm is very unstable. @tjtanaa # TODO: Bagel test on ROCm is very unstable. @tjtanaa
# Need to debug before reneable numerical changes across large PRs # Need to debug before reneable numerical changes across large PRs
# # split Bagel Model Test with H100 (Real Weights) into three tests # # split Bagel Model Test with H100 (Real Weights) into three tests
@@ -176,11 +163,10 @@ steps:
# grade: Blocking # grade: Blocking
# commands: # commands:
# - export GPU_ARCHS=gfx942 # - export GPU_ARCHS=gfx942
# - export VLLM_TEST_CLEAN_GPU_MEMORY=1
# - export VLLM_LOGGING_LEVEL=DEBUG # - export VLLM_LOGGING_LEVEL=DEBUG
# - export VLLM_WORKER_MULTIPROC_METHOD=spawn # - export VLLM_WORKER_MULTIPROC_METHOD=spawn
# - export VLLM_ROCM_USE_AITER_RMSNORM=0 # - export VLLM_ROCM_USE_AITER_RMSNORM=0
# - timeout 30m pytest -s -v tests/e2e/offline_inference/test_bagel_text2img.py -m "advanced_model" --run-level "advanced_model" -k "shared_memory" -k "rocm" # - timeout 30m pytest -s -v tests/e2e/offline_inference/test_bagel.py -m "advanced_model" --run-level "advanced_model" -k "shared_memory" -k "rocm"
# - label: "Bagel Img2Img Model Test (2/3)" # - label: "Bagel Img2Img Model Test (2/3)"
# agent_pool: mi325_1 # agent_pool: mi325_1
@@ -189,11 +175,10 @@ steps:
# grade: Blocking # grade: Blocking
# commands: # commands:
# - export GPU_ARCHS=gfx942 # - export GPU_ARCHS=gfx942
# - export VLLM_TEST_CLEAN_GPU_MEMORY=1
# - export VLLM_LOGGING_LEVEL=DEBUG # - export VLLM_LOGGING_LEVEL=DEBUG
# - export VLLM_WORKER_MULTIPROC_METHOD=spawn # - export VLLM_WORKER_MULTIPROC_METHOD=spawn
# - export VLLM_ROCM_USE_AITER_RMSNORM=0 # - export VLLM_ROCM_USE_AITER_RMSNORM=0
# - timeout 30m pytest -s -v tests/e2e/offline_inference/test_bagel_img2img.py -m "advanced_model" --run-level "advanced_model" -k "rocm" # - timeout 30m pytest -s -v tests/e2e/offline_inference/test_bagel.py -m "advanced_model" --run-level "advanced_model" -k "rocm"
# - label: "Bagel Online Serving Test (3/3)" # - label: "Bagel Online Serving Test (3/3)"
# agent_pool: mi325_1 # agent_pool: mi325_1
@@ -202,12 +187,23 @@ steps:
# grade: Blocking # grade: Blocking
# commands: # commands:
# - export GPU_ARCHS=gfx942 # - export GPU_ARCHS=gfx942
# - export VLLM_TEST_CLEAN_GPU_MEMORY=1
# - export VLLM_IMAGE_FETCH_TIMEOUT=60 # - export VLLM_IMAGE_FETCH_TIMEOUT=60
# - export VLLM_LOGGING_LEVEL=DEBUG # - export VLLM_LOGGING_LEVEL=DEBUG
# - export VLLM_WORKER_MULTIPROC_METHOD=spawn # - export VLLM_WORKER_MULTIPROC_METHOD=spawn
# - export VLLM_ROCM_USE_AITER_RMSNORM=0 # - export VLLM_ROCM_USE_AITER_RMSNORM=0
# - timeout 40m pytest -s -v tests/e2e/online_serving/test_bagel_online.py -m "advanced_model" --run-level "advanced_model" -k "rocm" # - timeout 40m pytest -s -v tests/e2e/online_serving/test_bagel.py -m "advanced_model" --run-level "advanced_model" -k "rocm"
- label: "Diffusion · Wan22 Test"
agent_pool: mi325_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: Blocking
commands:
- |
timeout 40m bash -c "
export VLLM_IMAGE_FETCH_TIMEOUT=60
pytest -s -v tests/e2e/offline_inference/test_wan22_t2v.py tests/e2e/online_serving/test_wan22_t2v.py -m 'advanced_model and cuda and diffusion' --run-level 'advanced_model'
"
- label: "Voxtral-TTS E2E Test" - label: "Voxtral-TTS E2E Test"
agent_pool: mi325_1 agent_pool: mi325_1
+30 -43
View File
@@ -9,7 +9,7 @@ steps:
- export VLLM_ROCM_USE_AITER=0 - export VLLM_ROCM_USE_AITER=0
# ignore test_teacache_extractors.py because it use rocm gemm kernel from vLLM # ignore test_teacache_extractors.py because it use rocm gemm kernel from vLLM
# that is not supported on CPU # that is not supported on CPU
- "timeout 20m pytest -vvvv -s -m 'core_model and cpu' --ignore=tests/diffusion/cache/test_teacache_extractors.py --cov=vllm_omni --cov-branch --cov-report=term-missing --cov-report=html --cov-report=xml" - "timeout 60m pytest -v -s -m 'core_model and cpu' --ignore=tests/diffusion/cache/test_teacache_extractors.py --cov=vllm_omni --cov-report=term-missing:skip-covered --cov-report=xml"
- label: "Voxtral TTS CUDA Unit Test" - label: "Voxtral TTS CUDA Unit Test"
agent_pool: mi325_1 agent_pool: mi325_1
@@ -19,21 +19,13 @@ steps:
commands: commands:
- timeout 10m pytest -s -v tests/model_executor/models/voxtral_tts/test_cuda_graph_acoustic_transformer.py - timeout 10m pytest -s -v tests/model_executor/models/voxtral_tts/test_cuda_graph_acoustic_transformer.py
- label: "Diffusion Model Test"
agent_pool: mi325_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: Blocking
commands:
- timeout 30m pytest -s -v tests/e2e/offline_inference/test_t2i_model.py -m "core_model and diffusion" --run-level "core_model"
- label: "Diffusion Batching Test" - label: "Diffusion Batching Test"
agent_pool: mi325_1 agent_pool: mi325_1
depends_on: amd-build depends_on: amd-build
mirror_hardwares: [amdproduction] mirror_hardwares: [amdproduction]
grade: Blocking grade: Blocking
commands: commands:
- timeout 20m pytest -s -v tests/e2e/offline_inference/test_qwen_image_diffusion_batching.py -m "core_model and diffusion" --run-level "core_model" - timeout 20m pytest -s -v tests/diffusion/batching/test_diffusion_batching.py -m "core_model and diffusion" --run-level "core_model"
- label: "Custom Pipeline Test" - label: "Custom Pipeline Test"
agent_pool: mi325_1 agent_pool: mi325_1
@@ -54,9 +46,9 @@ steps:
- | - |
timeout 20m bash -c ' timeout 20m bash -c '
set +e set +e
pytest -s -v tests/e2e/offline_inference/test_diffusion_cpu_offload.py pytest -s -v tests/diffusion/offloader/test_diffusion_cpu_offload.py
EXIT1=\$? EXIT1=\$?
pytest -s -v tests/e2e/offline_inference/test_diffusion_layerwise_offload.py pytest -s -v tests/diffusion/offloader/test_diffusion_layerwise_offload.py
EXIT2=\$? EXIT2=\$?
exit \$((EXIT1 | EXIT2)) exit \$((EXIT1 | EXIT2))
' '
@@ -73,17 +65,6 @@ steps:
# - export VLLM_WORKER_MULTIPROC_METHOD=spawn # - export VLLM_WORKER_MULTIPROC_METHOD=spawn
# - timeout 20m pytest -s -v tests/e2e/offline_inference/test_stable_audio_expansion.py -m "advanced_model and diffusion and L4" --run-level advanced_model # - timeout 20m pytest -s -v tests/e2e/offline_inference/test_stable_audio_expansion.py -m "advanced_model and diffusion and L4" --run-level advanced_model
- label: "AudioX Online Test"
agent_pool: mi325_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: Blocking
commands:
- export GPU_ARCHS=gfx942
- export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn
- timeout 20m pytest -s -v tests/e2e/online_serving/test_audiox_online.py
- label: "Diffusion Cache Backend Test" - label: "Diffusion Cache Backend Test"
agent_pool: mi325_1 agent_pool: mi325_1
depends_on: amd-build depends_on: amd-build
@@ -103,7 +84,7 @@ steps:
commands: commands:
- export VLLM_LOGGING_LEVEL=DEBUG - export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn - export VLLM_WORKER_MULTIPROC_METHOD=spawn
- timeout 20m pytest -s -v tests/e2e/offline_inference/test_sequence_parallel.py -m core_model - timeout 20m pytest -s -v tests/diffusion/distributed/test_sequence_parallel.py -m core_model
- label: "Diffusion GPU Worker Test" - label: "Diffusion GPU Worker Test"
agent_pool: mi325_2 agent_pool: mi325_2
@@ -113,6 +94,27 @@ steps:
commands: commands:
- timeout 20m pytest -s -v tests/diffusion/test_diffusion_worker.py - timeout 20m pytest -s -v tests/diffusion/test_diffusion_worker.py
- label: "Diffusion · Model Test"
agent_pool: mi325_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: Blocking
commands:
- timeout 15m pytest -sv tests/diffusion/models/ -m 'core_model and cuda' --run-level "core_model"
- label: "Diffusion · Wan22 Test"
agent_pool: mi325_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: Blocking
commands:
- |
timeout 40m bash -c "
export VLLM_TEST_CLEAN_GPU_MEMORY=1
export VLLM_IMAGE_FETCH_TIMEOUT=60
pytest -s -v tests/e2e/online_serving/test_wan22_t2v.py -m 'core_model' --run-level 'core_model'
"
- label: "Engine Test" - label: "Engine Test"
agent_pool: mi325_1 agent_pool: mi325_1
depends_on: amd-build depends_on: amd-build
@@ -133,7 +135,6 @@ steps:
# commands: # commands:
# - export VLLM_LOGGING_LEVEL=DEBUG # - export VLLM_LOGGING_LEVEL=DEBUG
# - export VLLM_WORKER_MULTIPROC_METHOD=spawn # - export VLLM_WORKER_MULTIPROC_METHOD=spawn
# - export VLLM_TEST_CLEAN_GPU_MEMORY=1
# - timeout 10m pytest -s -v tests/e2e/offline_inference/test_qwen3_omni.py # - timeout 10m pytest -s -v tests/e2e/offline_inference/test_qwen3_omni.py
# - timeout 20m pytest -s -v tests/e2e/online_serving/test_qwen3_omni.py -m "core_model" --run-level "core_model" # - timeout 20m pytest -s -v tests/e2e/online_serving/test_qwen3_omni.py -m "core_model" --run-level "core_model"
@@ -175,17 +176,6 @@ steps:
pytest -s -v tests/e2e/offline_inference/test_voxtral_tts.py -m "advanced_model" --run-level "advanced_model" pytest -s -v tests/e2e/offline_inference/test_voxtral_tts.py -m "advanced_model" --run-level "advanced_model"
' '
- label: "Diffusion Image Edit Test"
agent_pool: mi325_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: Blocking
commands:
- export GPU_ARCHS=gfx942
- export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn
- timeout 20m pytest -s -v tests/e2e/online_serving/test_image_gen_edit.py
# TODO: Bagel test on ROCm is very unstable. @tjtanaa # TODO: Bagel test on ROCm is very unstable. @tjtanaa
# Need to debug before reneable numerical changes across large PRs # Need to debug before reneable numerical changes across large PRs
# - label: "Bagel Text2Img Model Test" # - label: "Bagel Text2Img Model Test"
@@ -195,11 +185,10 @@ steps:
# grade: Blocking # grade: Blocking
# commands: # commands:
# - export GPU_ARCHS=gfx942 # - export GPU_ARCHS=gfx942
# - export VLLM_TEST_CLEAN_GPU_MEMORY=1
# - export VLLM_LOGGING_LEVEL=DEBUG # - export VLLM_LOGGING_LEVEL=DEBUG
# - export VLLM_WORKER_MULTIPROC_METHOD=spawn # - export VLLM_WORKER_MULTIPROC_METHOD=spawn
# - export VLLM_ROCM_USE_AITER_RMSNORM=0 # - export VLLM_ROCM_USE_AITER_RMSNORM=0
# - timeout 30m pytest -s -v tests/e2e/offline_inference/test_bagel_text2img.py -m "core_model" --run-level "core_model" -k "rocm" # - timeout 30m pytest -s -v tests/e2e/offline_inference/test_bagel.py -m "core_model" --run-level "core_model" -k "rocm"
# - label: "Bagel Img2Img Model Test" # - label: "Bagel Img2Img Model Test"
# agent_pool: mi325_1 # agent_pool: mi325_1
@@ -208,11 +197,10 @@ steps:
# grade: Blocking # grade: Blocking
# commands: # commands:
# - export GPU_ARCHS=gfx942 # - export GPU_ARCHS=gfx942
# - export VLLM_TEST_CLEAN_GPU_MEMORY=1
# - export VLLM_LOGGING_LEVEL=DEBUG # - export VLLM_LOGGING_LEVEL=DEBUG
# - export VLLM_WORKER_MULTIPROC_METHOD=spawn # - export VLLM_WORKER_MULTIPROC_METHOD=spawn
# - export VLLM_ROCM_USE_AITER_RMSNORM=0 # - export VLLM_ROCM_USE_AITER_RMSNORM=0
# - timeout 30m pytest -s -v tests/e2e/offline_inference/test_bagel_img2img.py -m "core_model" --run-level "core_model" -k "rocm" # - timeout 30m pytest -s -v tests/e2e/offline_inference/test_bagel.py -m "core_model" --run-level "core_model" -k "rocm"
# - label: "Bagel Online Serving Test" # - label: "Bagel Online Serving Test"
# agent_pool: mi325_1 # agent_pool: mi325_1
@@ -221,12 +209,11 @@ steps:
# grade: Blocking # grade: Blocking
# commands: # commands:
# - export GPU_ARCHS=gfx942 # - export GPU_ARCHS=gfx942
# - export VLLM_TEST_CLEAN_GPU_MEMORY=1
# - export VLLM_IMAGE_FETCH_TIMEOUT=60 # - export VLLM_IMAGE_FETCH_TIMEOUT=60
# - export VLLM_LOGGING_LEVEL=DEBUG # - export VLLM_LOGGING_LEVEL=DEBUG
# - export VLLM_WORKER_MULTIPROC_METHOD=spawn # - export VLLM_WORKER_MULTIPROC_METHOD=spawn
# - export VLLM_ROCM_USE_AITER_RMSNORM=0 # - export VLLM_ROCM_USE_AITER_RMSNORM=0
# - timeout 40m pytest -s -v tests/e2e/online_serving/test_bagel_online.py -m "core_model" --run-level "core_model" -k "rocm" # - timeout 40m pytest -s -v tests/e2e/online_serving/test_bagel.py -m "core_model" --run-level "core_model" -k "rocm"
- label: "CosyVoice3-TTS E2E Test" - label: "CosyVoice3-TTS E2E Test"
agent_pool: mi325_1 agent_pool: mi325_1
@@ -236,5 +223,5 @@ steps:
commands: commands:
- | - |
timeout 20m bash -c ' timeout 20m bash -c '
pytest -s -v tests/e2e/online_serving/test_cosyvoice3_tts.py -m "core_model" --run-level "core_model" pytest -s -v tests/e2e/online_serving/test_cosyvoice3_tts_expansion.py -m "full_model" --run-level "core_model"
' '
+7 -22
View File
@@ -8,7 +8,7 @@ steps:
grade: Blocking grade: Blocking
commands: commands:
- export GPU_ARCHS=gfx942 - export GPU_ARCHS=gfx942
- pytest -s -v tests/e2e/offline_inference/test_t2i_model.py - pytest -s -v tests/e2e/offline_inference/test_z_image.py
- label: "Diffusion Images API LoRA E2E" - label: "Diffusion Images API LoRA E2E"
timeout_in_minutes: 30 timeout_in_minutes: 30
@@ -20,7 +20,7 @@ steps:
- export GPU_ARCHS=gfx942 - export GPU_ARCHS=gfx942
- export VLLM_LOGGING_LEVEL=DEBUG - export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn - export VLLM_WORKER_MULTIPROC_METHOD=spawn
- pytest -s -v tests/e2e/online_serving/test_images_generations_lora.py - pytest -s -v tests/diffusion/lora/test_images_generations_lora.py
- label: "Diffusion Model CPU offloading Test" - label: "Diffusion Model CPU offloading Test"
timeout_in_minutes: 20 timeout_in_minutes: 20
@@ -32,8 +32,8 @@ steps:
- export GPU_ARCHS=gfx942 - export GPU_ARCHS=gfx942
- export VLLM_LOGGING_LEVEL=DEBUG - export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn - export VLLM_WORKER_MULTIPROC_METHOD=spawn
- pytest -s -v tests/e2e/offline_inference/test_diffusion_cpu_offload.py - pytest -s -v tests/diffusion/offloader/test_diffusion_cpu_offload.py
- pytest -s -v tests/e2e/offline_inference/test_diffusion_layerwise_offload.py - pytest -s -v tests/diffusion/offloader/test_diffusion_layerwise_offload.py
- label: "Diffusion Cache Backend Test" - label: "Diffusion Cache Backend Test"
timeout_in_minutes: 15 timeout_in_minutes: 15
@@ -57,7 +57,7 @@ steps:
- export GPU_ARCHS=gfx942 - export GPU_ARCHS=gfx942
- export VLLM_LOGGING_LEVEL=DEBUG - export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn - export VLLM_WORKER_MULTIPROC_METHOD=spawn
- pytest -s -v tests/e2e/offline_inference/test_sequence_parallel.py -m core_model - pytest -s -v tests/diffusion/distributed/test_sequence_parallel.py -m core_model
- label: "Diffusion Tensor Parallelism Test" - label: "Diffusion Tensor Parallelism Test"
timeout_in_minutes: 20 timeout_in_minutes: 20
@@ -67,7 +67,7 @@ steps:
- export GPU_ARCHS=gfx942 - export GPU_ARCHS=gfx942
- export VLLM_LOGGING_LEVEL=DEBUG - export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn - export VLLM_WORKER_MULTIPROC_METHOD=spawn
- pytest -s -v tests/e2e/offline_inference/test_zimage_parallelism.py - pytest -s -v tests/diffusion/distributed/test_zimage_parallelism.py
- label: "Diffusion GPU Worker Test" - label: "Diffusion GPU Worker Test"
timeout_in_minutes: 20 timeout_in_minutes: 20
@@ -100,25 +100,11 @@ steps:
commands: commands:
- export VLLM_LOGGING_LEVEL=DEBUG - export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn - export VLLM_WORKER_MULTIPROC_METHOD=spawn
- export VLLM_TEST_CLEAN_GPU_MEMORY="1"
- export VLLM_ROCM_USE_AITER=0 - export VLLM_ROCM_USE_AITER=0
- pytest -s -v tests/e2e/offline_inference/test_qwen3_omni.py - pytest -s -v tests/e2e/offline_inference/test_qwen3_omni.py
- pytest -s -v tests/e2e/online_serving/test_qwen3_omni.py - pytest -s -v tests/e2e/online_serving/test_qwen3_omni.py
- label: "Diffusion Image Edit Test"
timeout_in_minutes: 15
agent_pool: mi325_1
depends_on: amd-build
mirror_hardwares: [amdproduction]
grade: Blocking
commands:
- export GPU_ARCHS=gfx942
- export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn
- pytest -s -v tests/e2e/online_serving/test_image_gen_edit.py
- label: "Omni Sleep Mode Test" - label: "Omni Sleep Mode Test"
timeout_in_minutes: 40 timeout_in_minutes: 40
agent_pool: mi325_2 agent_pool: mi325_2
@@ -129,5 +115,4 @@ steps:
- export GPU_ARCHS=gfx942 - export GPU_ARCHS=gfx942
- export VLLM_LOGGING_LEVEL=DEBUG - export VLLM_LOGGING_LEVEL=DEBUG
- export VLLM_WORKER_MULTIPROC_METHOD=spawn - export VLLM_WORKER_MULTIPROC_METHOD=spawn
- export VLLM_TEST_CLEAN_GPU_MEMORY="1" - pytest -s -v tests/entrypoints/test_omni_sleep_mode.py -m "advanced_model and omni and MI325" --run-level "advanced_model"
- pytest -s -v tests/e2e/offline_inference/test_omni_sleep_mode.py -m "advanced_model and omni and MI325" --run-level "advanced_model"
+686 -431
View File
File diff suppressed because it is too large. Load diff
+431 -154
View File
@@ -1,5 +1,6 @@
env: env:
VLLM_WORKER_MULTIPROC_METHOD: spawn VLLM_WORKER_MULTIPROC_METHOD: spawn
VLLM_USE_FLASHINFER_MOE_FP16: 0
HF_HUB_DOWNLOAD_TIMEOUT: 300 HF_HUB_DOWNLOAD_TIMEOUT: 300
HF_HUB_ETAG_TIMEOUT: 60 HF_HUB_ETAG_TIMEOUT: 60
@@ -13,7 +14,7 @@ steps:
- label: ":full_moon: Omni · Function Test with H100" - label: ":full_moon: Omni · Function Test with H100"
timeout_in_minutes: 90 timeout_in_minutes: 90
commands: commands:
- pytest -s -v tests/e2e/ -m "full_model and H100 and omni" --run-level "full_model" --ignore=tests/e2e/accuracy - pytest -sv tests/e2e --run-level "full_model" -m "full_model and H100 and omni" --ignore=tests/e2e/accuracy --ignore=tests/e2e/online_serving/test_qwen3_omni_multi_replicas.py
agents: agents:
queue: "mithril-h100-pool" queue: "mithril-h100-pool"
plugins: plugins:
@@ -169,14 +170,104 @@ steps:
path: /mnt/hf-cache path: /mnt/hf-cache
type: DirectoryOrCreate type: DirectoryOrCreate
- label: ":full_moon: Omni · Perf Test" - label: ":full_moon: Omni · Perf Test · No Async Chunk"
key: nightly-omni-performance key: nightly-omni-performance-no-async-chunk
timeout_in_minutes: 180 timeout_in_minutes: 180
commands: commands:
- export BENCHMARK_DIR=tests/dfx/perf/results - export BENCHMARK_DIR=tests/dfx/perf/results
- | - |
set +e set +e
pytest -s -v tests/dfx/perf/scripts/run_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen_omni.json pytest -s -v tests/dfx/perf/scripts/run_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen3_omni_no_async_chunk.json
EXIT=$$?
buildkite-agent artifact upload "tests/dfx/perf/results/*.json"
exit $$EXIT
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 2
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
- label: ":full_moon: Omni · Perf Test · Async Chunk"
key: nightly-omni-performance-async-chunk
timeout_in_minutes: 180
commands:
- export BENCHMARK_DIR=tests/dfx/perf/results
- |
set +e
pytest -s -v tests/dfx/perf/scripts/run_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen3_omni_async_chunk.json
EXIT=$$?
buildkite-agent artifact upload "tests/dfx/perf/results/*.json"
exit $$EXIT
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 2
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
- label: ":full_moon: Omni · Perf Test · vLLM Text"
key: nightly-omni-performance-vllm-text
timeout_in_minutes: 180
commands:
- export BENCHMARK_DIR=tests/dfx/perf/results
- |
set +e
pytest -s -v tests/dfx/perf/scripts/run_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen3_omni_vllm_text.json
EXIT=$$? EXIT=$$?
buildkite-agent artifact upload "tests/dfx/perf/results/*.json" buildkite-agent artifact upload "tests/dfx/perf/results/*.json"
exit $$EXIT exit $$EXIT
@@ -218,7 +309,7 @@ steps:
- label: ":full_moon: Omni · Multi-Replica Startup Test with 4x H100" - label: ":full_moon: Omni · Multi-Replica Startup Test with 4x H100"
timeout_in_minutes: 45 timeout_in_minutes: 45
commands: commands:
- pytest -s -v tests/e2e/online_serving/test_qwen3_omni_multi_replicas.py -m "core_model" --run-level "core_model" - pytest -s -v tests/e2e/online_serving/test_qwen3_omni_multi_replicas.py -m "full_model" --run-level "core_model"
agents: agents:
queue: "mithril-h100-pool" queue: "mithril-h100-pool"
plugins: plugins:
@@ -262,7 +353,7 @@ steps:
timeout_in_minutes: 90 timeout_in_minutes: 90
commands: commands:
- export VLLM_ALLOW_LONG_MAX_MODEL_LEN="1" - export VLLM_ALLOW_LONG_MAX_MODEL_LEN="1"
- pytest -s -v tests/e2e/ -m "full_model and L4 and omni" -k "not test_qwen2_5" --run-level "full_model" --ignore=tests/e2e/accuracy - pytest -s -v tests/e2e/ -m "full_model and L4 and tts" --run-level "full_model" --ignore=tests/e2e/accuracy
agents: agents:
queue: "gpu_1_queue" # g6.12xlarge instance on AWS, has 4 L4 GPU queue: "gpu_1_queue" # g6.12xlarge instance on AWS, has 4 L4 GPU
plugins: plugins:
@@ -298,7 +389,7 @@ steps:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT - image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources: resources:
limits: limits:
nvidia.com/gpu: 1 nvidia.com/gpu: 2
volumeMounts: volumeMounts:
- name: devshm - name: devshm
mountPath: /dev/shm mountPath: /dev/shm
@@ -332,13 +423,65 @@ steps:
build.pull_request.labels includes "nightly-test" || build.pull_request.labels includes "nightly-test" ||
build.pull_request.labels includes "diffusion-x2iat-test" build.pull_request.labels includes "diffusion-x2iat-test"
steps: steps:
- label: ":full_moon: Diffusion X2I(&A&T) · Function Test with H100" # Use explicit file shards so single-card cases do not reserve 2+ GPUs,
# and Bagel feature coverage is not duplicated via a broad tests/e2e sweep.
- label: ":full_moon: Diffusion X2I(&A&T) · Function Test with H100 · Single-GPU"
timeout_in_minutes: 120 timeout_in_minutes: 120
commands: commands:
- pytest -sv tests/e2e/ -k "not test_wan and not test_bagel_expansion and not hunyuan" -m "full_model and diffusion and H100" --run-level "full_model" --ignore=tests/e2e/accuracy - >-
# revoke after bagel optimization finished pytest -sv
# Keep Bagel expansion and multi-replica tests in their dedicated H100 jobs below. tests/e2e/online_serving/test_qwen_image_expansion.py
# - pytest -sv tests/e2e/ -k "not test_wan and not test_bagel_expansion and not hunyuan" -m "full_model and diffusion and H100" --run-level "full_model" --ignore=tests/e2e/accuracy --ignore=tests/e2e/online_serving/test_bagel_multi_replicas.py tests/e2e/online_serving/test_qwen_image_edit_expansion.py
tests/e2e/online_serving/test_qwen_image_layered_expansion.py
tests/e2e/online_serving/test_longcat_image_expansion.py
tests/e2e/online_serving/test_longcat_image_edit_expansion.py
tests/e2e/online_serving/test_flux_2_dev_expansion.py
tests/e2e/online_serving/test_bagel_expansion.py
-m "full_model and diffusion and H100 and not distributed_cuda"
--run-level "full_model"
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 1
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · Function Test with H100 · Multi-GPU Qwen-Image"
timeout_in_minutes: 120
commands:
- >-
pytest -sv
tests/e2e/online_serving/test_qwen_image_expansion.py
tests/e2e/online_serving/test_qwen_image_edit_expansion.py
-m "full_model and diffusion and H100 and distributed_cuda"
--run-level "full_model"
agents: agents:
queue: "mithril-h100-pool" queue: "mithril-h100-pool"
plugins: plugins:
@@ -373,10 +516,59 @@ steps:
path: /mnt/hf-cache path: /mnt/hf-cache
type: DirectoryOrCreate type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · Bagel Function Test with H100" - label: ":full_moon: Diffusion X2I(&A&T) · Function Test with H100 · Multi-GPU Layered/LongCat/FLUX"
timeout_in_minutes: 90
commands:
- >-
pytest -sv
tests/e2e/online_serving/test_qwen_image_layered_expansion.py
tests/e2e/online_serving/test_longcat_image_expansion.py
tests/e2e/online_serving/test_longcat_image_edit_expansion.py
tests/e2e/online_serving/test_flux_2_dev_expansion.py
-m "full_model and diffusion and H100 and distributed_cuda"
--run-level "full_model"
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 2
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · Function Test with H100 · Multi-GPU Bagel"
timeout_in_minutes: 120 timeout_in_minutes: 120
commands: commands:
- pytest -sv tests/e2e/online_serving/test_bagel_expansion.py -m "full_model and diffusion and H100" --run-level "full_model" - >-
pytest -sv
tests/e2e/online_serving/test_bagel_expansion.py
-m "full_model and diffusion and H100 and distributed_cuda"
--run-level "full_model"
agents: agents:
queue: "mithril-h100-pool" queue: "mithril-h100-pool"
plugins: plugins:
@@ -411,44 +603,6 @@ steps:
path: /mnt/hf-cache path: /mnt/hf-cache
type: DirectoryOrCreate type: DirectoryOrCreate
# revoke after bagel optimization finished
# - label: ":full_moon: Diffusion X2I(&A&T) · BAGEL Multi-Replica with H100"
# timeout_in_minutes: 120
# commands:
# - pytest -s -v tests/e2e/online_serving/test_bagel_multi_replicas.py -m "full_model and diffusion and H100" --run-level "full_model"
# agents:
# queue: "mithril-h100-pool"
# plugins:
# - kubernetes:
# podSpec:
# containers:
# - image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
# resources:
# limits:
# nvidia.com/gpu: 4
# volumeMounts:
# - name: devshm
# mountPath: /dev/shm
# - name: hf-cache
# mountPath: /root/.cache/huggingface
# env:
# - name: HF_HOME
# value: /root/.cache/huggingface
# - name: HF_TOKEN
# valueFrom:
# secretKeyRef:
# name: hf-token-secret
# key: token
# nodeSelector:
# node.kubernetes.io/instance-type: gpu-h100-sxm
# volumes:
# - name: devshm
# emptyDir:
# medium: Memory
# - name: hf-cache
# hostPath:
# path: /mnt/hf-cache
# type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · Function Test with L4" - label: ":full_moon: Diffusion X2I(&A&T) · Function Test with L4"
timeout_in_minutes: 60 timeout_in_minutes: 60
@@ -471,7 +625,6 @@ steps:
- label: ":full_moon: Diffusion X2I(&A&T) · Doc Test" - label: ":full_moon: Diffusion X2I(&A&T) · Doc Test"
timeout_in_minutes: 60 timeout_in_minutes: 60
commands: commands:
- export VLLM_TEST_CLEAN_GPU_MEMORY="1"
- pytest -s -v tests/examples/*/test_text_to_image.py -m "full_model and example and H100" --run-level "full_model" - pytest -s -v tests/examples/*/test_text_to_image.py -m "full_model and example and H100" --run-level "full_model"
agents: agents:
queue: "mithril-h100-pool" queue: "mithril-h100-pool"
@@ -507,87 +660,6 @@ steps:
path: /mnt/hf-cache path: /mnt/hf-cache
type: DirectoryOrCreate type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · GEBench Accuracy Test"
timeout_in_minutes: 60
commands:
- pytest -s -v tests/e2e/accuracy/test_gebench_h100_smoke.py --run-level full_model --gebench-model Qwen/Qwen-Image-2512 --accuracy-judge-model QuantTrio/Qwen3-VL-30B-A3B-Instruct-AWQ --accuracy-gpu 0 --gebench-port 8093 --accuracy-workers 1
- buildkite-agent artifact upload "tests/e2e/accuracy/artifacts/gebench_qwen-image-2512/summary*.json"
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 1
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · GEdit-Bench Accuracy Test"
timeout_in_minutes: 60
commands:
- pytest -s -v tests/e2e/accuracy/test_gedit_bench_h100_smoke.py --run-level full_model --gedit-model Qwen/Qwen-Image-Edit --accuracy-judge-model QuantTrio/Qwen3-VL-30B-A3B-Instruct-AWQ --accuracy-gpu 0 --gedit-port 8093 --gedit-samples-per-group 20 --accuracy-workers 1
- buildkite-agent artifact upload "tests/e2e/accuracy/artifacts/gedit_scores_qwen-image-edit/qwen-image-edit_all_all_vie_score_*.csv"
- buildkite-agent artifact upload "tests/e2e/accuracy/artifacts/gedit_scores_qwen-image-edit/qwen-image-edit_all_all_summary_*.json"
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 1
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: VLLM_HTTP_TIMEOUT_KEEP_ALIVE
value: "120"
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · Accuracy Test" - label: ":full_moon: Diffusion X2I(&A&T) · Accuracy Test"
timeout_in_minutes: 180 timeout_in_minutes: 180
commands: commands:
@@ -629,28 +701,20 @@ steps:
path: /mnt/hf-cache path: /mnt/hf-cache
type: DirectoryOrCreate type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · Perf Test" - label: ":full_moon: Diffusion X2I(&A&T) · Perf Test · Qwen-Image"
key: nightly-diffusion-x2iat-performance key: nightly-diffusion-x2iat-performance-qwen-image
timeout_in_minutes: 360 timeout_in_minutes: 180
commands: commands:
- export DIFFUSION_BENCHMARK_DIR=tests/dfx/perf/results - export DIFFUSION_BENCHMARK_DIR=tests/dfx/perf/results
- export DIFFUSION_ATTENTION_BACKEND=FLASH_ATTN - export DIFFUSION_ATTENTION_BACKEND=FLASH_ATTN
- export CACHE_DIT_VERSION=1.3.0 - export CACHE_DIT_VERSION=1.3.0
# [HACK]: run upload in the same command block as pytest.
# Because `exit` aborts the entire commands list.
- | - |
set +e set +e
pytest -s -v tests/dfx/perf/scripts/run_diffusion_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen_image_vllm_omni.json pytest -s -v tests/dfx/perf/scripts/run_diffusion_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen_image_vllm_omni.json
EXIT1=$$? EXIT=$$?
pytest -s -v tests/dfx/perf/scripts/run_diffusion_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen_image_edit_vllm_omni.json
EXIT2=$$?
pytest -s -v tests/dfx/perf/scripts/run_diffusion_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen_image_edit_2509_vllm_omni.json
EXIT3=$$?
pytest -s -v tests/dfx/perf/scripts/run_diffusion_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen_image_layered_vllm_omni.json
EXIT4=$$?
buildkite-agent artifact upload "tests/dfx/perf/results/diffusion_result_*.json" buildkite-agent artifact upload "tests/dfx/perf/results/diffusion_result_*.json"
buildkite-agent artifact upload "tests/dfx/perf/results/logs/*.log" buildkite-agent artifact upload "tests/dfx/perf/results/logs/*.log"
exit $$((EXIT1 | EXIT2 | EXIT3 | EXIT4)) exit $$EXIT
agents: agents:
queue: "mithril-h100-pool" queue: "mithril-h100-pool"
plugins: plugins:
@@ -685,6 +749,150 @@ steps:
path: /mnt/hf-cache path: /mnt/hf-cache
type: DirectoryOrCreate type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · Perf Test · Qwen-Image-Edit"
key: nightly-diffusion-x2iat-performance-qwen-image-edit
timeout_in_minutes: 180
commands:
- export DIFFUSION_BENCHMARK_DIR=tests/dfx/perf/results
- export DIFFUSION_ATTENTION_BACKEND=FLASH_ATTN
- export CACHE_DIT_VERSION=1.3.0
- |
set +e
pytest -s -v tests/dfx/perf/scripts/run_diffusion_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen_image_edit_vllm_omni.json
EXIT=$$?
buildkite-agent artifact upload "tests/dfx/perf/results/diffusion_result_*.json"
buildkite-agent artifact upload "tests/dfx/perf/results/logs/*.log"
exit $$EXIT
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 4
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · Perf Test · Qwen-Image-Edit-2509"
key: nightly-diffusion-x2iat-performance-qwen-image-edit-2509
timeout_in_minutes: 180
commands:
- export DIFFUSION_BENCHMARK_DIR=tests/dfx/perf/results
- export DIFFUSION_ATTENTION_BACKEND=FLASH_ATTN
- export CACHE_DIT_VERSION=1.3.0
- |
set +e
pytest -s -v tests/dfx/perf/scripts/run_diffusion_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen_image_edit_2509_vllm_omni.json
EXIT=$$?
buildkite-agent artifact upload "tests/dfx/perf/results/diffusion_result_*.json"
buildkite-agent artifact upload "tests/dfx/perf/results/logs/*.log"
exit $$EXIT
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 4
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · Perf Test · Qwen-Image-Layered"
key: nightly-diffusion-x2iat-performance-qwen-image-layered
timeout_in_minutes: 120
commands:
- export DIFFUSION_BENCHMARK_DIR=tests/dfx/perf/results
- export DIFFUSION_ATTENTION_BACKEND=FLASH_ATTN
- export CACHE_DIT_VERSION=1.3.0
- |
set +e
pytest -s -v tests/dfx/perf/scripts/run_diffusion_benchmark.py --test-config-file tests/dfx/perf/tests/test_qwen_image_layered_vllm_omni.json
EXIT=$$?
buildkite-agent artifact upload "tests/dfx/perf/results/diffusion_result_*.json"
buildkite-agent artifact upload "tests/dfx/perf/results/logs/*.log"
exit $$EXIT
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 1
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
- label: ":full_moon: Diffusion X2I(&A&T) · HunyuanImage3 · DiT Perf Test" - label: ":full_moon: Diffusion X2I(&A&T) · HunyuanImage3 · DiT Perf Test"
key: nightly-hunyuan-image3-performance key: nightly-hunyuan-image3-performance
soft_fail: true soft_fail: true
@@ -751,7 +959,7 @@ steps:
- label: ":full_moon: Diffusion X2V · Function Test" - label: ":full_moon: Diffusion X2V · Function Test"
timeout_in_minutes: 90 timeout_in_minutes: 90
commands: commands:
- pytest -s -v tests/e2e/online_serving/test_wan22_expansion.py tests/e2e/online_serving/test_wan_2_1_vace_expansion.py tests/e2e/online_serving/test_hunyuan_video_15_expansion.py --run-level "full_model" - pytest -s -v tests/e2e/online_serving/test_wan22_expansion.py tests/e2e/online_serving/test_wan_2_1_vace_expansion.py tests/e2e/online_serving/test_hunyuan_video_15_expansion.py -m "full_model and cuda" --run-level "full_model"
agents: agents:
queue: "mithril-h100-pool" queue: "mithril-h100-pool"
plugins: plugins:
@@ -790,7 +998,7 @@ steps:
timeout_in_minutes: 180 timeout_in_minutes: 180
commands: commands:
- pytest -s -v tests/e2e/accuracy/wan22_i2v/test_wan22_i2v_video_similarity.py -m full_model --run-level full_model - pytest -s -v tests/e2e/accuracy/wan22_i2v/test_wan22_i2v_video_similarity.py -m full_model --run-level full_model
- pytest -s -v tests/e2e/accuracy/test_ltx2_3_video_similarity.py -m advanced_model --run-level advanced_model - pytest -s -v tests/e2e/accuracy/test_ltx2_3_video_similarity.py -m full_model --run-level full_model
- pytest -s -v tests/e2e/accuracy/test_diffusers_backend_similarity.py -k '2v_matches_diffusers' -m full_model --run-level full_model - pytest -s -v tests/e2e/accuracy/test_diffusers_backend_similarity.py -k '2v_matches_diffusers' -m full_model --run-level full_model
agents: agents:
queue: "mithril-h100-pool" queue: "mithril-h100-pool"
@@ -873,6 +1081,65 @@ steps:
path: /mnt/hf-cache path: /mnt/hf-cache
type: DirectoryOrCreate type: DirectoryOrCreate
- group: ":card_index_dividers: Diffusion · Quantization Test"
depends_on: upload-nightly-pipeline
if: build.env("NIGHTLY") == "1" || build.pull_request.labels includes "nightly-test" || build.pull_request.labels includes "diffusion-x2iat-test"
steps:
- label: ":full_moon: Quantization Test with H100"
timeout_in_minutes: 90
commands:
- timeout 30m pytest -sv tests/diffusion/quantization -m 'full_model and cuda and H100' --run-level "full_model"
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 1
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
- label: ":full_moon: Quantization Test with L4"
timeout_in_minutes: 60
commands:
- pytest -sv tests/diffusion/quantization -m 'full_model and cuda and L4' --run-level "full_model"
agents:
queue: "gpu_1_queue" # g6.4xlarge instance on AWS, has 1 L4 GPU
plugins:
- docker#v5.2.0:
image: public.ecr.aws/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
always-pull: true
propagate-environment: true
environment:
- "HF_HOME=/fsx/hf_cache"
- "HF_TOKEN"
volumes:
- "/fsx/hf_cache:/fsx/hf_cache"
- label: ":bar_chart: Testcase Statistics" - label: ":bar_chart: Testcase Statistics"
key: nightly-testcase-statistics key: nightly-testcase-statistics
timeout_in_minutes: 120 timeout_in_minutes: 120
@@ -890,7 +1157,7 @@ steps:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT - image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources: resources:
limits: limits:
nvidia.com/gpu: 2 nvidia.com/gpu: 1
volumeMounts: volumeMounts:
- name: devshm - name: devshm
mountPath: /dev/shm mountPath: /dev/shm
@@ -919,9 +1186,14 @@ steps:
- label: ":email: Nightly Collection & Email" - label: ":email: Nightly Collection & Email"
key: nightly-perf-distribution key: nightly-perf-distribution
depends_on: depends_on:
- nightly-omni-performance - nightly-omni-performance-no-async-chunk
- nightly-omni-performance-async-chunk
- nightly-omni-performance-vllm-text
- nightly-tts-performance - nightly-tts-performance
- nightly-diffusion-x2iat-performance - nightly-diffusion-x2iat-performance-qwen-image
- nightly-diffusion-x2iat-performance-qwen-image-edit
- nightly-diffusion-x2iat-performance-qwen-image-edit-2509
- nightly-diffusion-x2iat-performance-qwen-image-layered
- nightly-diffusion-x2v-performance - nightly-diffusion-x2v-performance
- nightly-testcase-statistics - nightly-testcase-statistics
if: build.env("NIGHTLY") == "1" if: build.env("NIGHTLY") == "1"
@@ -930,8 +1202,13 @@ steps:
- export DEFAULT_INPUT_DIR=tests/dfx/perf/results - export DEFAULT_INPUT_DIR=tests/dfx/perf/results
- export DEFAULT_OUTPUT_DIR=tests/dfx/perf/results - export DEFAULT_OUTPUT_DIR=tests/dfx/perf/results
- buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-tts-performance - buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-tts-performance
- buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-omni-performance - buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-omni-performance-no-async-chunk
- buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-diffusion-x2iat-performance - buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-omni-performance-async-chunk
- buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-omni-performance-vllm-text
- buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-diffusion-x2iat-performance-qwen-image
- buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-diffusion-x2iat-performance-qwen-image-edit
- buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-diffusion-x2iat-performance-qwen-image-edit-2509
- buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-diffusion-x2iat-performance-qwen-image-layered
- buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-diffusion-x2v-performance - buildkite-agent artifact download "tests/dfx/perf/results/*.json" . --step nightly-diffusion-x2v-performance
- buildkite-agent artifact download "tests/dfx/perf/results/*.html" . --step nightly-testcase-statistics - buildkite-agent artifact download "tests/dfx/perf/results/*.html" . --step nightly-testcase-statistics
- python tools/nightly/generate_nightly_perf_excel.py - python tools/nightly/generate_nightly_perf_excel.py
+24
View File
@@ -0,0 +1,24 @@
steps:
- group: ":card_index_dividers: Diffusion X2V Model Test"
key: nightly-diffusion-x2v-group
depends_on: upload-nightly-pipeline
if: >-
build.env("NIGHTLY") == "1" ||
build.pull_request.labels includes "nightly-test" ||
build.pull_request.labels includes "diffusion-x2v-test"
steps:
- label: ":full_moon: Diffusion X2V · Function Test"
agents:
queue: "ascend-a2b3"
resource_class: "npu-4"
image: "swr.cn-southwest-2.myhuaweicloud.com/modelfoundry/vllm-omni-ci-npu:${BUILDKITE_COMMIT}"
plugins:
- kubernetes:
podSpecPatch:
imagePullSecrets:
- name: swr-secret
env:
VLLM_WORKER_MULTIPROC_METHOD: spawn
HF_TOKEN: "${HF_TOKEN}"
commands:
- pytest -s -v tests/e2e/online_serving/test_wan22_expansion.py -k npu_i2v --run-level "full_model"
+21
View File
@@ -0,0 +1,21 @@
steps:
- group: ":card_index_dividers: TTS Model Test"
key: ready-tts-group
depends_on: upload-ready-pipeline
steps:
- label: ":sound: TTS · Qwen3-TTS CustomVoice Test"
agents:
queue: "ascend-a2b3"
resource_class: "npu-1"
image: "swr.cn-southwest-2.myhuaweicloud.com/modelfoundry/vllm-omni-ci-npu:${BUILDKITE_COMMIT}"
plugins:
- kubernetes:
podSpecPatch:
imagePullSecrets:
- name: swr-secret
env:
VLLM_WORKER_MULTIPROC_METHOD: spawn
VLLM_ALLOW_LONG_MAX_MODEL_LEN: "1"
HF_TOKEN: "${HF_TOKEN}"
commands:
- pytest -s -v tests/e2e/online_serving/test_qwen3_tts_customvoice.py -m 'core_model' --run-level 'core_model'
+452 -549
View File
File diff suppressed because it is too large. Load diff
+63
View File
@@ -1,5 +1,6 @@
env: env:
VLLM_WORKER_MULTIPROC_METHOD: spawn VLLM_WORKER_MULTIPROC_METHOD: spawn
VLLM_USE_FLASHINFER_MOE_FP16: 0
HF_HUB_DOWNLOAD_TIMEOUT: 300 HF_HUB_DOWNLOAD_TIMEOUT: 300
HF_HUB_ETAG_TIMEOUT: 60 HF_HUB_ETAG_TIMEOUT: 60
@@ -83,3 +84,65 @@ steps:
hostPath: hostPath:
path: /mnt/hf-cache path: /mnt/hf-cache
type: DirectoryOrCreate type: DirectoryOrCreate
# Invalid HTTP / WS payloads vs live Omni servers (Issue #3649 tracks known skips).
- group: "Reliability Test - Invalid parameters Test"
depends_on: upload-weekly-pipeline
if: build.env("WEEKLY") == "1" || build.pull_request.labels includes "weekly-test"
steps:
- label: "Invalid parameters Test · H100"
timeout_in_minutes: 120
commands:
- pytest -s -v tests/dfx/reliability/invalid_param_test/ -m "slow and H100"
agents:
queue: "mithril-h100-pool"
plugins:
- kubernetes:
podSpec:
containers:
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
resources:
limits:
nvidia.com/gpu: 2
volumeMounts:
- name: devshm
mountPath: /dev/shm
- name: hf-cache
mountPath: /root/.cache/huggingface
env:
- name: HF_HOME
value: /root/.cache/huggingface
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token-secret
key: token
nodeSelector:
node.kubernetes.io/instance-type: gpu-h100-sxm
volumes:
- name: devshm
emptyDir:
medium: Memory
- name: hf-cache
hostPath:
path: /mnt/hf-cache
type: DirectoryOrCreate
# Stable Audio Open, Qwen3-TTS speech/batch/voices paths marked ``L4``.
- label: "Invalid parameters Test · L4"
timeout_in_minutes: 120
commands:
- pytest -s -v tests/dfx/reliability/invalid_param_test/ -m "slow and L4"
agents:
queue: "gpu_1_queue"
plugins:
- docker#v5.2.0:
image: public.ecr.aws/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
always-pull: true
propagate-environment: true
shm-size: "8gb"
environment:
- "HF_HOME=/fsx/hf_cache"
- "HF_TOKEN"
volumes:
- "/fsx/hf_cache:/fsx/hf_cache"
+4 -4
View File
@@ -515,15 +515,15 @@ After adding parallelism support, update:
### Step 11: Add CPU Offload Support ### Step 11: Add CPU Offload Support
Implement `SupportsModuleOffload` on your pipeline class to enable Implement `SupportsComponentDiscovery` on your pipeline class to enable
`--enable-cpu-offload` and `--enable-layerwise-offload`. The protocol `--enable-cpu-offload` and `--enable-layerwise-offload`. The protocol
declares which submodules the offloader should manage: declares which submodules the offloader should manage:
```python ```python
from typing import ClassVar from typing import ClassVar
from vllm_omni.diffusion.models.interface import SupportsModuleOffload from vllm_omni.diffusion.models.interface import SupportsComponentDiscovery
class YourPipeline(nn.Module, SupportsModuleOffload): class YourPipeline(nn.Module, SupportsComponentDiscovery):
_dit_modules: ClassVar[list[str]] = ["transformer"] _dit_modules: ClassVar[list[str]] = ["transformer"]
_encoder_modules: ClassVar[list[str]] = ["text_encoder"] _encoder_modules: ClassVar[list[str]] = ["text_encoder"]
_vae_modules: ClassVar[list[str]] = ["vae"] _vae_modules: ClassVar[list[str]] = ["vae"]
@@ -540,7 +540,7 @@ class YourPipeline(nn.Module, SupportsModuleOffload):
All attribute names support dotted paths for nested submodules All attribute names support dotted paths for nested submodules
(e.g. `"pipe.transformer"`, `"bagel.time_embedder"`). (e.g. `"pipe.transformer"`, `"bagel.time_embedder"`).
Pipelines without `SupportsModuleOffload` fall back to scanning Pipelines without `SupportsComponentDiscovery` fall back to scanning
well-known attribute names (`transformer`, `text_encoder`, `vae`, well-known attribute names (`transformer`, `text_encoder`, `vae`,
etc.), which fails for non-standard names. etc.), which fails for non-standard names.
@@ -92,6 +92,7 @@ class TPSelfAttention(nn.Module):
softmax_scale=1.0 / (self.head_dim ** 0.5), softmax_scale=1.0 / (self.head_dim ** 0.5),
causal=False, causal=False,
num_kv_heads=self.to_qkv.num_kv_heads, # Local KV heads per GPU num_kv_heads=self.to_qkv.num_kv_heads, # Local KV heads per GPU
role="self",
) )
def forward(self, x): def forward(self, x):
@@ -61,6 +61,7 @@ class SelfAttentionBlock(nn.Module):
softmax_scale=1.0 / (self.head_dim ** 0.5), softmax_scale=1.0 / (self.head_dim ** 0.5),
causal=False, causal=False,
num_kv_heads=num_heads, num_kv_heads=num_heads,
role="self",
) )
def forward(self, x, attn_mask=None): def forward(self, x, attn_mask=None):
@@ -75,6 +76,24 @@ class SelfAttentionBlock(nn.Module):
return self.to_out(out) return self.to_out(out)
``` ```
**Declaring the attention role.** Always pass a `role` string to `Attention(...)`. It is the lookup key users target with `--diffusion-attention-config.per_role.<role>.backend` to swap kernels per site without touching model code.
| Convention | When to use |
|---|---|
| `role="self"` | Q/K/V come from the same hidden state |
| `role="cross"` | K/V come from a separate `encoder_hidden_states`; pair with `skip_sequence_parallel=True` if K/V is replicated across SP ranks |
For multi-modal sites that don't fit `self` / `cross`, use a dot-namespaced role and provide `role_category` so user config can fall back to the generic category:
```python
self.audio_to_video_attn = Attention(
num_heads=num_heads, head_size=self.head_dim,
softmax_scale=1.0 / (self.head_dim ** 0.5), causal=False,
role="mymodel.audio_to_video",
role_category="cross",
)
```
#### Fused QKV with TP (Advanced) #### Fused QKV with TP (Advanced)
For tensor parallelism, use vLLM's parallel linear layers: For tensor parallelism, use vLLM's parallel linear layers:
@@ -104,6 +123,7 @@ class TPSelfAttention(nn.Module):
softmax_scale=1.0 / (self.head_dim ** 0.5), softmax_scale=1.0 / (self.head_dim ** 0.5),
causal=False, causal=False,
num_kv_heads=num_heads, num_kv_heads=num_heads,
role="self",
) )
``` ```
@@ -68,7 +68,7 @@ out = out.reshape(batch, seq_len, -1)
### Different Output vs Diffusers Reference ### Different Output vs Diffusers Reference
**Common causes**: **Common causes**:
1. **Attention backend difference**: FlashAttention vs SDPA may produce slightly different results. Set `DIFFUSION_ATTENTION_BACKEND=TORCH_SDPA` to match diffusers 1. **Attention backend difference**: FlashAttention vs SDPA may produce slightly different results. Force SDPA via `--diffusion-attention-backend TORCH_SDPA` (or `DIFFUSION_ATTENTION_BACKEND=TORCH_SDPA`) to match diffusers; for finer-grained per-role overrides see `--diffusion-attention-config.per_role.<role>.backend`
2. **Float precision**: vLLM-Omni may use bfloat16 where diffusers uses float32 for some operations 2. **Float precision**: vLLM-Omni may use bfloat16 where diffusers uses float32 for some operations
3. **Missing normalization**: Check all LayerNorm/RMSNorm are preserved 3. **Missing normalization**: Check all LayerNorm/RMSNorm are preserved
4. **Scheduler rounding**: Some schedulers have numerical sensitivity 4. **Scheduler rounding**: Some schedulers have numerical sensitivity
+488
View File
@@ -0,0 +1,488 @@
---
name: diffusion-perf-opt
description: Diagnose and optimize vLLM Omni diffusion workloads, especially Wan/Qwen/Flux-style image and video generation. Use when Codex is asked to analyze profiling traces, choose parallel strategies, inspect torch profiler trace.json or trace.json.gz timelines, estimate optimization ROI, investigate GPU idle/free bubbles, compare USP/CFG/HSDP/VAE parallelism, or design operator/host/quantization optimizations for vLLM Omni.
---
# vLLM Omni Performance Optimization
Use this skill to run a disciplined optimization loop for vLLM Omni diffusion workloads. Keep two ideas separate: real performance baselines are collected with low overhead, while torch profiler traces are diagnostic artifacts and may distort latency.
## First Questions
Before proposing changes, ask for the optimization scene if it is not already known:
- GPU model, card count, topology, and whether NVLink is present.
- Model and pipeline, for example Wan2.2 I2V A14B.
- User workload: resolution, frames, steps, batch/concurrency, CFG scales, prompt/image inputs.
- Three runnable commands for each model/workload, or scripts that generate them:
- **Server startup command**: exact `vllm serve` command, environment variables, model path, port, parallelism flags, profiler flags, and precision/compile settings.
- **User request command**: exact single-request client command (for example curl or Python client) with fixed prompt/media/seed/size/steps. Use this to validate correctness and collect per-request stage timings.
- **User benchmark command**: exact repeatable benchmark command or script with warmup count, measured iteration count, concurrency/batch policy, output directory, and summary format.
- Current enabled strategies: USP/SP, CFG parallel, HSDP/FSDP, VAE patch parallel, torch.compile, profiler options.
- Optimization target: latency, throughput, memory, cost, or quality-preserving speed.
- Precision/quality tolerance: bf16/fp8/quantization/sparsity/approximate attention allowed or not.
## Workflow
1. **Freeze the measurement protocol and commands.**
- Before analyzing parallel strategies or traces, establish the three commands for the target model/workload: server startup, single user request, and user benchmark.
- Prefer checked-in scripts or one-off shell scripts over free-form commands in chat. The scripts should make workload variables explicit, including model path, port/base URL, prompt/media inputs, resolution, frames, steps, seed, warmup, measured iterations, concurrency, and output path.
- Measurement reports must preserve the concrete process, not just the final numbers. For each tested configuration, write down the server startup command, the user request command, the user benchmark command or polling loop, the final server response metrics, and the client-side timing/HTTP result.
- Present all duration and latency values in **milliseconds (ms)** in tables and summaries. If an API returns seconds, convert to ms before reporting. Keep raw units only inside quoted raw logs or source snippets.
- Keep profiler modes separate in the command set:
- Baseline/benchmark commands should avoid torch profiler and stack collection.
- Diagnostic commands may enable torch profiler after a baseline identifies a bottleneck.
- If any of the three commands is missing, ask for it or create a proposed script before proceeding.
- Make clear which command is authoritative for correctness validation and which command is authoritative for performance numbers.
2. **Collect a real baseline.**
- Disable PyTorch profiler and stack collection.
- Disable or fix torch.compile state so A/B is fair.
- Avoid `--enforce-eager` for production-speed baselines unless eager is the target.
- Prefer `--log-stats` and `--enable-diffusion-pipeline-profiler` for low-overhead stage timing. PR 3069 has the relevant metrics/log-stats changes; if local code does not include them, fetch or cherry-pick the minimal metrics changes rather than merging unrelated PR drift.
- Run warmup requests and exclude first-request lazy init.
3. **Model the parallel strategy before testing.**
- Estimate compute and communication for self-attention, cross-attention, FFN, CFG, VAE encode/decode, and HSDP.
- Select a small candidate matrix rather than exhaustively testing everything.
- Typical 2-card candidates: `USP=2`, `CFG=2`, `USP=1/HSDP on-off`, VAE parallel on-off if memory allows.
- Typical 4-card candidates: `USP=4`, `CFG=2 x USP=2`, `USP=2 x HSDP`, VAE parallel on-off.
- Typical 8-card candidates for official CFG-enabled video diffusion models:
- Primary candidate: `CFG=2 x USP=4` with VAE patch parallel across all 8 ranks.
- Compare against: `CFG=1 x USP=8` to test whether larger sequence parallel groups beat CFG branch parallelism.
- Isolate VAE parallelism: keep the DiT strategy fixed and compare VAE patch parallel on/off or different VAE patch sizes.
- If long-video `diffuse` remains dominant and Ulysses all-to-all is suspected, test a hybrid sequence strategy such as `CFG=2 x USP=2 x Ring=2`.
- Test HSDP off only if the model fits without it; treat HSDP primarily as a memory strategy until A/B proves otherwise.
- Prefer USP/SP for long video token sequences; prefer CFG parallel when CFG doubles transformer forwards and sequence length is modest.
- Convert workload shape to latent/patch token counts before choosing candidates. For video models, record frames, latent frames, latent HxW, patch size, approximate token count, and which stages should scale with the token count.
4. **Search for the best parallel configuration.**
- Start with a small matrix that answers one question per comparison:
- `CFG parallel vs larger USP`: compare `CFG=2 x USP=world/2` against `CFG=1 x USP=world` for CFG-enabled workloads.
- `VAE patch parallel value`: compare the best DiT strategy with VAE patch parallel enabled and disabled.
- `HSDP cost`: compare HSDP on/off only when both configurations fit memory.
- `Ulysses vs Ring`: test a Ring/Ulysses hybrid only after long-sequence `diffuse` is confirmed dominant.
- For each configuration, create or record a stable config id, for example `A_cfg2_usp4_vaepp8_hsdp_tiling`.
- For each config id, capture:
- exact server command and environment variables,
- observed distributed setup from logs, such as SP groups, CFG groups, HSDP shard/replicate sizes, VAE patch size,
- exact request command for every scenario,
- final server response metrics and client-side elapsed time,
- output artifact paths and any failed/empty responses.
- Use one warmup request per scenario, then at least three measured repeats for the shortlisted config. For exploratory matrix pruning, one measured request is acceptable only if the margin is large; label it as one-shot.
- Compare configurations by stage, not only end-to-end latency. A configuration can improve `diffuse` while hurting `vae.decode`; record both effects.
- Select the best config only after checking the target metric, dominant stages, memory headroom, and output correctness.
5. **Run targeted A/B tests.**
- Change one variable per test.
- Keep model, input, seed, request parameters, GPU placement, and warmup policy fixed.
- Record latency, stage timings, memory, output quality, and logs.
- Report comparison tables in ms. Include at least end-to-end client time, server `inference_time`, server stage generation time if available, `vae.encode`, `diffuse`, `vae.decode`, and peak memory.
6. **Collect diagnostic trace only after narrowing hypotheses.**
- Use torch profiler for a small number of requests.
- Run two separate diagnostic traces instead of mixing concerns:
- **Operator/shape trace**: enable `torch_profiler_record_shapes=True` and keep stack collection disabled. Use this to rank CUDA kernels, NCCL collectives, attention/MLP/norm/RoPE work, and shape-specific hot operators.
- **Host-stack trace**: enable `torch_profiler_with_stack=True` and normally keep shape collection disabled. Use this to map CPU/Python host gaps, synchronization points, scheduler paths, and request handling overhead.
- Keep both trace commands and reports separate from baseline/benchmark commands. Torch profiler latency is diagnostic only and must not be used as the final latency claim.
- Prefer profiling only the narrowed dominant scenario, for example the highest-resolution/video-length case where `diffuse` dominates.
- If profiler endpoints are available, run one warmup request first, then call `/start_profile`, run one profiled request, and call `/stop_profile`. This keeps model initialization and warmup out of the diagnostic trace.
- Start by analyzing rank 0 only. Expand to more ranks only if rank 0 suggests imbalance, unclear GPU idle/free bubbles, high NCCL wait, server timing mismatch, CFG branch imbalance, or USP group stragglers.
- Diagnostic reports must be written to disk and preserve: server command, profiler config, warmup/request/polling commands, trace artifact paths, rank analyzed, analyzer output or summary, and the decision about whether additional ranks are necessary.
- Analyze both rank-level balance and device-level free bubbles when additional ranks are opened.
7. **Analyze host, communication, and operators.**
- Find GPU idle/free intervals and map each large gap to the enclosing CPU/Python code.
- Separate real GPU idle from profiler overhead such as CUPTI `Command Buffer Full`.
- Compare NCCL kernel time to user annotations; annotations can overcount nested intervals.
- Rank operator work by total CUDA time and by repeated small-kernel launch count.
8. **Produce an optimization plan.**
- Classify candidates as P0/P1/P2.
- For each candidate, state necessity, expected benefit, implementation path, validation plan, and quality risk.
- Do not implement high-risk operator rewrites before proving the operator is a bottleneck for the target shapes.
- End the plan with a user-facing candidate selection table. The assistant
should not automatically choose a risky optimization just because it is
technically possible. Present the options clearly and let the user decide
which item is worth implementing based on latency target, engineering
budget, memory headroom, and quality tolerance.
- Organize the plan by optimization layer, not by a flat list of ideas:
- host/runtime optimization,
- measurement/benchmark reliability,
- parallelism and communication,
- VAE encode/decode and media pre/post processing,
- operator fusion and layout cleanup,
- attention main-path optimization,
- algorithmic, precision, or approximation changes.
- For each layer, tie every candidate back to evidence from baseline
metrics, diagnostic trace, source code, or output quality requirements.
Do not list generic optimizations without a trace or workload reason.
## Priority Rules
- **P0:** low risk, likely useful, or required for trustworthy measurement. Examples: real baseline, warmup, targeted parallel A/B, disabling avoidable `empty_cache`, scheduler coefficient caching.
- **P1:** meaningful code changes with contained risk. Examples: cross-attention KV caching, VAE gather/broadcast reduction, AdaLayerNorm/RMSNorm/RoPE fusion after trace evidence.
- **P2:** high implementation or quality risk. Examples: FA to LA replacement, custom Triton/CUDA fused kernels, FP8/quantization, sparsity/Rainfusion-style acceleration.
Every implemented optimization needs A/B validation and quality regression. A/B means same workload and hardware before/after. Quality regression means checking generated image/video stability, artifacts, temporal flicker, and seed behavior when precision or approximate kernels change.
## Optimization Layers
After baseline, parallel-search, and diagnostic traces, summarize optimization
opportunities by layer. This is the core of the performance analysis: the goal
is to connect evidence to a scoped implementation and a validation plan.
### Host and Runtime Optimization
Purpose: remove CPU/Python stalls, synchronization points, allocator overhead,
and request-path overhead that leave GPU lanes empty.
Evidence to look for:
- High `idle_pct` or large `GAP` blocks in `trace_analyzer.py`.
- Host-stack trace lines such as `torch.cuda.empty_cache`,
`cudaStreamSynchronize`, `cudaDeviceSynchronize`, Python locks, scheduler
waits, image/video preprocessing, or repeated small allocation paths.
- Difference between client wall-clock time and server `inference_time_s`.
Typical candidates:
- Make avoidable `torch.cuda.empty_cache()` optional or guard it by memory
headroom.
- Cache scheduler coefficients, timesteps, masks, or other tiny repeated CPU
computations when the request shape/steps are fixed.
- Remove avoidable host-device synchronizations and blocking logging/stat calls.
- Move expensive preprocessing out of the critical path or cache fixed prompt,
image, and transform work for benchmark scenarios.
- Ensure benchmark scripts record client-side elapsed time, HTTP status, output
path, and server response metrics.
Priority guidance:
- Usually P0 when the change is measurement reliability or an obvious removable
synchronization.
- Usually P1 when it changes scheduling, memory lifetime, or request execution
order.
Validation:
- Re-run non-profiler baseline with same workload and seed.
- Confirm peak memory headroom if disabling cache cleanup.
- Confirm generated output exists and quality/seed behavior is unchanged.
### Parallelism and Communication Optimization
Purpose: choose the right decomposition for CFG branches, sequence tokens, model
weights, VAE tiles, and rank topology.
Evidence to look for:
- Baseline A/B across `CFG`, `USP/SP`, `Ring`, `HSDP/FSDP`, and VAE patch
parallelism.
- Stage timing shifts: `diffuse`, `vae.encode`, `vae.decode`, and server
end-to-end.
- NCCL kernel time from trace, not only `user_annotation` time.
- Rank imbalance across SP group ranks or CFG branch ranks.
- Memory headroom and OOM risk.
Typical candidates:
- `CFG=2 x USP=world/2` versus `CFG=1 x USP=world` for CFG-enabled models.
- VAE patch parallel on/off or patch size tuning.
- HSDP/FSDP on/off only if both configurations fit memory.
- Ulysses versus Ulysses+Ring only after long-sequence `diffuse` is confirmed
dominant and all-to-all is suspected.
- Rank mapping/topology changes if all-rank traces show stragglers or NCCL wait.
- Buffer reuse or preallocation for FSDP/HSDP all-gather paths.
Priority guidance:
- P0/P1 for configuration-only changes with strong measured wins.
- P1 for buffer reuse or rank mapping changes.
- P2 for invasive distributed algorithm changes.
Validation:
- Measure by stage and memory, not only end-to-end.
- Use one-variable A/B with identical prompt/media/seed/shape/steps.
- When communication is suspected, compare rank0-3 in one USP group and rank0
versus rank4 across CFG branches for `CFG=2 x USP=4`.
### VAE and Media Pipeline Optimization
Purpose: reduce encode/decode, tiling, split/gather, and media conversion time.
Evidence to look for:
- Large `vae.encode` or `vae.decode` in low-overhead stage timings.
- Host-stack gaps in VAE tile split, gather, merge, broadcast, or image/video
transforms.
- VAE kernels or cuDNN convolution in operator trace.
- Whether every rank needs the final decoded tensor.
Typical candidates:
- Keep VAE patch parallel enabled when it has clear measured benefit.
- Reduce VAE gather/broadcast to only ranks that need the final media output.
- Reuse tile metadata, split buffers, or gather buffers.
- Evaluate bf16/autocast behavior for VAE only with visual quality checks.
- Avoid redundant image conversion, resize, or tensor construction in repeated
benchmark runs.
Priority guidance:
- P0/P1 if VAE is a large share of the target workload or if a host gap is
obvious and low risk.
- Lower priority when `diffuse` dominates and VAE is already patch-parallelized.
Validation:
- Compare `vae.encode`, `vae.decode`, server end-to-end, and peak memory.
- Check output video integrity, artifacts, flicker, and seed stability.
### Operator Fusion and Layout Cleanup
Purpose: reduce high-frequency small kernels, memory bandwidth pressure, layout
conversions, and launch overhead in transformer and VAE blocks.
Evidence to look for:
- Top operator tables showing many `aten::copy_`, `aten::cat`,
`split_with_sizes_copy`, `aten::add`, `aten::mul`, `aten::div`, norm,
activation, RoPE, or reshape/layout kernels.
- `ops_rankN.xlsx` `by_shape` sheet showing repeated small shapes inside the
same block path.
- Trace lanes showing many short kernels between larger GEMM/attention kernels.
- Source code patterns with repeated elementwise chains or layout conversions.
Typical fusion targets:
- AdaLayerNorm / RMSNorm / LayerNorm plus scale/shift fusion.
- RoPE fusion with Q/K layout preparation when shapes are stable.
- Residual add, scale, gate, and elementwise chains.
- MLP gate/up/down cleanup, such as fusing activation and multiply around
GEMM outputs when feasible.
- QKV projection and reshape/split/cat path cleanup.
- Attention pre/post layout cleanup to avoid unnecessary copies, cats, and
splits around sequence parallel all-to-all.
Priority guidance:
- P1 when implemented with existing PyTorch/Triton/local helper patterns and
validated against exact outputs or tolerances.
- P2 when it requires custom CUDA/Triton kernels, changes numerics, or touches
attention math directly.
Validation:
- First prove the operator family is material for the target shape.
- Use non-profiler A/B for latency and stage timing.
- Use quality regression checks for generated video stability.
- Check compile behavior and graph breaks if using `torch.compile`.
### Attention Main-Path Optimization
Purpose: address the dominant self-attention cost when FlashAttention or other
attention kernels dominate CUDA time.
Evidence to look for:
- Operator trace where FlashAttention/SDPA kernels dominate total CUDA time.
- Attention shape from `ops_rankN.xlsx` `by_shape`, model code, or trace
metadata.
- Whether attention cost scales with latent frames, latent H/W, patch size, or
CFG duplication.
- Layout/copy/all-to-all work around attention.
Typical candidates:
- Verify the attention backend and shape are on the intended fast kernel path.
- Compare supported attention backends only with identical workload and quality
settings.
- Reduce attention input size by safe model/config choices when allowed:
latent resolution, frame count, patching, boundary ratio, or windowing.
- Remove avoidable layout conversions before/after attention.
- Reuse condition-side KV or other static inputs if the model structure allows.
- Consider custom kernels, sparse/window/linear attention, or approximation only
after quality risk is accepted.
Priority guidance:
- P1 for backend/config/layout changes with preserved math.
- P2 for approximate attention, sparsity, custom kernels, or any change that can
alter quality/temporal consistency.
Validation:
- Always include output quality and seed behavior checks.
- Compare `diffuse`, server end-to-end, peak memory, and attention kernel time
in diagnostic traces if needed.
### Algorithmic, Precision, and Approximation Optimization
Purpose: reduce mathematical work or precision cost beyond local code cleanup.
Evidence to look for:
- A single operator family dominates even after low-risk runtime, parallel, and
fusion work.
- Memory bandwidth or compute utilization suggests precision or quantization
could matter.
- The user explicitly allows quality-preserving or approximate methods.
Typical candidates:
- FP8/quantization for transformer or selected projections.
- Sparsity or Rainfusion-style acceleration.
- Reduced steps, scheduler changes, distillation, or caching across frames.
- Approximate attention or linear attention.
Priority guidance:
- Usually P2 because quality, numerics, and implementation risk are high.
Validation:
- Requires strict A/B, visual quality review, temporal flicker checks, seed
stability, and possibly human evaluation.
### Interpolation, Super-Resolution, and E2E Pipeline Optimization
Purpose: optimize the whole user-visible video product, not only the base
diffusion invocation. Some deployments trade base-model latency against
post-processing, interpolation, or super-resolution stages.
Evidence to look for:
- E2E latency breakdown across base generation, interpolation, super-resolution,
encoding, storage, and response streaming.
- Fast/slow GPU or fast/slow stage analysis across multiple cards and pipeline
stages.
- User quality target: resolution, FPS, temporal smoothness, and acceptable
post-processing artifacts.
Typical candidates:
- Add or optimize a frame interpolation stage when it reduces required base
model frames for the same perceived FPS.
- Add or optimize a super-resolution model when generating lower base
resolution plus SR is faster for the target quality.
- Analyze E2E2 pipeline behavior: client request, service scheduling, diffusion,
VAE/media, post-process, file write, and response.
- Identify fast/slow cards or stages and rebalance pipeline placement.
Priority guidance:
- P1 when using proven interpolation/SR components without changing diffusion
math.
- P2 when quality risk is high or the pipeline adds significant operational
complexity.
Validation:
- Measure E2E wall-clock, per-stage server timings, output FPS/resolution,
artifacts, flicker, and user-visible quality.
### Optimization Candidate Library
Use this table as a compact menu, not as automatic recommendations. Pick items
only when baseline metrics, trace evidence, source inspection, or quality
tolerance supports them.
| Layer | Candidate | Evidence | Priority | Validation focus |
|---|---|---|---|---|
| Measurement | Freeze server/request/benchmark commands | Missing or drifting commands | P0 | Repeatable non-profiler A/B |
| Measurement | Separate baseline and diagnostic profiler runs | Profiler used for latency claims | P0 | Low-overhead stage timings |
| Host/runtime | Guard or remove avoidable `empty_cache` | Host-stack gaps or sync stalls | P0/P1 | Latency, peak memory, OOM safety |
| Host/runtime | Cache scheduler coefficients/timesteps | Repeated tiny CPU/GPU work | P0/P1 | Same seed/output, stage timing |
| Host/runtime | Reduce framework scheduling overhead | Client time exceeds server time | P1 | E2E latency, throughput |
| Parallel | `CFG=2 x USP=world/2` vs `USP=world` | CFG doubles forward work | P0/P1 | `diffuse`, NCCL, memory |
| Parallel | Tune VAE patch parallelism | VAE encode/decode is material | P0/P1 | VAE time, output correctness |
| Parallel | HSDP on/off or buffer reuse | HSDP affects memory/all-gather | P1 | Memory, latency, OOM risk |
| Parallel | Ulysses vs Ulysses+Ring | Long sequence all-to-all suspected | P1/P2 | Rank balance, NCCL kernels |
| Cross-attn | Disable SP for short condition tokens | Cross-attn comm exceeds compute | P1 | `diffuse`, correctness |
| VAE/media | Reduce VAE gather/broadcast | Rank traces show VAE wait | P1 | Rank balance, output file |
| VAE/media | Reuse tile metadata/buffers | Tile split/merge host gaps | P1 | `vae.encode/decode`, memory |
| VAE/media | VAE bf16/autocast | VAE float kernels are slow | P1/P2 | Artifacts, flicker, seed stability |
| Operator fusion | AdaLayerNorm/LayerNorm fusion | Norm plus scale/shift kernels | P1 | Numeric tolerance, latency |
| Operator fusion | RMSNorm fusion | Many small RMSNorm kernels | P1 | Numeric tolerance, latency |
| Operator fusion | RoPE cache/fuse/layout cleanup | RoPE copy/reshape kernels | P1/P2 | Kernel count, correctness |
| Operator layout | QKV or attention layout cleanup | Copy/cat/split around attention | P1 | Copy kernels, compile behavior |
| Attention | Verify backend fast path | FA/SDPA dominates trace | P1 | `diffuse`, attention kernels |
| Attention | FA to LA or selected-head LA | Attention remains dominant | P2 | Quality, temporal stability |
| Precision | Transformer FP8/quantization | Compute/bandwidth bound and allowed | P2 | Quality, speed, stability |
| Sparsity | Rainfusion-style acceleration | DiT compute remains dominant | P2 | Prompt diversity, quality |
| Pipeline | Frame interpolation | Fewer base frames can meet FPS | P1/P2 | E2E latency, motion artifacts |
| Pipeline | Super-resolution | Lower base res plus SR may win | P1/P2 | Detail quality, artifacts |
| E2E | Fast/slow-card analysis | Multi-card stragglers | P0/P1 | Per-rank/stage wall-clock |
### Optimization Plan Template
Use this table shape when reporting the next work items:
| Priority | Layer | Candidate | Evidence | Expected benefit | Implementation path | Validation | Quality risk |
|---|---|---|---|---|---|---|---|
| P0 | Host/runtime | Guard `empty_cache` | Host-stack gap points to `torch.cuda.empty_cache` | Small latency reduction, less idle | Add config/env guard | Non-profiler A/B, memory check | Low |
| P1 | Operator fusion | RMSNorm/AdaLayerNorm fusion | High-frequency norm/elementwise kernels | Lower launch/bandwidth overhead | Use existing fusion helper or targeted Triton | A/B + output check | Medium |
| P1/P2 | Attention | Attention layout/backend investigation | FA kernel dominates CUDA time | Potentially large | Inspect shapes/backend and remove layout copies | A/B + trace + quality | Medium/high |
Then present a short selection prompt using the same rows:
```text
Which candidate should we implement next?
1. P0 Host/runtime: guard empty_cache
- Expected benefit: small but low-risk latency reduction.
- Risk: possible memory increase/OOM if memory headroom is insufficient.
2. P1 Operator fusion: inspect by_shape and implement first norm/RoPE/layout fusion
- Expected benefit: medium if high-frequency small kernels are confirmed.
- Risk: numerical/compile/quality validation needed.
3. P1/P2 Attention: FA/LA/backend/layout investigation
- Expected benefit: potentially large.
- Risk: high quality and implementation risk.
```
If the user has not chosen an item, default to explaining tradeoffs and asking
which candidate to execute. Only proceed autonomously on low-risk P0 measurement
or instrumentation fixes.
## Analysis Helpers
PyTorch profiler traces are Chrome/Perfetto-compatible JSON files, usually
`trace_rankN.json` or `trace_rankN.json.gz`. They normally contain a top-level
`traceEvents` list, though some exporters emit the raw event list directly.
Use the checked-in analyzer from the repository root:
```bash
python3 .claude/skills/diffusion-perf-opt/scripts/trace_analyzer.py \
vllm_profile/.../trace_rank0.json.gz \
--min-gap-ms 5 \
--topn 20
```
For rank imbalance or communication questions, pass all relevant rank traces in
one command. For host gaps, lower `--min-gap-ms` to `1` and use a host-stack
trace. Read `gpu_span_s`, `busy_union_s`, `idle_union_s`, `idle_pct`, `GAP`
blocks, `Top GPU/operator events by total duration`, and `Top NCCL-like events
by category`. Treat `cat=user_annotation` NCCL ranges as enclosing annotations;
prefer `cat=kernel` or `cat=gpu_user_annotation` for real device work.
The analyzer summarizes timing only. It does not parse tensor shapes, attribute
overlap to individual streams, prove quality, or provide final latency claims.
Use `ops_rankN.xlsx` or PyTorch key averages for shape analysis, and re-test any
optimization with non-profiler baseline commands.
Read `references/optimization-playbook.md` when drafting the optimization table or comparing candidate techniques.
## vLLM Omni Heuristics
- Cross-attention usually should not use USP/SP when text/image condition token count is much smaller than latent video tokens. Confirm via trace; in Wan2.2 I2V, self-attention dominates cross-attention.
- VAE bf16/autocast is often worthwhile but requires visual quality checks.
- VAE patch parallel can help decode/encode but may add gather/merge/broadcast overhead. Check whether all ranks need the final decoded tensor.
- HSDP/FSDP is primarily a memory strategy. If the model fits without it, run an on/off latency comparison.
- Scheduler work can create small host/device gaps; cache tiny solve coefficients when timesteps/order are known.
- `torch.cuda.empty_cache()` can prevent OOM but creates synchronization/idle. Make it optional if memory headroom is sufficient.
- `Command Buffer Full` in profiler output is profiler overhead, not a model optimization target.
@@ -0,0 +1,4 @@
interface:
display_name: "vLLM Omni Perf Opt"
short_description: "Profile and optimize vLLM Omni diffusion performance."
default_prompt: "Analyze and optimize a vLLM Omni diffusion workload using baselines, traces, parallel strategy modeling, and prioritized optimization plans."
@@ -0,0 +1,82 @@
# Optimization Playbook
Use this reference when turning trace evidence into prioritized vLLM Omni optimizations.
## Parallel Strategy
Start from workload shape:
- Long video tokens, high resolution, many frames: self-attention and FFN dominate. USP/SP is usually valuable.
- CFG enabled with positive and negative prompts: transformer forward count doubles. CFG parallel may beat USP on small/moderate sequence lengths.
- Cross-attention: skip SP/USP when condition tokens are much smaller than latent tokens; extra all-to-all can exceed compute saved.
- HSDP/FSDP: use to fit memory first. If memory allows, compare without HSDP because all-gathers and lazy init add overhead.
- VAE parallel: useful when encode/decode is material. Check gather/merge/broadcast and whether every rank needs final decoded output.
Candidate matrices:
- 2 cards: `USP=2`, `CFG=2`, HSDP on/off, VAE PP on/off.
- 4 cards: `USP=4`, `CFG=2 x USP=2`, `USP=2 x HSDP`, VAE PP on/off.
- 8 cards: add pipeline/stage parallel only after measuring orchestration and inter-stage transfer overhead.
## Host And Free Bubble Analysis
Look for device intervals where no kernel, memcpy, or memset is active.
Common causes:
- First request FSDP/HSDP lazy init.
- Python preprocessing: image load, resize, `to_tensor`, tokenizer, request marshaling.
- `torch.cuda.empty_cache()` or explicit synchronizations.
- Scheduler small linear algebra such as tiny `torch.linalg.solve`.
- VAE tile split/merge Python loops.
- Profiler overhead such as CUPTI command buffer flush/full.
Also inspect trace-external time:
- API request parsing and multipart upload.
- Engine/orchestrator dispatch.
- Worker RPC and queue wait.
- Output serialization, video write, base64, HTTP response.
## Operator Optimization Candidates
Evaluate necessity before implementation:
- **FA to LA / LA for head:** high potential only if attention is the main CUDA hotspot and target shapes fit LA well.
- **Cross-attention BSND/no transpose:** do only if transpose/copy around cross-attn is measurable.
- **AdaLayerNorm fusion:** fuse norm, scale/shift, gate, residual, and dtype conversion if repeated elementwise kernels are significant.
- **RMSNorm fusion:** useful for Q/K norms and VAE norms when small-kernel launch count is high.
- **FP32 LayerNorm to bf16:** quality-sensitive; run visual regression.
- **LA preprocess:** integrate only if preprocess overhead is cached or amortized.
- **RoPE move/cache/fuse:** useful when RoPE small kernels are visible around every self-attn block.
- **VAE RMSNorm replacement:** only after VAE internal trace shows norm as a real component of decode/encode.
- **Scheduler fusion/cache:** cache coefficient solves and fuse CFG combine with scheduler arithmetic when possible.
Before writing a custom kernel, check:
- Local vLLM Omni implementations.
- Upstream vLLM kernels and attention backends.
- SGLang, FlashInfer, FlashAttention, and Triton examples.
- Shape constraints and layout expectations.
## Quantization And Sparsity
Treat these as high-risk optimizations:
- Rainfusion/sparse diffusion can be high reward but needs model-specific quality validation.
- VAE decode quantization can reduce decode time but usually has smaller end-to-end upside than transformer attention/FFN work.
- FP8 or approximate kernels require seed-stable visual regression, temporal flicker checks, and prompt diversity.
## Output Format For Recommendations
Return a table with:
- Priority: P0/P1/P2.
- Optimization.
- Evidence from trace or code.
- Expected benefit.
- Risk/quality impact.
- Implementation path.
- Validation command or A/B test.
State clearly when a recommendation is a hypothesis requiring new trace with shapes.
+144
View File
@@ -0,0 +1,144 @@
#!/usr/bin/env python3
"""Summarize torch profiler Chrome traces for vLLM Omni diffusion optimization."""
from __future__ import annotations
import argparse
import collections
import gzip
import json
from pathlib import Path
from typing import Any
GPU_CATS = {"kernel", "gpu_memcpy", "gpu_memset"}
CPU_CATS = {"python_function", "user_annotation", "cpu_op", "cuda_runtime", "cuda_driver"}
def open_trace(path: Path):
if path.suffix == ".gz":
return gzip.open(path, "rt")
return path.open("rt")
def event_name(event: dict[str, Any]) -> str:
return str(event.get("name", ""))
def summarize_trace(path: Path, min_gap_us: float, topn: int) -> None:
with open_trace(path) as f:
data = json.load(f)
events = data if isinstance(data, list) else data.get("traceEvents", [])
gpu: list[tuple[float, float, float, str, str, Any, Any]] = []
cpu: list[tuple[float, float, float, str, str, Any, Any]] = []
by_gpu_name: dict[str, list[float]] = collections.defaultdict(lambda: [0, 0.0, 0.0])
by_name: dict[str, list[float]] = collections.defaultdict(lambda: [0, 0.0, 0.0])
nccl: dict[tuple[str, str], list[float]] = collections.defaultdict(lambda: [0, 0.0, 0.0])
for event in events:
dur = event.get("dur")
ts = event.get("ts")
if dur is None or ts is None or dur <= 0:
continue
cat = str(event.get("cat", ""))
name = event_name(event)
row = (float(ts), float(ts + dur), float(dur), name, cat, event.get("pid"), event.get("tid"))
stat = by_name[name]
stat[0] += 1
stat[1] += dur
stat[2] = max(stat[2], dur)
if cat in GPU_CATS:
gpu.append(row)
gstat = by_gpu_name[name]
gstat[0] += 1
gstat[1] += dur
gstat[2] = max(gstat[2], dur)
elif cat in CPU_CATS:
cpu.append(row)
if "nccl" in name.lower():
nstat = nccl[(cat, name)]
nstat[0] += 1
nstat[1] += dur
nstat[2] = max(nstat[2], dur)
print(f"\n== {path}")
print(f"events={len(events)} gpu_events={len(gpu)} cpu_events={len(cpu)}")
if not gpu:
print("No GPU events found.")
return
gpu.sort()
merged: list[list[Any]] = []
for start, end, dur, name, cat, pid, tid in gpu:
if not merged or start > merged[-1][1]:
merged.append([start, end, [(start, end, dur, name, cat, pid, tid)]])
else:
merged[-1][1] = max(merged[-1][1], end)
merged[-1][2].append((start, end, dur, name, cat, pid, tid))
span = max(end for _, end, *_ in gpu) - min(start for start, *_ in gpu)
busy = sum(end - start for start, end, _ in merged)
idle = span - busy
print(
f"gpu_span_s={span / 1e6:.3f} "
f"busy_union_s={busy / 1e6:.3f} "
f"idle_union_s={idle / 1e6:.3f} "
f"idle_pct={idle / span * 100:.2f}"
)
interesting_cpu = [
row
for row in cpu
if row[2] >= 1000
and (
row[4] in {"python_function", "user_annotation"}
or "cudaStreamSynchronize" in row[3]
or "cudaDeviceSynchronize" in row[3]
or "cudaLaunch" in row[3]
or "cudaMemcpy" in row[3]
)
]
gaps = []
for idx in range(1, len(merged)):
gap_start = merged[idx - 1][1]
gap_end = merged[idx][0]
gap_dur = gap_end - gap_start
if gap_dur >= min_gap_us:
prev_event = max(merged[idx - 1][2], key=lambda x: x[1])
next_event = min(merged[idx][2], key=lambda x: x[0])
mid = (gap_start + gap_end) / 2
containers = [row for row in interesting_cpu if row[0] <= mid <= row[1]]
containers = sorted(containers, key=lambda x: x[2])[:8]
gaps.append((gap_dur, gap_start, gap_end, prev_event, next_event, containers))
print(f"gaps_ge_{min_gap_us / 1000:.3f}ms count={len(gaps)} sum_s={sum(g[0] for g in gaps) / 1e6:.3f}")
for gap_dur, gap_start, gap_end, prev_event, next_event, containers in sorted(gaps, reverse=True)[:topn]:
print(f"\nGAP {gap_dur / 1000:.3f} ms ts={gap_start:.0f}->{gap_end:.0f}")
print(f" prev {prev_event[4]} {prev_event[2] / 1000:.3f} ms {prev_event[3][:160]}")
print(f" next {next_event[4]} {next_event[2] / 1000:.3f} ms {next_event[3][:160]}")
for row in containers:
print(f" in {row[4]} {row[2] / 1000:.3f} ms {row[3][:180]}")
print("\nTop GPU/operator events by total duration:")
for name, (count, total, max_dur) in sorted(by_gpu_name.items(), key=lambda kv: kv[1][1], reverse=True)[:topn]:
print(f" {int(count):8d} total={total / 1e6:9.3f}s max={max_dur / 1000:9.3f}ms {name[:180]}")
print("\nTop NCCL-like events by category:")
for (cat, name), (count, total, max_dur) in sorted(nccl.items(), key=lambda kv: kv[1][1], reverse=True)[:topn]:
print(f" {int(count):8d} total={total / 1e6:9.3f}s max={max_dur / 1000:9.3f}ms cat={cat} {name[:160]}")
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("traces", nargs="+", type=Path, help="trace.json or trace.json.gz files")
parser.add_argument("--min-gap-ms", type=float, default=5.0, help="minimum GPU idle gap to print")
parser.add_argument("--topn", type=int, default=20, help="number of gaps/hotspots to print")
args = parser.parse_args()
for trace in args.traces:
summarize_trace(trace, args.min_gap_ms * 1000.0, args.topn)
if __name__ == "__main__":
main()
+3
View File
@@ -18,6 +18,9 @@ include:
- `add-diffusion-model`: guides integration of a new diffusion model into - `add-diffusion-model`: guides integration of a new diffusion model into
`vllm-omni` `vllm-omni`
- `diffusion-perf-opt`: guides diffusion model performance optimization,
including profiling traces, parallel strategies, stage timing analysis, and
benchmark-driven tuning
- `add-omni-model`: covers addition of new omni-modality model support - `add-omni-model`: covers addition of new omni-modality model support
- `add-tts-model`: covers integration of new TTS models and related serving - `add-tts-model`: covers integration of new TTS models and related serving
workflows workflows
+59 -10
View File
@@ -3,15 +3,64 @@
# More info: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners # More info: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# Default owners (fallback reviewers for all files) # Default owners (fallback reviewers for all files)
# later, we can add more reviewers here * @Gaohan123 @hsliuustc0106 @ywang96
* @hsliuustc0106
# Assign reviewers by directory # Repository operations
/.github/ @owner1 /.github/ @hsliuustc0106
/tests/ @owner1
/docs/ @owner1
# Example: reviewers for specific directories # Documentation and Recipes
# /examples/offline_inference/ @offline-inference-team /docs/ @hsliuustc0106 @Gaohan123 @david6666666
# /vllm_omni/core/ @core-team /recipes @hsliuustc0106 @Gaohan123 @david6666666
# /vllm_omni/model_executor/ @model-team
# Skills
/.claude/ @hsliuustc0106
# Tests
/tests/ @yenuo26
# CI
/.buildkite/ @yenuo26 @congw729
# Engine and model_executor
/vllm_omni/core/ @tzhouam @yinpeiqi @fake0fan
/vllm_omni/engine/ @tzhouam @yinpeiqi @fake0fan
/vllm_omni/entrypoints/ @tzhouam @yinpeiqi @fake0fan
/vllm_omni/model_executor/ @tzhouam @gcanlin
/vllm_omni/worker/ @tzhouam @gcanlin
# Diffusion
/vllm_omni/diffusion/ @Isotr0py @princepride @SamitHuang @wtomin @ZJY0516 @RuixiangMa @david6666666
/vllm_omni/lora/ @princepride @SamitHuang @wtomin @ZJY0516 @RuixiangMa @yuanheng-zhao
# Diffusion CPU offloading
/vllm_omni/diffusion/offloader/ @Isotr0py @princepride @SamitHuang @wtomin @ZJY0516 @RuixiangMa @yuanheng-zhao
# Omni and TTS support
/vllm_omni/transformers_utils/ @tzhouam @yuanheng-zhao
/vllm_omni/model_executor/models/ @linyueqian @ZeldaHuang @princepride @yuanheng-zhao @amy-why-3459 @Bounty-hunter
# Benchmarks
/benchmarks/accuracy/ @david6666666
/benchmarks/build_dataset/ @linyueqian @yuanheng-zhao
/benchmarks/diffusion/ @Isotr0py @princepride @SamitHuang @wtomin @ZJY0516 @RuixiangMa
/benchmarks/distributed/ @princepride
/benchmarks/glm_image/ @princepride @JaredforReal
/benchmarks/tts/ @linyueqian @ZeldaHuang @princepride
# Usability and Observability
/vllm_omni/deploy/ @tzhouam @lishunyang12
/vllm_omni/metrics/ @lishunyang12
/vllm_omni/profiler/ @gcanlin
# ComfyUI
/apps/ @fhfuih
# Large Scale Serving
/vllm_omni/distributed/ @princepride @yuanheng-zhao @xuechendi @natureofnature @fake0fan
# Quantization and configuration
/vllm_omni/config/ @lishunyang12
/vllm_omni/quantization/ @david6666666 @Isotr0py @lishunyang12
# Hardware plugins and accelerators
/vllm_omni/platforms/ @gcanlin @tjtanaa @xuechendi
+1
View File
@@ -239,6 +239,7 @@ data/
datasets/ datasets/
*.csv *.csv
*.json *.json
!tests/dfx/perf/tests/*.json
!apps/ComfyUI-vLLM-Omni/example_workflows/*.json !apps/ComfyUI-vLLM-Omni/example_workflows/*.json
*.jsonl *.jsonl
*.parquet *.parquet
+2 -2
View File
@@ -9,19 +9,19 @@ Easy, fast, and cheap omni-modality model serving for everyone
</h3> </h3>
<p align="center"> <p align="center">
| <a href="https://vllm-omni.readthedocs.io/en/latest/"><b>Documentation</b></a> | <a href="https://discuss.vllm.ai"><b>User Forum</b></a> | <a href="https://slack.vllm.ai"><b>Developer Slack</b></a> | <a href="docs/assets/WeChat.jpg"><b>WeChat</b></a> | <a href="https://arxiv.org/abs/2602.02204"><b>Paper</b></a> | <a href="https://docs.google.com/presentation/d/1XJWgv79lORl8rbaVvp2d5Sqs6ZEBgAgj/edit?slide=id.p1#slide=id.p1"><b>Slides</b></a> | | <a href="https://vllm-omni.readthedocs.io/en/latest/"><b>Documentation</b></a> | <a href="https://deepwiki.com/vllm-project/vllm-omni"><b>DeepWiki</b></a> | <a href="https://discuss.vllm.ai"><b>User Forum</b></a> | <a href="https://slack.vllm.ai"><b>Developer Slack</b></a> | <a href="docs/assets/WeChat.jpg"><b>WeChat</b></a> | <a href="https://arxiv.org/abs/2602.02204"><b>Paper</b></a> | <a href="https://docs.google.com/presentation/d/111-L8zF7A1j_YI_cR8JsblofdScdRr2f/edit?usp=sharing&ouid=110473603432222024453&rtpof=true&sd=true"><b>Slides</b></a> |
</p> </p>
--- ---
*Latest News* 🔥 *Latest News* 🔥
- [2026/05] We released [0.20.0](https://github.com/vllm-project/vllm-omni/releases/tag/v0.20.0) - refreshes the serving/runtime stack for large-scale omni workloads, and improves diffusion model performance, quantization, and hardware readiness across CUDA, ROCm, MUSA, NPU, and XPU backends.
- [2026/03] We released [0.18.0](https://github.com/vllm-project/vllm-omni/releases/tag/v0.18.0) - strengthens the core runtime through a large entrypoint refactor and scheduler/runtime cleanups, expands unified quantization and diffusion execution, broadens multimodal model coverage, and improves production readiness across audio, omni, image, video, RL, and multi-platform deployments. - [2026/03] We released [0.18.0](https://github.com/vllm-project/vllm-omni/releases/tag/v0.18.0) - strengthens the core runtime through a large entrypoint refactor and scheduler/runtime cleanups, expands unified quantization and diffusion execution, broadens multimodal model coverage, and improves production readiness across audio, omni, image, video, RL, and multi-platform deployments.
- [2026/03] Check out our first public [project deepdive](https://youtu.be/sgwNfsNnR9I) at the vLLM Hong Kong Meetup! - [2026/03] Check out our first public [project deepdive](https://youtu.be/sgwNfsNnR9I) at the vLLM Hong Kong Meetup!
- [2026/03] **[vllm-omni-skills](https://github.com/hsliuustc0106/vllm-omni-skills)** is a community-driven collection of AI assistant skills that help developers work with vLLM-Omni more effectively. These skills can be used with popular agentic AI coding assistants like **Cursor IDE**, **Claude**, **Codex**, and more. - [2026/03] **[vllm-omni-skills](https://github.com/hsliuustc0106/vllm-omni-skills)** is a community-driven collection of AI assistant skills that help developers work with vLLM-Omni more effectively. These skills can be used with popular agentic AI coding assistants like **Cursor IDE**, **Claude**, **Codex**, and more.
- [2026/02] We released [0.16.0](https://github.com/vllm-project/vllm-omni/releases/tag/v0.16.0) - A major alignment + capability release that rebases onto **upstream vLLM v0.16.0** and significantly expands performance, distributed execution, and production readiness across **Qwen3-Omni / Qwen3-TTS**, **Bagel**, **MiMo-Audio**, **GLM-Image** and the **Diffusion (DiT) image/video stack**—while also improving platform coverage (CUDA / ROCm / NPU / XPU), CI quality, and documentation. - [2026/02] We released [0.16.0](https://github.com/vllm-project/vllm-omni/releases/tag/v0.16.0) - A major alignment + capability release that rebases onto **upstream vLLM v0.16.0** and significantly expands performance, distributed execution, and production readiness across **Qwen3-Omni / Qwen3-TTS**, **Bagel**, **MiMo-Audio**, **GLM-Image** and the **Diffusion (DiT) image/video stack**—while also improving platform coverage (CUDA / ROCm / NPU / XPU), CI quality, and documentation.
- [2026/02] We released [0.14.0](https://github.com/vllm-project/vllm-omni/releases/tag/v0.14.0) - This is the first **stable release** of vLLM-Omni that expands Omni’s diffusion / image-video generation and audio / TTS stack, improves distributed execution and memory efficiency, and broadens platform/backend coverage (GPU/ROCm/NPU/XPU). It also brings meaningful upgrades to serving APIs, profiling & benchmarking, and overall stability. Please check our latest [paper](https://arxiv.org/abs/2602.02204) for architecture design and performance results. - [2026/02] We released [0.14.0](https://github.com/vllm-project/vllm-omni/releases/tag/v0.14.0) - This is the first **stable release** of vLLM-Omni that expands Omni’s diffusion / image-video generation and audio / TTS stack, improves distributed execution and memory efficiency, and broadens platform/backend coverage (GPU/ROCm/NPU/XPU). It also brings meaningful upgrades to serving APIs, profiling & benchmarking, and overall stability. Please check our latest [paper](https://arxiv.org/abs/2602.02204) for architecture design and performance results.
- [2026/01] We released [0.12.0rc1](https://github.com/vllm-project/vllm-omni/releases/tag/v0.12.0rc1) - a major RC milestone focused on maturing the diffusion stack, strengthening OpenAI-compatible serving, expanding omni-model coverage, and improving stability across platforms (GPU/NPU/ROCm).
- [2025/11] vLLM community officially released [vllm-project/vllm-omni](https://github.com/vllm-project/vllm-omni) in order to support omni-modality models serving. - [2025/11] vLLM community officially released [vllm-project/vllm-omni](https://github.com/vllm-project/vllm-omni) in order to support omni-modality models serving.
--- ---
@@ -18,3 +18,13 @@ vd017|||The quarterly results exceed expectations across all major metrics.|A sh
vd018|||Chapter one. The morning sun filtered gently through the forest canopy.|A smooth, rich male audiobook narrator voice, expressive and engaging. vd018|||Chapter one. The morning sun filtered gently through the forest canopy.|A smooth, rich male audiobook narrator voice, expressive and engaging.
vd019|||To be or not to be, that is the question.|A theatrical female voice, dramatic and expressive, stage projection quality. vd019|||To be or not to be, that is the question.|A theatrical female voice, dramatic and expressive, stage projection quality.
vd020|||And that is all for tonight. Stay well out there, everyone.|A warm, velvety male late-night radio DJ voice, smooth and intimate. vd020|||And that is all for tonight. Stay well out there, everyone.|A warm, velvety male late-night radio DJ voice, smooth and intimate.
vd021|||Please fasten your seatbelts; we will be experiencing some turbulence shortly.|A calm, professional female flight attendant voice, reassuring and clear.
vd022|||The fossil record suggests these creatures lived more than sixty million years ago.|A measured male documentary narrator voice, scientific and curious.
vd023|||Hey kiddo, did you finish your homework before dinner today?|A gentle, caring father voice, warm and slightly tired after a long day.
vd024|||Live from courtside, the home team has just taken a commanding lead.|A high-energy male sports broadcaster, urgent and engaged.
vd025|||Please remain on the line while I transfer you to the next available agent.|A polite, neutral female call-center voice, professional and patient.
vd026|||I have a dream that one day this nation will rise up and live out the true meaning of its creed.|A powerful, resonant male orator voice, slow and impassioned.
vd027|||Mama always said life was like a box of chocolates; you never know what you're gonna get.|A folksy male voice with a Southern American accent, warm and reflective.
vd028|||Ahoy there, matey! Hoist the colors and prepare to set sail.|A theatrical pirate male voice, gruff and boisterous.
vd029|||Ladies and gentlemen, please welcome to the stage our keynote speaker.|A polished female event MC voice, gracious and articulate.
vd030|||Once you've completed the form, click submit and we'll review it within two business days.|A friendly female product-onboarding voice, encouraging and clear.
@@ -18,3 +18,13 @@ smoke017|||In the distance, the mountains were shrouded in thick morning fog.
smoke018|||Our company reported record revenue for the fourth quarter of the fiscal year. smoke018|||Our company reported record revenue for the fourth quarter of the fiscal year.
smoke019|||She explained the new policy in detail during the staff meeting this morning. smoke019|||She explained the new policy in detail during the staff meeting this morning.
smoke020|||The children laughed and played in the garden until the sun began to set. smoke020|||The children laughed and played in the garden until the sun began to set.
smoke021|||A long line of cars stretched along the coastal highway after the lighthouse opened.
smoke022|||The chef recommended the seared scallops with a lemon butter sauce as the special.
smoke023|||Our quarterly engineering review begins promptly at two in the afternoon on Thursday.
smoke024|||Despite the heavy rain, the marathon runners kept a steady pace through downtown.
smoke025|||She finished the crossword puzzle in under fifteen minutes while sipping her tea.
smoke026|||The new release adds streaming output, faster startup, and better error reporting.
smoke027|||His grandfather taught him how to whittle small wooden birds from cedar offcuts.
smoke028|||Travelers were advised to check the timetable since trains were running on a holiday schedule.
smoke029|||The startup launched a public beta after eighteen months of closed development.
smoke030|||At night the desert sky filled with stars so bright the Milky Way was clearly visible.
+3 -33
View File
@@ -30,10 +30,8 @@ python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
**Notes** **Notes**
- The benchmark talks to `http://<host>:<port>/v1/chat/completions`. - By default, image tasks talk to `http://<host>:<port>/v1/chat/completions`; video tasks talk to `/v1/videos`.
- If you run the server on another host or port, pass `--base-url` accordingly. - If you run the server on another host or port, pass `--base-url` accordingly.
- To compare diffusion multi-replica serving, restart the server with different
`runtime.num_replicas` values and keep the benchmark parameters unchanged.
## 2. Supported Datasets ## 2. Supported Datasets
@@ -95,39 +93,11 @@ You can point to your own trace using `--dataset-path`.
## 3. Benchmark Parameters ## 3. Benchmark Parameters
### Multi-replica sweep
For replica scaling, use the same model, prompt count, image/video size, and
step count for every run. Change only the serving stage config:
```yaml
runtime:
devices: "0,1,2,3"
num_replicas: 4
```
Then run a fixed benchmark and write one JSON file per replica count:
```bash
python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
--base-url http://localhost:8099 \
--model Qwen/Qwen-Image \
--task t2i \
--dataset random \
--num-prompts 32 \
--max-concurrency 8 \
--request-rate inf \
--num-inference-steps 10 \
--width 512 --height 512 \
--output-file diffusion_replica_4.json
```
Repeat for `num_replicas=1`, `2`, `3`, and `4`, using matching `runtime.devices`.
### Basic flags ### Basic flags
- `--base-url`: Server address (the script calls `.../v1/chat/completions`). - `--base-url`: Server address; `--endpoint` selects the path appended to this base URL.
- `--model`: The OpenAI-compatible `model` field. - `--model`: The OpenAI-compatible `model` field.
- `--endpoint`: API endpoint path. Leading `/` is optional, e.g. `/v1/videos` or `v1/videos`.
- `--task`: Task type (e.g., `t2i`, `t2v`, `i2i`, `i2v`). - `--task`: Task type (e.g., `t2i`, `t2v`, `i2i`, `i2v`).
- `--dataset`: Dataset mode (`vbench` / `trace` / `random`). - `--dataset`: Dataset mode (`vbench` / `trace` / `random`).
- `--num-prompts`: Number of requests to send. - `--num-prompts`: Number of requests to send.
+30 -4
View File
@@ -151,7 +151,7 @@ async def async_request_chat_completions(
return output return output
async def async_request_openai_images( async def async_request_openai_image_generations(
input: RequestFuncInput, input: RequestFuncInput,
session: aiohttp.ClientSession, session: aiohttp.ClientSession,
pbar: tqdm | None = None, pbar: tqdm | None = None,
@@ -348,12 +348,38 @@ async def async_request_v1_videos(
return output return output
LEGACY_BACKEND_ENDPOINT_ALIASES = {
"vllm-omni": "/v1/chat/completions",
"openai": "/v1/images/generations",
}
def normalize_endpoint(value: str) -> str:
endpoint = str(value).strip()
if not endpoint:
raise ValueError("endpoint must not be empty.")
endpoint = LEGACY_BACKEND_ENDPOINT_ALIASES.get(
endpoint,
LEGACY_BACKEND_ENDPOINT_ALIASES.get(endpoint.lstrip("/"), endpoint),
)
if not endpoint.startswith("/"):
endpoint = f"/{endpoint}"
return endpoint
def endpoint_filename_token(value: str) -> str:
token = normalize_endpoint(value).lstrip("/")
for bad in ("/", "\\", ":", "*", "?", '"', "<", ">", "|"):
token = token.replace(bad, "_")
return token or "endpoint"
backends_function_mapping = { backends_function_mapping = {
"2i": { "2i": {
"vllm-omni": (async_request_chat_completions, "/v1/chat/completions"), "/v1/chat/completions": (async_request_chat_completions, "/v1/chat/completions"),
"openai": (async_request_openai_images, "/v1/images/generations"), "/v1/images/generations": (async_request_openai_image_generations, "/v1/images/generations"),
}, },
"2v": { "2v": {
"v1/videos": (async_request_v1_videos, "/v1/videos"), "/v1/videos": (async_request_v1_videos, "/v1/videos"),
}, },
} }
@@ -6,31 +6,31 @@
Benchmark online serving for diffusion models (Image/Video Generation). Benchmark online serving for diffusion models (Image/Video Generation).
If you want to use i2v, i2i dataset, you should `uv pip install gdown` first If you want to use i2v, i2i dataset, you should `uv pip install gdown` first
Supports multiple backends: Supports multiple endpoints:
- vllm-omni: Uses /v1/chat/completions endpoint (default) - /v1/chat/completions: OpenAI chat-compatible image requests
- openai: Uses /v1/images/generations endpoint - /v1/images/generations: OpenAI image generation requests
- v1/videos: Use /v1/videos endpoint - /v1/videos: Async video jobs
Usage: Usage:
# Video (v1/videos backend) # Video (/v1/videos endpoint)
t2v: t2v:
python3 benchmarks/diffusion/diffusion_benchmark_serving.py \ python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend v1/videos --dataset vbench --task t2v --num-prompts 10 \ --endpoint /v1/videos --dataset vbench --task t2v --num-prompts 10 \
--height 480 --width 640 --fps 16 --num-frames 80 --height 480 --width 640 --fps 16 --num-frames 80
i2v: i2v:
python3 benchmarks/diffusion/diffusion_benchmark_serving.py \ python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend v1/videos --dataset vbench --task i2v --num-prompts 10 --endpoint /v1/videos --dataset vbench --task i2v --num-prompts 10
# Image (vllm-omni backend) # Image (/v1/chat/completions endpoint)
t2i: t2i:
python3 benchmarks/diffusion/diffusion_benchmark_serving.py \ python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend vllm-omni --dataset vbench --task t2i --num-prompts 10 \ --endpoint /v1/chat/completions --dataset vbench --task t2i --num-prompts 10 \
--height 1024 --width 1024 --height 1024 --width 1024
python3 benchmarks/diffusion/diffusion_benchmark_serving.py \ python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend vllm-omni --dataset random --task t2i --num-prompts 1 \ --endpoint /v1/chat/completions --dataset random --task t2i --num-prompts 1 \
--max-concurrency 1 --enable-negative-prompt \ --max-concurrency 1 --enable-negative-prompt \
--random-request-config '[ --random-request-config '[
{"width":512,"height":512,"num_inference_steps":20,"weight":0.15}, {"width":512,"height":512,"num_inference_steps":20,"weight":0.15},
@@ -41,18 +41,18 @@ Usage:
i2i: i2i:
python3 benchmarks/diffusion/diffusion_benchmark_serving.py \ python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend vllm-omni --dataset vbench --task i2i --num-prompts 10 --endpoint /v1/chat/completions --dataset vbench --task i2i --num-prompts 10
# Image (openai backend) # Image (/v1/images/generations endpoint)
t2i: t2i:
python3 benchmarks/diffusion/diffusion_benchmark_serving.py \ python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend openai --dataset vbench --task t2i --num-prompts 10 \ --endpoint /v1/images/generations --dataset vbench --task t2i --num-prompts 10 \
--height 1024 --width 1024 --port 3000 --height 1024 --width 1024 --port 3000
# Video (v1/videos) # Video (v1/videos)
t2v: t2v:
python3 benchmarks/diffusion/diffusion_benchmark_serving.py \ python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend v1/videos --dataset random --task t2v --num-prompts 1 \ --endpoint /v1/videos --dataset random --task t2v --num-prompts 1 \
--max-concurrency 1 --enable-negative-prompt \ --max-concurrency 1 --enable-negative-prompt \
--random-request-config '[ --random-request-config '[
{"width":854,"height":480,"num_inference_steps":18,"num_frames":120,"fps":24,"weight":1} {"width":854,"height":480,"num_inference_steps":18,"num_frames":120,"fps":24,"weight":1}
@@ -80,7 +80,12 @@ from typing import Any
import aiohttp import aiohttp
import numpy as np import numpy as np
import requests import requests
from backends import RequestFuncInput, RequestFuncOutput, backends_function_mapping from backends import (
RequestFuncInput,
RequestFuncOutput,
backends_function_mapping,
normalize_endpoint,
)
from PIL import Image from PIL import Image
from tqdm.asyncio import tqdm from tqdm.asyncio import tqdm
@@ -883,6 +888,14 @@ def wait_for_service(base_url: str, timeout: int = 120) -> None:
time.sleep(1) time.sleep(1)
def _default_endpoint_for_task(task: str) -> str:
if task in {"t2v", "i2v", "ti2v"}:
return "/v1/videos"
if task in {"t2i", "i2i", "ti2i"}:
return "/v1/chat/completions"
raise ValueError(f"Unsupported task for endpoint resolution: {task}")
async def benchmark(args): async def benchmark(args):
# Construct base_url if not provided # Construct base_url if not provided
if args.base_url is None: if args.base_url is None:
@@ -902,18 +915,23 @@ async def benchmark(args):
f"Valid image tasks: {sorted(IMAGE_TASKS)}" f"Valid image tasks: {sorted(IMAGE_TASKS)}"
) )
valid_backends = sorted(backends_function_mapping[task_type].keys()) raw_endpoint = args.endpoint if args.endpoint is not None else args.backend
if raw_endpoint is None:
raw_endpoint = _default_endpoint_for_task(args.task)
args.endpoint = normalize_endpoint(raw_endpoint)
if args.backend not in valid_backends: valid_endpoints = sorted(backends_function_mapping[task_type].keys())
if args.endpoint not in valid_endpoints:
logger.error( logger.error(
f"Invalid backend '{args.backend}' for task '{args.task}' (task type: '{task_type}').\n" f"Invalid endpoint '{args.endpoint}' for task '{args.task}' (task type: '{task_type}').\n"
f"Valid backends for this task type: {valid_backends}\n" f"Valid endpoints for this task type: {valid_endpoints}\n"
f"Example usage: --task {args.task} --backend {valid_backends[0]}" f"Example usage: --task {args.task} --endpoint {valid_endpoints[0]}"
) )
raise ValueError("Backend validation failed. See log above for valid options.") raise ValueError("Endpoint validation failed. See log above for valid options.")
# Setup API URL and request function based on backend # Setup API URL and request function based on endpoint.
request_func, api_url = backends_function_mapping[task_type][args.backend] request_func, api_url = backends_function_mapping[task_type][args.endpoint]
api_url = f"{args.base_url}{api_url}" api_url = f"{args.base_url}{api_url}"
if args.dataset == "vbench": if args.dataset == "vbench":
@@ -976,15 +994,15 @@ async def benchmark(args):
metrics = calculate_metrics(outputs, total_duration, requests_list, args, args.slo) metrics = calculate_metrics(outputs, total_duration, requests_list, args, args.slo)
# Add configuration info to metrics for JSON output # Add configuration info to metrics for JSON output
metrics["backend"] = args.backend metrics["endpoint"] = args.endpoint
metrics["model"] = args.model metrics["model"] = args.model
metrics["dataset"] = args.dataset metrics["dataset"] = args.dataset
metrics["task"] = args.task metrics["task"] = args.task
print("\n{s:{c}^{n}}".format(s=" Serving Benchmark Result ", n=60, c="=")) print("\n{s:{c}^{n}}".format(s=" Serving Benchmark Result ", n=50, c="="))
# Section 1: Configuration # Section 1: Configuration
print("{:<40} {:<15}".format("Backend:", args.backend)) print("{:<40} {:<15}".format("Endpoint:", args.endpoint))
print("{:<40} {:<15}".format("Model:", args.model)) print("{:<40} {:<15}".format("Model:", args.model))
print("{:<40} {:<15}".format("Dataset:", args.dataset)) print("{:<40} {:<15}".format("Dataset:", args.dataset))
print("{:<40} {:<15}".format("Task:", args.task)) print("{:<40} {:<15}".format("Task:", args.task))
@@ -1047,12 +1065,17 @@ if __name__ == "__main__":
parser.add_argument("--host", type=str, default="localhost", help="Server host.") parser.add_argument("--host", type=str, default="localhost", help="Server host.")
parser.add_argument("--port", type=int, default=8091, help="Server port.") parser.add_argument("--port", type=int, default=8091, help="Server port.")
parser.add_argument("--model", type=str, default="default", help="Model name.") parser.add_argument("--model", type=str, default="default", help="Model name.")
parser.add_argument(
"--endpoint",
type=str,
default=None,
help=("Endpoint path to target. Leading '/' is optional, e.g. /v1/videos or v1/videos."),
)
parser.add_argument( parser.add_argument(
"--backend", "--backend",
type=str, type=str,
default="vllm-omni", default=None,
choices=["vllm-omni", "openai", "v1/videos"], help=argparse.SUPPRESS,
help="Backend to target the benchmark to.",
) )
parser.add_argument( parser.add_argument(
"--dataset", "--dataset",
@@ -58,7 +58,7 @@ Record these parameters when reporting performance results.
```bash ```bash
python benchmarks/diffusion/diffusion_benchmark_serving.py \ python benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend vllm-omni \ --endpoint /v1/chat/completions \
--dataset <DATASET_NAME> \ --dataset <DATASET_NAME> \
--task t2i \ --task t2i \
--num-prompts <N> \ --num-prompts <N> \
@@ -71,7 +71,7 @@ python benchmarks/diffusion/diffusion_benchmark_serving.py \
| Parameter | Description | | Parameter | Description |
| ---------------------- | --------------------------------- | | ---------------------- | --------------------------------- |
| `--backend` | Serving backend (use `vllm-omni`) | | `--endpoint` | API endpoint (use `/v1/chat/completions`; leading `/` is optional) |
| `--dataset` | Dataset name (`random` or custom) | | `--dataset` | Dataset name (`random` or custom) |
| `--task` | Task type (e.g., `t2i`) | | `--task` | Task type (e.g., `t2i`) |
| `--num-prompts` | Total number of requests | | `--num-prompts` | Total number of requests |
@@ -129,7 +129,7 @@ python benchmarks/diffusion/diffusion_benchmark_serving.py \
```bash ```bash
python benchmarks/diffusion/diffusion_benchmark_serving.py \ python benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend vllm-omni \ --endpoint /v1/chat/completions \
--dataset random \ --dataset random \
--task t2i \ --task t2i \
--num-prompts 1 \ --num-prompts 1 \
@@ -53,7 +53,7 @@ Record these parameters when reporting performance results.
```bash ```bash
python benchmarks/diffusion/diffusion_benchmark_serving.py \ python benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend v1/videos \ --endpoint /v1/videos \
--dataset <DATASET_NAME> \ --dataset <DATASET_NAME> \
--task t2v \ --task t2v \
--num-prompts <N> \ --num-prompts <N> \
@@ -66,7 +66,7 @@ python benchmarks/diffusion/diffusion_benchmark_serving.py \
| Parameter | Description | | Parameter | Description |
| ---------------------- | --------------------------------- | | ---------------------- | --------------------------------- |
| `--backend` | Serving backend (use `v1/videos`) | | `--endpoint` | API endpoint (use `/v1/videos`; leading `/` is optional) |
| `--dataset` | Dataset name (`random` or custom) | | `--dataset` | Dataset name (`random` or custom) |
| `--task` | Task type (e.g., `t2v`) | | `--task` | Task type (e.g., `t2v`) |
| `--num-prompts` | Total number of requests | | `--num-prompts` | Total number of requests |
@@ -120,7 +120,7 @@ python benchmarks/diffusion/diffusion_benchmark_serving.py \
```bash ```bash
python benchmarks/diffusion/diffusion_benchmark_serving.py \ python benchmarks/diffusion/diffusion_benchmark_serving.py \
--backend v1/videos \ --endpoint /v1/videos \
--dataset random \ --dataset random \
--task t2v \ --task t2v \
--num-prompts 1 \ --num-prompts 1 \
+10 -8
View File
@@ -148,10 +148,11 @@ def _build_omni_kwargs(args, quantization=None):
def _generate_image(omni, args, prompt, seed): def _generate_image(omni, args, prompt, seed):
"""Generate a single image and return (PIL.Image, time_seconds, memory_gib).""" """Generate a single image and return (PIL.Image, time_seconds, memory_gib)."""
from vllm_omni.inputs.data import OmniDiffusionSamplingParams from vllm_omni.inputs.data import OmniDiffusionSamplingParams
from vllm_omni.outputs import OmniRequestOutput
from vllm_omni.platforms import current_omni_platform from vllm_omni.platforms import current_omni_platform
generator = torch.Generator(device=current_omni_platform.device_type).manual_seed(seed) generator = torch.Generator(device=current_omni_platform.device_type).manual_seed(seed)
torch.cuda.reset_peak_memory_stats() torch.accelerator.reset_peak_memory_stats()
start = time.perf_counter() start = time.perf_counter()
outputs = omni.generate( outputs = omni.generate(
{"prompt": prompt}, {"prompt": prompt},
@@ -163,10 +164,11 @@ def _generate_image(omni, args, prompt, seed):
), ),
) )
elapsed = time.perf_counter() - start elapsed = time.perf_counter() - start
peak_mem = torch.cuda.max_memory_allocated() / (1024**3) peak_mem = torch.accelerator.max_memory_allocated() / (1024**3)
first = outputs[0] req_out = OmniRequestOutput.unwrap_result(outputs)
req_out = first.request_output[0] if hasattr(first, "request_output") else first if not req_out.images:
raise ValueError("Could not extract image output from result.")
img = req_out.images[0] img = req_out.images[0]
return img, elapsed, peak_mem return img, elapsed, peak_mem
@@ -178,7 +180,7 @@ def _generate_video(omni, args, prompt, seed):
from vllm_omni.platforms import current_omni_platform from vllm_omni.platforms import current_omni_platform
generator = torch.Generator(device=current_omni_platform.device_type).manual_seed(seed) generator = torch.Generator(device=current_omni_platform.device_type).manual_seed(seed)
torch.cuda.reset_peak_memory_stats() torch.accelerator.reset_peak_memory_stats()
start = time.perf_counter() start = time.perf_counter()
outputs = omni.generate( outputs = omni.generate(
{"prompt": prompt, "negative_prompt": ""}, {"prompt": prompt, "negative_prompt": ""},
@@ -192,7 +194,7 @@ def _generate_video(omni, args, prompt, seed):
), ),
) )
elapsed = time.perf_counter() - start elapsed = time.perf_counter() - start
peak_mem = torch.cuda.max_memory_allocated() / (1024**3) peak_mem = torch.accelerator.max_memory_allocated() / (1024**3)
first = outputs[0] first = outputs[0]
if hasattr(first, "request_output") and isinstance(first.request_output, list): if hasattr(first, "request_output") and isinstance(first.request_output, list):
@@ -228,8 +230,8 @@ def _unload_omni(omni):
del omni del omni
gc.collect() gc.collect()
if torch.cuda.is_available(): if torch.cuda.is_available():
torch.cuda.empty_cache() torch.accelerator.empty_cache()
torch.cuda.synchronize() torch.accelerator.synchronize()
def run_benchmark(args): def run_benchmark(args):
@@ -614,8 +614,8 @@ Examples:
if not torch.cuda.is_available(): if not torch.cuda.is_available():
print("[ERROR] CUDA is not available but GPU mode was requested.") print("[ERROR] CUDA is not available but GPU mode was requested.")
sys.exit(1) sys.exit(1)
if args.gpu_id >= torch.cuda.device_count(): if args.gpu_id >= torch.accelerator.device_count():
print(f"[ERROR] GPU {args.gpu_id} not available. Found {torch.cuda.device_count()} GPUs.") print(f"[ERROR] GPU {args.gpu_id} not available. Found {torch.accelerator.device_count()} GPUs.")
sys.exit(1) sys.exit(1)
print(f"[INFO] Using GPU {args.gpu_id}: {torch.cuda.get_device_name(args.gpu_id)}") print(f"[INFO] Using GPU {args.gpu_id}: {torch.cuda.get_device_name(args.gpu_id)}")
+1 -1
View File
@@ -63,7 +63,7 @@ def _find_repo_root(start: Path) -> Path:
REPO_ROOT = _find_repo_root(Path(__file__).resolve()) REPO_ROOT = _find_repo_root(Path(__file__).resolve())
DEFAULT_STAGE_ASYNC = REPO_ROOT / "vllm_omni" / "model_executor" / "stage_configs" / "voxcpm_async_chunk.yaml" DEFAULT_STAGE_ASYNC = REPO_ROOT / "vllm_omni" / "model_executor" / "stage_configs" / "voxcpm_async_chunk.yaml"
DEFAULT_STAGE_SYNC = REPO_ROOT / "vllm_omni" / "model_executor" / "stage_configs" / "voxcpm.yaml" DEFAULT_STAGE_SYNC = REPO_ROOT / "vllm_omni" / "deploy" / "voxcpm.yaml"
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
+1 -1
View File
@@ -1,5 +1,5 @@
ARG VLLM_BASE_IMAGE=vllm/vllm-openai ARG VLLM_BASE_IMAGE=vllm/vllm-openai
ARG VLLM_BASE_TAG=v0.20.0 ARG VLLM_BASE_TAG=v0.21.0
FROM ${VLLM_BASE_IMAGE}:${VLLM_BASE_TAG} FROM ${VLLM_BASE_IMAGE}:${VLLM_BASE_TAG}
ARG APP_DIR=/workspace/vllm-omni ARG APP_DIR=/workspace/vllm-omni
WORKDIR ${APP_DIR} WORKDIR ${APP_DIR}
+48
View File
@@ -0,0 +1,48 @@
ARG VLLM_ASCEND_IMAGE=quay.io/ascend/vllm-ascend
ARG VLLM_ASCEND_TAG=nightly-main
FROM ${VLLM_ASCEND_IMAGE}:${VLLM_ASCEND_TAG}
ARG APP_DIR=/vllm-workspace/vllm-omni
WORKDIR ${APP_DIR}
COPY . .
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ffmpeg \
libgl1 \
libglib2.0-0 \
libgomp1 \
libsm6 \
libxext6 \
libxrender1 \
libxcb1 \
espeak-ng && \
rm -rf /var/cache/apt/* && \
rm -rf /var/lib/apt/lists/*
RUN source /usr/local/Ascend/ascend-toolkit/set_env.sh && \
source /usr/local/Ascend/nnal/atb/set_env.sh && \
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/Ascend/ascend-toolkit/latest/`uname -i`-linux/devlib && \
python3 -m pip install -U pip && \
python3 -m pip install -v \
"pytest>=7.0.0" \
"pytest-asyncio>=0.21.0" \
"pytest-cov>=4.0.0" \
"pytest-mock>=3.10.0" \
"openpyxl>=3.0.0" \
"psutil>=7.2.0" \
"soundfile>=0.13.1" \
"pyttsx3>=2.99" \
"opencv-python>=4.12.0.88" \
"imageio[ffmpeg]>=0.6.0" \
--index-url http://cache-service.nginx-pypi-cache.svc.cluster.local/pypi/simple \
--trusted-host cache-service.nginx-pypi-cache.svc.cluster.local \
--default-timeout=60 && \
python3 -m pip install -v -e /vllm-workspace/vllm-omni/ --no-build-isolation \
--index-url http://cache-service.nginx-pypi-cache.svc.cluster.local/pypi/simple \
--trusted-host cache-service.nginx-pypi-cache.svc.cluster.local \
--default-timeout=60
ENV VLLM_WORKER_MULTIPROC_METHOD=spawn
ENTRYPOINT []
+48
View File
@@ -0,0 +1,48 @@
ARG VLLM_ASCEND_IMAGE=quay.io/ascend/vllm-ascend
ARG VLLM_ASCEND_TAG=nightly-main-a3
FROM ${VLLM_ASCEND_IMAGE}:${VLLM_ASCEND_TAG}
ARG APP_DIR=/vllm-workspace/vllm-omni
WORKDIR ${APP_DIR}
COPY . .
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ffmpeg \
libgl1 \
libglib2.0-0 \
libgomp1 \
libsm6 \
libxext6 \
libxrender1 \
libxcb1 \
espeak-ng && \
rm -rf /var/cache/apt/* && \
rm -rf /var/lib/apt/lists/*
RUN source /usr/local/Ascend/ascend-toolkit/set_env.sh && \
source /usr/local/Ascend/nnal/atb/set_env.sh && \
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/Ascend/ascend-toolkit/latest/`uname -i`-linux/devlib && \
python3 -m pip install -U pip && \
python3 -m pip install -v \
"pytest>=7.0.0" \
"pytest-asyncio>=0.21.0" \
"pytest-cov>=4.0.0" \
"pytest-mock>=3.10.0" \
"openpyxl>=3.0.0" \
"psutil>=7.2.0" \
"soundfile>=0.13.1" \
"pyttsx3>=2.99" \
"opencv-python>=4.12.0.88" \
"imageio[ffmpeg]>=0.6.0" \
--index-url http://cache-service.nginx-pypi-cache.svc.cluster.local/pypi/simple \
--trusted-host cache-service.nginx-pypi-cache.svc.cluster.local \
--default-timeout=60 && \
python3 -m pip install -v -e /vllm-workspace/vllm-omni/ --no-build-isolation \
--index-url http://cache-service.nginx-pypi-cache.svc.cluster.local/pypi/simple \
--trusted-host cache-service.nginx-pypi-cache.svc.cluster.local \
--default-timeout=60
ENV VLLM_WORKER_MULTIPROC_METHOD=spawn
ENTRYPOINT []
+1 -1
View File
@@ -1,4 +1,4 @@
ARG BASE_IMAGE=vllm/vllm-openai-rocm:v0.20.0 ARG BASE_IMAGE=vllm/vllm-openai-rocm:v0.21.0
FROM ${BASE_IMAGE} AS base FROM ${BASE_IMAGE} AS base
# Declare a variable to know if we want to use the nightly build or the stable build. # Declare a variable to know if we want to use the nightly build or the stable build.
+1 -1
View File
@@ -88,7 +88,7 @@ ENV UV_EXTRA_INDEX_URL=${PIP_EXTRA_INDEX_URL}
ENV UV_INDEX_STRATEGY="unsafe-best-match" ENV UV_INDEX_STRATEGY="unsafe-best-match"
ENV UV_LINK_MODE="copy" ENV UV_LINK_MODE="copy"
ARG VLLM_VERSION=v0.20.0 ARG VLLM_VERSION=v0.21.0
RUN git clone -b ${VLLM_VERSION} https://github.com/vllm-project/vllm RUN git clone -b ${VLLM_VERSION} https://github.com/vllm-project/vllm
WORKDIR /workspace/vllm WORKDIR /workspace/vllm
+4 -1
View File
@@ -6,10 +6,11 @@ nav:
- getting_started/installation/* - getting_started/installation/*
- Serving: - Serving:
- OpenAI-Compatible API: - OpenAI-Compatible API:
- Diffusion Chat API: serving/diffusion_chat_api.md - Chat Completions API: serving/chat_completions_api.md
- Image Generation: serving/image_generation_api.md - Image Generation: serving/image_generation_api.md
- Image Edit: serving/image_edit_api.md - Image Edit: serving/image_edit_api.md
- Text to Speech: serving/speech_api.md - Text to Speech: serving/speech_api.md
- Videos: serving/videos_api.md
- Audio Generation: serving/audio_generate_api.md - Audio Generation: serving/audio_generate_api.md
- Streaming Video Input: serving/video_stream_api.md - Streaming Video Input: serving/video_stream_api.md
- Examples: - Examples:
@@ -76,8 +77,10 @@ nav:
- Quantization: - Quantization:
- Overview: user_guide/quantization/overview.md - Overview: user_guide/quantization/overview.md
- Online Quantization: user_guide/quantization/online.md - Online Quantization: user_guide/quantization/online.md
- Quantized KV Cache: user_guide/quantization/quantized_kvcache.md
- FP8 W8A8: user_guide/quantization/fp8.md - FP8 W8A8: user_guide/quantization/fp8.md
- Int8 W8A8: user_guide/quantization/int8.md - Int8 W8A8: user_guide/quantization/int8.md
- ModelOpt: user_guide/quantization/modelopt.md
- GGUF: user_guide/quantization/gguf.md - GGUF: user_guide/quantization/gguf.md
- AutoRound: user_guide/quantization/autoround.md - AutoRound: user_guide/quantization/autoround.md
- msModelSlim: user_guide/quantization/msmodelslim.md - msModelSlim: user_guide/quantization/msmodelslim.md
+4 -1
View File
@@ -99,7 +99,6 @@ Configuration classes.
- [vllm_omni.diffusion.cache.teacache.config.TeaCacheConfig][] - [vllm_omni.diffusion.cache.teacache.config.TeaCacheConfig][]
- [vllm_omni.distributed.omni_connectors.utils.config.ConnectorSpec][] - [vllm_omni.distributed.omni_connectors.utils.config.ConnectorSpec][]
- [vllm_omni.distributed.omni_connectors.utils.config.OmniTransferConfig][] - [vllm_omni.distributed.omni_connectors.utils.config.OmniTransferConfig][]
- [vllm_omni.model_executor.models.cosyvoice3.config.CosyVoice3Config][]
- [vllm_omni.model_executor.models.fish_speech.configuration_fish_speech.FishSpeechConfig][] - [vllm_omni.model_executor.models.fish_speech.configuration_fish_speech.FishSpeechConfig][]
- [vllm_omni.model_executor.models.fish_speech.configuration_fish_speech.FishSpeechFastARConfig][] - [vllm_omni.model_executor.models.fish_speech.configuration_fish_speech.FishSpeechFastARConfig][]
- [vllm_omni.model_executor.models.fish_speech.configuration_fish_speech.FishSpeechSlowARConfig][] - [vllm_omni.model_executor.models.fish_speech.configuration_fish_speech.FishSpeechSlowARConfig][]
@@ -116,10 +115,14 @@ Configuration classes.
- [vllm_omni.model_executor.models.qwen3_tts.tokenizer_25hz.configuration_qwen3_tts_tokenizer_v1.Qwen3TTSTokenizerV1DecoderConfig][] - [vllm_omni.model_executor.models.qwen3_tts.tokenizer_25hz.configuration_qwen3_tts_tokenizer_v1.Qwen3TTSTokenizerV1DecoderConfig][]
- [vllm_omni.model_executor.models.qwen3_tts.tokenizer_25hz.configuration_qwen3_tts_tokenizer_v1.Qwen3TTSTokenizerV1DecoderDiTConfig][] - [vllm_omni.model_executor.models.qwen3_tts.tokenizer_25hz.configuration_qwen3_tts_tokenizer_v1.Qwen3TTSTokenizerV1DecoderDiTConfig][]
- [vllm_omni.model_executor.models.qwen3_tts.tokenizer_25hz.configuration_qwen3_tts_tokenizer_v1.Qwen3TTSTokenizerV1EncoderConfig][] - [vllm_omni.model_executor.models.qwen3_tts.tokenizer_25hz.configuration_qwen3_tts_tokenizer_v1.Qwen3TTSTokenizerV1EncoderConfig][]
- [vllm_omni.transformers_utils.configs.cosyvoice3.CosyVoice3Config][]
- [vllm_omni.transformers_utils.configs.mammoth_moda2.Mammothmoda2Config][] - [vllm_omni.transformers_utils.configs.mammoth_moda2.Mammothmoda2Config][]
- [vllm_omni.transformers_utils.configs.mammoth_moda2.Mammothmoda2Qwen2_5_VLConfig][] - [vllm_omni.transformers_utils.configs.mammoth_moda2.Mammothmoda2Qwen2_5_VLConfig][]
- [vllm_omni.transformers_utils.configs.mammoth_moda2.Mammothmoda2Qwen2_5_VLTextConfig][] - [vllm_omni.transformers_utils.configs.mammoth_moda2.Mammothmoda2Qwen2_5_VLTextConfig][]
- [vllm_omni.transformers_utils.configs.mammoth_moda2.Mammothmoda2Qwen2_5_VLVisionConfig][] - [vllm_omni.transformers_utils.configs.mammoth_moda2.Mammothmoda2Qwen2_5_VLVisionConfig][]
- [vllm_omni.transformers_utils.configs.omnivoice.OmniVoiceConfig][]
- [vllm_omni.transformers_utils.configs.voxcpm.VoxCPMConfig][]
- [vllm_omni.transformers_utils.configs.voxcpm2.VoxCPM2Config][]
## Workers ## Workers
Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 261 KiB

+26 -3
View File
@@ -39,11 +39,34 @@ Committers have write access and merge rights. They typically have deep expertis
- [@linyueqian](https://github.com/linyueqian): TTS and Omni Support - [@linyueqian](https://github.com/linyueqian): TTS and Omni Support
- [@lishunyang12](https://github.com/lishunyang12): Quantization and Configuration - [@lishunyang12](https://github.com/lishunyang12): Quantization and Configuration
- [@princepride](https://github.com/princepride): Diffusion and Omni Support - [@princepride](https://github.com/princepride): Diffusion and Omni Support
- [@SamitHuang](https://github.com/SamitHuang): RL and Diffusion - [@RuixiangMa](https://github.com/RuixiangMa): Diffusion models, parallel, cache, and docs
- [@SamitHuang](https://github.com/SamitHuang): RL, Diffusion, and cache
- [@tzhouam](https://github.com/tzhouam): Engine and New Model Support - [@tzhouam](https://github.com/tzhouam): Engine and New Model Support
- [@wtomin](https://github.com/wtomin): Diffusion and Parallelism - [@wtomin](https://github.com/wtomin): Diffusion models, parallel, and docs
- [@ZeldaHuang](https://github.com/ZeldaHuang): Omni Support - [@ZeldaHuang](https://github.com/ZeldaHuang): Omni Support
- [@ZJY0516](https://github.com/ZJY0516): Diffusion and CustomOp - [@ZJY0516](https://github.com/ZJY0516): Diffusion attention backend, kernel fusion, and CustomOp
- [@yuanheng-zhao](https://github.com/yuanheng-zhao): Diffusion cache, offload, and Omni Support
## Diffusion Workload Division
This section breaks down diffusion-related responsibilities. If you have PRs touching these areas, please ping the listed owners for review.
### Runtime-related
- Diffusion models: @RuixiangMa, @wtomin
### Optimization
- Parallel: @RuixiangMa, @wtomin
- Attention backend: @ZJY0516
- Cache: @yuanheng-zhao, @RuixiangMa, @SamitHuang
- Offload: @yuanheng-zhao
- Quantization: @lishunyang12, @david6666666
- Kernel fusion / communication-computation: @ZJY0516
### Docs & Test
- Diffusion docs: @RuixiangMa, @wtomin
## Meetings ## Meetings
+4 -4
View File
@@ -152,6 +152,8 @@ vllm_omni/ tests/
│ │ │ │ └── test_lora_manager.py ✅ │ │ │ │ └── test_lora_manager.py ✅
│ ├── models/ │ ├── models/ │ ├── models/ │ ├── models/
│ │ ├── qwen_image/ │ │ ├── qwen_image/ (e2e coverage) │ │ ├── qwen_image/ │ │ ├── qwen_image/ (e2e coverage)
│ │ ├── ovis_image/ │ │ ├── ovis_image/
│ │ │ └── ... │ │ │ └── test_ovis_image.py ✅
│ │ ├── z_image/ │ │ └── z_image/ │ │ ├── z_image/ │ │ └── z_image/
│ │ └── ... │ │ └── test_zimage_tp_constraints.py ✅ │ │ └── ... │ │ └── test_zimage_tp_constraints.py ✅
│ └── worker/ │ └── worker/ │ └── worker/ │ └── worker/
@@ -232,15 +234,13 @@ vllm_omni/ tests/
│ ├── test_qwen3_omni.py │ ├── test_qwen3_omni.py
│ ├── test_qwen3_omni_expansion.py │ ├── test_qwen3_omni_expansion.py
│ ├── test_mimo_audio.py │ ├── test_mimo_audio.py
│ ├── test_image_gen_edit.py
│ └── test_images_generations_lora.py │ └── test_images_generations_lora.py
└── offline_inference/ ✅ └── offline_inference/ ✅
├── test_qwen2_5_omni.py ├── test_qwen2_5_omni.py
├── test_qwen3_omni.py ├── test_qwen3_omni.py
├── test_bagel_text2img.py ├── test_bagel_text2img.py
├── test_t2i_model.py ├── test_z_image.py
├── test_t2v_model.py ├── test_wan22.py
├── test_ovis_image.py
├── test_zimage_tensor_parallel.py ├── test_zimage_tensor_parallel.py
├── test_cache_dit.py ├── test_cache_dit.py
├── test_teacache.py ├── test_teacache.py
+9 -3
View File
@@ -80,7 +80,7 @@ Our test scripts use the pytest framework. First, please use `git clone https://
```bash ```bash
cd tests cd tests
pytest -s -v -m "full_model and omni and H100" --run-level=full_model pytest -s -v -m "full_model and (omni or tts) and H100" --run-level=full_model
``` ```
=== "L5 level" === "L5 level"
@@ -91,10 +91,16 @@ Our test scripts use the pytest framework. First, please use `git clone https://
cd tests cd tests
# Stability: Qwen3-Omni # Stability: Qwen3-Omni
pytest -s -v dfx/stability/scripts/test_stability_qwen3_omni.py pytest -s -v dfx/stability/scripts/test_stability_qwen3_omni.py -m slow
# Stability: Wan2.2 (v1/videos diffusion benchmark loop) # Stability: Wan2.2 (v1/videos diffusion benchmark loop)
pytest -s -v dfx/stability/scripts/test_stability_wan22.py pytest -s -v dfx/stability/scripts/test_stability_wan22.py -m slow
# Reliability: Qwen3-Omni
pytest -s -v dfx/reliability/test_reliability_qwen3_omni.py -m slow
# Reliability: Wan2.2
pytest -s -v dfx/reliability/test_reliability_wan22.py -m slow
``` ```
+2 -1
View File
@@ -11,7 +11,8 @@ Defined in `pyproject.toml`:
| `advanced_model` | L3 tests (run on each merge to main) | | `advanced_model` | L3 tests (run on each merge to main) |
| `full_model` | L4 tests (run nightly) | | `full_model` | L4 tests (run nightly) |
| `diffusion` | Diffusion model tests | | `diffusion` | Diffusion model tests |
| `omni` | Omni model tests | | `omni` | Omni multimodal model tests |
| `tts` | TTS model tests |
| `cache` | Cache backend tests | | `cache` | Cache backend tests |
| `parallel` | Parallelism/distributed tests | | `parallel` | Parallelism/distributed tests |
| `cpu` | Tests that run on CPU | | `cpu` | Tests that run on CPU |
+4 -5
View File
@@ -54,6 +54,8 @@ vllm_omni/ tests/
│ │ │ │ └── test_lora_manager.py ✅ │ │ │ │ └── test_lora_manager.py ✅
│ ├── models/ │ ├── models/ │ ├── models/ │ ├── models/
│ │ ├── qwen_image/ │ │ ├── qwen_image/ (e2e coverage) │ │ ├── qwen_image/ │ │ ├── qwen_image/ (e2e coverage)
│ │ ├── ovis_image/ │ │ ├── ovis_image/
│ │ │ └── ... │ │ │ └── test_ovis_image.py ✅
│ │ ├── z_image/ │ │ └── z_image/ │ │ ├── z_image/ │ │ └── z_image/
│ │ └── ... │ │ └── test_zimage_tp_constraints.py ✅ │ │ └── ... │ │ └── test_zimage_tp_constraints.py ✅
│ └── worker/ │ └── worker/ │ └── worker/ │ └── worker/
@@ -134,15 +136,13 @@ vllm_omni/ tests/
│ ├── test_qwen3_omni.py │ ├── test_qwen3_omni.py
│ ├── test_qwen3_omni_expansion.py │ ├── test_qwen3_omni_expansion.py
│ ├── test_mimo_audio.py │ ├── test_mimo_audio.py
│ ├── test_image_gen_edit.py
│ └── test_images_generations_lora.py │ └── test_images_generations_lora.py
└── offline_inference/ ✅ └── offline_inference/ ✅
├── test_qwen2_5_omni.py ├── test_qwen2_5_omni.py
├── test_qwen3_omni.py ├── test_qwen3_omni.py
├── test_bagel_text2img.py ├── test_bagel_text2img.py
├── test_t2i_model.py ├── test_z_image.py
├── test_t2v_model.py ├── test_wan22.py
├── test_ovis_image.py
├── test_zimage_tensor_parallel.py ├── test_zimage_tensor_parallel.py
├── test_cache_dit.py ├── test_cache_dit.py
├── test_teacache.py ├── test_teacache.py
@@ -213,7 +213,6 @@ E2E Online tests for Qwen3-Omni model with mix input and audio+text output.
import os import os
os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn" os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn"
os.environ["VLLM_TEST_CLEAN_GPU_MEMORY"] = "0"
import threading import threading
from pathlib import Path from pathlib import Path
@@ -115,17 +115,20 @@ class YourAttentionBlock(nn.Module):
from vllm_omni.diffusion.attention.layer import Attention from vllm_omni.diffusion.attention.layer import Attention
from vllm_omni.diffusion.attention.backends.abstract import AttentionMetadata from vllm_omni.diffusion.attention.backends.abstract import AttentionMetadata
class YourAttentionBlock(nn.Module): class YourSelfAttentionBlock(nn.Module):
def __init__(self, ...): def __init__(self, ...):
super().__init__() super().__init__()
# Initialize vLLM-Omni's Attention layer # Initialize vLLM-Omni's Attention layer.
# `role` lets users target this site with --diffusion-attention-config
# (e.g. --diffusion-attention-config.per_role.self.backend SAGE_ATTN).
self.attn = Attention( self.attn = Attention(
num_heads=self.num_heads, num_heads=self.num_heads,
head_size=self.head_dim, head_size=self.head_dim,
softmax_scale=1.0 / (self.head_dim ** 0.5), softmax_scale=1.0 / (self.head_dim ** 0.5),
causal=False, # Diffusion models typically use bidirectional attention causal=False, # Diffusion models typically use bidirectional attention
num_kv_heads=self.num_kv_heads, num_kv_heads=self.num_kv_heads,
role="self",
) )
def forward(self, hidden_states, encoder_hidden_states=None, attention_mask=None, ...): def forward(self, hidden_states, encoder_hidden_states=None, attention_mask=None, ...):
@@ -133,7 +136,6 @@ class YourAttentionBlock(nn.Module):
# Create attention metadata # Create attention metadata
attn_metadata = AttentionMetadata(attn_mask=attention_mask) attn_metadata = AttentionMetadata(attn_mask=attention_mask)
hidden_states = self.attn(query, key, value, attn_metadata=attn_metadata) hidden_states = self.attn(query, key, value, attn_metadata=attn_metadata)
``` ```
**Key Points:** **Key Points:**
@@ -141,8 +143,35 @@ class YourAttentionBlock(nn.Module):
- **Attention layer initialization:** Done in `__init__`, not per-forward - **Attention layer initialization:** Done in `__init__`, not per-forward
- **Tensor shapes:** vLLM-Omni `Attention` expects QKV to have `[B, seq, num_heads, head_dim]` shape - **Tensor shapes:** vLLM-Omni `Attention` expects QKV to have `[B, seq, num_heads, head_dim]` shape
- **AttentionMetadata:** Wraps attention mask and other metadata - **AttentionMetadata:** Wraps attention mask and other metadata
- **Role:** Tag every `Attention` site with a `role` string so users can configure backends per role (see below)
**Attention backends:** vLLM-Omni automatically selects the attention backend given the environmental variable `DIFFUSION_ATTENTION_BACKEND`. The default attention backend is `FLASH_ATTN` for diffusion models. **Declaring attention roles**
The `role` argument is a free-form string that identifies this attention site. Users can match it from `--diffusion-attention-config.per_role.<role>.*` to swap backends without touching model code. Two conventions cover the common cases:
| Convention | When to use | Example |
|---|---|---|
| `"self"` | Q/K/V come from the same hidden state | DiT self-attention block |
| `"cross"` | K/V come from a separate `encoder_hidden_states` | Text-conditioned cross-attention |
For multi-modal or unusual sites, use a dot-namespaced role and pair it with `role_category` so it can fall back to the generic config when nothing model-specific is set:
```python
# A model-specific cross-attention site that user config can target
# either as 'mymodel.audio_to_video' (exact) or as 'cross' (category fallback).
self.audio_to_video_attn = Attention(
num_heads=self.num_heads,
head_size=self.head_dim,
softmax_scale=1.0 / (self.head_dim ** 0.5),
causal=False,
role="mymodel.audio_to_video",
role_category="cross",
)
```
For cross-attention sites whose K/V are replicated across ranks (e.g. text encoder output), pass `skip_sequence_parallel=True` to opt this layer out of sequence-parallel sharding.
**Attention backends:** When the user does not configure a backend, vLLM-Omni asks the current platform for its default (typically `FLASH_ATTN` on CUDA when available). Users override the default via `--diffusion-attention-backend`, the `DIFFUSION_ATTENTION_BACKEND` env var, or finer-grained `--diffusion-attention-config.per_role.*` flags. See [Diffusion Attention Backends](../../user_guide/diffusion/attention_backends.md) for the full configuration surface.
#### 1.3: Replace Imports and Utilities #### 1.3: Replace Imports and Utilities
+356
View File
@@ -0,0 +1,356 @@
# Pipeline Parallel
This section describes how to add Pipeline Parallelism (PP) to a diffusion pipeline. We use the Wan2.2 text-to-video and
image-to-video pipelines as the reference implementations.
## Implementation Checklist
Adding Pipeline Parallel support requires:
1. ✅ **Inherit the mixin** - add `PipelineParallelMixin` before `CFGParallelMixin` in the pipeline class
2. ✅ **Make stage forward resumable** - support `intermediate_tensors` in the transformer forward path
3. ✅ **Return the right object type** - `IntermediateTensors` on non-last PP ranks, final model output on the last rank
4. ✅ **Use the standard denoising loop** - call `predict_noise_maybe_with_cfg()` and `scheduler_step_maybe_with_cfg()`
5. ✅ **Test parity** - compare PP results against the single-GPU baseline
---
## Table of Contents
- [Overview](#overview)
- [Step-by-Step Implementation](#step-by-step-implementation)
- [Testing](#testing)
- [Troubleshooting](#troubleshooting)
- [Reference Implementations](#reference-implementations)
---
## Overview
### What is Pipeline Parallelism?
Pipeline Parallelism splits the denoising transformer into multiple sequential stages and places each stage on a
different rank. Instead of every rank holding the full DiT, each PP rank owns only a slice of the layers.
For each denoising step:
1. Rank 0 starts the forward pass using the current latents.
2. Each intermediate rank receives hidden states from the previous rank, runs its local layer slice, and forwards the
intermediate tensors downstream.
3. The last PP rank produces the final noise prediction.
4. The last PP rank applies the scheduler step and sends the updated latents back to rank 0 for the next timestep.
This reduces per-rank model memory and enables larger diffusion transformers to run across multiple GPUs. It can also be
combined with CFG-Parallel, where each PP pipeline carries one CFG branch.
### Architecture
vLLM-Omni provides `PipelineParallelMixin` to encapsulate the PP communication pattern for diffusion pipelines.
| Method | Purpose | Automatic Behavior |
|-----------------------------------|-------------------------------|---------------------------------------------------------------------------------|
| `diffuse()` | Denoising loop boundary | Wrapped by `PipelineParallelMixin` to flush pending async PP sends on exit |
| `predict_noise_maybe_with_cfg()` | Predict noise with PP support | Runs partial forwards on non-last PP ranks, combines with CFG logic when needed |
| `scheduler_step_maybe_with_cfg()` | Step scheduler with PP sync | Runs scheduler on the last PP rank, returns updated latents to rank 0 |
| `_sync_pp_send()` | Flush pending async sends | Waits for outstanding `isend` handles before later collectives or decode |
`PipelineParallelMixin` is intentionally a pipeline-level abstraction. Your model-specific `predict_noise()` still
defines how a local stage executes.
### How It Works
`predict_noise_maybe_with_cfg()` automatically switches between these modes:
- **PP disabled** (`pipeline_parallel_size == 1`):
- Falls back to `CFGParallelMixin.predict_noise_maybe_with_cfg()`
- **PP only** (`pipeline_parallel_size > 1`, `cfg_parallel_size == 1`):
- Rank 0 starts with the input latents
- Middle ranks receive `intermediate_tensors`, run their local layer range, and asynchronously send downstream
- The last rank returns the final noise prediction
- When CFG is enabled in this mode (sequential CFG), both the positive and negative branches run through the same
PP pipeline, doubling the communication volume per denoising step. Prefer PP + CFG-Parallel when
`cfg_parallel_size > 1` is available to avoid this overhead.
- **PP + CFG-Parallel** (`pipeline_parallel_size > 1`, `cfg_parallel_size > 1`):
- Each PP pipeline carries one CFG branch
- The last PP rank all-gathers across the CFG group
- CFG combination happens on every last PP rank in the CFG group, matching the non-PP CFG-parallel behavior
`scheduler_step_maybe_with_cfg()` keeps the denoising loop consistent:
- **PP disabled**:
- Falls back to `scheduler_step_maybe_with_cfg()`
- **PP enabled**:
- Only the last PP rank has `noise_pred` and runs the scheduler step
- The resulting latents are sent back to rank 0
- Rank 0 receives an `AsyncLatents` wrapper that resolves only when the tensor is next consumed
This asynchronous design avoids unnecessary blocking between denoising steps.
When a pipeline class is defined, `PipelineParallelMixin` wraps its `diffuse()` method and calls `_sync_pp_send()` in a
`finally` block after `diffuse()` returns or raises. This keeps model code free of explicit PP cleanup while still
ensuring that the final non-blocking PP send completes before decode or later collectives.
---
## Step-by-Step Implementation
### Step 1: Inherit `PipelineParallelMixin` and `CFGParallelMixin`
`PipelineParallelMixin` **requires** `CFGParallelMixin` and must appear before it in the class MRO. This is enforced at
class definition time via `__init_subclass__`: defining a pipeline that inherits `PipelineParallelMixin` without
`CFGParallelMixin`, or after `CFGParallelMixin`, raises a `TypeError` immediately on import.
`PipelineParallelMixin` delegates noise prediction, CFG combination, and scheduler stepping to `CFGParallelMixin`, which
supplies `predict_noise()`, `predict_noise_maybe_with_cfg()`, `scheduler_step_maybe_with_cfg()`, and
`combine_cfg_noise()`.
**Example:**
```python
from vllm_omni.diffusion.distributed.cfg_parallel import CFGParallelMixin
from vllm_omni.diffusion.distributed.pipeline_parallel import PipelineParallelMixin
import torch.nn as nn
class YourPipeline(nn.Module, PipelineParallelMixin, CFGParallelMixin):
...
```
The order matters: `PipelineParallelMixin` must be listed before `CFGParallelMixin` so calls to
`predict_noise_maybe_with_cfg()` and `scheduler_step_maybe_with_cfg()` resolve to the PP-aware wrappers, while their
`super()` calls delegate to the CFG implementation when PP is disabled or after the last PP stage.
### Step 2: Make model forward and `predict_noise()` PP-aware
The PP mixin injects `intermediate_tensors` into the normal `predict_noise()` call. Your model forward path must support
two inputs:
- Normal input from rank 0, typically passed as `hidden_states` or `x`
- `intermediate_tensors` from upstream PP ranks
The standard model forward pattern is:
1. If `intermediate_tensors` is present, read the local hidden state from it.
2. Run only this rank's layer slice.
3. Return `IntermediateTensors(...)` on non-last PP ranks.
4. Return the final model output on the last PP rank. `CFGParallelMixin.predict_noise()` already follows this contract
for common pipelines.
**Minimal example:**
```python
from vllm.sequence import IntermediateTensors
from vllm_omni.diffusion.distributed.parallel_state import get_pp_group
def forward(self, hidden_states=None, intermediate_tensors=None, **kwargs):
if intermediate_tensors is not None:
hidden_states = intermediate_tensors["hidden_states"]
for i in range(self.start_layer, self.end_layer):
hidden_states = self.layers[i](hidden_states)
pp_group = get_pp_group()
if not pp_group.is_last_rank:
return IntermediateTensors({"hidden_states": hidden_states})
return (hidden_states,)
```
### Step 3: Partition the transformer layers
The local module on each PP rank must expose only that rank's layer slice. In the reference tests and vLLM model
implementations, this is typically done with vLLM utilities such as `make_layers(...)`, which fill missing ranges with
`PPMissingLayer`. Besides partitioning the layers themselves, model authors should also wire
`make_empty_intermediate_tensors_factory(...)` for intermediate tensor allocation and `is_pp_missing_parameter(...)` for
PP-aware weight loading.
To prepare a transformer for PP, implement the following pieces in order.
#### 3.1 Split the transformer layers across PP ranks
Each PP rank should own only its local layer range, typically exposed as `[start_layer, end_layer)`. In practice this is
usually done with `make_layers(...)`, which constructs local layers and fills missing ranges with `PPMissingLayer`.
The goal is:
- every PP rank knows its `[start_layer, end_layer)` range
- non-local layers do not execute on this rank
- the forward can resume from incoming `intermediate_tensors`
By default, layers are distributed evenly across PP ranks using `get_pp_indices()`. When the number of layers is not
divisible by the PP size, remaining layers are assigned to middle partitions to balance compute and memory. You can
override this with the `VLLM_PP_LAYER_PARTITION` environment variable to specify an exact per-rank layer count:
```bash
# Example: 40 layers across 4 PP ranks, assigning 8 / 12 / 12 / 8 layers
export VLLM_PP_LAYER_PARTITION=8,12,12,8
```
The value must be a comma-separated list of integers whose length equals `pipeline_parallel_size` and whose sum equals
the total number of transformer layers.
#### 3.2 Expose `make_empty_intermediate_tensors`
Your transformer module should expose `self.make_empty_intermediate_tensors`, typically created with
`make_empty_intermediate_tensors_factory(...)`.
This is important because PP ranks need a consistent way to allocate placeholder `IntermediateTensors` with the expected
keys and hidden dimension.
**Example:**
```python
from vllm.model_executor.models.utils import make_empty_intermediate_tensors_factory
self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
["hidden_states"],
inner_dim,
)
```
For Wan2.2, the intermediate payload between PP stages is the token sequence stored under `"hidden_states"`, so the
factory is created with that key and the transformer hidden size.
#### 3.3 Return `IntermediateTensors` on non-last PP ranks
Your model `forward()` or custom `predict_noise()` implementation should consume `intermediate_tensors` on non-first
ranks and return `IntermediateTensors(...)` on non-last ranks.
This lets each PP stage resume from the upstream hidden states and pass the local result to the next stage.
#### 3.4 Skip non-local weights during `load_weights()`
When a model is PP-partitioned, many parameters in the checkpoint belong to layers that do not exist on the current
rank. `load_weights()` must therefore skip parameters for missing PP stages using `is_pp_missing_parameter(...)`.
If this is not done, weight loading will either fail or incorrectly try to load tensors into `PPMissingLayer`
placeholders.
The Wan2.2 transformer is the best reference here: it uses `is_pp_missing_parameter(...)` before loading both remapped
and fused parameters.
If your model has multiple transformer variants, PP still works as long as each selected transformer obeys the same
contract.
### Step 4: Use the standard denoising contract
vLLM-Omni diffusion pipelines already route denoising through `diffuse()`, `predict_noise_maybe_with_cfg()`, and
`scheduler_step_maybe_with_cfg()`. `PipelineParallelMixin` overrides those standard helpers when
`pipeline_parallel_size > 1`, so model integrations should not add separate PP-specific helper names or manual post-loop
synchronization.
In PP mode:
- Non-last PP ranks return `None` from `predict_noise_maybe_with_cfg()` after sending `IntermediateTensors` downstream.
- The last PP rank returns the final prediction, and with CFG-Parallel every last PP rank in the CFG group receives the
combined result.
- Rank 0 receives `AsyncLatents` from `scheduler_step_maybe_with_cfg()`, which resolves only when the latents are
consumed.
- Pending non-blocking sends are flushed automatically when `diffuse()` exits.
---
## Testing
Use an offline inference script with `pipeline_parallel_size > 1`.
```bash
python examples/offline_inference/text_to_video/text_to_video.py \
--model=Wan-AI/Wan2.2-TI2V-5B-Diffusers \
--width=1280 \
--height=704 \
--guidance-scale=5.0 \
--prompt="Two anthropomorphic cats in comfy boxing gear and bright gloves fight intensely on a spotlighted stage" \
--output=t2v_5B_pp2.mp4 \
--pipeline-parallel-size=2
```
For PP + CFG-Parallel together:
```bash
python examples/offline_inference/text_to_video/text_to_video.py \
--model=Wan-AI/Wan2.2-TI2V-5B-Diffusers \
--width=1280 \
--height=704 \
--guidance-scale=5.0 \
--prompt="Two anthropomorphic cats in comfy boxing gear and bright gloves fight intensely on a spotlighted stage" \
--output=t2v_5B_pp2_cfg2.mp4 \
--pipeline-parallel-size=2 \
--cfg-parallel-size=2
```
**Verify:**
1. The run completes without hangs at the PP stage boundary
2. Output quality matches the non-PP baseline within normal numerical variance
3. Peak memory per GPU drops compared with the single-rank model
4. There are no outstanding communication errors before decode
---
## Troubleshooting
### Issue: `TypeError` on import — `CFGParallelMixin` missing
**Symptoms:** Importing a pipeline that inherits `PipelineParallelMixin` raises:
```
TypeError: YourPipeline inherits PipelineParallelMixin but not CFGParallelMixin.
```
or:
```
TypeError: YourPipeline must inherit PipelineParallelMixin before CFGParallelMixin ...
```
**Cause:** `PipelineParallelMixin` enforces via `__init_subclass__` that the subclass also inherits `CFGParallelMixin`
and lists `PipelineParallelMixin` first in the MRO.
**Solution:** Add `CFGParallelMixin` to the pipeline's base classes after `PipelineParallelMixin`:
```python
from vllm_omni.diffusion.distributed.cfg_parallel import CFGParallelMixin
from vllm_omni.diffusion.distributed.pipeline_parallel import PipelineParallelMixin
class YourPipeline(nn.Module, PipelineParallelMixin, CFGParallelMixin):
...
```
### Issue: Non-last PP ranks crash when calling `predict_noise`
**Symptoms:** Shape or missing-input errors on ranks other than the first or last PP rank.
**Cause:** The model forward path assumes direct input tensors and ignores `intermediate_tensors`.
**Solution:** Update the transformer `forward()` or custom `predict_noise()` path to load hidden states from
`intermediate_tensors` when present.
### Issue: PP outputs differ from single-GPU baseline
**Symptoms:** The PP run finishes but produces numerically inconsistent outputs.
**Causes & Solutions:**
- **Local layer partitioning is wrong**
- Verify each rank runs only its `[start_layer, end_layer)` slice
- **Non-last ranks return a plain tensor instead of `IntermediateTensors`**
- Return `IntermediateTensors({...})` until the last PP stage
- **CFG branch wiring is incorrect**
- When CFG is enabled, confirm positive and negative kwargs are passed exactly as in the non-PP path
---
## Reference Implementations
Complete examples in the codebase:
| Component | Path | Notes |
|-------------------------|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|
| `PipelineParallelMixin` | `vllm_omni/diffusion/distributed/pipeline_parallel.py` | Core PP communication and scheduler helpers |
| `CFGParallelMixin` | `vllm_omni/diffusion/distributed/cfg_parallel.py` | Default `predict_noise()` tuple normalization and CFG helper fallback |
| Wan2.2 transformer | `vllm_omni/diffusion/models/wan2_2/wan2_2_transformer.py` | Reference for layer partitioning, `IntermediateTensors`, `make_empty_intermediate_tensors`, and PP-aware weight loading |
| Wan2.2 T2V pipeline | `vllm_omni/diffusion/models/wan2_2/pipeline_wan2_2.py` | Reference PP + CFG integration for text-to-video |
| Wan2.2 I2V pipeline | `vllm_omni/diffusion/models/wan2_2/pipeline_wan2_2_i2v.py` | Reference PP + CFG integration for image-to-video |
| PP tests | `tests/diffusion/distributed/test_pipeline_parallel.py` | Baseline parity and async communication tests |
+50 -52
View File
@@ -455,7 +455,7 @@ To learn more about the diffusion pipeline and how to add a new diffusion pipeli
#### Architecture #### Architecture
The attention system uses a **backend selector pattern** that automatically chooses the optimal attention implementation based on hardware and model configuration. The attention system uses a **role-aware backend selector pattern**. Each `Attention` site declares a semantic `role` (`"self"`, `"cross"`, or a model-specific string). The selector consults the user's `AttentionConfig` to pick a backend per role and falls back to the platform default when nothing is configured.
#### Backend Selection #### Backend Selection
@@ -463,92 +463,90 @@ The attention system uses a **backend selector pattern** that automatically choo
```python ```python
class Attention(nn.Module): class Attention(nn.Module):
def __init__(self, num_heads, head_size, causal, softmax_scale, ...): def __init__(self, num_heads, head_size, causal, softmax_scale, *,
# Auto-select backend role="self", role_category=None, ...):
self.attn_backend = get_attn_backend(-1) # Resolve backend for this role from the active OmniDiffusionConfig
self.attn_impl_cls = self.attn_backend.get_impl_cls() config = get_current_diffusion_config_or_none()
self.attention = self.attn_impl_cls(...) attention_config = config.attention_config if config is not None else None
attn_backend_cls, spec = get_attn_backend_for_role(
role=role,
head_size=head_size,
attention_config=attention_config,
role_category=role_category,
)
self.attn_impl_cls = attn_backend_cls.get_impl_cls()
self.attention = self.attn_impl_cls(..., backend_kwargs=spec.extra if spec else None)
``` ```
**Available Backends**: **Available Backends**:
- **FlashAttention**: Optimized CUDA kernel (FA2/FA3) - memory efficient via tiling - **FlashAttention** (`FLASH_ATTN`): Memory-efficient kernel that dispatches to FA2/FA3 on CUDA, AITER on ROCm, and `mindiesd` on Ascend NPU.
- **SDPA**: PyTorch's scaled dot-product attention - default, cross-platform - **SDPA** (`TORCH_SDPA`): PyTorch's scaled dot-product attention — cross-platform fallback.
- **SageAttention**: Sparse attention implementation from SageAttention library - **SageAttention** (`SAGE_ATTN`): Quantized attention implementation from the SageAttention library.
- **AscendAttention**: NPU-optimized attention for Ascend hardware
These backends provide the **kernel implementations** for attention computation. For attention-level sequence parallelism strategies (Ring Attention, Ulysses), see [Parallel Attention](#52-parallel-attention). These backends provide the **kernel implementations** for attention computation. For attention-level sequence parallelism strategies (Ring Attention, Ulysses), see [Parallel Attention](#52-parallel-attention).
#### Backend Selection Mechanism #### Backend Selection Mechanism
Selection is driven by `AttentionConfig` on `OmniDiffusionConfig`, which carries a global `default` plus a `per_role` map of `AttentionSpec` entries. A role string is resolved in this order:
```python ```python
def get_attn_backend(head_size: int) -> type[AttentionBackend]: def get_attn_backend_for_role(role, head_size, attention_config=None, role_category=None):
# Check environment variable # 1. attention_config.per_role[role] — exact match
backend_name = os.environ.get("DIFFUSION_ATTENTION_BACKEND") # 2. attention_config.per_role[role_category] — category fallback
# 3. attention_config.default — global default
if backend_name: # 4. Platform default — hardware-specific
return load_backend(backend_name.upper()) if attention_config is not None:
spec, source = attention_config.resolve_with_source(
# Default to SDPA role=role, role_category=role_category,
return SDPABackend )
if spec is not None:
return load_backend(spec.backend), spec
return current_omni_platform.get_diffusion_attn_backend_cls(...), None
``` ```
**Selection Priority**: **Selection Priority**:
1. **Environment Variable**: `DIFFUSION_ATTENTION_BACKEND` for manual override 1. **Per-role override** (`--diffusion-attention-config.per_role.<role>.backend`) — finest control; matched on the layer's exact `role` string.
- Valid values: `FLASH_ATTN`, `TORCH_SDPA`, `SAGE_ATTN`, `ASCEND` 2. **Role category fallback** (`--diffusion-attention-config.per_role.<category>.backend`) — used when an exact match is missing and the layer declared `role_category` (e.g. a `"mymodel.audio_to_video"` site can fall back to `"cross"`).
- Example: `export DIFFUSION_ATTENTION_BACKEND=SAGE_ATTN` 3. **Global default** (`--diffusion-attention-backend FLASH_ATTN`, or `--diffusion-attention-config.default.backend`, or `DIFFUSION_ATTENTION_BACKEND` env var).
2. **Automatic Fallback**: Falls back to SDPA if selected backend unavailable 4. **Platform default** — `current_omni_platform.get_diffusion_attn_backend_cls(...)` picks the best-available kernel for the hardware.
3. **Hardware Detection**: Can select based on device type (NPU, CUDA, etc.) `AttentionSpec.extra` is forwarded to the backend constructor as `backend_kwargs`, so backend-specific parameters (e.g. SparseBlock `block_size`) can be set without changing model code.
For the user-facing CLI surface, see [Diffusion Attention Backends](../../user_guide/diffusion/attention_backends.md). For the role declaration contract on the model side, see [Adding a Diffusion Model](../../contributing/model/adding_diffusion_model.md).
**Backend Availability**: **Backend Availability**:
- **SDPA**: Always available (PyTorch built-in) - **SDPA**: Always available (PyTorch built-in)
- **FlashAttention**: Requires `flash-attn` package installed - **FlashAttention**: Requires `flash-attn` on CUDA, `aiter` on ROCm, or `mindiesd` on Ascend NPU
- **SageAttention**: Requires `sage-attention` package (from THU-ML GitHub) - **SageAttention**: Requires `sage-attention` package (from THU-ML GitHub)
- **AscendAttention**: Only available on Ascend NPU hardware
#### Attention Backend Registry #### Attention Backend Registry
**Location**: `vllm_omni/diffusion/attention/selector.py` **Location**: `vllm_omni/diffusion/attention/selector.py` + `vllm_omni/platforms/<device>/platform.py`
The attention system uses a **registry pattern** to manage and dynamically load attention backends. This allows for easy extension and runtime selection of backends. Backend resolution is delegated to the active platform: `current_omni_platform.get_diffusion_attn_backend_cls(selected_backend, head_size)` returns a fully-qualified class path that `_load_backend_cls` imports lazily, with the result cached by `(backend_name, head_size)`.
**Registry Structure**:
```python ```python
# Registry mapping backend names to their module paths and class names @cache
_BACKEND_CONFIG = { def _cached_get_backend_cls(backend_name: str | None, head_size: int) -> type[AttentionBackend]:
"FLASH_ATTN": { backend_cls_path = current_omni_platform.get_diffusion_attn_backend_cls(
"module": "vllm_omni.diffusion.attention.backends.flash_attn", selected_backend=backend_name,
"class": "FlashAttentionBackend", head_size=head_size,
}, )
"TORCH_SDPA": { return _load_backend_cls(backend_cls_path)
"module": "vllm_omni.diffusion.attention.backends.sdpa",
"class": "SDPABackend",
},
"SAGE_ATTN": {
"module": "vllm_omni.diffusion.attention.backends.sage_attn",
"class": "SageAttentionBackend",
},
"ASCEND": {
"module": "vllm_omni.diffusion.attention.backends.ascend_attn",
"class": "AscendAttentionBackend",
},
}
``` ```
Each platform (`cuda`, `rocm`, `xpu`, `musa`, `npu`) maps backend names like `"FLASH_ATTN"`, `"TORCH_SDPA"`, `"SAGE_ATTN"` to the right backend class path for that hardware, including head-size compatibility checks. Passing `selected_backend=None` lets the platform pick its own default.
#### Attention Backend Integration #### Attention Backend Integration
The `Attention` layer integrates backends through a unified interface. Here's how **FlashAttentionBackend** is integrated as an example: The `Attention` layer integrates backends through a unified interface. Here's how **FlashAttentionBackend** is integrated as an example:
+1 -1
View File
@@ -34,7 +34,7 @@ vLLM-Omni is a Python library that supports the following GPU variants. The libr
### Pre-built wheels ### Pre-built wheels
Note: Pre-built wheels are currently available for vLLM-Omni 0.11.0rc1, 0.12.0rc1, 0.14.0rc1, 0.14.0, 0.16.0, and 0.18.0. If you need a newer unreleased revision, please [build from source](https://docs.vllm.ai/projects/vllm-omni/en/latest/getting_started/installation/gpu/#build-wheel-from-source). Note: Pre-built wheels are currently available for vLLM-Omni 0.11.0rc1, 0.12.0rc1, 0.14.0rc1, 0.14.0, 0.16.0, 0.18.0, 0.20.0, and 0.21.0. If you need a newer unreleased revision, please [build from source](https://docs.vllm.ai/projects/vllm-omni/en/latest/getting_started/installation/gpu/#build-wheel-from-source).
=== "NVIDIA CUDA" === "NVIDIA CUDA"
@@ -20,7 +20,7 @@ Therefore, it is recommended to install vLLM and vLLM-Omni with a **fresh new**
vLLM-Omni is built based on vLLM. Please install it with command below. vLLM-Omni is built based on vLLM. Please install it with command below.
```bash ```bash
uv pip install vllm --torch-backend=auto uv pip install vllm==0.21.0 --torch-backend=auto
``` ```
#### Installation of vLLM-Omni #### Installation of vLLM-Omni
@@ -39,13 +39,13 @@ uv pip install 'vllm-omni[demo]'
# --8<-- [start:build-wheel-from-source] # --8<-- [start:build-wheel-from-source]
#### Installation of vLLM #### Installation of vLLM
If you do not need to modify source code of vLLM, you can directly install the stable 0.20.0 release version of the library If you do not need to modify source code of vLLM, you can directly install the stable 0.21.0 release version of the library
```bash ```bash
uv pip install vllm==0.20.0 --torch-backend=auto uv pip install vllm==0.21.0 --torch-backend=auto
``` ```
The 0.20.0 release of vLLM ships CUDA 13.0-compatible binaries by default. If you need a different CUDA variant or want to reuse an existing PyTorch installation, build vLLM from source instead. The 0.21.0 release of vLLM ships CUDA 13.0-compatible binaries by default. If you need a different CUDA variant or want to reuse an existing PyTorch installation, build vLLM from source instead.
#### Installation of vLLM-Omni #### Installation of vLLM-Omni
Since vllm-omni is rapidly evolving, it's recommended to install it from source Since vllm-omni is rapidly evolving, it's recommended to install it from source
@@ -66,12 +66,12 @@ If you want to check, modify or debug with source code of vLLM, install the libr
```bash ```bash
git clone https://github.com/vllm-project/vllm.git git clone https://github.com/vllm-project/vllm.git
cd vllm cd vllm
git checkout v0.20.0 git checkout v0.21.0
``` ```
Set up environment variables to get pre-built wheels. If there are internet problems, just download the whl file manually. And set `VLLM_PRECOMPILED_WHEEL_LOCATION` as your local absolute path of whl file. Set up environment variables to get pre-built wheels. If there are internet problems, just download the whl file manually. And set `VLLM_PRECOMPILED_WHEEL_LOCATION` as your local absolute path of whl file.
```bash ```bash
#For CUDA 13.0 (the default for v0.20.0; the wheel filename has no `+cu130` suffix) #For CUDA 13.0 (the default for v0.21.0; the wheel filename has no `+cu130` suffix)
export VLLM_PRECOMPILED_WHEEL_LOCATION=https://github.com/vllm-project/vllm/releases/download/v0.20.0/vllm-0.20.0-cp38-abi3-manylinux_2_35_x86_64.whl export VLLM_PRECOMPILED_WHEEL_LOCATION=https://github.com/vllm-project/vllm/releases/download/v0.21.0/vllm-0.21.0-cp38-abi3-manylinux_2_35_x86_64.whl
``` ```
Install vllm with command below (If you have no existing PyTorch). Install vllm with command below (If you have no existing PyTorch).
```bash ```bash
@@ -102,7 +102,7 @@ docker run --runtime nvidia --gpus 2 \
--env "HF_TOKEN=$HF_TOKEN" \ --env "HF_TOKEN=$HF_TOKEN" \
-p 8091:8091 \ -p 8091:8091 \
--ipc=host \ --ipc=host \
vllm/vllm-omni:v0.18.0 \ vllm/vllm-omni:v0.21.0 \
--model Qwen/Qwen3-Omni-30B-A3B-Instruct --port 8091 --model Qwen/Qwen3-Omni-30B-A3B-Instruct --port 8091
``` ```
@@ -13,7 +13,7 @@ vLLM-Omni current recommends the steps in under setup through Docker Images.
vLLM-Omni is built based on vLLM. Please install it with command below. vLLM-Omni is built based on vLLM. Please install it with command below.
```bash ```bash
uv pip install vllm==0.20.0+rocm721 --extra-index-url https://wheels.vllm.ai/rocm/0.20.0/rocm721 uv pip install vllm==0.21.0+rocm722 --extra-index-url https://wheels.vllm.ai/rocm/0.21.0/rocm722
``` ```
#### Installation of vLLM-Omni #### Installation of vLLM-Omni
@@ -34,13 +34,13 @@ uv pip install onnxruntime-rocm
# --8<-- [start:build-wheel-from-source] # --8<-- [start:build-wheel-from-source]
#### Installation of vLLM #### Installation of vLLM
If you do not need to modify source code of vLLM, you can directly install the stable 0.20.0 release version of the library If you do not need to modify source code of vLLM, you can directly install the stable 0.21.0 release version of the library
```bash ```bash
uv pip install vllm==0.20.0+rocm721 --extra-index-url https://wheels.vllm.ai/rocm/0.20.0/rocm721 uv pip install vllm==0.21.0+rocm722 --extra-index-url https://wheels.vllm.ai/rocm/0.21.0/rocm722
``` ```
The pre-built 0.20.0 vLLM wheel targets ROCm 7.2.1. If you need a different ROCm stack or want to reuse an existing PyTorch installation, build vLLM from source instead. The pre-built 0.21.0 vLLM wheel targets ROCm 7.2.2. If you need a different ROCm stack or want to reuse an existing PyTorch installation, build vLLM from source instead.
#### Installation of vLLM-Omni #### Installation of vLLM-Omni
Since vllm-omni is rapidly evolving, it's recommended to install it from source Since vllm-omni is rapidly evolving, it's recommended to install it from source
@@ -58,7 +58,7 @@ If you want to check, modify or debug with source code of vLLM, install the libr
```bash ```bash
git clone https://github.com/vllm-project/vllm.git git clone https://github.com/vllm-project/vllm.git
cd vllm cd vllm
git checkout v0.20.0 git checkout v0.21.0
python3 -m pip install -r requirements/rocm.txt python3 -m pip install -r requirements/rocm.txt
python3 setup.py develop python3 setup.py develop
``` ```
@@ -130,7 +130,7 @@ docker run --rm \
-v ~/.cache/huggingface:/root/.cache/huggingface \ -v ~/.cache/huggingface:/root/.cache/huggingface \
--env "HF_TOKEN=$HF_TOKEN" \ --env "HF_TOKEN=$HF_TOKEN" \
-p 8091:8091 \ -p 8091:8091 \
vllm/vllm-omni-rocm:v0.20.0 \ vllm/vllm-omni-rocm:v0.21.0 \
--model Qwen/Qwen3-Omni-30B-A3B-Instruct --omni --port 8091 --model Qwen/Qwen3-Omni-30B-A3B-Instruct --omni --port 8091
``` ```
@@ -149,7 +149,7 @@ docker run --rm -it \
-v ~/.cache/huggingface:/root/.cache/huggingface \ -v ~/.cache/huggingface:/root/.cache/huggingface \
--env "HF_TOKEN=$HF_TOKEN" \ --env "HF_TOKEN=$HF_TOKEN" \
--entrypoint bash \ --entrypoint bash \
vllm/vllm-omni-rocm:v0.20.0 vllm/vllm-omni-rocm:v0.21.0
``` ```
# --8<-- [end:pre-built-images] # --8<-- [end:pre-built-images]
+3 -3
View File
@@ -19,10 +19,10 @@ uv venv --python 3.12 --seed
source .venv/bin/activate source .venv/bin/activate
# On CUDA # On CUDA
uv pip install vllm==0.20.0 --torch-backend=auto uv pip install vllm==0.21.0 --torch-backend=auto
# On ROCm # On ROCm
uv pip install vllm==0.20.0+rocm721 --extra-index-url https://wheels.vllm.ai/rocm/0.20.0/rocm721 uv pip install vllm==0.21.0+rocm721 --extra-index-url https://wheels.vllm.ai/rocm/0.21.0/rocm721
git clone https://github.com/vllm-project/vllm-omni.git git clone https://github.com/vllm-project/vllm-omni.git
cd vllm-omni cd vllm-omni
@@ -35,7 +35,7 @@ For additional installation methods — please see the [installation guide](inst
!!! note !!! note
It is important to install the same major & minor version of vLLM and vLLM Omni, otherwise things may not work as expected. If the versions are misaligned, you will see a warning when you import vLLM Omni. It is important to install the same major & minor version of vLLM and vLLM Omni, otherwise things may not work as expected. If the versions are misaligned, you will see a warning when you import vLLM Omni.
If you are seeing strange behavior with the `vllm` command not handling the `--omni` flag correctly, you most likely have a version mismatch with vLLM < `0.20.0` and vLLM Omni `0.20.0`, as vLLM Omni no longer hijacks the vLLM entrypoint. Updating vLLM should resolve this issue. If you are seeing strange behavior with the `vllm` command not handling the `--omni` flag correctly, you most likely have a version mismatch with vLLM < `0.21.0` and vLLM Omni `0.21.0`, as vLLM Omni no longer hijacks the vLLM entrypoint. Updating vLLM should resolve this issue.
## Offline Inference ## Offline Inference
+2
View File
@@ -64,9 +64,11 @@ th {
| `Flux2Pipeline` | FLUX.2-dev | `black-forest-labs/FLUX.2-dev` | ✅︎ | ✅︎ | | | | `Flux2Pipeline` | FLUX.2-dev | `black-forest-labs/FLUX.2-dev` | ✅︎ | ✅︎ | | |
| `FishSpeechSlowARForConditionalGeneration` | Fish Speech S2 Pro | `fishaudio/s2-pro` | ✅︎ | ✅︎ | | | | `FishSpeechSlowARForConditionalGeneration` | Fish Speech S2 Pro | `fishaudio/s2-pro` | ✅︎ | ✅︎ | | |
| `DreamIDOmniPipeline` | DreamID-Omni | `XuGuo699/DreamID-Omni` | ✅︎ | ✅︎ | | | | `DreamIDOmniPipeline` | DreamID-Omni | `XuGuo699/DreamID-Omni` | ✅︎ | ✅︎ | | |
| `SenseNovaU1Pipeline` | SenseNova-U1 (DiT-only) | `SenseNova/SenseNova-U1-8B-MoT` | ✅︎ | | | |
| `HunyuanVideo15Pipeline` | HunyuanVideo-1.5-T2V | `hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-480p_t2v`, `hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-720p_t2v` | ✅︎ | ✅︎ | | | | `HunyuanVideo15Pipeline` | HunyuanVideo-1.5-T2V | `hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-480p_t2v`, `hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-720p_t2v` | ✅︎ | ✅︎ | | |
| `HunyuanVideo15ImageToVideoPipeline` | HunyuanVideo-1.5-I2V | `hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-480p_i2v`, `hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-720p_i2v` | ✅︎ | ✅︎ | | | | `HunyuanVideo15ImageToVideoPipeline` | HunyuanVideo-1.5-I2V | `hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-480p_i2v`, `hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-720p_i2v` | ✅︎ | ✅︎ | | |
| `VoxtralTTSForConditionalGeneration` | Voxtral TTS | `mistralai/Voxtral-4B-TTS-2603` | ✅︎ | ✅︎ | | | | `VoxtralTTSForConditionalGeneration` | Voxtral TTS | `mistralai/Voxtral-4B-TTS-2603` | ✅︎ | ✅︎ | | |
| `CovoAudioForConditionalGeneration` | Covo-Audio-Chat | `tencent/Covo-Audio-Chat` | ✅︎ | | | |
|`DyninOmniForConditionalGeneration` | Dynin-Omni | `snu-aidas/Dynin-Omni` | ✅︎ | | | | |`DyninOmniForConditionalGeneration` | Dynin-Omni | `snu-aidas/Dynin-Omni` | ✅︎ | | | |
| `ErnieImagePipeline` | ERNIE-Image | `baidu/ERNIE-Image`, `baidu/ERNIE-Image-Turbo` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | | `ErnieImagePipeline` | ERNIE-Image | `baidu/ERNIE-Image`, `baidu/ERNIE-Image-Turbo` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
✅︎ indicates the model is supported on that backend. Empty cells mean not listed as supported on that backend. ✅︎ indicates the model is supported on that backend. Empty cells mean not listed as supported on that backend.
@@ -1,4 +1,4 @@
# Diffusion Chat Completions API # Chat Completions API
vLLM-Omni supports generating and editing images via the `/v1/chat/completions` vLLM-Omni supports generating and editing images via the `/v1/chat/completions`
endpoint using diffusion models. This page explains how to pass generation endpoint using diffusion models. This page explains how to pass generation
+6 -4
View File
@@ -531,16 +531,18 @@ for result in response.json()["results"]:
All items are fanned out to `generate()` concurrently. The engine's stage worker automatically batches them up to the configured `max_batch_size` and queues the rest — no client-side throttling needed. All items are fanned out to `generate()` concurrently. The engine's stage worker automatically batches them up to the configured `max_batch_size` and queues the rest — no client-side throttling needed.
For best throughput, set both stages' `max_num_seqs` to ≥4 via `--stage-overrides`: For best throughput, set both stages' `max_num_seqs` above 1 via `--stage-overrides`. On the current Qwen3-TTS CustomVoice benchmark, stage 1 performed best at `max_num_seqs: 10`:
```bash ```bash
vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \ vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \
--omni --port 8091 --trust-remote-code --enforce-eager \ --omni --port 8091 --trust-remote-code --enforce-eager \
--stage-overrides '{"0":{"max_num_seqs":4,"gpu_memory_utilization":0.2}, --stage-overrides '{"0":{"max_num_seqs":10,"gpu_memory_utilization":0.2},
"1":{"max_num_seqs":4,"gpu_memory_utilization":0.2}}' "1":{"max_num_seqs":10,"gpu_memory_utilization":0.2}}'
``` ```
The bundled `qwen3_tts.yaml` uses `max_num_seqs: 1` (single request) on both stages. Bumping to 4 yields roughly 4× throughput on the talker and lets stage 1 batch chunks across in-flight requests. The bundled `qwen3_tts.yaml` uses a multi-request default and lets stage 1 batch chunks across in-flight requests. For latency-sensitive deployments, avoid forcing stage 1 back to `max_num_seqs: 1`; benchmark before reducing it below `10`.
The bundled config also sets `initial_codec_chunk_frames: 1`. This emits only the first audio chunk early for lower TTFA, then returns to the normal `codec_chunk_frames` window so Code2Wav does not repeatedly decode tiny overlapping chunks.
## Supported Models ## Supported Models
+152
View File
@@ -0,0 +1,152 @@
# Videos API
vLLM-Omni provides an OpenAI-compatible video generation API for diffusion
video models. The API supports asynchronous video jobs through `/v1/videos` and
a synchronous benchmark-oriented endpoint through `/v1/videos/sync`.
Each server instance runs a single model specified at startup with
`vllm serve <model> --omni`.
## Quick Start
### Start the Server
```bash
vllm serve Wan-AI/Wan2.2-T2V-A14B-Diffusers --omni --port 8091
```
### Create a Video Job
```bash
create_response=$(curl -s http://localhost:8091/v1/videos \
-F "prompt=A cinematic tracking shot of a mountain lake at sunrise" \
-F "width=1280" \
-F "height=720" \
-F "num_frames=80" \
-F "fps=16" \
-F "num_inference_steps=40")
video_id=$(echo "${create_response}" | jq -r '.id')
```
### Poll and Download
```bash
curl -s "http://localhost:8091/v1/videos/${video_id}" | jq .
curl -L "http://localhost:8091/v1/videos/${video_id}/content" -o output.mp4
```
## API Reference
### Endpoints
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/v1/videos` | `POST` | Create an asynchronous video generation job |
| `/v1/videos/sync` | `POST` | Generate a video synchronously and return raw video bytes |
| `/v1/videos/{video_id}` | `GET` | Retrieve job status and metadata |
| `/v1/videos` | `GET` | List stored video jobs |
| `/v1/videos/{video_id}/content` | `GET` | Download generated video content |
| `/v1/videos/{video_id}` | `DELETE` | Delete a video job and stored output |
### Request Parameters
`POST /v1/videos` and `POST /v1/videos/sync` accept `multipart/form-data`.
#### OpenAI-style fields
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `prompt` | string | **required** | Text prompt for video generation |
| `model` | string | server's model | Optional model name |
| `seconds` | string | null | Requested clip duration in seconds |
| `size` | string | null | Requested output size in `WIDTHxHEIGHT` format |
| `user` | string | null | Optional user identifier |
#### vLLM-Omni extension fields
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `input_reference` | file | null | Uploaded reference image for image-to-video requests |
| `image_reference` | string | null | JSON-encoded reference image payload; do not combine with `input_reference` |
| `width` | integer | model default | Output video width |
| `height` | integer | model default | Output video height |
| `num_frames` | integer | model default | Number of generated frames |
| `fps` | integer | model default | Output frames per second |
| `num_inference_steps` | integer | model default | Number of diffusion steps |
| `guidance_scale` | number | null | CFG guidance scale for the low-noise stage |
| `guidance_scale_2` | number | null | CFG guidance scale for the high-noise stage |
| `boundary_ratio` | number | null | Boundary split ratio for multi-stage denoising |
| `flow_shift` | number | null | Scheduler flow-shift value |
| `true_cfg_scale` | number | null | True CFG scale when supported by the model |
| `seed` | integer | null | Random seed for reproducibility |
| `negative_prompt` | string | null | Text describing what to avoid in the generated video |
| `enable_frame_interpolation` | boolean | null | Enable post-generation frame interpolation |
| `frame_interpolation_exp` | integer | null | Interpolation exponent; `1=2x`, `2=4x`, and so on |
| `frame_interpolation_scale` | number | null | RIFE inference scale |
| `frame_interpolation_model_path` | string | null | Local path or Hugging Face repo for the interpolation model |
| `lora` | string | null | JSON-encoded LoRA configuration object |
| `extra_params` | string | null | JSON-encoded object for additional model-specific parameters |
### Create Response
`POST /v1/videos` returns a job record:
```json
{
"id": "video-123",
"status": "queued",
"created_at": 1701234567
}
```
The final content is available from `/v1/videos/{video_id}/content` after the
job status becomes `completed`.
### Synchronous Response
`POST /v1/videos/sync` blocks until generation finishes and returns raw video
bytes. It is useful for benchmarks and simple scripts that do not need job
storage or polling.
## Examples
### Image-to-Video
```bash
curl -s http://localhost:8091/v1/videos \
-F "prompt=animate this image with subtle camera movement" \
-F "input_reference=@input.png" \
-F "width=1280" \
-F "height=720" \
-F "num_frames=80" \
-F "fps=16"
```
### Synchronous Generation
```bash
curl -X POST http://localhost:8091/v1/videos/sync \
-F "prompt=A small robot walking through a neon city" \
-F "width=854" \
-F "height=480" \
-F "num_frames=80" \
-F "fps=16" \
-o output.mp4
```
## Storage
Set `VLLM_OMNI_STORAGE_PATH` to control where asynchronous video outputs are
stored:
```bash
export VLLM_OMNI_STORAGE_PATH=/var/tmp/vllm-omni-videos
```
## Model-Specific Examples
For complete text-to-video and image-to-video walkthroughs, see:
- [Text-to-Video](../user_guide/examples/online_serving/text_to_video.md)
- [Image-to-Video](../user_guide/examples/online_serving/image_to_video.md)
@@ -1,10 +1,10 @@
# Diffusion Attention Backends # Diffusion Attention Backends
This document describes the diffusion attention backends available in vLLM-Omni, how to select them, and how to use SageAttention. This document describes the diffusion attention backends available in vLLM-Omni, how to select them globally and per-role, and how to use SageAttention.
## Overview ## Overview
Diffusion attention backend selection is controlled by the `DIFFUSION_ATTENTION_BACKEND` environment variable and resolved in `vllm_omni.diffusion.attention.selector`. Diffusion attention backend selection is resolved in `vllm_omni.diffusion.attention.selector`. It looks up the backend from a structured `AttentionConfig` carried on `OmniDiffusionConfig` and falls back to the platform default when nothing is configured.
This backend is used by diffusion attention layers such as the DiT attention in video and image generation models. This backend is used by diffusion attention layers such as the DiT attention in video and image generation models.
@@ -14,7 +14,7 @@ On CUDA, the practical choices today are:
- `TORCH_SDPA`: PyTorch `scaled_dot_product_attention`. - `TORCH_SDPA`: PyTorch `scaled_dot_product_attention`.
- `SAGE_ATTN`: SageAttention backend, if `sageattention` is installed. - `SAGE_ATTN`: SageAttention backend, if `sageattention` is installed.
If `DIFFUSION_ATTENTION_BACKEND` is unset, vLLM-Omni asks the current platform to choose the default backend. On CUDA, that normally means `FLASH_ATTN` when available, otherwise `TORCH_SDPA`. If no attention backend is configured, vLLM-Omni asks the current platform to choose the default. On CUDA, that normally means `FLASH_ATTN` when available, otherwise `TORCH_SDPA`.
## Backend Options ## Backend Options
@@ -24,19 +24,77 @@ If `DIFFUSION_ATTENTION_BACKEND` is unset, vLLM-Omni asks the current platform t
| `TORCH_SDPA` | Most conservative fallback. Useful for debugging or compatibility. | | `TORCH_SDPA` | Most conservative fallback. Useful for debugging or compatibility. |
| `SAGE_ATTN` | Requires `sageattention`. Can improve performance on some workloads, but output quality must be validated model-by-model. | | `SAGE_ATTN` | Requires `sageattention`. Can improve performance on some workloads, but output quality must be validated model-by-model. |
## Selection Priority ## Configuration
Diffusion attention backend selection follows this order: Diffusion attention backends can be configured three ways, in priority order:
1. `DIFFUSION_ATTENTION_BACKEND` 1. **`--diffusion-attention-config`** — structured per-role config (highest priority).
2. Platform default 2. **`--diffusion-attention-backend` / `DIFFUSION_ATTENTION_BACKEND` env var** — global shorthand that sets the default backend.
3. **Platform default** — used when nothing is configured.
Example: `--diffusion-attention-backend` is shorthand for `--diffusion-attention-config.default.backend`. It may be combined with `--diffusion-attention-config.per_role.*` overrides, but is mutually exclusive with `--diffusion-attention-config.default.backend`.
### Global default
Set the default backend for every diffusion attention layer:
```bash ```bash
# CLI flag
vllm-omni serve <model> --diffusion-attention-backend SAGE_ATTN
# Environment variable (also recognized for backwards compatibility)
export DIFFUSION_ATTENTION_BACKEND=SAGE_ATTN export DIFFUSION_ATTENTION_BACKEND=SAGE_ATTN
``` ```
### Per-role configuration
Roles are free-form strings declared by each diffusion model. The two common categories are `"self"` and `"cross"`; model-specific roles (e.g. `"ltx2.audio_to_video"`) may also be declared. A role string is matched in this order:
1. Exact `per_role[role]` match
2. `per_role[role_category]` fallback (e.g. `"ltx2.audio_to_video"` → `"cross"`)
3. `default`
4. Platform default
Use vLLM-style dotted flags or one JSON blob:
```bash
# Dotted flags
vllm-omni serve <model> \
--diffusion-attention-config.default.backend FLASH_ATTN \
--diffusion-attention-config.per_role.cross.backend TORCH_SDPA
# JSON
vllm-omni serve <model> \
--diffusion-attention-config '{"default":{"backend":"FLASH_ATTN"},"per_role":{"cross":{"backend":"TORCH_SDPA"}}}'
```
Backends may also accept backend-specific parameters via `extra`:
```bash
--diffusion-attention-config.per_role.self.backend SPARSE_BLOCK \
--diffusion-attention-config.per_role.self.extra.block_size 128
```
### Programmatic API
When constructing `OmniDiffusionConfig` directly:
```python
from vllm_omni.diffusion.data import AttentionConfig, AttentionSpec, OmniDiffusionConfig
config = OmniDiffusionConfig(
attention_config=AttentionConfig(
default=AttentionSpec(backend="FLASH_ATTN"),
per_role={
"cross": AttentionSpec(backend="TORCH_SDPA"),
},
),
...,
)
```
A plain dict is also accepted and normalized to `AttentionConfig`.
## SageAttention Installation ## SageAttention Installation
vLLM-Omni expects SageAttention to be installed into the same Python environment as vLLM-Omni. vLLM-Omni expects SageAttention to be installed into the same Python environment as vLLM-Omni.
@@ -85,7 +143,21 @@ DIFFUSION_ATTENTION_BACKEND=SAGE_ATTN python examples/offline_inference/text_to_
--output outputs/wan22_sage.mp4 --output outputs/wan22_sage.mp4
``` ```
### Compare Against FlashAttention ### Mixed backends across roles
Use `FLASH_ATTN` for self-attention and `TORCH_SDPA` for cross-attention:
```bash
python examples/offline_inference/text_to_video/text_to_video.py \
--model Wan-AI/Wan2.2-TI2V-5B-Diffusers \
--prompt "A dog running across a field of golden wheat." \
--diffusion-attention-config.per_role.self.backend FLASH_ATTN \
--diffusion-attention-config.per_role.cross.backend TORCH_SDPA \
--tensor-parallel-size 2 \
--output outputs/wan22_mixed.mp4
```
### Compare against FlashAttention
Unset the backend override, or explicitly use `FLASH_ATTN`: Unset the backend override, or explicitly use `FLASH_ATTN`:
@@ -38,14 +38,15 @@ vllm-omni serve diffusion Wan-AI/Wan2.2-T2V-A14B-Diffusers --enable-cpu-offload
### To Support a Model ### To Support a Model
Implement the `SupportsModuleOffload` protocol to declare which Implement the `SupportsComponentDiscovery` protocol to declare which
submodules participate in offloading: submodules serve as pipeline components (used by offloading, HSDP
sharding, and other framework features):
```python ```python
from typing import ClassVar from typing import ClassVar
from vllm_omni.diffusion.models.interface import SupportsModuleOffload from vllm_omni.diffusion.models.interface import SupportsComponentDiscovery
class MyPipeline(nn.Module, SupportsModuleOffload): class MyPipeline(nn.Module, SupportsComponentDiscovery):
_dit_modules: ClassVar[list[str]] = ["transformer"] _dit_modules: ClassVar[list[str]] = ["transformer"]
_encoder_modules: ClassVar[list[str]] = ["text_encoder", "vision_model"] _encoder_modules: ClassVar[list[str]] = ["text_encoder", "vision_model"]
_vae_modules: ClassVar[list[str]] = ["vae"] _vae_modules: ClassVar[list[str]] = ["vae"]
@@ -158,7 +159,7 @@ class Flux2Transformer2DModel(nn.Module):
The offloader discovers pipeline components in two ways: The offloader discovers pipeline components in two ways:
1. **Protocol-based** (preferred): If the pipeline implements 1. **Protocol-based** (preferred): If the pipeline implements
`SupportsModuleOffload`, its `_dit_modules`, `_encoder_modules`, `SupportsComponentDiscovery`, its `_dit_modules`, `_encoder_modules`,
`_vae_modules`, and `_resident_modules` class variables are used `_vae_modules`, and `_resident_modules` class variables are used
directly. All attribute names support dotted paths (e.g. directly. All attribute names support dotted paths (e.g.
`"pipe.transformer"`, `"bagel.time_embedder"`) for nested submodules. `"pipe.transformer"`, `"bagel.time_embedder"`) for nested submodules.
@@ -28,7 +28,6 @@ Frame interpolation is currently supported for:
- `WanPipeline` (Wan2.2 text-to-video) - `WanPipeline` (Wan2.2 text-to-video)
- `WanImageToVideoPipeline` - `WanImageToVideoPipeline`
- `Wan22TI2VPipeline`
## Request Parameters ## Request Parameters
@@ -1,80 +0,0 @@
# Diffusion Multi-Replica Serving
Diffusion stages can be replicated in online serving by setting `runtime.num_replicas` on a `stage_type: diffusion` stage. Each replica owns an independent diffusion engine process and receives requests through round-robin routing with request affinity.
## When to Use
Use multi-replica serving when one diffusion process does not saturate the available GPUs, or when you want higher request throughput for many independent image/video generation requests. It improves concurrency throughput; it does not make a single request faster.
## Configuration
A replica consumes the devices required by that diffusion stage. For single-device diffusion pipelines, set `runtime.devices` to one device per replica:
```yaml
stages:
- stage_id: 0
stage_type: diffusion
runtime:
devices: "0,1,2,3"
num_replicas: 4
engine_args:
model: Qwen/Qwen-Image
parallel_config:
world_size: 1
```
For multi-device diffusion pipelines, `parallel_config.world_size` is treated as the number of devices per replica. For example, four 2-GPU replicas need eight devices:
```yaml
stages:
- stage_id: 0
stage_type: diffusion
runtime:
devices: "0,1,2,3,4,5,6,7"
num_replicas: 4
engine_args:
model: Wan-AI/Wan2.2-T2V-A14B-Diffusers
parallel_config:
world_size: 2
```
The runtime splits the device list into contiguous chunks per replica. With the second example, replica 0 uses `0,1`, replica 1 uses `2,3`, replica 2 uses `4,5`, and replica 3 uses `6,7`.
## Serving
Start the server with the stage config:
```bash
vllm serve Qwen/Qwen-Image \
--omni \
--port 8099 \
--stage-configs-path /path/to/diffusion_multi_replica.yaml
```
Then send normal OpenAI-compatible diffusion chat requests. The orchestrator routes new requests across replicas and keeps updates/abort calls on the same replica for each request.
## Benchmarking
Benchmark each replica count by changing `runtime.num_replicas` and `runtime.devices`, restarting the server, then running:
```bash
python benchmarks/diffusion/diffusion_benchmark_serving.py \
--base-url http://localhost:8099 \
--model Qwen/Qwen-Image \
--task t2i \
--dataset random \
--num-prompts 8 \
--request-rate inf \
--num-inference-steps 2 \
--width 256 --height 256 \
--output-file diffusion_replica_4.json
```
For a quick scaling sweep, test `num_replicas` values `1`, `2`, `3`, and `4` with the same prompt count and generation parameters. Compare throughput and end-to-end latency from the JSON outputs.
## Notes and Limitations
- Multi-replica serving is currently supported for diffusion stages. Single-stage mode still rejects non-diffusion replicas.
- `runtime.devices` must provide exactly `num_replicas * devices_per_replica` devices.
- Request-level latency may stay similar or slightly increase because each request still runs on one replica.
- Throughput improves only when enough concurrent requests are issued and each replica has enough GPU memory.
@@ -4,13 +4,14 @@ This guide covers the parallelism methods in vLLM-Omni for speeding up diffusion
## Supported Methods ## Supported Methods
| Method | Description | | Method | Description |
|--------|-------------| |----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|
| **[Tensor Parallelism](tensor_parallel.md)** | Shards DiT weights across GPUs to reduce per-GPU memory | | **[Tensor Parallelism](tensor_parallel.md)** | Shards DiT weights across GPUs to reduce per-GPU memory |
| **[Sequence Parallelism](sequence_parallel.md)** | Splits sequence dimension across GPUs (Ulysses-SP, Ring-Attention, or hybrid) for high-resolution images and videos | | **[Sequence Parallelism](sequence_parallel.md)** | Splits sequence dimension across GPUs (Ulysses-SP, Ring-Attention, or hybrid) for high-resolution images and videos |
| **[CFG-Parallel](cfg_parallel.md)** | Runs CFG positive/negative branches on separate GPUs for ~1.8x speedup on guided generation | | **[CFG-Parallel](cfg_parallel.md)** | Runs CFG positive/negative branches on separate GPUs for ~1.8x speedup on guided generation |
| **[VAE Patch Parallelism](vae_patch_parallel.md)** | Distributes VAE decode spatially across GPUs to reduce peak VAE memory | | **[Pipeline Parallelism](pipeline_parallel.md)** | Splits the denoising transformer block-wise across sequential GPU stages to reduce per-GPU model memory |
| **[HSDP](hsdp.md)** | Shards full model weights via PyTorch FSDP2 to enable large-model inference on memory-constrained GPUs | | **[VAE Patch Parallelism](vae_patch_parallel.md)** | Distributes VAE decode spatially across GPUs to reduce peak VAE memory |
| **[Expert Parallelism](expert_parallel.md)** | Shards MoE expert blocks across GPUs for MoE models (e.g. HunyuanImage3.0) | | **[HSDP](hsdp.md)** | Shards full model weights via PyTorch FSDP2 to enable large-model inference on memory-constrained GPUs |
| **[Expert Parallelism](expert_parallel.md)** | Shards MoE expert blocks across GPUs for MoE models (e.g. HunyuanImage3.0) |
See [Supported Models](../../diffusion_features.md#supported-models) for per-model compatibility. See [Supported Models](../../diffusion_features.md#supported-models) for per-model compatibility.
@@ -0,0 +1,205 @@
# Pipeline Parallelism Guide
## Table of Content
- [Overview](#overview)
- [Quick Start](#quick-start)
- [Example Script](#example-script)
- [Configuration Parameters](#configuration-parameters)
- [Best Practices](#best-practices)
- [Troubleshooting](#troubleshooting)
- [Summary](#summary)
---
## Overview
Pipeline Parallelism splits the denoising transformer block-wise into sequential stages across GPUs. Each rank owns only
part of the transformer, which reduces per-GPU model memory and enables larger diffusion models to run across multiple
devices.
It can also be combined with other distributed methods such as CFG-Parallel, Tensor Parallelism, and Sequence
Parallelism.
See supported models list in [Supported Models](../../diffusion_features.md#supported-models).
---
## Quick Start
### Basic Usage
Simplest working example:
```python
from vllm_omni import Omni
from vllm_omni.diffusion.data import DiffusionParallelConfig
from vllm_omni.inputs.data import OmniDiffusionSamplingParams
omni = Omni(
model="Wan-AI/Wan2.2-TI2V-5B-Diffusers",
parallel_config=DiffusionParallelConfig(
pipeline_parallel_size=2,
),
)
outputs = omni.generate(
{"prompt": "A cinematic drone shot over snowy mountains"},
OmniDiffusionSamplingParams(
num_inference_steps=40,
num_frames=81,
height=704,
width=1280,
),
)
```
---
## Example Script
### Offline Inference
Use python scripts under:
- `examples/offline_inference/text_to_video/text_to_video.py`
- `examples/offline_inference/image_to_video/image_to_video.py`
Text-to-video example:
```bash
python examples/offline_inference/text_to_video/text_to_video.py \
--model=Wan-AI/Wan2.2-TI2V-5B-Diffusers \
--width=1280 \
--height=704 \
--guidance-scale=5.0 \
--prompt="Two anthropomorphic cats in comfy boxing gear and bright gloves fight intensely on a spotlighted stage" \
--output=t2v_5B_pp2.mp4 \
--pipeline-parallel-size=2
```
Pipeline Parallelism can also be combined with CFG-Parallel:
```bash
python examples/offline_inference/text_to_video/text_to_video.py \
--model=Wan-AI/Wan2.2-TI2V-5B-Diffusers \
--width=1280 \
--height=704 \
--guidance-scale=5.0 \
--prompt="Two anthropomorphic cats in comfy boxing gear and bright gloves fight intensely on a spotlighted stage" \
--output=t2v_5B_pp2_cfg2.mp4 \
--pipeline-parallel-size=2 \
--cfg-parallel-size=2
```
### Online Serving
Enable Pipeline Parallelism in online serving:
```bash
# Default PP configuration
vllm serve Wan-AI/Wan2.2-TI2V-5B-Diffusers --omni --port 8091 --pipeline-parallel-size 2
# PP + CFG-Parallel
vllm serve Wan-AI/Wan2.2-TI2V-5B-Diffusers --omni --port 8091 \
--pipeline-parallel-size 2 \
--cfg-parallel-size 2
```
---
## Configuration Parameters
In `DiffusionParallelConfig`
| Parameter | Type | Default | Description |
|--------------------------|------|---------|------------------------------------------------------------------------------------------------------------------|
| `pipeline_parallel_size` | int | 1 | Number of pipeline-parallel stages. Set to a value greater than 1 to split the denoising transformer across GPUs |
> [!NOTE]
> Total GPU count is the product of all enabled distributed dimensions, for example
`pipeline_parallel_size * cfg_parallel_size * tensor_parallel_size * ulysses_degree * ring_degree`.
### Manual Layer Partitioning
By default, transformer layers are distributed evenly across PP ranks. You can override this with the
`VLLM_PP_LAYER_PARTITION` environment variable to assign a specific number of layers to each rank:
```bash
# Example: 40 layers across 4 PP ranks, assigning 8 / 12 / 12 / 8 layers
export VLLM_PP_LAYER_PARTITION=8,12,12,8
```
The value must be a comma-separated list of integers whose length equals `pipeline_parallel_size` and whose sum equals
the total number of transformer layers. This is useful when you want to balance memory or compute asymmetrically across
ranks.
---
## Best Practices
### When to Use
**Good for:**
- Large diffusion transformers that do not fit comfortably on one GPU
- Multi-GPU setups where reducing per-GPU model memory is more important than minimizing communication
- Combining with CFG-Parallel or other distributed methods on supported models
**Not for:**
- Single GPU setups
- Models that do not support Pipeline Parallelism
(check [supported models](../../diffusion_features.md#supported-models))
- Very small models where inter-stage communication overhead may outweigh the benefit
### Expected Behavior
Pipeline Parallelism primarily reduces per-GPU memory usage by splitting the transformer into stage-local blocks.
Depending on the model, topology, and resolution, it may also help execution fit into available hardware, but it is not
primarily a latency optimization.
---
## Troubleshooting
### Common Issue 1: No benefit from Pipeline Parallelism
**Symptoms**: PP is enabled, but latency does not improve or becomes slightly worse.
**Solutions**:
1. **Check your goal:**
```python
# PP is mainly for memory scaling, not guaranteed latency speedup
parallel_config = DiffusionParallelConfig(pipeline_parallel_size=2)
```
2. **Check model support:**
- Verify your model in [supported models](../../diffusion_features.md#supported-models)
- PP is currently validated only on selected pipelines
3. **Combine with other methods when appropriate:**
- PP can be combined with CFG-Parallel, Tensor Parallelism, or Sequence Parallelism on supported models
### Common Issue 2: PP pipeline fails at import
**Symptoms**: Importing a custom pipeline raises a `TypeError` about `CFGParallelMixin` or mixin order.
**Solutions**:
1. Inherit both mixins.
2. Put `PipelineParallelMixin` before `CFGParallelMixin` in the class base list.
3. Use `class YourPipeline(nn.Module, PipelineParallelMixin, CFGParallelMixin): ...` as the reference pattern.
---
## Summary
1. ✅ **Enable Pipeline Parallelism** - Set `pipeline_parallel_size > 1` in `DiffusionParallelConfig`
2. ✅ **Use Supported Models** - Verify your model supports PP in
[supported models](../../diffusion_features.md#supported-models)
3. ✅ **Combine When Needed** - PP can be combined with CFG-Parallel and other distributed methods on supported pipelines
4. ✅ **Scale for Memory** - Use PP primarily to reduce per-GPU model memory and fit larger transformers
+49 -48
View File
@@ -15,7 +15,7 @@ vLLM-Omni supports various advanced features for diffusion models:
- Acceleration: **cache methods**, **parallelism methods**, **startup optimizations** - Acceleration: **cache methods**, **parallelism methods**, **startup optimizations**
- Memory optimization: **cpu offloading**, **quantization** - Memory optimization: **cpu offloading**, **quantization**
- Extensions: **LoRA inference**, **frame interpolation** - Extensions: **LoRA inference**, **frame interpolation**
- Execution modes: **step execution**, **multi-replica serving** - Execution modes: **step execution**
## Supported Features ## Supported Features
@@ -35,15 +35,15 @@ Cache methods trade minimal quality for significant speedup. Quality loss is typ
Parallelism methods distribute computation across GPUs without quality loss (mathematically equivalent to single-GPU). Parallelism methods distribute computation across GPUs without quality loss (mathematically equivalent to single-GPU).
| Method | Description | Best For | | Method | Description | Best For |
|--------|-------------|----------| |------------------------------------------------------------------------|--------------------------------------------------------------------------|-------------------------------------------------------------------|
| **[Ulysses-SP](diffusion/parallelism/sequence_parallel.md)** | Sequence parallelism via all-to-all communication | High-resolution images (>1536px) or long videos with 2-8 GPUs | | **[Ulysses-SP](diffusion/parallelism/sequence_parallel.md)** | Sequence parallelism via all-to-all communication | High-resolution images (>1536px) or long videos with 2-8 GPUs |
| **[Ring-Attention](diffusion/parallelism/sequence_parallel.md)** | Sequence parallelism via ring-based communication | Videos, very long sequences, memory-constrained, with 2-8 GPUs | | **[Ring-Attention](diffusion/parallelism/sequence_parallel.md)** | Sequence parallelism via ring-based communication | Videos, very long sequences, memory-constrained, with 2-8 GPUs |
| **[CFG-Parallel](diffusion/parallelism/cfg_parallel.md)** | Splits CFG positive/negative branches across devices | Image editing with CFG guidance (true_cfg_scale > 1) on 2 GPUs | | **[CFG-Parallel](diffusion/parallelism/cfg_parallel.md)** | Splits CFG positive/negative branches across devices | Image editing with CFG guidance (true_cfg_scale > 1) on 2 GPUs |
| **[Tensor Parallelism](diffusion/parallelism/tensor_parallel.md)** | Shards model weights across devices | Large models that don't fit in single GPU, with 2+ GPUs | | **[Tensor Parallelism](diffusion/parallelism/tensor_parallel.md)** | Shards model weights across devices | Large models that don't fit in single GPU, with 2+ GPUs |
| **[HSDP](diffusion/parallelism/hsdp.md)** | Weight sharding via FSDP2, redistributed on-demand at runtime | Very large models (14B+) on limited VRAM, combinable with SP | | **[Pipeline Parallelism](diffusion/parallelism/pipeline_parallel.md)** | Splits the denoising transformer block-wise across sequential GPU stages | Large diffusion transformers that need lower per-GPU model memory |
| **[Expert Parallelism](diffusion/parallelism/expert_parallel.md)** | Shards MoE expert MLP blocks across devices | MoE diffusion models (e.g., HunyuanImage3.0) | | **[HSDP](diffusion/parallelism/hsdp.md)** | Weight sharding via FSDP2, redistributed on-demand at runtime | Very large models (14B+) on limited VRAM, combinable with SP |
| **[Multi-Replica Serving](diffusion/multi_replica_serving.md)** | Runs multiple independent diffusion stage replicas and routes requests across them | Online serving throughput with many concurrent requests | | **[Expert Parallelism](diffusion/parallelism/expert_parallel.md)** | Shards MoE expert MLP blocks across devices | MoE diffusion models (e.g., HunyuanImage3.0) |
#### Startup Optimization #### Startup Optimization
@@ -106,31 +106,32 @@ The following tables show which models support each feature:
### ImageGen ### ImageGen
| Model | ⚡TeaCache | ⚡Cache-DiT | 🔀SP (Ulysses & Ring) | 🔀CFG-Parallel | 🔀Tensor-Parallel | 🔀HSDP | 💾CPU Offload (Layerwise) | 💾VAE-Patch-Parallel | 💾Quantization | 🔄Step Execution | | Model | ⚡TeaCache | ⚡Cache-DiT | 🔀SP (Ulysses & Ring) | 🔀CFG-Parallel | 🔀Tensor-Parallel | 🔀Pipeline-Parallel | 🔀HSDP | 💾CPU Offload (Layerwise) | 💾VAE-Patch-Parallel | 💾Quantization | 🔄Step Execution |
|-------|:----------:|:-----------:|:---------------------:|:--------------:|:-----------------:|:------:|:------------------------:|:--------------------:|:--------------:|:----------------:| |--------------------------|:---------:|:----------:|:---------------------:|:--------------:|:-----------------:|:-------------------:|:------:|:-------------------------:|:--------------------:|:--------------:|:----------------:|
| **Bagel** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | | **Bagel** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| **FLUX.1-dev** | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | | **FLUX.1-dev** | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ |
| **FLUX.1-schnell** | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | | **FLUX.1-schnell** | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ |
| **FLUX.2-klein** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | | **FLUX.2-klein** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ |
| **FLUX.1-Kontext-dev** | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | | **FLUX.1-Kontext-dev** | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| **FLUX.2-dev** | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | | **FLUX.2-dev** | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| **GLM-Image** | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | | **GLM-Image** | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| **HunyuanImage3** | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | **HunyuanImage3** | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| **LongCat-Image** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | | **LongCat-Image** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| **LongCat-Image-Edit** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | | **LongCat-Image-Edit** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| **MagiHuman** | ❌ | ❌ | ❌ | ❓ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | | **MagiHuman** | ❌ | ❌ | ❌ | ❓ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| **MammothModa2(T2I)** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | **MammothModa2(T2I)** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Nextstep_1(T2I)** | ❓ | ❓ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | | **Nextstep_1(T2I)** | ❓ | ❓ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| **OmniGen2** | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | **OmniGen2** | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Ovis-Image** | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | | **Ovis-Image** | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| **Qwen-Image** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (decode) | ✅ | ✅ | | **Qwen-Image** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ (decode) | ✅ | ✅ |
| **Qwen-Image-2512** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (decode) | ✅ | ✅ | | **Qwen-Image-2512** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ (decode) | ✅ | ✅ |
| **Qwen-Image-Edit** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (decode) | ❌ | ❌ | | **Qwen-Image-Edit** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ (decode) | ❌ | ❌ |
| **Qwen-Image-Edit-2509** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (decode) | ✅ | ❌ | ❌ | | **Qwen-Image-Edit-2509** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ (decode) | ✅ | ❌ | ❌ |
| **Qwen-Image-Layered** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (decode) | ❌ | ❌ | | **Qwen-Image-Layered** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ (decode) | ❌ | ❌ |
| **Stable-Diffusion3.5** | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ (decode) | ❌ | ❌ | | **SenseNova-U1** | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| **Z-Image** | ✅ | ✅ | ✅ | ❓ | ✅ (TP=2 only) | ✅ | ❌ | ✅ (decode) | ✅ | ❌ | | **Stable-Diffusion3.5** | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ (decode) | ❌ | ❌ |
| **ERNIE-Image** | ❌ | ✅ | ✅ | ❓ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | | **Z-Image** | ✅ | ✅ | ✅ | ❓ | ✅ (TP=2 only) | ❌ | ✅ | ❌ | ✅ (decode) | ✅ | ❌ |
| **ERNIE-Image** | ❌ | ✅ | ✅ | ❓ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
> Notes: > Notes:
> 1. Nextstep_1(T2I) does not support cache acceleration methods such as TeaCache or Cache-DiT. > 1. Nextstep_1(T2I) does not support cache acceleration methods such as TeaCache or Cache-DiT.
@@ -138,15 +139,15 @@ The following tables show which models support each feature:
### VideoGen ### VideoGen
| Model | ⚡TeaCache | ⚡Cache-DiT | 🔀SP (Ulysses & Ring) | 🔀CFG-Parallel | 🔀Tensor-Parallel | 🔀HSDP | 💾CPU Offload (Layerwise) | 💾VAE-Patch-Parallel | 💾Quantization | 🔄Step Execution | | Model | ⚡TeaCache | ⚡Cache-DiT | 🔀SP (Ulysses & Ring) | 🔀CFG-Parallel | 🔀Tensor-Parallel | Pipeline-Parallel | 🔀HSDP | 💾CPU Offload (Layerwise) | 💾VAE-Patch-Parallel | 💾Quantization | 🔄Step Execution |
|-------|:----------:|:-----------:|:---------------------:|:--------------:|:-----------------:|:------:|:------------------------:|:--------------------:|:--------------:|:----------------:| |------------------------------|:---------:|:----------:|:---------------------:|:--------------:|:-----------------:|:-----------------:|:------:|:-------------------------:|:--------------------:|:--------------:|:----------------:|
| **Wan2.2** | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (encode/decode) | ❌ | ❌ | | **Wan2.2** | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (encode/decode) | ❌ | ❌ |
| **Wan2.1-VACE** | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (decode) | ❌ | ❌ | | **Wan2.1-VACE** | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ (decode) | ❌ | ❌ |
| **LTX-2** | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | | **LTX-2** | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| **LTX-2.3** | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | **LTX-2.3** | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Helios** | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | | **Helios** | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| **HunyuanVideo-1.5 T2V I2V** | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ (decode) | ✅ | ❌ | | **HunyuanVideo-1.5 T2V I2V** | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ (decode) | ✅ | ❌ |
| **DreamID-Omni** | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | | **DreamID-Omni** | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
**Frame Interpolation Support** **Frame Interpolation Support**
@@ -155,9 +156,9 @@ The following tables show which models support each feature:
### AudioGen ### AudioGen
| Model | ⚡TeaCache | ⚡Cache-DiT | 🔀SP (Ulysses & Ring) | 🔀CFG-Parallel | 🔀Tensor-Parallel | 🔀HSDP | 💾CPU Offload (Layerwise) | 💾VAE-Patch-Parallel | 💾Quantization | 🔄Step Execution | | Model | ⚡TeaCache | ⚡Cache-DiT | 🔀SP (Ulysses & Ring) | 🔀CFG-Parallel | 🔀Tensor-Parallel | 🔀Pipeline-Parallel | 🔀HSDP | 💾CPU Offload (Layerwise) | 💾VAE-Patch-Parallel | 💾Quantization | 🔄Step Execution |
|-------|:----------:|:-----------:|:---------------------:|:--------------:|:-----------------:|:------:|:------------------------:|:--------------------:|:--------------:|:----------------:| |-----------------------|:---------:|:----------:|:---------------------:|:--------------:|:-----------------:|:-------------------:|:------:|:-------------------------:|:--------------------:|:--------------:|:----------------:|
| **Stable-Audio-Open** | ✅ | ❌ | ❓ | ❓ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | | **Stable-Audio-Open** | ✅ | ❌ | ❓ | ❓ | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ |
## Feature Compatibility ## Feature Compatibility
@@ -257,7 +258,7 @@ Measured on NVIDIA H800:
**Parallelism Methods:** **Parallelism Methods:**
- **[Parallelism Overview](diffusion/parallelism/overview.md)** - Tensor Parallelism, Sequence Parallelism, CFG Parallelism, HSDP, and Expert Parallelism - **[Parallelism Overview](diffusion/parallelism/overview.md)** - Tensor Parallelism, Sequence Parallelism, CFG Parallelism, Pipeline Parallelism, HSDP, and Expert Parallelism
**Memory Optimization:** **Memory Optimization:**
@@ -101,7 +101,7 @@ guides.
When using `/v1/chat/completions`, pass these inside `extra_body` in the curl When using `/v1/chat/completions`, pass these inside `extra_body` in the curl
JSON, or via the `extra_body` keyword argument in the OpenAI Python SDK (see the JSON, or via the `extra_body` keyword argument in the OpenAI Python SDK (see the
[Diffusion Chat API guide](../../../serving/diffusion_chat_api.md)). [Chat Completions API guide](../../../serving/chat_completions_api.md)).
When using the dedicated [`/v1/images/generations`](../../../serving/image_generation_api.md) When using the dedicated [`/v1/images/generations`](../../../serving/image_generation_api.md)
or [`/v1/images/edits`](../../../serving/image_edit_api.md) endpoints, pass or [`/v1/images/edits`](../../../serving/image_edit_api.md) endpoints, pass
the supported generation controls as top-level fields directly. For image the supported generation controls as top-level fields directly. For image
@@ -208,7 +208,7 @@ Wrap generation parameters inside `extra_body` in the request JSON:
``` ```
For details on how generation parameters are handled across different clients, see the For details on how generation parameters are handled across different clients, see the
[Diffusion Chat API guide](../../../../serving/diffusion_chat_api.md). [Chat Completions API guide](../../../../serving/chat_completions_api.md).
### Layered Image Generation (Qwen-Image-Layered) ### Layered Image Generation (Qwen-Image-Layered)
@@ -361,7 +361,7 @@ Provide multiple images in `content` (order matters):
When using `/v1/chat/completions`, pass these inside `extra_body` in the curl When using `/v1/chat/completions`, pass these inside `extra_body` in the curl
JSON, or via the `extra_body` keyword argument in the OpenAI Python SDK (see the JSON, or via the `extra_body` keyword argument in the OpenAI Python SDK (see the
[Diffusion Chat API guide](../../../../serving/diffusion_chat_api.md)). [Chat Completions API guide](../../../../serving/chat_completions_api.md)).
When using the dedicated [`/v1/images/edits`](../../../../serving/image_edit_api.md) When using the dedicated [`/v1/images/edits`](../../../../serving/image_edit_api.md)
endpoint, pass the supported generation controls as top-level form fields endpoint, pass the supported generation controls as top-level form fields
directly. For image dimensions and count, use `size` and `n` rather than directly. For image dimensions and count, use `size` and `n` rather than
@@ -216,7 +216,7 @@ Wrap generation parameters inside `extra_body` in the request JSON:
``` ```
For details on how generation parameters are handled across different clients, see the For details on how generation parameters are handled across different clients, see the
[Diffusion Chat API guide](../../../../serving/diffusion_chat_api.md). [Chat Completions API guide](../../../../serving/chat_completions_api.md).
### Multimodal Input (Text + Structured Content) ### Multimodal Input (Text + Structured Content)
@@ -237,7 +237,7 @@ Wrap generation parameters inside `extra_body` in the request JSON:
When using `/v1/chat/completions`, pass these inside `extra_body` in the curl When using `/v1/chat/completions`, pass these inside `extra_body` in the curl
JSON, or via the `extra_body` keyword argument in the OpenAI Python SDK (see the JSON, or via the `extra_body` keyword argument in the OpenAI Python SDK (see the
[Diffusion Chat API guide](../../../../serving/diffusion_chat_api.md)). [Chat Completions API guide](../../../../serving/chat_completions_api.md)).
When using the dedicated [`/v1/images/generations`](../../../../serving/image_generation_api.md) When using the dedicated [`/v1/images/generations`](../../../../serving/image_generation_api.md)
endpoint, pass the supported generation controls as top-level JSON fields endpoint, pass the supported generation controls as top-level JSON fields
directly. For image dimensions and count, use `size` and `n` rather than directly. For image dimensions and count, use `size` and `n` rather than
+6 -6
View File
@@ -2,11 +2,11 @@
## Overview ## Overview
FP8 quantization converts BF16/FP16 weights to FP8 at model load time, or loads FP8 quantization converts BF16/FP16 weights to FP8 at model load time. Online
a checkpoint whose target stage already declares an FP8 quantization config. activation scaling is the default and does not require calibration. Static
Online activation scaling is the default and does not require calibration. activation scaling is supported when calibrated scale information is available.
Static activation scaling is supported when calibrated scale information is For ModelOpt-produced pre-quantized checkpoints, see
available. [ModelOpt Quantization](modelopt.md).
Some architectures can quantize all linear layers. Others have Some architectures can quantize all linear layers. Others have
quality-sensitive layers that should stay in BF16 through `ignored_layers`. quality-sensitive layers that should stay in BF16 through `ignored_layers`.
@@ -46,7 +46,7 @@ guide. FP8 on Ampere may use a weight-only path where available.
| Model | Scope | Format | Status | | Model | Scope | Format | Status |
|-------|-------|--------|--------| |-------|-------|--------|--------|
| Qwen3-Omni | Thinker language-model stage | ModelOpt `quant_algo=FP8` | Tested for thinker memory reduction | | Qwen3-Omni | Thinker language-model stage | [ModelOpt](modelopt.md) `quant_algo=FP8` | Tested for thinker memory reduction |
| Qwen3-TTS | TTS language-model stage | Checkpoint config | Not validated | | Qwen3-TTS | TTS language-model stage | Checkpoint config | Not validated |
Audio encoder, vision encoder, talker, and code2wav stay in BF16 unless a Audio encoder, vision encoder, talker, and code2wav stay in BF16 unless a
+138
View File
@@ -0,0 +1,138 @@
# ModelOpt Quantization
## Overview
ModelOpt quantization loads checkpoints produced by NVIDIA ModelOpt. The
quantized weights and scale tensors are generated before serving, so inference
does not run online calibration or convert a BF16 checkpoint at startup.
vLLM-Omni currently validates the ModelOpt FP8 checkpoint path for diffusion
transformers. The loader auto-detects supported ModelOpt FP8 checkpoint configs
and keeps non-transformer components, such as the tokenizer, scheduler, text
encoder, and VAE, on the base checkpoint unless a model-specific guide says
otherwise.
!!! note
`--force-cutlass-fp8` is an explicit runtime override for diffusion
checkpoints that already carry a supported ModelOpt FP8 config. It does not
quantize BF16 checkpoints and it does not apply to online `--quantization
fp8`. The flag only takes effect for ModelOpt FP8 diffusion stages on CUDA
SM89+ devices; other platforms and non-ModelOpt FP8 paths fall back to the
normal vLLM kernel selection.
## Supported ModelOpt Checkpoint Formats
vLLM-Omni treats ModelOpt checkpoints as pre-quantized checkpoints. The
checkpoint config must identify ModelOpt as the quantization method or producer,
and the quantization algorithm must be one of the validated FP8 algorithms.
| Checkpoint field | Supported value |
|------------------|-----------------|
| `method` / `quant_method` | `modelopt` |
| `producer.name` | `modelopt` |
| `quant_algo` | `FP8`, `FP8_PER_CHANNEL_PER_TOKEN` |
Other ModelOpt algorithms, such as NVFP4, are not enabled by this diffusion
FP8 path until they have separate model and quality validation.
## Hardware Support
| Device | Support |
|--------|---------|
| NVIDIA Blackwell GPU (SM 100+) | ✅ |
| NVIDIA Ada/Hopper GPU (SM 89+) | ✅ |
| NVIDIA Ampere GPU (SM 80+) | ⭕ |
| AMD ROCm | ⭕ |
| Intel XPU | ⭕ |
| Ascend NPU | ❌ |
Legend: `✅` supported, `❌` unsupported, `⭕` not verified in this guide.
The optional CUTLASS FP8 runtime override requires CUDA SM89+.
## Model Type Support
### Diffusion Model
| Model | HF checkpoint | Scope | Status |
|-------|---------------|-------|--------|
| Qwen-Image 2512 | `feizhai123/qwen-image-2512-modelopt-fp8-dynamic-all` | Diffusion transformer | Validated for ModelOpt FP8 checkpoints |
| Z-Image | `feizhai123/z-image-modelopt-fp8-conservative` | Diffusion transformer | Validated for ModelOpt FP8 checkpoints |
| FLUX.2-dev | `feizhai123/flux2-dev-modelopt-fp8` | Diffusion transformer | Validated for ModelOpt FP8 checkpoints |
| FLUX.2-klein 4B | `feizhai123/flux2-klein-4b-modelopt-fp8` | Diffusion transformer | Validated for ModelOpt FP8 checkpoints |
| HunyuanImage-3.0 | `feizhai123/hunyuan-image3-modelopt-fp8` | MoE diffusion transformer | Validated for ModelOpt FP8 checkpoints |
| Wan2.2 | Not available | Diffusion transformer | Not validated |
### Multi-Stage Omni/TTS Model
| Model | Scope | Status |
|-------|-------|--------|
| Qwen3-Omni | Thinker language-model stage | ModelOpt FP8 checkpoint path |
| Qwen3-TTS | TTS language-model stage | Not validated |
Audio encoder, vision encoder, talker, and code2wav stages stay in BF16 unless
a model-specific guide documents otherwise.
### Multi-Stage Diffusion Model
ModelOpt checkpoints must be routed to the stage whose checkpoint contains the
ModelOpt `quantization_config`. BAGEL and GLM-Image are not listed as validated
ModelOpt targets yet.
## Configuration
For pre-quantized ModelOpt FP8 checkpoints, no `--quantization fp8` flag is
needed. The checkpoint config selects the ModelOpt path.
Online serving:
```bash
vllm serve <modelopt-fp8-checkpoint> \
--omni \
--tensor-parallel-size <N> \
--force-cutlass-fp8
```
Offline inference:
```bash
python examples/offline_inference/text_to_image/text_to_image.py \
--model <modelopt-fp8-checkpoint> \
--tensor-parallel-size <N> \
--prompt "a red ceramic teapot on a wooden table" \
--height 1024 \
--width 1024 \
--num-inference-steps 20 \
--seed 42 \
--output outputs/modelopt_fp8.png
```
Python API:
```python
from vllm_omni import Omni
omni = Omni(
model="<modelopt-fp8-checkpoint>",
tensor_parallel_size=2,
force_cutlass_fp8=True,
)
```
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `force_cutlass_fp8` / `--force-cutlass-fp8` | bool | `False` | Force CUTLASS FP8 linear kernels for supported ModelOpt FP8 diffusion stages on CUDA SM89+ |
## Validation and Notes
1. Compare the ModelOpt FP8 checkpoint against the BF16 baseline with the same
prompt, resolution, seed, and inference steps.
2. Use `tests/diffusion/quantization/test_quantization_quality.py` with
`VLLM_OMNI_QUALITY_CONFIGS` to validate local baseline and quantized model
paths.
3. Report LPIPS, PSNR, MAE, throughput, latency, and peak memory when adding a
new validated ModelOpt diffusion checkpoint.
4. Keep `--quantization fp8` for online FP8 from BF16 checkpoints; use this
ModelOpt path only when the checkpoint already contains ModelOpt FP8 weights
and scales.
+187
View File
@@ -0,0 +1,187 @@
# W8A8 MXFP8 Quantization
## Overview
W8A8 MXFP8 (Microscaling FP8) quantizes both weights and activations to FP8
using the OCP MX format: groups of 32 K-dimension elements share a single
`float8_e8m0fnu` exponent scale. This gives better accuracy than channel-wise
FP8 while keeping the same 8-bit weight footprint.
This method supports two modes:
| Mode | Description |
|------|-------------|
| **Online** | BF16 weights are quantized to MXFP8 at load time — no pre-processing needed |
| **Offline** | msModelSlim-exported MXFP8 weights converted to diffusers format via `merge_mxfp8_checkpoint.py` — weights and scales are loaded directly from the preprocessed checkpoint |
## Hardware Support
| Device | Support |
|--------|---------|
| NVIDIA Blackwell GPU (SM 100+) | ⭕ |
| NVIDIA Ada/Hopper GPU (SM 89+) | ⭕ |
| NVIDIA Ampere GPU (SM 80+) | ⭕ |
| AMD ROCm | ⭕ |
| Intel XPU | ⭕ |
| Ascend NPU (Atlas 950 A5) | ✅ |
Legend: `✅` supported, `❌` unsupported, `⭕` not verified in this
guide.
## Model Type Support
### Diffusion Model (Wan2.2)
| Model | Mode | Notes |
|-------|------|-------|
| Wan2.2-T2V-A14B | Online + Offline | MoE cascade; quantizes two transformers (`transformer` + `transformer_2`) |
| Wan2.2-I2V-A14B | Online + Offline | MoE cascade; quantizes two transformers (`transformer` + `transformer_2`) |
| Wan2.2-TI2V-5B | Online + Offline | Single transformer |
### Multi-Stage Omni/TTS Model (Qwen3-Omni, Qwen3-TTS)
| Model | Status | Notes |
|-------|--------|-------|
| Qwen3-Omni | Not validated | — |
| Qwen3-TTS | Not validated | — |
### Multi-Stage Diffusion Model (BAGEL, GLM-Image)
| Model | Status | Notes |
|-------|--------|-------|
| BAGEL | Not validated | — |
| GLM-Image | Not validated | — |
## Configuration
### Online Mode
Online mode requires no pre-processing. vLLM-Omni quantizes BF16 weights to
MXFP8 at load time.
Python API:
```python
from vllm_omni import Omni
from vllm_omni.inputs.data import OmniDiffusionSamplingParams
omni = Omni(model="<your-model>", quantization="mxfp8")
outputs = omni.generate(
"A cat sitting on a windowsill",
OmniDiffusionSamplingParams(num_inference_steps=50),
)
```
CLI:
```bash
python text_to_video.py --model <your-model> --quantization mxfp8
# Online serving
vllm serve <your-model> --omni --quantization mxfp8
```
### Offline Mode
Offline mode loads a pre-quantized checkpoint from msModelSlim. A preprocessing
step converts the raw quantized output to the diffusers format expected by
vLLM-Omni and injects the quantization config into `transformer/config.json` so
that vLLM-Omni auto-detects the offline path without a `--quantization` flag.
#### Step 1 — Quantize with msModelSlim
```bash
msmodelslim quant \
--model_path /path/to/Wan2.2-TI2V-5B-Diffusers \
--save_path /path/to/wan2_2_ti2v_quantized_raw \
--device npu \
--model_type Wan2_2 \
--config_path /path/to/wan2_2_w8a8f8_mxfp.yaml \
--trust_remote_code True
```
After this step, `--save_path` contains the raw quantized safetensors files and
a metadata JSON (`quant_model_description*.json`).
For cascade MoE models (T2V-A14B, I2V-A14B), msModelSlim outputs two
subdirectories: `high_noise_model/` and `low_noise_model/`.
#### Step 2 — Preprocess with merge_mxfp8_checkpoint.py
The script (`vllm_omni/quantization/tools/merge_mxfp8_checkpoint.py`):
1. Copies the original diffusers model to `--output-path` (VAE, text encoder,
scheduler, etc. are preserved).
2. Remaps tensor names from msModelSlim convention to diffusers convention.
3. Saves the converted weights as `diffusion_pytorch_model.safetensors`.
4. Copies the original `transformer/config.json` and injects
`quantization_config` so that vLLM-Omni auto-detects offline MXFP8.
For cascade MoE models, steps 2–4 run separately for `high_noise_model/` →
`transformer/` and `low_noise_model/` → `transformer_2/`.
```bash
python vllm_omni/quantization/tools/merge_mxfp8_checkpoint.py \
--model-type Wan2.2-TI2V-5B \
--original-model /path/to/Wan2.2-TI2V-5B-Diffusers \
--quant-path /path/to/wan2_2_ti2v_quantized_raw \
--output-path /path/to/Wan2.2-TI2V-5B-MXFP8
```
| Argument | Description |
|----------|-------------|
| `--model-type` | Model variant: `Wan2.2-T2V-A14B`, `Wan2.2-I2V-A14B`, or `Wan2.2-TI2V-5B` |
| `--original-model` | Root directory of the original BF16 diffusers model |
| `--quant-path` | Root directory of the msModelSlim quantized output |
| `--output-path` | Output directory for the merged model (created by the script) |
The script outputs a complete diffusers model directory at `--output-path`,
with each transformer subfolder containing:
- `diffusion_pytorch_model.safetensors` — converted FP8 weights
- `config.json` — original transformer config with `quantization_config` injected
- `quant_model_description.json` — renamed quantization metadata (reference only)
#### Step 3 — Serve
```bash
python text_to_video.py --model /path/to/Wan2.2-TI2V-5B-MXFP8
# Online serving
vllm serve /path/to/Wan2.2-TI2V-5B-MXFP8 --omni
```
Python API:
```python
omni = Omni(model="/path/to/Wan2.2-TI2V-5B-MXFP8")
```
!!! note
No `--quantization` flag is needed for offline mode. The preprocessing
script injects `quantization_config` into each `transformer/config.json`,
which vLLM-Omni reads automatically to activate the offline MXFP8 method.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `method` | str | — | Must be `"mxfp8"` |
| `is_checkpoint_mxfp8_serialized` | bool | `False` | `True` for offline pre-quantized checkpoints; auto-set from `config.json` when using the preprocessing script |
| `ignored_layers` | list[str] | `[]` | Layer name substrings to keep in BF16 (e.g. `"to_out"` matches `blocks.0.attn1.to_out.0`) |
## Validation and Notes
1. Online mode quantizes BF16 weights at load time using
`npu_dynamic_mx_quant`. This adds a one-time overhead on the first load
but requires no checkpoint preparation.
2. Offline mode loads FP8 weights directly from the checkpoint. Scales are
stored as `uint8` bytes in safetensors (same bit layout as
`float8_e8m0fnu`) and are reinterpreted at load time without a dtype
conversion.
3. If the offline checkpoint was produced with the old `merge_mxfp8_checkpoint.py`
interface (arguments `--quant-dir`, `--orig-dir`, `--meta-json`,
`--output-dir`), regenerate it with the current script. The old script
wrote a separate `quantization_config.json` that is not read by vLLM-Omni;
the current script injects the config directly into `transformer/config.json`.
+114 -13
View File
@@ -1,4 +1,4 @@
# Quantization # Quantization
vLLM-Omni exposes quantization through the unified `quantization_config` vLLM-Omni exposes quantization through the unified `quantization_config`
path. The same configuration entrypoint is used across diffusion-only models, path. The same configuration entrypoint is used across diffusion-only models,
@@ -9,19 +9,20 @@ type has a different quantization scope.
| Mode | Guide | Description | Methods | | Mode | Guide | Description | Methods |
|------|-------|-------------|---------| |------|-------|-------------|---------|
| Online quantization | [Online Quantization](online.md) | vLLM-Omni computes quantized weights and scales while loading the model. | FP8 W8A8, Int8 W8A8 | | Online quantization | [Online Quantization](online.md) | vLLM-Omni computes quantized weights and scales while loading the model. | FP8 W8A8, Int8 W8A8, MXFP8 W8A8 |
| Pre-quantized checkpoints | Method-specific guides | The checkpoint or an offline quantizer provides quantized weights and scales before serving. | GGUF, AutoRound, msModelSlim, serialized Int8 | | Runtime attention quantization | [Quantized KV Cache](quantized_kvcache.md) | vLLM-Omni dynamically quantizes eligible diffusion Flash Attention tensors during inference. | FP8 FA |
| Pre-quantized checkpoints | Method-specific guides | The checkpoint or an offline quantizer provides quantized weights and scales before serving. | ModelOpt, GGUF, AutoRound, msModelSlim, serialized Int8, offline MXFP8 |
## Hardware Support ## Hardware Support
| Device | FP8 W8A8 | Int8 W8A8 | GGUF | AutoRound | msModelSlim | | Device | FP8 W8A8 | Int8 W8A8 | ModelOpt | MXFP8 W8A8 | GGUF | AutoRound | msModelSlim |
|--------|----------|-----------|------|-----------|-------------| |--------|----------|-----------|----------|------------|------|-----------|-------------|
| NVIDIA Blackwell GPU (SM 100+) | ✅ | ✅ | ✅ | ✅ | ❌ | | NVIDIA Blackwell GPU (SM 100+) | ✅ | ✅ | ✅ | ⭕ | ✅ | ✅ | ❌ |
| NVIDIA Ada/Hopper GPU (SM 89+) | ✅ | ✅ | ✅ | ✅ | ❌ | | NVIDIA Ada/Hopper GPU (SM 89+) | ✅ | ✅ | ✅ | ⭕ | ✅ | ✅ | ❌ |
| NVIDIA Ampere GPU (SM 80+) | ✅ | ✅ | ✅ | ✅ | ❌ | | NVIDIA Ampere GPU (SM 80+) | ✅ | ✅ | ⭕ | ⭕ | ✅ | ✅ | ❌ |
| AMD ROCm | ⭕ | ⭕ | ⭕ | ⭕ | ❌ | | AMD ROCm | ⭕ | ⭕ | ⭕ | ⭕ | ⭕ | ⭕ | ❌ |
| Intel XPU | ⭕ | ⭕ | ⭕ | ✅ | ❌ | | Intel XPU | ⭕ | ⭕ | ⭕ | ⭕ | ⭕ | ✅ | ❌ |
| Ascend NPU | ❌ | ✅ | ❌ | ❌ | ✅ | | Ascend NPU | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ |
Legend: `✅` supported, `❌` unsupported, `⭕` not verified in this Legend: `✅` supported, `❌` unsupported, `⭕` not verified in this
guide. FP8 on Ampere may use a weight-only path where available. guide. FP8 on Ampere may use a weight-only path where available.
@@ -39,6 +40,8 @@ otherwise.
|--------|-------|------|----------------|--------| |--------|-------|------|----------------|--------|
| FP8 W8A8 | [FP8](fp8.md) | Online W8A8 or checkpoint FP8 | Qwen-Image; Wan2.2 is not validated | Validated for Qwen-Image family and other DiT models | | FP8 W8A8 | [FP8](fp8.md) | Online W8A8 or checkpoint FP8 | Qwen-Image; Wan2.2 is not validated | Validated for Qwen-Image family and other DiT models |
| Int8 W8A8 | [Int8](int8.md) | Online or serialized W8A8 | Qwen-Image; Wan2.2 is not validated | Validated for Qwen-Image and Z-Image | | Int8 W8A8 | [Int8](int8.md) | Online or serialized W8A8 | Qwen-Image; Wan2.2 is not validated | Validated for Qwen-Image and Z-Image |
| ModelOpt | [ModelOpt](modelopt.md) | Pre-quantized FP8 checkpoints | Qwen-Image, Z-Image, FLUX.2, HunyuanImage-3.0 | Validated for ModelOpt FP8 diffusion checkpoints |
| MXFP8 W8A8 | [MXFP8](mxfp8.md) | Online W8A8 or offline pre-quantized | Wan2.2-T2V-A14B, I2V-A14B, TI2V-5B | Ascend NPU only; validated for Wan2.2 |
| GGUF | [GGUF](gguf.md) | Pre-quantized transformer weights | Qwen-Image | Validated where a model-specific GGUF adapter exists | | GGUF | [GGUF](gguf.md) | Pre-quantized transformer weights | Qwen-Image | Validated where a model-specific GGUF adapter exists |
| AutoRound | [AutoRound](autoround.md) | Pre-quantized W4A16 checkpoints | FLUX.1-dev; Qwen-Image/Wan2.2 not validated | Checkpoint-driven | | AutoRound | [AutoRound](autoround.md) | Pre-quantized W4A16 checkpoints | FLUX.1-dev; Qwen-Image/Wan2.2 not validated | Checkpoint-driven |
| msModelSlim | [msModelSlim](msmodelslim.md) | Pre-quantized Ascend checkpoints | Wan2.2 recipe; HunyuanImage-3.0 inference target | Ascend/NPU path | | msModelSlim | [msModelSlim](msmodelslim.md) | Pre-quantized Ascend checkpoints | Wan2.2 recipe; HunyuanImage-3.0 inference target | Ascend/NPU path |
@@ -52,8 +55,9 @@ in BF16 unless the model guide explicitly adds support.
| Method | Guide | Scope | Example models | Status | | Method | Guide | Scope | Example models | Status |
|--------|-------|-------|----------------|--------| |--------|-------|-------|----------------|--------|
| FP8 | [FP8](fp8.md) | Thinker or language-model checkpoint config | Qwen3-Omni thinker | ModelOpt checkpoint path | | ModelOpt | [ModelOpt](modelopt.md) | Thinker or language-model checkpoint config | Qwen3-Omni thinker | ModelOpt checkpoint path |
| Int8 | [Int8](int8.md) | Not currently validated for omni/TTS stages | Qwen3-Omni, Qwen3-TTS | Not validated | | Int8 | [Int8](int8.md) | Not currently validated for omni/TTS stages | Qwen3-Omni, Qwen3-TTS | Not validated |
| MXFP8 | [MXFP8](mxfp8.md) | Not currently validated for omni/TTS stages | Qwen3-Omni, Qwen3-TTS | Not validated |
| GGUF | [GGUF](gguf.md) | Not currently validated for omni/TTS stages | Qwen3-Omni, Qwen3-TTS | Not validated | | GGUF | [GGUF](gguf.md) | Not currently validated for omni/TTS stages | Qwen3-Omni, Qwen3-TTS | Not validated |
| AutoRound | [AutoRound](autoround.md) | Thinker or language-model checkpoint config | Qwen2.5-Omni, Qwen3-Omni | Supported through AutoRound checkpoints | | AutoRound | [AutoRound](autoround.md) | Thinker or language-model checkpoint config | Qwen2.5-Omni, Qwen3-Omni | Supported through AutoRound checkpoints |
| msModelSlim | [msModelSlim](msmodelslim.md) | Not currently validated for omni/TTS stages | Qwen3-Omni, Qwen3-TTS | Not validated | | msModelSlim | [msModelSlim](msmodelslim.md) | Not currently validated for omni/TTS stages | Qwen3-Omni, Qwen3-TTS | Not validated |
@@ -67,6 +71,8 @@ attached to the intended stage rather than applied globally.
|--------|-------|-------|----------------|--------| |--------|-------|-------|----------------|--------|
| FP8 | [FP8](fp8.md) | Stage-specific DiT or transformer module | BAGEL, GLM-Image | Requires model-specific validation | | FP8 | [FP8](fp8.md) | Stage-specific DiT or transformer module | BAGEL, GLM-Image | Requires model-specific validation |
| Int8 | [Int8](int8.md) | Stage-specific DiT or transformer module | BAGEL, GLM-Image | Requires model-specific validation | | Int8 | [Int8](int8.md) | Stage-specific DiT or transformer module | BAGEL, GLM-Image | Requires model-specific validation |
| ModelOpt | [ModelOpt](modelopt.md) | Checkpoint-defined diffusion stage | BAGEL, GLM-Image | Requires model-specific validation |
| MXFP8 | [MXFP8](mxfp8.md) | Stage-specific DiT or transformer module | BAGEL, GLM-Image | Not validated |
| GGUF | [GGUF](gguf.md) | Stage-specific transformer weights | BAGEL, GLM-Image | No validated adapter listed | | GGUF | [GGUF](gguf.md) | Stage-specific transformer weights | BAGEL, GLM-Image | No validated adapter listed |
| AutoRound | [AutoRound](autoround.md) | Checkpoint-defined stage | BAGEL, GLM-Image | No validated checkpoint listed | | AutoRound | [AutoRound](autoround.md) | Checkpoint-defined stage | BAGEL, GLM-Image | No validated checkpoint listed |
| msModelSlim | [msModelSlim](msmodelslim.md) | Ascend-generated stage weights | GLM-Image | Requires model-specific adaptation | | msModelSlim | [msModelSlim](msmodelslim.md) | Ascend-generated stage weights | GLM-Image | Requires model-specific adaptation |
@@ -94,7 +100,7 @@ config = build_quant_config({
| Component | Default quantized? | Notes | | Component | Default quantized? | Notes |
|-----------|--------------------|-------| |-----------|--------------------|-------|
| Diffusion transformer | Yes | Primary target for FP8, Int8, GGUF, AutoRound, and msModelSlim | | Diffusion transformer | Yes | Primary target for FP8, Int8, ModelOpt, GGUF, AutoRound, and msModelSlim |
| Text encoder | No | Keep BF16 unless a method-specific guide documents support | | Text encoder | No | Keep BF16 unless a method-specific guide documents support |
| VAE | No | Keep BF16; storage-only paths are method-specific | | VAE | No | Keep BF16; storage-only paths are method-specific |
| Scheduler/tokenizer | No | Loaded from the base model repository | | Scheduler/tokenizer | No | Loaded from the base model repository |
@@ -132,3 +138,98 @@ build_quant_config({"method": "gguf", "gguf_model": "/path/to/model.gguf"})
build_quant_config({"transformer": {"method": "fp8"}, "vae": None}) build_quant_config({"transformer": {"method": "fp8"}, "vae": None})
build_quant_config(None) build_quant_config(None)
``` ```
## Output Similarity Comparison Tool
Use `vllm_omni.quantization.tools.compare_diffusion_trajectory_similarity`
to compare a reference diffusion run with a quantized candidate run using the
same prompt, seed, resolution, scheduler settings, and inference steps. The
tool compares final decoded images or video frames, and also reports generation
latency and worker-reported peak memory when available.
This is useful when validating whether online quantization, an offline
pre-quantized checkpoint, or a new `ignored_layers` choice keeps generation
quality close to the BF16 reference.
### Online Quantization Example
```bash
python -m vllm_omni.quantization.tools.compare_diffusion_trajectory_similarity \
--task t2i \
--model Qwen/Qwen-Image \
--candidate-quantization fp8 \
--ignored-layers img_mlp \
--prompt "a cup of coffee on the table" \
--height 512 --width 512 \
--num-inference-steps 20 \
--seed 142 \
--output-json /tmp/qwen_image_fp8_similarity/result.json \
--save-output-dir /tmp/qwen_image_fp8_similarity/images \
--enforce-eager
```
### Offline Checkpoint Example
Use `--candidate-model` when the candidate is already quantized or lives at a
different model path:
```bash
python -m vllm_omni.quantization.tools.compare_diffusion_trajectory_similarity \
--task t2i \
--reference-model Qwen/Qwen-Image \
--candidate-model /path/to/qwen-image-fp8-checkpoint \
--prompt "a cup of coffee on the table" \
--height 512 --width 512 \
--num-inference-steps 20 \
--seed 142 \
--output-json /tmp/qwen_image_fp8_checkpoint_similarity/result.json
```
If the checkpoint does not include a loadable quantization config, pass one
explicitly:
```bash
--candidate-quantization-config-json '{"method":"fp8"}'
```
### Output Metrics
The output JSON includes `output_metrics`, `reference_generation`, and
`candidate_generation`.
| Metric | Direction | Meaning |
|--------|-----------|---------|
| `cosine_similarity` | Higher is better | Vector direction similarity between output pixels or frames. Useful as a broad sanity check. |
| `mae` | Lower is better | Mean absolute pixel or frame error. For decoded outputs, values are in uint8 pixel units. |
| `mse` / `rmse` | Lower is better | Squared error and its square root. These penalize localized large differences more than `mae`. |
| `max_abs` | Lower is better | Worst single-element absolute error. Treat it as an outlier/debug signal, not as a release gate. |
| `l2` / `relative_l2` | Lower is better | Absolute and reference-normalized L2 distance. `relative_l2` is easier to compare across resolutions. |
| `psnr_db` | Higher is better | Pixel-space signal-to-noise ratio in dB for uint8 images or frames. |
| `avg_generation_time_s` | Lower is better | Average wall-clock generation time across measured runs. |
| `max_peak_memory_mb` | Lower is better | Maximum worker-reported peak device memory across measured runs, when the worker reports it. |
Recommended starting thresholds for same-seed diffusion comparisons:
| Metric | Smoke threshold | Stricter target | Notes |
|--------|-----------------|-----------------|-------|
| `psnr_db` | `>= 20.0` | `>= 25.0` | Good for quick image or frame regression checks. |
| `mae` | `<= 12.0` | `<= 6.0` | Interpreted in decoded uint8 pixel units. |
| `cosine_similarity` | `>= 0.98` | `>= 0.995` | Less sensitive to global scale than L2-style metrics. |
| `relative_l2` | `<= 0.20` | `<= 0.08` | Useful when comparing across prompts or resolutions. |
These thresholds are heuristics. Tune them by model family, task, resolution,
quantization method, and deployment tolerance. For release gating, pair the
numeric report with visual inspection of saved reference and candidate outputs.
The tool intentionally reports separate quality, latency, and memory metrics
instead of a single consolidated similarity score. A single score can hide
important tradeoffs, for example a candidate with good PSNR but a meaningful
memory regression, or a candidate with low average error but localized visual
artifacts. If you need a project-specific pass/fail gate, define it as an
explicit policy over the individual metrics.
Pixel-level metrics do not measure semantic consistency. For higher-cost
evaluation, you can complement this report with a vision-language judge that
describes the reference and candidate outputs and compares those descriptions.
Keep that semantic check separate from this lightweight tool so users can
choose whether the additional model cost and latency are appropriate.
@@ -0,0 +1,125 @@
# Quantized KV Cache
## Overview
In DiT-based image and video generation, Flash Attention can take a large share
of denoising time, especially for high-resolution or long-frame workloads.
vLLM-Omni supports online FP8 quantization for eligible diffusion Flash
Attention (FA) to reduce FA latency while keeping model weights in their
original dtype.
This feature is configured through `diffusion_kv_cache_dtype` on
`OmniDiffusionConfig` (CLI: `--diffusion-kv-cache-dtype`). It is intentionally
**not** the same as vLLM's `--kv-cache-dtype`, which controls autoregressive
language-model KV cache storage and defaults to `"auto"`. Diffusion FA
quantization uses the dedicated diffusion flags so omni serve does not inherit
that default.
In vLLM-Omni diffusion pipelines, this is a runtime FA path: Q/K/V tensors are
dynamically quantized before the attention operator. It does not quantize model
weights and is separate from [FP8 W8A8](fp8.md), [Int8 W8A8](int8.md), or
pre-quantized checkpoint formats.
If `diffusion_kv_cache_dtype` is not set, behavior is unchanged and attention
runs in the native dtype.
## Hardware Support
| Device | FP8 FA |
|--------|--------|
| Ascend NPU | ✅ |
| NVIDIA GPU | ❌ |
| AMD ROCm | ❌ |
| Intel XPU | ❌ |
Legend: `✅` supported, `❌` unsupported.
FP8 FA is currently implemented only for the NPU Flash Attention backend. Other
backends do not support `diffusion_kv_cache_dtype="fp8"` for diffusion attention
and fall back to native dtype execution.
## Model Type Support
### Diffusion Model
| Model | Scope | Status | Notes |
|-------|-------|--------|-------|
| Wan2.2 | Eligible DiT full-attention FA on Ascend NPU | Tested | Compare quality and latency against a BF16 baseline before production use |
| Other diffusion models | Eligible DiT full-attention FA on Ascend NPU | Not tested | You can try `diffusion_kv_cache_dtype="fp8"`; tune `diffusion_kv_cache_skip_steps` and `diffusion_kv_cache_skip_layers` when higher precision is needed |
### Multi-Stage Omni/TTS Model (Qwen3-Omni, Qwen3-TTS)
Not tested for FP8 FA. Treat any use as experimental unless a model-specific
guide documents support.
### Multi-Stage Diffusion Model (BAGEL, GLM-Image)
Not tested. If the diffusion stage uses the same NPU Flash Attention backend,
`diffusion_kv_cache_dtype` may apply in theory; validate quality and latency for
each stage and model.
## Configuration
Offline diffusion example:
```bash
python examples/offline_inference/image_to_video/image_to_video.py \
--model <your-wan2.2-model> \
--prompt "A cat sitting on a surfboard at the beach" \
--height 1280 \
--width 720 \
--num-frames 61 \
--num-inference-steps 4 \
--ulysses-degree 4 \
--vae-patch-parallel-size 4 \
--diffusion-kv-cache-dtype fp8 \
--diffusion-kv-cache-skip-steps "0,1" \
--diffusion-kv-cache-skip-layers "0-2"
```
Online serving:
```bash
vllm serve <your-model> --omni --diffusion-kv-cache-dtype fp8
```
Stage config:
```yaml
stage_args:
- stage_id: 0
stage_type: diffusion
engine_args:
model_stage: dit
diffusion_kv_cache_dtype: "fp8"
diffusion_kv_cache_skip_steps: "0,1"
diffusion_kv_cache_skip_layers: "0-2"
```
Legacy YAML keys `kv_cache_dtype`, `kv_cache_skip_steps`, and
`kv_cache_skip_layers` are still accepted when constructing
`OmniDiffusionConfig` (for example via `from_kwargs`); prefer the `diffusion_*`
names for new configs.
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `diffusion_kv_cache_dtype` | str \| None | `None` | Set to `"fp8"` to enable dynamic FP8 FA on supported attention backends |
| `diffusion_kv_cache_skip_steps` | str \| None | `None` | Denoising step selector to keep in native dtype, for example `"0,1,4-6"` |
| `diffusion_kv_cache_skip_layers` | str \| None | `None` | Transformer layer selector to keep in native dtype, for example `"0-2,10"` |
Selectors use comma-separated integers and inclusive ranges. Listed steps or
layers skip FP8 FA; all other eligible full-attention forwards use the FP8 path.
## Validation and Notes
1. Compare generated images or videos against a BF16 baseline with the same
seed, prompt, resolution, frame count, and denoising steps.
2. Use `diffusion_kv_cache_skip_steps` for denoising steps where quality is more
sensitive.
3. Use `diffusion_kv_cache_skip_layers` for transformer layers that show visible quality
regressions.
4. Report both latency and quality results when enabling this option for a new
model. For image or video models, include visual comparison and quantitative
metrics when available, such as PSNR or SSIM.
@@ -195,6 +195,25 @@ stages:
devices: "0,1" devices: "0,1"
``` ```
#### Hybrid Sharded Data Parallel (HSDP)
For larger Bagel deployments on multiple GPUs, you can enable HSDP (Hybrid Sharded Data Parallel) by modifying the stage configuration (for example, [`bagel.yaml`](../../../vllm_omni/deploy/bagel.yaml)). HSDP shards transformer weights across GPUs to reduce per-GPU memory usage.
1. **Enable HSDP**: Set `use_hsdp: true`.
2. **Set shard size**: Set `hsdp_shard_size` to the number of GPUs used for sharding (for example, `4`).
3. **Set replicate size**: Usually keep `hsdp_replicate_size: 1` unless you want replicated HSDP groups.
4. **Set devices**: Specify the comma-separated GPU IDs used by the diffusion stage (for example, `"0,1,2,3"`).
Example configuration for HSDP across 4 GPUs:
```yaml
- stage_id: 1
devices: "0,1,2,3"
parallel_config:
use_hsdp: true
hsdp_shard_size: 4
hsdp_replicate_size: 1
```
Then pass the custom deploy YAML: Then pass the custom deploy YAML:
```bash ```bash
@@ -0,0 +1,76 @@
# Covo-Audio-Chat (Offline Inference)
## Setup
Please refer to the [stage configuration documentation](https://docs.vllm.ai/projects/vllm-omni/en/latest/configuration/stage_configs/) to configure memory allocation appropriately for your hardware setup.
> **Note**
> Covo-Audio code2wav requires `torchdiffeq`. Install it with: `pip install torchdiffeq`
## Run examples
Get into the example folder:
```bash
cd examples/offline_inference/covo_audio
```
### Audio input chat
Using the default audio asset:
```bash
python end2end.py
```
Using a custom audio file:
```bash
python end2end.py --audio-path /path/to/audio.wav
```
Using a local model:
```bash
python end2end.py -m /path/to/Covo-Audio-Chat --output-dir ./my_output
```
### Command-line Arguments
| Argument | Short | Default | Description |
|----------|-------|---------|-------------|
| `--model-name` | `-m` | `tencent/Covo-Audio-Chat` | Model path or HuggingFace model ID |
| `--text` | `-t` | `请回答这段音频里的问题。` | Text prompt / question for the audio |
| `--audio-path` | `-a` | default audio asset | Path to local audio file |
| `--sampling-rate` | | `16000` | Sampling rate for audio loading (Hz) |
| `--output-dir` | | `./output_audio` | Output directory for generated files |
| `--num-prompts` | | `1` | Number of prompts to generate |
| `--stage-configs-path` | | (auto) | Path to stage configs YAML file |
| `--log-stats` | | `false` | Enable detailed statistics logging |
| `--stage-init-timeout` | | `300` | Stage initialization timeout (seconds) |
| `--batch-timeout` | | `5` | Batching timeout (seconds) |
| `--init-timeout` | | `300` | Overall initialization timeout (seconds) |
| `--shm-threshold-bytes` | | `65536` | Shared memory threshold (bytes) |
## Pipeline
Covo-Audio-Chat uses a 2-stage pipeline:
- **Stage 0 (fused_thinker_talker):** The 7B LLM generates interleaved text and audio tokens in a single autoregressive pass.
- **Stage 1 (code2wav):** A BigVGAN-based vocoder converts the extracted audio codes into a 24kHz WAV waveform.
## Output
The script generates two files per request in the output directory:
- `{request_id}.txt` -- prompt and generated text
- `{request_id}.wav` -- generated audio (24kHz WAV)
## FAQ
If you encounter `ModuleNotFoundError: No module named 'librosa'`, install it with:
```bash
pip install librosa
```
## Environment
- GPU: 1x A100 (80 GiB)
- Stage 0 (7B LLM): ~16 GiB VRAM
- Stage 1 (BigVGAN vocoder): ~2 GiB VRAM
@@ -0,0 +1,222 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""
This example shows how to use vLLM-Omni for running offline inference
with the correct prompt format on Covo-Audio-Chat.
Usage:
python end2end.py --audio-path /path/to/audio.wav
"""
import os
import soundfile as sf
from vllm.assets.audio import AudioAsset
from vllm.multimodal.media.audio import load_audio
from vllm.sampling_params import SamplingParams
from vllm.utils.argparse_utils import FlexibleArgumentParser
from vllm_omni.entrypoints.omni import Omni
from vllm_omni.model_executor.models.covo_audio.prompt_utils import (
COVO_AUDIO_INPUT_PREFIX,
build_covo_audio_chat_prompt,
)
SEED = 42
def get_audio_query(
question: str | None = None,
audio_path: str | None = None,
sampling_rate: int = 16000,
) -> dict:
if question is None:
question = "请回答这段音频里的问题。"
user_content = COVO_AUDIO_INPUT_PREFIX + question
prompt = build_covo_audio_chat_prompt(user_content)
if audio_path is None:
audio_data = AudioAsset("mary_had_lamb").audio_and_sample_rate
else:
import numpy as np
audio_signal, sr = load_audio(audio_path, sr=sampling_rate)
audio_data = (audio_signal.astype(np.float32), sr)
return {
"prompt": prompt,
"multi_modal_data": {"audio": audio_data},
"modalities": ["audio"],
}
def main(args):
query_result = get_audio_query(
question=args.text,
audio_path=args.audio_path,
sampling_rate=args.sampling_rate,
)
omni = Omni(
model=args.model_name,
stage_configs_path=args.stage_configs_path,
log_stats=args.log_stats,
stage_init_timeout=args.stage_init_timeout,
batch_timeout=args.batch_timeout,
init_timeout=args.init_timeout,
shm_threshold_bytes=args.shm_threshold_bytes,
)
# Stage 0: fused_thinker_talker
# stop_token_ids=[151645] (<|im_end|>) and ignore_eos=True are required
# so the model generates interleaved text+audio tokens before stopping.
thinker_sampling_params = SamplingParams(
temperature=0.0,
top_p=1.0,
top_k=-1,
max_tokens=2048,
seed=SEED,
detokenize=True,
repetition_penalty=1.05,
stop_token_ids=[151645],
ignore_eos=True,
)
# Stage 1: code2wav (audio codes, not real token IDs — skip detokenize)
code2wav_sampling_params = SamplingParams(
temperature=0.0,
top_p=1.0,
top_k=-1,
max_tokens=2048,
seed=SEED,
detokenize=False,
repetition_penalty=1.1,
)
sampling_params_list = [
thinker_sampling_params,
code2wav_sampling_params,
]
prompts = [query_result for _ in range(args.num_prompts)]
omni_outputs = omni.generate(prompts, sampling_params_list)
output_dir = args.output_dir
os.makedirs(output_dir, exist_ok=True)
for stage_outputs in omni_outputs:
output = stage_outputs.request_output
if stage_outputs.final_output_type == "text":
request_id = output.request_id
text_output = output.outputs[0].text
prompt_text = output.prompt
out_txt = os.path.join(output_dir, f"{request_id}.txt")
lines = [
"Prompt:\n",
str(prompt_text) + "\n",
"vllm_text_output:\n",
str(text_output).strip() + "\n",
]
try:
with open(out_txt, "w", encoding="utf-8") as f:
f.writelines(lines)
except Exception as e:
print(f"[Warn] Failed writing text file {out_txt}: {e}")
print(f"Request ID: {request_id}, Text saved to {out_txt}")
elif stage_outputs.final_output_type == "audio":
request_id = output.request_id
audio_tensor = output.outputs[0].multimodal_output.get("audio")
if audio_tensor is None:
continue
output_wav = os.path.join(output_dir, f"{request_id}.wav")
audio_numpy = audio_tensor.float().detach().cpu().numpy()
if audio_numpy.ndim > 1:
audio_numpy = audio_numpy.flatten()
sf.write(output_wav, audio_numpy, samplerate=24000, format="WAV")
print(f"Request ID: {request_id}, Audio saved to {output_wav}")
omni.close()
def parse_args():
parser = FlexibleArgumentParser(description="Offline inference demo for Covo-Audio-Chat")
parser.add_argument(
"--model-name",
"-m",
type=str,
default="tencent/Covo-Audio-Chat",
help="Model path or HuggingFace model ID.",
)
parser.add_argument(
"--text",
"-t",
type=str,
default=None,
help="Text prompt / question for the audio.",
)
parser.add_argument(
"--audio-path",
"-a",
type=str,
default=None,
help="Path to local audio file. Uses default asset if not provided.",
)
parser.add_argument(
"--sampling-rate",
type=int,
default=16000,
help="Sampling rate for audio loading (default: 16000).",
)
parser.add_argument(
"--stage-configs-path",
type=str,
default=None,
help="Path to stage configs YAML file.",
)
parser.add_argument(
"--log-stats",
action="store_true",
default=False,
help="Enable writing detailed statistics.",
)
parser.add_argument(
"--stage-init-timeout",
type=int,
default=300,
help="Timeout for initializing a single stage in seconds.",
)
parser.add_argument(
"--batch-timeout",
type=int,
default=5,
help="Timeout for batching in seconds.",
)
parser.add_argument(
"--init-timeout",
type=int,
default=300,
help="Timeout for initializing stages in seconds.",
)
parser.add_argument(
"--shm-threshold-bytes",
type=int,
default=65536,
help="Threshold for using shared memory in bytes.",
)
parser.add_argument(
"--output-dir",
default="./output_audio",
help="Output directory for generated files.",
)
parser.add_argument(
"--num-prompts",
type=int,
default=1,
help="Number of prompts to generate.",
)
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
main(args)
@@ -1,172 +1,155 @@
# HunyuanImage-3.0-Instruct # HunyuanImage-3.0-Instruct
## Set up This example runs HunyuanImage-3.0-Instruct offline with the unified deploy
YAMLs under `vllm_omni/deploy/`.
Please refer to the [stage configuration documentation](https://docs.vllm.ai/projects/vllm-omni/en/latest/configuration/stage_configs/) to configure memory allocation appropriately for your hardware setup. ## Deploy Configs
## Run examples | File | Topology | Default use |
| :--- | :--- | :--- |
| `vllm_omni/deploy/hunyuan_image3.yaml` | AR + DiT | Default for `text2img` and `img2img`. |
| `vllm_omni/deploy/hunyuan_image3_ar.yaml` | AR only | Default for `img2text` and `text2text`. |
| `vllm_omni/deploy/hunyuan_image3_dit.yaml` | DiT only | Standalone diffusion stage. Pass it explicitly with `--deploy-config`. |
**Note**: These examples work with the default configuration on **8x NVIDIA L40S (48GB)**. For different GPU setups, modify the stage configuration to adjust device allocation and memory utilization. The example chooses a deploy config automatically when `--deploy-config` and
`--stage-configs-path` are both omitted:
Get into the hunyuan_image3 folder: | `--modality` | `mode` passed to Omni | Default deploy |
| :--- | :--- | :--- |
| `text2img` | `text-to-image` | `hunyuan_image3.yaml` |
| `img2img` | `image-editing` | `hunyuan_image3.yaml` |
| `img2text` | `image-to-text` | `hunyuan_image3_ar.yaml` |
| `text2text` | `text-to-text` | `hunyuan_image3_ar.yaml` |
`--modality` is an offline example convenience flag. It maps to the internal
`mode` argument passed to `Omni(...)` by this script. HunyuanImage3 uses
separate deploy YAMLs for AR + DiT, AR-only, and DiT-only topologies, so the
stage topology is selected by the deploy file rather than by YAML mode
overrides.
Online serving does not expose a `--modality` flag or accept `mode` as an API
request field. Choose the deploy topology when starting the server with
`--deploy-config`, then use the OpenAI-compatible endpoint and request shape for
the scenario. The `modalities` request field is used by the chat completions
path; the image endpoints infer the image task from the endpoint and payload.
| Online scenario | Server deploy | Request |
| :--- | :--- | :--- |
| Text to image | `--deploy-config vllm_omni/deploy/hunyuan_image3.yaml` | `POST /v1/images/generations`, or `POST /v1/chat/completions` with `"modalities": ["image"]`. |
| Image editing | `--deploy-config vllm_omni/deploy/hunyuan_image3.yaml` | `POST /v1/images/edits`. |
| Image/text to text | `--deploy-config vllm_omni/deploy/hunyuan_image3_ar.yaml` | `POST /v1/chat/completions` for text output, for example with `"modalities": ["text"]`. |
| DiT-only image generation | `--deploy-config vllm_omni/deploy/hunyuan_image3_dit.yaml` | `POST /v1/images/generations`. |
## Run Examples
Text to image, using the default AR + DiT deploy:
```bash ```bash
cd examples/offline_inference/hunyuan_image3 python examples/offline_inference/hunyuan_image3/end2end.py \
--model tencent/HunyuanImage-3.0-Instruct \
--modality text2img \
--prompts "A cute cat sitting on a windowsill watching the sunset"
``` ```
### Modality Control Image editing, using the default AR + DiT deploy:
HunyuanImage-3.0-Instruct supports multiple modality modes. You can control the mode using the `--modality` argument:
#### Text to Image (text2img)
- **Pipeline**: Text → AR (CoT + latent tokens) → DiT (denoise) → VAE Decode → Image
- **Stages Used**: Stage 0 (AR) + Stage 1 (DiT)
- **KV Transfer**: AR sends KV cache to DiT for conditioned generation
- **Default Config**: `hunyuan_image3_t2i.yaml`
```bash ```bash
python end2end.py --model tencent/HunyuanImage-3.0-Instruct \ python examples/offline_inference/hunyuan_image3/end2end.py \
--modality text2img \ --model tencent/HunyuanImage-3.0-Instruct \
--prompts "A cute cat sitting on a windowsill watching the sunset" --modality img2img \
--image-path /path/to/image.png \
--prompts "Make the petals neon pink"
``` ```
**With VAE tiling (required on A100 GPUs):** Image to text, using the AR-only deploy:
```bash
python end2end.py --model tencent/HunyuanImage-3.0-Instruct \
--modality text2img \
--prompts "A cute cat sitting on a windowsill watching the sunset" \
--vae-use-tiling
```
#### Image to Image (img2img)
- **Pipeline**: Image + Text → AR (CoT + recaption + latent) → DiT → Edited Image
- **Stages Used**: Stage 0 (AR) + Stage 1 (DiT)
- **KV Transfer**: AR sends KV cache to DiT
- **Default Config**: `hunyuan_image3_it2i.yaml`
```bash ```bash
python end2end.py --model tencent/HunyuanImage-3.0-Instruct \ python examples/offline_inference/hunyuan_image3/end2end.py \
--modality img2img \ --model tencent/HunyuanImage-3.0-Instruct \
--image-path /path/to/image.png \ --modality img2text \
--prompts "Make the petals neon pink" --image-path /path/to/image.jpg \
--prompts "Describe the content of the picture."
``` ```
#### Image to Text (img2text) Text to text, using the AR-only deploy:
- **Pipeline**: Image + Question → AR → Text description
- **Stages Used**: Stage 0 (AR) only
- **Default Config**: `hunyuan_image3_i2t.yaml`
```bash ```bash
python end2end.py --model tencent/HunyuanImage-3.0-Instruct \ python examples/offline_inference/hunyuan_image3/end2end.py \
--modality img2text \ --model tencent/HunyuanImage-3.0-Instruct \
--image-path /path/to/image.jpg \ --modality text2text \
--prompts "Describe the content of the picture." --prompts "What is the capital of France?"
``` ```
#### Text to Text (text2text) Standalone DiT, using the DiT-only deploy explicitly:
- **Pipeline**: Text → AR → Text
- **Stages Used**: Stage 0 (AR) only
- **Default Config**: `hunyuan_image3_t2t.yaml`
```bash ```bash
python end2end.py --model tencent/HunyuanImage-3.0-Instruct \ python examples/offline_inference/hunyuan_image3/end2end.py \
--modality text2text \ --model tencent/HunyuanImage-3.0-Instruct \
--prompts "What is the capital of France?" --modality text2img \
--deploy-config vllm_omni/deploy/hunyuan_image3_dit.yaml \
--prompts "A cinematic portrait of an astronaut in a greenhouse"
``` ```
### Inference Steps & Guidance Override the default full AR + DiT deploy explicitly:
Control generation quality for image modalities: ```bash
python examples/offline_inference/hunyuan_image3/end2end.py \
--model tencent/HunyuanImage-3.0-Instruct \
--modality text2img \
--deploy-config vllm_omni/deploy/hunyuan_image3.yaml \
--prompts "A cute cat"
```
## Additional Config
You can pass diffusion worker `additional_config` from the offline example as a JSON object.
This maps to the upstream vLLM `VllmConfig.additional_config` platform extension field:
https://docs.vllm.ai/en/stable/api/vllm/config/#vllm.config.VllmConfig.additional_config
```bash ```bash
python end2end.py --modality text2img \ python end2end.py --modality text2img \
--steps 50 \ --prompts "A cute cat" \
--guidance-scale 5.0 \ --additional-config '{"torchair_graph_config":{"enabled":true}}'
--height 1024 --width 1024 \
--prompts "A photo-realistic sunset over the ocean"
``` ```
### Key Arguments
#### 📌 Command Line Arguments (end2end.py) ## Key Arguments
| Argument | Type | Default | Description | | Argument | Description |
| :--------------------- | :----- | :----------------------------------- | :----------------------------------------------------------- | | :--- | :--- |
| `--model` | string | `tencent/HunyuanImage-3.0-Instruct` | Model path or name | | `--deploy-config` | Preferred config path for unified deploy YAMLs. |
| `--modality` | choice | `text2img` | Modality: `text2img`, `img2img`, `img2text`, `text2text` | | `--stage-configs-path` | Legacy stage config path, kept only for compatibility. Prefer `--deploy-config`. |
| `--prompts` | list | `None` | Input text prompts | | `--additional-config` | JSON object forwarded to diffusion worker `additional_config`. |
| `--image-path` | string | `None` | Input image path (for `img2img`/`img2text`) | | `--modality` | Offline-only convenience flag. One of `text2img`, `img2img`, `img2text`, `text2text`. It selects prompt formatting, internal `mode`, and default deploy config for this script. Online serving uses `--deploy-config` plus the endpoint and, for chat completions, request `modalities` instead. |
| `--output` | string | `.` | Output directory for saved images | | `--steps` | Number of diffusion inference steps for image generation. |
| `--steps` | int | `50` | Number of inference steps | | `--guidance-scale` | Classifier-free guidance scale for image generation. |
| `--guidance-scale` | float | `5.0` | Classifier-free guidance scale | | `--height`, `--width` | Output image size for `text2img`. |
| `--seed` | int | `42` | Random seed | | `--bot-task` | Override prompt mode. `none`, `think`, `recaption`, `think_recaption`, or `vanilla`. |
| `--height` | int | `1024` | Output image height | | `--sys-type` | Override the system prompt type, for example `en_unified` or `en_vanilla`. |
| `--width` | int | `1024` | Output image width | | `--vae-use-tiling` | Enable VAE tiling for memory reduction. |
| `--bot-task` | string | auto | Override prompt task (e.g. `it2i_think`, `t2i_recaption`) |
| `--sys-type` | string | auto | Override system prompt type (e.g. `en_unified`, `en_vanilla`) |
| `--stage-configs-path` | string | auto | Custom stage config YAML path |
| `--enforce-eager` | flag | `False` | Disable torch.compile |
| `--init-timeout` | int | `300` | Initialization timeout (seconds) |
| `--vae-use-tiling` | flag | `False` | Enable VAE tiling for memory optimization (required to avoid OOM on A100) |
------ ## Notes
#### ⚙️ Stage Configurations - `hunyuan_image3_ar.yaml` is a 4-card AR-only text/comprehension deploy.
- `hunyuan_image3_dit.yaml` is a single-stage DiT deploy with `stage_id: 0`.
| Config YAML | Modality | Stages | GPUs | Description | - The old HunyuanImage3 YAMLs under `model_executor/stage_configs/` and `platforms/*/stage_configs/` have been folded into the deploy YAMLs.
| :---------------------------------- | :-------- | :----- | :----- | :------------------------------------ |
| `hunyuan_image3_t2i.yaml` | text2img | 2 | 8 | T2I with AR→DiT, 4 GPU each |
| `hunyuan_image3_it2i.yaml` | img2img | 2 | 8 | IT2I with AR→DiT, 4 GPU each |
| `hunyuan_image3_i2t.yaml` | img2text | 1 | 4 | I2T (AR only) |
| `hunyuan_image3_t2t.yaml` | text2text | 1 | 4 | T2T (AR only) |
| `hunyuan_image3_t2i_2gpu.yaml` | text2img | 2 | 2 | T2I for 2-GPU setups |
| `hunyuan_image3_moe.yaml` | text2img | 2 | 8 | T2I with MoE AR→DiT KV reuse |
| `hunyuan_image3_moe_dit_2gpu_fp8.yaml` | text2img | 2 | 2 | T2I with FP8 quantization |
------
## Using MoE Config
The `hunyuan_image3_moe.yaml` config enables AR→DiT KV cache reuse with 8 GPUs (4 for AR + 4 for DiT).
```bash
python end2end.py --model tencent/HunyuanImage-3.0-Instruct \
--modality text2img \
--stage-configs-path hunyuan_image3_moe.yaml \
--prompts "A cute cat"
```
------
## Prompt Format ## Prompt Format
HunyuanImage-3.0-Instruct uses an instruct chat template: HunyuanImage-3.0-Instruct uses an instruct chat template:
``` ```text
<|startoftext|>{system_prompt}\n\nUser: {<img>?}{user_prompt}\n\nAssistant: {trigger_tag?} <|startoftext|>{system_prompt}
User: {<img>?}{user_prompt}
Assistant: {trigger_tag?}
``` ```
- `<img>`: Placeholder for each input image (single token; expanded by the multimodal pipeline) - `<img>`: Placeholder for each input image (single token; expanded by the multimodal pipeline).
- Trigger tags: `<think>` (CoT), `<recaption>` (recaptioning) — placed AFTER `Assistant: ` - Trigger tags: `<think>` for CoT and `<recaption>` for recaptioning, placed after `Assistant: `.
- System prompt: Auto-selected based on task - System prompt: Auto-selected from `task` and `bot_task`.
- `t2i_vanilla` is the only task that uses the bare pretrain template (no chat structure) - `bot_task='vanilla'` with `task='t2i'` uses the bare pretrain template.
The shared `vllm_omni.diffusion.models.hunyuan_image3.prompt_utils.build_prompt_tokens()` The shared `vllm_omni.diffusion.models.hunyuan_image3.prompt_utils.build_prompt_tokens()`
helper handles segment-by-segment tokenization (matches HF `apply_chat_template` byte-for-byte). helper handles segment-by-segment tokenization and matches HF `apply_chat_template`.
------
## FAQ
- **OOM errors**: Decrease `gpu_memory_utilization` in the YAML stage config, use a smaller `max_num_batched_tokens`, or enable VAE tiling with `--vae-use-tiling` (required on A100 GPUs).
- **Custom image sizes**: Use `--height` and `--width` flags (multiples of 16 recommended).
| Stage | VRAM (approx) |
| :---------------- | :------------------- |
| Stage 0 (AR) | ~15 GiB + KV Cache |
| Stage 1 (DiT) | ~30 GiB |
| Total (8-GPU) | ~45 GiB + KV Cache |
@@ -1,114 +1,111 @@
""" """
HunyuanImage-3.0-Instruct unified end-to-end inference script. HunyuanImage-3.0-Instruct unified end-to-end inference script.
Supports all modalities through a single entry point:
- text2img: Text → AR → DiT → Image
- img2img: Text+Image → AR → DiT → Edited Image (IT2I)
- img2text: Image+Text → AR → Text description (I2T)
- text2text: Text → AR → Text (comprehension, no image)
Usage:
python end2end.py --modality text2img --prompts "A cute cat"
python end2end.py --modality img2img --image-path input.png --prompts "Make it snowy"
python end2end.py --modality img2text --image-path input.png --prompts "Describe this image"
""" """
import argparse import argparse
import json
import os import os
from pathlib import Path
from vllm_omni.diffusion.models.hunyuan_image3.prompt_utils import ( from vllm_omni.diffusion.models.hunyuan_image3.prompt_utils import (
MAX_IMAGES_PER_REQUEST,
build_prompt_tokens, build_prompt_tokens,
resolve_stop_token_ids,
resolve_sys_type,
) )
from vllm_omni.entrypoints.omni import Omni from vllm_omni.entrypoints.omni import Omni
from vllm_omni.inputs.data import OmniPromptType from vllm_omni.inputs.data import OmniPromptType
# task -> (sys_type, bot_task, trigger_tag) _REPO_ROOT = Path(__file__).resolve().parents[3]
_TASK_PRESETS: dict[str, tuple[str, str | None, str | None]] = { _DEFAULT_DEPLOY_CONFIG = str(_REPO_ROOT / "vllm_omni" / "deploy" / "hunyuan_image3.yaml")
"t2t": ("en_unified", None, None), _DEFAULT_AR_DEPLOY_CONFIG = str(_REPO_ROOT / "vllm_omni" / "deploy" / "hunyuan_image3_ar.yaml")
"i2t": ("en_unified", None, None),
"it2i_think": ("en_unified", "think", "<think>"), _MODALITY_TASK_MAP: dict[str, tuple[str, str | None]] = {
"it2i_recaption": ("en_unified", "recaption", "<recaption>"), "text2img": ("t2i", "think"),
"t2i_think": ("en_unified", "think", "<think>"), "img2img": ("it2i", "think"),
"t2i_recaption": ("en_unified", "recaption", "<recaption>"), "img2text": ("i2t", None),
"t2i_vanilla": ("en_vanilla", "image", None), "text2text": ("t2t", None),
} }
# Modality → prompt_utils task mapping _MODALITY_DEFAULT_DEPLOY_CONFIG = {
_MODALITY_TASK_MAP = { "text2img": _DEFAULT_DEPLOY_CONFIG,
"text2img": "t2i_think", "img2img": _DEFAULT_DEPLOY_CONFIG,
"img2img": "it2i_think", "img2text": _DEFAULT_AR_DEPLOY_CONFIG,
"img2text": "i2t", "text2text": _DEFAULT_AR_DEPLOY_CONFIG,
"text2text": "t2t",
} }
_MODALITY_MODE = {
# Modality → default stage config "text2img": "text-to-image",
_MODALITY_DEFAULT_CONFIG = { "img2img": "image-editing",
"text2img": "hunyuan_image3_t2i.yaml", "img2text": "image-to-text",
"img2img": "hunyuan_image3_it2i.yaml", "text2text": "text-to-text",
"img2text": "hunyuan_image3_i2t.yaml",
"text2text": "hunyuan_image3_t2t.yaml",
} }
def parse_args(): def parse_args():
parser = argparse.ArgumentParser(description="HunyuanImage-3.0-Instruct end-to-end inference.") parser = argparse.ArgumentParser(description="HunyuanImage-3.0-Instruct end-to-end inference.")
parser.add_argument( parser.add_argument("--model", default="tencent/HunyuanImage-3.0-Instruct", help="Model name or local path.")
"--model",
default="tencent/HunyuanImage-3.0-Instruct",
help="Model name or local path.",
)
parser.add_argument( parser.add_argument(
"--modality", "--modality",
default="text2img", default="text2img",
choices=["text2img", "img2img", "img2text", "text2text"], choices=list(_MODALITY_TASK_MAP),
help="Modality mode to control stage execution.",
) )
parser.add_argument("--prompts", nargs="+", default=None, help="Input text prompts.") parser.add_argument("--prompts", nargs="+", default=None, help="Input text prompts.")
parser.add_argument( parser.add_argument(
"--image-path", "--image-path",
type=str, type=str,
default=None, default=None,
help="Path to input image (for img2img/img2text).", help="Input image path(s) for img2img/img2text. Comma-separated for multi-image (up to 3).",
) )
parser.add_argument( parser.add_argument("--output", type=str, default=".", help="Output directory to save results.")
"--output",
type=str,
default=".",
help="Output directory to save results.",
)
# Generation parameters
parser.add_argument("--steps", type=int, default=50, help="Number of inference steps.") parser.add_argument("--steps", type=int, default=50, help="Number of inference steps.")
parser.add_argument("--guidance-scale", type=float, default=5.0, help="Classifier-free guidance scale.") parser.add_argument("--guidance-scale", type=float, default=5.0, help="Classifier-free guidance scale.")
parser.add_argument("--seed", type=int, default=42, help="Random seed.") parser.add_argument("--seed", type=int, default=42, help="Random seed.")
parser.add_argument("--height", type=int, default=1024, help="Output image height.") parser.add_argument("--height", type=int, default=1024, help="Output image height.")
parser.add_argument("--width", type=int, default=1024, help="Output image width.") parser.add_argument("--width", type=int, default=1024, help="Output image width.")
parser.add_argument( parser.add_argument("--vae-use-tiling", action="store_true", help="Enable VAE tiling.")
"--vae-use-tiling",
action="store_true",
help="Enable VAE tiling for memory optimization.",
)
# Prompt configuration
parser.add_argument( parser.add_argument(
"--bot-task", "--bot-task",
type=str, type=str,
default=None, default=None,
help="Override prompt task (e.g. it2i_think, t2i_recaption). Default: auto from modality.", choices=["none", "think", "recaption", "think_recaption", "vanilla"],
help="Override prompt mode. Default: auto from --modality.",
) )
parser.add_argument( parser.add_argument("--sys-type", type=str, default=None, help="Override system prompt type.")
"--sys-type", parser.add_argument("--deploy-config", type=str, default=None, help="Custom deploy YAML path.")
type=str, parser.add_argument("--stage-configs-path", type=str, default=None, help="Custom legacy stage config YAML path.")
default=None,
help="Override system prompt type (e.g. en_unified, en_vanilla).",
)
# Omni init args
parser.add_argument("--stage-configs-path", type=str, default=None, help="Custom stage config YAML path.")
parser.add_argument("--log-stats", action="store_true", default=False) parser.add_argument("--log-stats", action="store_true", default=False)
parser.add_argument("--init-timeout", type=int, default=300, help="Initialization timeout in seconds.") parser.add_argument("--init-timeout", type=int, default=300, help="Initialization timeout in seconds.")
parser.add_argument("--enforce-eager", action="store_true", help="Disable torch.compile.") parser.add_argument("--enforce-eager", action="store_true", help="Disable torch.compile.")
parser.add_argument(
"--diffusion-kv-cache-dtype",
type=str,
default=None,
help="Diffusion attention KV cache dtype, for example 'fp8'. Separate from vLLM --kv-cache-dtype.",
)
parser.add_argument(
"--diffusion-kv-cache-skip-steps",
type=str,
default=None,
help="Denoising step selector to keep diffusion KV cache in native dtype, for example '0,1,4-6'.",
)
parser.add_argument(
"--diffusion-kv-cache-skip-layers",
type=str,
default=None,
help="Transformer layer selector to keep diffusion KV cache in native dtype, for example '0-2,10'.",
)
parser.add_argument(
"--additional-config",
type=str,
default=None,
help=(
"JSON object forwarded to Omni/additional_config, for example "
'\'{"torchair_graph_config":{"enabled":true}}\'. Different platforms may support different '
"configs. Make sure the configs are valid for the platform you are using. "
"Contents must be hashable."
),
)
from vllm_omni.engine.arg_utils import nullify_stage_engine_defaults from vllm_omni.engine.arg_utils import nullify_stage_engine_defaults
@@ -116,135 +113,182 @@ def parse_args():
return parser.parse_args() return parser.parse_args()
def parse_additional_config(raw_value: str | None) -> dict | None:
"""Parse a JSON string into an additional_config mapping."""
if raw_value is None:
return None
try:
additional_config = json.loads(raw_value)
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid --additional-config JSON: {exc}") from exc
if additional_config is None:
return None
if not isinstance(additional_config, dict):
raise ValueError(f"--additional-config must decode to a JSON object, got {type(additional_config).__name__}")
return additional_config
def main(): def main():
args = parse_args() args = parse_args()
os.makedirs(args.output, exist_ok=True) os.makedirs(args.output, exist_ok=True)
additional_config = parse_additional_config(args.additional_config)
# Determine task for prompt formatting task, default_bot_task = _MODALITY_TASK_MAP[args.modality]
task = args.bot_task or _MODALITY_TASK_MAP[args.modality] if args.bot_task is None:
bot_task: str | None = default_bot_task
elif args.bot_task == "none":
bot_task = None
else:
bot_task = args.bot_task
# Determine stage config if args.deploy_config is not None and args.stage_configs_path is not None:
stage_configs_path = args.stage_configs_path or _MODALITY_DEFAULT_CONFIG[args.modality] raise ValueError("--deploy-config and --stage-configs-path are mutually exclusive.")
deploy_config = args.deploy_config
stage_configs_path = args.stage_configs_path
if deploy_config is None and stage_configs_path is None:
deploy_config = _MODALITY_DEFAULT_DEPLOY_CONFIG[args.modality]
# Build Omni
omni_kwargs = { omni_kwargs = {
"model": args.model, "model": args.model,
"vae_use_tiling": args.vae_use_tiling, "vae_use_tiling": args.vae_use_tiling,
"stage_configs_path": stage_configs_path,
"log_stats": args.log_stats, "log_stats": args.log_stats,
"init_timeout": args.init_timeout, "init_timeout": args.init_timeout,
"enforce_eager": args.enforce_eager, "enforce_eager": args.enforce_eager,
"mode": _MODALITY_MODE[args.modality],
"diffusion_kv_cache_dtype": args.diffusion_kv_cache_dtype,
"diffusion_kv_cache_skip_steps": args.diffusion_kv_cache_skip_steps,
"diffusion_kv_cache_skip_layers": args.diffusion_kv_cache_skip_layers,
} }
if args.modality in ("text2img", "img2img"):
omni_kwargs["mode"] = "text-to-image" if additional_config is not None:
omni_kwargs["additional_config"] = additional_config
if deploy_config is not None:
omni_kwargs["deploy_config"] = deploy_config
else:
omni_kwargs["stage_configs_path"] = stage_configs_path
omni = Omni(**omni_kwargs) omni = Omni(**omni_kwargs)
# Prepare prompts
prompts = args.prompts or ["A cute cat"] prompts = args.prompts or ["A cute cat"]
if not prompts: input_images: list = []
print("[Info] No prompts provided, using default.")
prompts = ["A cute cat"]
# Load image if needed
input_image = None
if args.modality in ("img2img", "img2text"): if args.modality in ("img2img", "img2text"):
if not args.image_path or not os.path.exists(args.image_path): if not args.image_path:
raise ValueError(f"--image-path required for {args.modality}, got: {args.image_path}") raise ValueError(f"--image-path required for {args.modality}, got: {args.image_path}")
from PIL import Image from PIL import Image
input_image = Image.open(args.image_path).convert("RGB") image_paths = [p.strip() for p in args.image_path.split(",") if p.strip()]
if len(image_paths) > MAX_IMAGES_PER_REQUEST:
raise ValueError(
f"--image-path accepts at most {MAX_IMAGES_PER_REQUEST} images for "
f"HunyuanImage-3.0 IT2I, got {len(image_paths)}: {args.image_path}"
)
for image_path in image_paths:
if not os.path.exists(image_path):
raise ValueError(f"Image path does not exist: {image_path}")
input_images.append(Image.open(image_path).convert("RGB"))
if not input_images:
raise ValueError(f"--image-path produced no usable paths: {args.image_path!r}")
# Load tokenizer for segment-wise prompt tokenization (matches HF
# apply_chat_template byte-for-byte; see build_prompt_tokens docstring).
from transformers import AutoTokenizer from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(args.model, trust_remote_code=True) tokenizer = AutoTokenizer.from_pretrained(args.model, trust_remote_code=True)
mm_image_payload = (input_images[0] if len(input_images) == 1 else input_images) if input_images else None
# Format prompts
formatted_prompts: list[OmniPromptType] = [] formatted_prompts: list[OmniPromptType] = []
for p in prompts: for prompt in prompts:
token_ids = build_prompt_tokens(p, tokenizer, task=task, sys_type=args.sys_type) build_kwargs: dict = {"task": task, "bot_task": bot_task, "sys_type": args.sys_type}
preset_sys_type, _, _ = _TASK_PRESETS[task] if input_images:
effective_sys_type = args.sys_type or preset_sys_type build_kwargs["num_images"] = len(input_images)
result = build_prompt_tokens(prompt, tokenizer, **build_kwargs)
token_ids = result.token_ids
effective_sys_type = args.sys_type or resolve_sys_type(bot_task)
# `prompt_token_ids` drives the AR stage (matches HF byte-for-byte).
# `prompt` and `use_system_prompt` are forwarded by ar2diffusion to
# the DiT stage so the diffusion pipeline can rebuild the same
# system prefix when constructing its model inputs.
prompt_dict: dict = { prompt_dict: dict = {
"prompt_token_ids": token_ids, "prompt_token_ids": token_ids,
"prompt": p, "prompt": prompt,
"use_system_prompt": effective_sys_type, "use_system_prompt": effective_sys_type,
} }
if args.modality == "text2img": if args.modality == "text2img":
prompt_dict["modalities"] = ["image"] prompt_dict["modalities"] = ["image"]
elif args.modality == "img2img": elif args.modality == "img2img":
prompt_dict["modalities"] = ["image"] prompt_dict["modalities"] = ["image"]
prompt_dict["multi_modal_data"] = {"image": input_image} prompt_dict["multi_modal_data"] = {"image": mm_image_payload}
prompt_dict["height"] = input_image.height prompt_dict["height"] = input_images[0].height
prompt_dict["width"] = input_image.width prompt_dict["width"] = input_images[0].width
elif args.modality == "img2text": elif args.modality == "img2text":
prompt_dict["modalities"] = ["text"] prompt_dict["modalities"] = ["text"]
prompt_dict["multi_modal_data"] = {"image": input_image} prompt_dict["multi_modal_data"] = {"image": mm_image_payload}
elif args.modality == "text2text": else:
prompt_dict["modalities"] = ["text"] prompt_dict["modalities"] = ["text"]
formatted_prompts.append(prompt_dict) formatted_prompts.append(prompt_dict)
# Build sampling params from defaults
params_list = list(omni.default_sampling_params_list) params_list = list(omni.default_sampling_params_list)
# Override diffusion params if applicable
from vllm_omni.inputs.data import OmniDiffusionSamplingParams from vllm_omni.inputs.data import OmniDiffusionSamplingParams
for i, sp in enumerate(params_list): ar_stop_token_ids = resolve_stop_token_ids(task=task, bot_task=bot_task, tokenizer=tokenizer)
for sp in params_list:
if isinstance(sp, OmniDiffusionSamplingParams): if isinstance(sp, OmniDiffusionSamplingParams):
sp.num_inference_steps = args.steps sp.num_inference_steps = args.steps
sp.guidance_scale = args.guidance_scale sp.guidance_scale = args.guidance_scale
sp.guidance_scale_provided = True
if args.seed is not None: if args.seed is not None:
sp.seed = args.seed sp.seed = args.seed
if args.modality in ("text2img",): if args.modality == "text2img":
sp.height = args.height sp.height = args.height
sp.width = args.width sp.width = args.width
elif hasattr(sp, "stop_token_ids"):
sp.stop_token_ids = ar_stop_token_ids
# Print configuration
print(f"\n{'=' * 60}") print(f"\n{'=' * 60}")
print("HunyuanImage-3.0 Generation Configuration:") print("HunyuanImage-3.0 Generation Configuration:")
print(f" Model: {args.model}") print(f" Model: {args.model}")
print(f" Modality: {args.modality}") print(f" Modality: {args.modality}")
print(f" Stage config: {stage_configs_path}") print(f" Prompt task: {task}")
print(f" Bot task: {bot_task}")
if deploy_config is not None:
print(f" Deploy config: {deploy_config}")
else:
print(f" Stage config: {stage_configs_path}")
print(f" Num stages: {omni.num_stages}") print(f" Num stages: {omni.num_stages}")
if args.modality in ("text2img", "img2img"): if args.modality in ("text2img", "img2img"):
print(f" Inference steps: {args.steps}") print(f" Inference steps: {args.steps}")
print(f" Guidance scale: {args.guidance_scale}") print(f" Guidance scale: {args.guidance_scale}")
print(f" Seed: {args.seed}") print(f" Seed: {args.seed}")
print(f" diffusion_kv_cache_dtype: {args.diffusion_kv_cache_dtype}")
print(f" diffusion_kv_cache_skip_steps: {args.diffusion_kv_cache_skip_steps}")
print(f" diffusion_kv_cache_skip_layers: {args.diffusion_kv_cache_skip_layers}")
if args.modality == "text2img": if args.modality == "text2img":
print(f" Output size: {args.width}x{args.height}") print(f" Output size: {args.width}x{args.height}")
if args.image_path: if args.image_path:
print(f" Input image: {args.image_path}") print(f" Input image: {args.image_path}")
if additional_config is not None:
print(f" Additional config: {additional_config}")
print(f" Prompts: {prompts}") print(f" Prompts: {prompts}")
print(f"{'=' * 60}\n") print(f"{'=' * 60}\n")
# Generate
omni_outputs = list(omni.generate(prompts=formatted_prompts, sampling_params_list=params_list)) omni_outputs = list(omni.generate(prompts=formatted_prompts, sampling_params_list=params_list))
# Process outputs
img_idx = 0 img_idx = 0
for req_output in omni_outputs: for req_output in omni_outputs:
# Text output (AR stage or text-only)
ro = getattr(req_output, "request_output", None) ro = getattr(req_output, "request_output", None)
txt = ""
if ro and getattr(ro, "outputs", None): if ro and getattr(ro, "outputs", None):
txt = "".join(getattr(o, "text", "") or "" for o in ro.outputs) txt = "".join(getattr(o, "text", "") or "" for o in ro.outputs)
if txt: if not txt:
print(f"[Output] Text:\n{txt}") ar_text = getattr(req_output, "custom_output", {}).get("ar_generated_text")
if isinstance(ar_text, list):
txt = "\n".join(text for text in ar_text if text)
else:
txt = ar_text or ""
if txt:
print(f"[Output] Text:\n{txt}")
# Image output (DiT stage)
images = getattr(req_output, "images", None) images = getattr(req_output, "images", None)
if not images and ro and hasattr(ro, "images"): if not images and ro and hasattr(ro, "images"):
images = ro.images images = ro.images
if images: if images:
for j, img in enumerate(images): for j, img in enumerate(images):
save_path = os.path.join(args.output, f"output_{img_idx}_{j}.png") save_path = os.path.join(args.output, f"output_{img_idx}_{j}.png")
@@ -102,6 +102,24 @@ def parse_args() -> argparse.Namespace:
choices=["unipc", "euler"], choices=["unipc", "euler"],
help="Sampling solver for Wan2.2 pipelines. Use 'euler' for Lightning/Distill setups.", help="Sampling solver for Wan2.2 pipelines. Use 'euler' for Lightning/Distill setups.",
) )
parser.add_argument(
"--diffusion-kv-cache-dtype",
type=str,
default=None,
help="Diffusion attention KV cache dtype (e.g. float8_e4m3fn). Separate from vLLM --kv-cache-dtype.",
)
parser.add_argument(
"--diffusion-kv-cache-skip-steps",
type=str,
default=None,
help="Diffusion KV-cache quantization skip-step selector, e.g. '0-9,20,25-30'.",
)
parser.add_argument(
"--diffusion-kv-cache-skip-layers",
type=str,
default=None,
help="Diffusion KV-cache quantization skip-layer selector, e.g. '0,1,4-8'.",
)
parser.add_argument("--output", type=str, default="i2v_output.mp4", help="Path to save the video (mp4).") parser.add_argument("--output", type=str, default="i2v_output.mp4", help="Path to save the video (mp4).")
parser.add_argument("--fps", type=int, default=None, help="Frames per second for the output video.") parser.add_argument("--fps", type=int, default=None, help="Frames per second for the output video.")
parser.add_argument( parser.add_argument(
@@ -124,6 +142,42 @@ def parse_args() -> argparse.Namespace:
action="store_true", action="store_true",
help="Enable layerwise (blockwise) offloading on DiT modules.", help="Enable layerwise (blockwise) offloading on DiT modules.",
) )
parser.add_argument(
"--enforce-eager",
action="store_true",
help="Disable torch.compile and force eager execution.",
)
parser.add_argument(
"--audio-sample-rate",
type=int,
default=24000,
help="Sample rate for audio output when saved (default: 24000).",
)
parser.add_argument(
"--cache-backend",
type=str,
default=None,
choices=["cache_dit", "tea_cache"],
help=(
"Cache backend to use for acceleration. "
"Options: 'cache_dit' (DBCache + SCM + TaylorSeer), 'tea_cache' (Timestep Embedding Aware Cache). "
"Default: None (no cache acceleration)."
),
)
parser.add_argument(
"--enable-diffusion-pipeline-profiler",
action="store_true",
help="Enable diffusion pipeline profiler to display stage durations.",
)
parser.add_argument(
"--quantization",
type=str,
default=None,
choices=["fp8", "mxfp8", "int8", "gguf"],
help="Quantization method for the transformer. mxfp8: W8A8 MXFP8 online quant (NPU). fp8: online FP8 (GPU).",
)
# Distributed and parallel execution
parser.add_argument( parser.add_argument(
"--ulysses-degree", "--ulysses-degree",
type=int, type=int,
@@ -155,28 +209,6 @@ def parse_args() -> argparse.Namespace:
default=1, default=1,
help="Number of GPUs used for VAE patch/tile parallelism (decode).", help="Number of GPUs used for VAE patch/tile parallelism (decode).",
) )
parser.add_argument(
"--enforce-eager",
action="store_true",
help="Disable torch.compile and force eager execution.",
)
parser.add_argument(
"--audio-sample-rate",
type=int,
default=24000,
help="Sample rate for audio output when saved (default: 24000).",
)
parser.add_argument(
"--cache-backend",
type=str,
default=None,
choices=["cache_dit", "tea_cache"],
help=(
"Cache backend to use for acceleration. "
"Options: 'cache_dit' (DBCache + SCM + TaylorSeer), 'tea_cache' (Timestep Embedding Aware Cache). "
"Default: None (no cache acceleration)."
),
)
parser.add_argument( parser.add_argument(
"--use-hsdp", "--use-hsdp",
action="store_true", action="store_true",
@@ -201,9 +233,10 @@ def parse_args() -> argparse.Namespace:
), ),
) )
parser.add_argument( parser.add_argument(
"--enable-diffusion-pipeline-profiler", "--pipeline-parallel-size",
action="store_true", type=int,
help="Enable diffusion pipeline profiler to display stage durations.", default=1,
help="Number of pipeline parallel stages.",
) )
parser.add_argument( parser.add_argument(
"--profiler-config", "--profiler-config",
@@ -301,14 +334,18 @@ def main():
use_hsdp=args.use_hsdp, use_hsdp=args.use_hsdp,
hsdp_shard_size=args.hsdp_shard_size, hsdp_shard_size=args.hsdp_shard_size,
hsdp_replicate_size=args.hsdp_replicate_size, hsdp_replicate_size=args.hsdp_replicate_size,
pipeline_parallel_size=args.pipeline_parallel_size,
) )
omni = Omni( omni_kwargs = dict(
model=args.model, model=args.model,
enable_layerwise_offload=args.enable_layerwise_offload, enable_layerwise_offload=args.enable_layerwise_offload,
vae_use_slicing=args.vae_use_slicing, vae_use_slicing=args.vae_use_slicing,
vae_use_tiling=args.vae_use_tiling, vae_use_tiling=args.vae_use_tiling,
boundary_ratio=args.boundary_ratio, boundary_ratio=args.boundary_ratio,
flow_shift=args.flow_shift, flow_shift=args.flow_shift,
diffusion_kv_cache_dtype=args.diffusion_kv_cache_dtype,
diffusion_kv_cache_skip_steps=args.diffusion_kv_cache_skip_steps,
diffusion_kv_cache_skip_layers=args.diffusion_kv_cache_skip_layers,
enable_cpu_offload=args.enable_cpu_offload, enable_cpu_offload=args.enable_cpu_offload,
parallel_config=parallel_config, parallel_config=parallel_config,
enforce_eager=args.enforce_eager, enforce_eager=args.enforce_eager,
@@ -318,6 +355,9 @@ def main():
enable_diffusion_pipeline_profiler=args.enable_diffusion_pipeline_profiler, enable_diffusion_pipeline_profiler=args.enable_diffusion_pipeline_profiler,
profiler_config=args.profiler_config, profiler_config=args.profiler_config,
) )
if args.quantization is not None:
omni_kwargs["quantization"] = args.quantization
omni = Omni(**omni_kwargs)
if profiler_enabled: if profiler_enabled:
print("[Profiler] Starting profiling...") print("[Profiler] Starting profiling...")
@@ -330,9 +370,13 @@ def main():
print(f" Inference steps: {args.num_inference_steps}") print(f" Inference steps: {args.num_inference_steps}")
print(f" Frames: {args.num_frames}") print(f" Frames: {args.num_frames}")
print(f" Solver: {args.sample_solver}") print(f" Solver: {args.sample_solver}")
print(f" diffusion_kv_cache_dtype(config): {args.diffusion_kv_cache_dtype}")
print(f" diffusion_kv_cache_skip_steps(config): {args.diffusion_kv_cache_skip_steps}")
print(f" diffusion_kv_cache_skip_layers(config): {args.diffusion_kv_cache_skip_layers}")
print( print(
f" Parallel configuration: cfg_parallel_size={args.cfg_parallel_size}," f" Parallel configuration: cfg_parallel_size={args.cfg_parallel_size},"
f" tensor_parallel_size={args.tensor_parallel_size}, vae_patch_parallel_size={args.vae_patch_parallel_size}" f" tensor_parallel_size={args.tensor_parallel_size}, vae_patch_parallel_size={args.vae_patch_parallel_size},"
f" pipeline_parallel_size={args.pipeline_parallel_size}"
) )
print(f" Video size: {args.width}x{args.height}") print(f" Video size: {args.width}x{args.height}")
print(f"{'=' * 60}\n") print(f"{'=' * 60}\n")
@@ -172,7 +172,7 @@ def run_pipeline_forward(
def _synchronize(device: str) -> None: def _synchronize(device: str) -> None:
if device.startswith("cuda") and torch.cuda.is_available(): if device.startswith("cuda") and torch.cuda.is_available():
torch.cuda.synchronize() torch.accelerator.synchronize()
def _latency_summary(values_ms: list[float]) -> dict[str, float]: def _latency_summary(values_ms: list[float]) -> dict[str, float]:
@@ -0,0 +1,208 @@
# SenseNova-U1-8B-MoT
## Architecture
SenseNova-U1 is a unified Qwen3-based LLM with Mixture-of-Tokenizers (MoT) attention. Unlike two-stage pipelines (e.g., BAGEL), it handles text encoding, optional reasoning ("think mode"), and flow-matching-based image denoising entirely within a single diffusion stage.
| Feature | Description |
| :------ | :---------- |
| **Model type** | Unified LLM (single-stage diffusion pipeline) |
| **Base LLM** | Qwen3 with MoT attention and 3D RoPE |
| **Image generation** | Flow-matching Euler sampler, no separate VAE |
| **Think mode** | Optional chain-of-thought reasoning before image generation |
| **Parallelism** | Tensor Parallelism (TP) with fused QKV and fused gate/up projections |
| **Modalities** | text2img, img2img, img2text (understanding), text2text |
## Quick Start
```bash
cd examples/offline_inference/sensenova_u1
# Text-to-image
python end2end.py --prompt "A cute cat" --think
# Image-to-image editing
python end2end.py --prompt "Turn this into an oil painting" \
--image input.png --think
# Image understanding (img2text)
python end2end.py --modality img2text \
--prompt "Describe this image in detail" \
--image photo.jpg
# Text-to-text (pure chat)
python end2end.py --modality text2text \
--prompt "What is the capital of France?"
# Custom resolution (image generation)
python end2end.py --prompt "A futuristic cityscape at sunset" \
--width 2048 --height 1024 --think
```
> **Note**: Default configuration works on a single **NVIDIA A100 (80GB)** or **H100** GPU.
## Text-to-Image (t2i)
Standard text-to-image with optional think mode:
```bash
python end2end.py \
--prompt "Close portrait of an elderly woman by a farmhouse window, textured skin, gentle smile, warm natural light" \
--width 1536 --height 2720 \
--think --print-think
```
## Image-to-Image Editing (img2img)
Pass one or more `--image` paths to trigger img2img mode. The model uses the
input image(s) as visual context for editing:
```bash
# Single input image
python end2end.py \
--prompt "Add a sunset sky in the background" \
--image photo.jpg \
--width 2048 --height 2048 \
--think
# Multiple reference images
python end2end.py \
--prompt "Combine the style of Image-1 with the content of Image-2" \
--image style_ref.png content_ref.png \
--width 2048 --height 2048 \
--think
```
### Dual CFG for img2img
img2img supports dual classifier-free guidance (text CFG + image CFG):
```bash
python end2end.py \
--prompt "Make the person smile" \
--image portrait.jpg \
--cfg-scale 4.0 \
--img-cfg-scale 2.0 \
--think
```
| `img_cfg_scale` | Behavior |
| :-------------- | :------- |
| `1.0` (default) | Text-only CFG: guidance = image-condition → full-condition |
| `== cfg_scale` | Standard CFG: guidance = unconditional → full-condition |
| Other value | Dual CFG: separate text and image guidance strengths |
## Full Parameter Reference
### General Parameters
| Parameter | Default | Description |
| :-------- | :------ | :---------- |
| `--modality` | `auto` | Task modality: `auto`, `text2img`, `img2img`, `img2text`, `text2text` |
| `--prompt` | "A cute cat..." | Text prompt / editing instruction / question |
| `--image` | None | Input image path(s) for img2img or img2text |
### Image Generation Parameters (text2img / img2img)
| Parameter | Default | Description |
| :-------- | :------ | :---------- |
| `--height` | 2048 | Height of generated image (pixels) |
| `--width` | 2048 | Width of generated image (pixels) |
| `--seed` | 42 | Random seed for reproducibility |
| `--num-steps` | 50 | Number of denoising steps |
| `--cfg-scale` | 4.0 | Text classifier-free guidance scale |
| `--img-cfg-scale` | 1.0 | Image CFG scale (img2img only, 1.0 = disabled) |
| `--cfg-norm` | "none" | CFG normalization: none, global, channel, cfg_zero_star (t2i only) |
| `--timestep-shift` | 3.0 | Timestep shift for flow-matching schedule |
| `--t-eps` | 0.02 | Epsilon for timestep schedule |
| `--think` | False | Enable think mode |
| `--print-think` | False | Print think text to stdout |
### Text Generation Parameters (text2text / img2text)
| Parameter | Default | Description |
| :-------- | :------ | :---------- |
| `--max-tokens` | 512 | Maximum number of tokens to generate |
| `--do-sample` | False | Use sampling instead of greedy decoding |
| `--temperature` | 0.7 | Sampling temperature (higher = more diverse) |
### Infrastructure Parameters
| Parameter | Default | Description |
| :-------- | :------ | :---------- |
| `--model` | `SenseNova/SenseNova-U1-8B-MoT` | HuggingFace model ID or local path |
| `--output` | `.` | Output directory for saved images |
| `--tensor-parallel-size` | 1 | Number of GPUs for tensor parallelism |
| `--enforce-eager` | False | Disable torch.compile |
| `--enable-cpu-offload` | False | Enable module-wise (sequential) CPU offload to reduce peak VRAM |
## Reducing GPU Memory Usage
For hardware with limited VRAM, enable **module-wise CPU offload** with
`--enable-cpu-offload`. The pipeline implements `SupportsModuleOffload`, so the
vision encoder (`vision_model`) and the Qwen3 LLM (`language_model`) are
swapped between CPU and GPU on demand:
- During text/vision encoding, the LLM is on CPU.
- During the diffusion loop, the vision encoder is on CPU.
- Lightweight FM modules stay resident on GPU.
This lowers peak VRAM at the cost of extra CPU<->GPU transfers (use pinned
memory) — useful for running SenseNova-U1 on consumer-grade GPUs.
```bash
# Text-to-image with CPU offload
python end2end.py \
--prompt "A cute cat sitting on a windowsill" \
--width 2048 --height 2048 \
--enable-cpu-offload --think
# Image-to-image editing with CPU offload
python end2end.py \
--prompt "Turn this into an oil painting" \
--image input.png \
--width 2048 --height 2048 \
--enable-cpu-offload --think
# Image understanding (img2text) with CPU offload
python end2end.py \
--modality img2text \
--prompt "Describe this image in detail" \
--image photo.jpg \
--enable-cpu-offload
```
> **Notes**
> - CPU offload is **single-GPU only** (incompatible with `--tensor-parallel-size > 1`).
> - First-step latency is higher because of the cold-start CPU<->GPU transfers.
> - For more details and other offloading strategies, see
> [CPU Offloading for Diffusion Models](../../../docs/user_guide/diffusion/cpu_offload_diffusion.md).
## Reproducing the E2E Test
The following command reproduces the pixel-validated CI test case:
```bash
python end2end.py \
--prompt "Close portrait of an elderly woman by a farmhouse window, textured skin, gentle smile, warm natural light, emotional documentary look. The portrait should feel polished and natural, with sharp eyes, realistic skin texture, accurate facial anatomy, and premium lighting that keeps the face as the main focus." \
--width 1536 --height 2720 \
--seed 42 --num-steps 50 \
--cfg-scale 4.0 --timestep-shift 3.0 --cfg-norm none \
--think --print-think \
--output outputs
```
The corresponding pytest:
```bash
pytest -s -v tests/e2e/offline_inference/test_sensenova_u1_text2img.py \
-m "advanced_model" --run-level "advanced_model"
```
The img2img counterpart lives at
`tests/e2e/offline_inference/test_sensenova_u1_img2img.py`.
## Online Serving
For OpenAI-compatible API serving, see
[`examples/online_serving/sensenova_u1/`](../../examples/online_serving/sensenova_u1/).
@@ -0,0 +1,281 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""
End-to-end offline inference for SenseNova-U1-8B-MoT.
Supports four modalities: text2img, img2img, img2text, text2text.
Text-to-image:
python end2end.py --prompt "A cute cat" --think
Image-to-image (editing):
python end2end.py --prompt "Turn this into an oil painting" \
--image input.png --think
Image understanding:
python end2end.py --modality img2text \
--prompt "Describe this image" --image photo.jpg
Text chat:
python end2end.py --modality text2text \
--prompt "What is the capital of France?"
See README.md for more examples.
"""
import argparse
import os
from PIL import Image
from vllm_omni.engine.arg_utils import nullify_stage_engine_defaults
from vllm_omni.entrypoints.omni import Omni
from vllm_omni.inputs.data import OmniDiffusionSamplingParams
def parse_args():
parser = argparse.ArgumentParser(
description="SenseNova-U1 text-to-image / image-to-image / text / understanding via vLLM-Omni.",
)
parser.add_argument(
"--model",
default="SenseNova/SenseNova-U1-8B-MoT",
help="HuggingFace model ID or local path.",
)
parser.add_argument(
"--modality",
default="auto",
choices=["auto", "text2img", "img2img", "img2text", "text2text"],
help="Task modality. 'auto' infers from --image (img2img/img2text if images, else t2i/t2t).",
)
parser.add_argument(
"--prompt",
default="A cute cat sitting on a windowsill, soft natural light",
help="Text prompt for generation or editing instruction.",
)
parser.add_argument(
"--image",
nargs="+",
metavar="PATH",
default=None,
help="Input image path(s) for img2img or img2text.",
)
parser.add_argument(
"--output",
type=str,
default=".",
help="Output directory to save generated images.",
)
# Image dimensions
parser.add_argument("--height", type=int, default=2048, help="Height of generated image.")
parser.add_argument("--width", type=int, default=2048, help="Width of generated image.")
# Generation parameters
parser.add_argument("--seed", type=int, default=42, help="Random seed for deterministic results.")
parser.add_argument("--num-steps", type=int, default=50, help="Number of denoising steps.")
parser.add_argument("--cfg-scale", type=float, default=4.0, help="Text classifier-free guidance scale.")
parser.add_argument(
"--img-cfg-scale",
type=float,
default=1.0,
help="Image CFG scale for img2img (1.0 = disabled). "
"When both --cfg-scale and --img-cfg-scale differ, dual CFG is used.",
)
parser.add_argument(
"--cfg-norm",
type=str,
default="none",
choices=["none", "global", "channel", "cfg_zero_star"],
help="CFG normalization mode. cfg_zero_star is only for t2i.",
)
parser.add_argument(
"--timestep-shift",
type=float,
default=3.0,
help="Timestep shift for flow-matching schedule.",
)
parser.add_argument(
"--t-eps",
type=float,
default=0.02,
help="Epsilon for flow-matching timestep schedule.",
)
# Text generation parameters (for text2text / img2text)
parser.add_argument(
"--max-tokens",
type=int,
default=2048,
help="Max tokens for text generation (text2text / img2text).",
)
parser.add_argument(
"--do-sample",
action="store_true",
help="Use sampling instead of greedy decoding for text generation.",
)
parser.add_argument(
"--temperature",
type=float,
default=0.7,
help="Sampling temperature for text generation.",
)
# Think mode
parser.add_argument(
"--think",
action="store_true",
help="Enable think mode: the model reasons about the prompt before generating the image.",
)
parser.add_argument(
"--print-think",
action="store_true",
help="Print the generated think text to stdout.",
)
# Advanced
parser.add_argument(
"--tensor-parallel-size",
type=int,
default=1,
help="Number of GPUs for tensor parallelism.",
)
parser.add_argument(
"--enforce-eager",
action="store_true",
help="Disable torch.compile and force eager execution.",
)
parser.add_argument(
"--enable-cpu-offload",
action="store_true",
help=(
"Enable model-level (sequential) CPU offloading. "
"Mutually exclusive between vision_model and language_model: "
"only one is on GPU at a time, reducing peak VRAM at the cost "
"of extra CPU<->GPU transfers."
),
)
nullify_stage_engine_defaults(parser)
return parser.parse_args()
def _resolve_modality(args):
"""Determine the effective modality from CLI args."""
if args.modality != "auto":
return args.modality
has_images = args.image is not None
# Default: image-producing tasks for backward compatibility
return "img2img" if has_images else "text2img"
def main():
args = parse_args()
os.makedirs(args.output, exist_ok=True)
modality = _resolve_modality(args)
is_text_output = modality in ("text2text", "img2text")
has_images = modality in ("img2img", "img2text")
omni = Omni(
model=args.model,
tensor_parallel_size=args.tensor_parallel_size,
enforce_eager=args.enforce_eager,
enable_cpu_offload=args.enable_cpu_offload,
)
extra_args = {
"cfg_scale": args.cfg_scale,
"cfg_norm": args.cfg_norm,
"timestep_shift": args.timestep_shift,
"cfg_interval": (0.0, 1.0),
"batch_size": 1,
"think": args.think,
"t_eps": args.t_eps,
}
if modality == "img2img":
extra_args["img_cfg_scale"] = args.img_cfg_scale
if is_text_output:
extra_args["max_tokens"] = args.max_tokens
extra_args["do_sample"] = args.do_sample
extra_args["temperature"] = args.temperature
sampling_params = OmniDiffusionSamplingParams(
height=args.height,
width=args.width,
seed=args.seed,
num_inference_steps=args.num_steps,
extra_args=extra_args,
)
print(f"\n{'=' * 60}")
print(f"SenseNova-U1 Configuration ({modality}):")
print(f" Model : {args.model}")
if not is_text_output:
print(f" Image size : {args.width}x{args.height}")
print(f" Steps : {args.num_steps}")
print(f" CFG scale : {args.cfg_scale}")
if modality == "img2img":
print(f" Img CFG scale : {args.img_cfg_scale}")
if has_images:
print(f" Input images : {args.image}")
if is_text_output:
print(f" Max tokens : {args.max_tokens}")
print(f" Temperature : {args.temperature}")
print(f" Seed : {args.seed}")
print(f" Think mode : {args.think}")
print(f" TP size : {args.tensor_parallel_size}")
print(f"{'=' * 60}\n")
# Build prompt dict
if has_images:
if not args.image:
raise ValueError(f"{modality} requires --image.")
input_images = [Image.open(p).convert("RGB") for p in args.image]
if is_text_output:
prompt_dict = {
"prompt": args.prompt,
"multi_modal_data": {"image": input_images},
"modalities": ["text"],
}
else:
prompt_dict = {
"prompt": args.prompt,
"multi_modal_data": {"image": input_images},
"modalities": ["img2img"],
}
elif is_text_output:
prompt_dict = {"prompt": args.prompt, "modalities": ["text"]}
else:
prompt_dict = {"prompt": args.prompt, "modalities": ["image"]}
outputs = list(
omni.generate(
prompts=prompt_dict,
sampling_params_list=sampling_params,
)
)
for req_output in outputs:
custom = getattr(req_output, "_custom_output", {}) or {}
if args.print_think and custom.get("think_text"):
print(f"[Think]\n{custom['think_text']}\n")
if is_text_output:
text = custom.get("text_output", "")
if not text:
text = getattr(req_output, "text", "") or ""
print(f"[Response]\n{text}")
else:
images = getattr(req_output, "images", None) or []
if not images:
print("[Warning] No images generated.")
continue
for j, img in enumerate(images):
save_path = os.path.join(args.output, f"sensenova_u1_output_{j}.png")
img.save(save_path)
print(f"[Output] Saved {img.size[0]}x{img.size[1]} image to {save_path}")
if __name__ == "__main__":
main()
@@ -73,13 +73,15 @@ python examples/offline_inference/text_to_speech/cosyvoice3/end2end.py \
``` ```
### Voice cloning ### Voice cloning
Pass a reference audio. Note that CosyVoice3's `--prompt-text` is a system-style prompt for the GPT stage, not a reference transcript: If `--ref-audio` is omitted, the script downloads the upstream
[`zero_shot_prompt.wav`](https://github.com/FunAudioLLM/CosyVoice/blob/main/asset/zero_shot_prompt.wav) from the CosyVoice repo into the current directory.
To use your own clip, pass `--ref-audio /path/to/reference.wav`, and modify `--prompt-text` correspondingly.
```bash ```bash
python examples/offline_inference/text_to_speech/cosyvoice3/end2end.py \ python examples/offline_inference/text_to_speech/cosyvoice3/end2end.py \
--model pretrained_models/Fun-CosyVoice3-0.5B \ --model pretrained_models/Fun-CosyVoice3-0.5B \
--tokenizer pretrained_models/Fun-CosyVoice3-0.5B/CosyVoice-BlankEN \ --tokenizer pretrained_models/Fun-CosyVoice3-0.5B/CosyVoice-BlankEN \
--ref-audio prompt.wav \ --ref-audio /path/to/reference.wav \
--prompt-text "You are a helpful assistant.<|endofprompt|>Testing my voices. Why should I not?" --prompt-text "You are a helpful assistant.<|endofprompt|>Trascript in your ref audio clip"
``` ```
### Streaming ### Streaming
@@ -317,44 +319,39 @@ pip install voxcpm soundfile
export VLLM_OMNI_VOXCPM_CODE_PATH=/path/to/VoxCPM/src export VLLM_OMNI_VOXCPM_CODE_PATH=/path/to/VoxCPM/src
``` ```
If the native VoxCPM `config.json` does not contain HF metadata such as `model_type`, prepare a persistent HF-compatible config directory and point the stage configs to it via `VLLM_OMNI_VOXCPM_HF_CONFIG_PATH`:
```bash
export VOXCPM_MODEL=/path/to/voxcpm-model
export VLLM_OMNI_VOXCPM_HF_CONFIG_PATH=/tmp/voxcpm_hf_config
mkdir -p "$VLLM_OMNI_VOXCPM_HF_CONFIG_PATH"
cp "$VOXCPM_MODEL/config.json" "$VLLM_OMNI_VOXCPM_HF_CONFIG_PATH/config.json"
cp "$VOXCPM_MODEL/generation_config.json" "$VLLM_OMNI_VOXCPM_HF_CONFIG_PATH/generation_config.json" 2>/dev/null || true
python3 -c 'import json, os; p=os.path.join(os.environ["VLLM_OMNI_VOXCPM_HF_CONFIG_PATH"], "config.json"); cfg=json.load(open(p, "r", encoding="utf-8")); cfg["model_type"]="voxcpm"; cfg.setdefault("architectures", ["VoxCPMForConditionalGeneration"]); json.dump(cfg, open(p, "w", encoding="utf-8"), indent=2, ensure_ascii=False)'
```
### Quick start ### Quick start
```bash ```bash
python examples/offline_inference/text_to_speech/voxcpm/end2end.py \ python examples/offline_inference/text_to_speech/voxcpm/end2end.py \
--model "$VOXCPM_MODEL" \ --model openbmb/VoxCPM-0.5B \
--text "This is a split-stage VoxCPM synthesis example running on vLLM Omni." --text "This is a split-stage VoxCPM synthesis example running on vLLM Omni."
``` ```
### Voice cloning ### Voice cloning
```bash ```bash
python examples/offline_inference/text_to_speech/voxcpm/end2end.py \ python examples/offline_inference/text_to_speech/voxcpm/end2end.py \
--model "$VOXCPM_MODEL" \ --model openbmb/VoxCPM-0.5B \
--text "This sentence is synthesized with a cloned voice." \ --text "This sentence is synthesized with a cloned voice." \
--ref-audio /path/to/reference.wav \ --ref-audio /path/to/reference.wav \
--ref-text "The exact transcript spoken in reference.wav." --ref-text "The exact transcript spoken in reference.wav."
``` ```
### Streaming ### Streaming
Pass the async-chunk stage config: Pass `--streaming` together with the legacy async-chunk stage config:
```bash ```bash
python examples/offline_inference/text_to_speech/voxcpm/end2end.py \ python examples/offline_inference/text_to_speech/voxcpm/end2end.py \
--model "$VOXCPM_MODEL" \ --model openbmb/VoxCPM-0.5B \
--streaming \
--stage-configs-path vllm_omni/model_executor/stage_configs/voxcpm_async_chunk.yaml \ --stage-configs-path vllm_omni/model_executor/stage_configs/voxcpm_async_chunk.yaml \
--text "This is a split-stage VoxCPM streaming example running on vLLM Omni." --text "This is a split-stage VoxCPM streaming example running on vLLM Omni."
``` ```
### Persistent HF config (optional)
The engine auto-patches a missing `model_type` in the checkpoint's `config.json` in-memory at startup (see `vllm_omni/engine/arg_utils.py`), so no manual setup is required.
If you'd rather maintain a persistent patched config dir (e.g. to share across runs or to add fields the auto-patcher doesn't set), export `VLLM_OMNI_VOXCPM_HF_CONFIG_PATH` to its location — both the bundled deploy and async-chunk yamls read it via `${oc.env:VLLM_OMNI_VOXCPM_HF_CONFIG_PATH,}`.
### Notes ### Notes
- `voxcpm.yaml` is the default non-streaming stage config; `voxcpm_async_chunk.yaml` enables streaming. - Non-streaming auto-loads `vllm_omni/deploy/voxcpm.yaml` from the model directory name / HF `model_type`. Streaming uses the legacy `voxcpm_async_chunk.yaml` passed via `--stage-configs-path`.
- Streaming is currently single-request oriented. - Streaming is currently single-request oriented.
- `--ref-text` must be the real transcript of `--ref-audio`; mismatched text degrades quality. - `--ref-text` must be the real transcript of `--ref-audio`; mismatched text degrades quality.
- For online serving, see the [VoxCPM section in the online hub](../../online_serving/text_to_speech/README.md#voxcpm). For benchmark reporting, see [`benchmarks/voxcpm`](../../../benchmarks/voxcpm/README.md). - For online serving, see the [VoxCPM section in the online hub](../../online_serving/text_to_speech/README.md#voxcpm). For benchmark reporting, see [`benchmarks/voxcpm`](../../../benchmarks/voxcpm/README.md).
@@ -2,18 +2,32 @@
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import argparse import argparse
import os import os
import urllib.request
from pathlib import Path
import numpy as np import numpy as np
import soundfile as sf import soundfile as sf
from vllm import SamplingParams from vllm import SamplingParams
from vllm.assets.audio import AudioAsset
from vllm.multimodal.media.audio import load_audio from vllm.multimodal.media.audio import load_audio
from vllm_omni.engine.arg_utils import nullify_stage_engine_defaults from vllm_omni.engine.arg_utils import nullify_stage_engine_defaults
from vllm_omni.entrypoints.omni import Omni from vllm_omni.entrypoints.omni import Omni
from vllm_omni.model_executor.models.cosyvoice3.config import CosyVoice3Config
from vllm_omni.model_executor.models.cosyvoice3.tokenizer import get_qwen_tokenizer from vllm_omni.model_executor.models.cosyvoice3.tokenizer import get_qwen_tokenizer
from vllm_omni.model_executor.models.cosyvoice3.utils import extract_text_token from vllm_omni.model_executor.models.cosyvoice3.utils import extract_text_token
from vllm_omni.transformers_utils.configs.cosyvoice3 import CosyVoice3Config
# Upstream zero-shot reference clip
ZERO_SHOT_PROMPT_URL = "https://raw.githubusercontent.com/FunAudioLLM/CosyVoice/main/asset/zero_shot_prompt.wav"
def _default_ref_audio() -> str:
# Download the upstream zero_shot_prompt.wav into the current dir
dest = Path("zero_shot_prompt.wav")
if not dest.exists() or dest.stat().st_size == 0:
print(f"Downloading default reference audio to {dest}")
urllib.request.urlretrieve(ZERO_SHOT_PROMPT_URL, dest)
return str(dest)
def run_e2e(): def run_e2e():
@@ -36,9 +50,15 @@ def run_e2e():
parser.add_argument( parser.add_argument(
"--prompt-text", "--prompt-text",
type=str, type=str,
default="You are a helpful assistant.<|endofprompt|>Testing my voices. Why should I not?", default="You are a helpful assistant.<|endofprompt|>希望你以后,能够做的比我还好呦!",
)
parser.add_argument(
"--ref-audio",
type=str,
default=None,
help="Path to reference audio for voice cloning. "
"If unset, downloads the upstream CosyVoice3 zero-shot prompt audio clip",
) )
parser.add_argument("--ref-audio", type=str, default="prompt.wav")
parser.add_argument( parser.add_argument(
"--tokenizer", "--tokenizer",
type=str, type=str,
@@ -66,24 +86,22 @@ def run_e2e():
sampling_cfg = {"top_p": 0.8, "top_k": 25, "eos_token_id": 6561 + 1} sampling_cfg = {"top_p": 0.8, "top_k": 25, "eos_token_id": 6561 + 1}
print("Model initialized. Preparing inputs...") print("Model initialized. Preparing inputs...")
if args.ref_audio: ref_audio_path = args.ref_audio or _default_ref_audio()
if not os.path.exists(args.ref_audio): if not os.path.exists(ref_audio_path):
raise FileNotFoundError(f"Audio file not found: {args.ref_audio}") raise FileNotFoundError(f"Audio file not found: {ref_audio_path}")
# Load at native sample rate # Load at native sample rate
audio_signal, sr = load_audio(args.ref_audio, sr=None) audio_signal, sr = load_audio(ref_audio_path, sr=None)
# Validate sample rate before processing (similar to original CosyVoice) # Validate sample rate before processing (similar to original CosyVoice)
min_sr = 16000 min_sr = 16000
if sr < min_sr: if sr < min_sr:
raise ValueError( raise ValueError(
f"Audio sample rate {sr} Hz is too low. " f"Audio sample rate {sr} Hz is too low. "
f"Minimum required: {min_sr} Hz. " f"Minimum required: {min_sr} Hz. "
f"Please provide audio with sample rate >= {min_sr} Hz." f"Please provide audio with sample rate >= {min_sr} Hz."
) )
audio_data = (audio_signal.astype(np.float32), sr) audio_data = (audio_signal.astype(np.float32), sr)
else:
audio_data = AudioAsset("mary_had_lamb").audio_and_sample_rate
prompts = { prompts = {
"prompt": args.text, "prompt": args.text,
@@ -37,7 +37,7 @@ def run_e2e():
parser.add_argument( parser.add_argument(
"--stage-config", "--stage-config",
type=str, type=str,
default="vllm_omni/model_executor/stage_configs/omnivoice.yaml", default="vllm_omni/deploy/omnivoice.yaml",
) )
parser.add_argument( parser.add_argument(
"--text", "--text",
@@ -14,9 +14,6 @@ from vllm.utils.argparse_utils import FlexibleArgumentParser
from vllm_omni import AsyncOmni, Omni from vllm_omni import AsyncOmni, Omni
from vllm_omni.engine.arg_utils import nullify_stage_engine_defaults from vllm_omni.engine.arg_utils import nullify_stage_engine_defaults
REPO_ROOT = Path(__file__).resolve().parents[4]
DEFAULT_SYNC_STAGE_CONFIG = REPO_ROOT / "vllm_omni" / "model_executor" / "stage_configs" / "voxcpm.yaml"
def _build_prompt(args) -> dict[str, Any]: def _build_prompt(args) -> dict[str, Any]:
additional_information: dict[str, list[Any]] = { additional_information: dict[str, list[Any]] = {
@@ -59,10 +56,6 @@ def _extract_sample_rate(mm: dict[str, Any]) -> int:
return int(sr_raw) return int(sr_raw)
def _is_streaming_stage_config(stage_config_path: str) -> bool:
return "async_chunk" in Path(stage_config_path).stem
def _save_audio(audio: torch.Tensor, sample_rate: int, output_dir: Path, request_id: str) -> Path: def _save_audio(audio: torch.Tensor, sample_rate: int, output_dir: Path, request_id: str) -> Path:
output_dir.mkdir(parents=True, exist_ok=True) output_dir.mkdir(parents=True, exist_ok=True)
output_path = output_dir / f"output_{request_id}.wav" output_path = output_dir / f"output_{request_id}.wav"
@@ -87,6 +80,7 @@ async def _run_streaming(args) -> Path:
omni = AsyncOmni( omni = AsyncOmni(
model=args.model, model=args.model,
stage_configs_path=args.stage_configs_path, stage_configs_path=args.stage_configs_path,
deploy_config=args.deploy_config,
log_stats=args.log_stats, log_stats=args.log_stats,
stage_init_timeout=args.stage_init_timeout, stage_init_timeout=args.stage_init_timeout,
) )
@@ -133,6 +127,7 @@ def _run_sync(args) -> Path:
omni = Omni( omni = Omni(
model=args.model, model=args.model,
stage_configs_path=args.stage_configs_path, stage_configs_path=args.stage_configs_path,
deploy_config=args.deploy_config,
log_stats=args.log_stats, log_stats=args.log_stats,
stage_init_timeout=args.stage_init_timeout, stage_init_timeout=args.stage_init_timeout,
) )
@@ -164,11 +159,27 @@ def _run_sync(args) -> Path:
def parse_args(): def parse_args():
parser = FlexibleArgumentParser(description="Minimal offline VoxCPM example for vLLM Omni.") parser = FlexibleArgumentParser(description="Minimal offline VoxCPM example for vLLM Omni.")
parser.add_argument("--model", type=str, required=True, help="Local VoxCPM model directory.") parser.add_argument("--model", type=str, required=True, help="Local VoxCPM model directory.")
parser.add_argument(
"--deploy-config",
type=str,
default=None,
help=("Override the deploy config path."),
)
parser.add_argument( parser.add_argument(
"--stage-configs-path", "--stage-configs-path",
type=str, type=str,
default=str(DEFAULT_SYNC_STAGE_CONFIG), default=None,
help=("Stage config path. Use voxcpm.yaml for non-streaming or voxcpm_async_chunk.yaml for streaming."), help=(
"Legacy stage_args yaml path. Required for streaming "
"(vllm_omni/model_executor/stage_configs/voxcpm_async_chunk.yaml); "
"leave unset for non-streaming to use the auto-resolved deploy config."
),
)
parser.add_argument(
"--streaming",
action="store_true",
default=False,
help="Stream audio chunks as they arrive via AsyncOmni.",
) )
parser.add_argument("--text", type=str, required=True, help="Input text for synthesis.") parser.add_argument("--text", type=str, required=True, help="Input text for synthesis.")
parser.add_argument("--ref-audio", type=str, default=None, help="Reference audio path for voice cloning.") parser.add_argument("--ref-audio", type=str, default=None, help="Reference audio path for voice cloning.")
@@ -194,9 +205,10 @@ def parse_args():
def main(args) -> None: def main(args) -> None:
route = "streaming" if _is_streaming_stage_config(args.stage_configs_path) else "sync" route = "streaming" if args.streaming else "sync"
print(f"Model: {args.model}") print(f"Model: {args.model}")
print(f"Stage config: {args.stage_configs_path}") print(f"Deploy config: {args.deploy_config or '<auto from HF model_type>'}")
print(f"Stage configs path: {args.stage_configs_path or '<unused>'}")
print(f"Route: {route}") print(f"Route: {route}")
if route == "streaming": if route == "streaming":
asyncio.run(_run_streaming(args)) asyncio.run(_run_streaming(args))
@@ -18,7 +18,6 @@ from vllm_omni import Omni
from vllm_omni.engine.arg_utils import nullify_stage_engine_defaults from vllm_omni.engine.arg_utils import nullify_stage_engine_defaults
REPO_ROOT = Path(__file__).resolve().parents[4] REPO_ROOT = Path(__file__).resolve().parents[4]
DEFAULT_STAGE_CONFIGS_PATH = str(REPO_ROOT / "vllm_omni" / "model_executor" / "stage_configs" / "voxcpm2.yaml")
SAMPLE_RATE = 48_000 SAMPLE_RATE = 48_000
@@ -43,10 +42,11 @@ def parse_args():
help="Directory for output WAV files.", help="Directory for output WAV files.",
) )
parser.add_argument( parser.add_argument(
"--stage-configs-path", "--deploy-config",
type=str, type=str,
default=DEFAULT_STAGE_CONFIGS_PATH, default=None,
help="Path to the stage config YAML file.", help="Override the deploy config path. If unset, auto-loads "
"vllm_omni/deploy/voxcpm2.yaml based on the HF model_type.",
) )
parser.add_argument( parser.add_argument(
"--ref-audio", "--ref-audio",
@@ -96,7 +96,7 @@ def main():
engine = Omni( engine = Omni(
model=args.model, model=args.model,
stage_configs_path=args.stage_configs_path, deploy_config=args.deploy_config,
) )
from transformers import AutoTokenizer from transformers import AutoTokenizer
@@ -42,6 +42,7 @@ async def run_streaming(inputs, sampling_params_list, model_name, args, output_d
model=model_name, model=model_name,
deploy_config=args.deploy_config, deploy_config=args.deploy_config,
log_stats=args.log_stats, log_stats=args.log_stats,
quantization=args.quantization,
) )
# Normalize to a list so batch and single-input share the same code path # Normalize to a list so batch and single-input share the same code path
@@ -184,7 +185,7 @@ async def run_streaming(inputs, sampling_params_list, model_name, args, output_d
) )
async_omni.shutdown() async_omni.shutdown()
torch.cuda.empty_cache() torch.accelerator.empty_cache()
gc.collect() gc.collect()
@@ -194,6 +195,7 @@ def run_non_streaming(inputs, sampling_params_list, model_name, args, output_dir
model=model_name, model=model_name,
log_stats=args.log_stats, log_stats=args.log_stats,
deploy_config=args.deploy_config, deploy_config=args.deploy_config,
quantization=args.quantization,
) )
if args.profiling_mode: if args.profiling_mode:
@@ -223,7 +225,7 @@ def run_non_streaming(inputs, sampling_params_list, model_name, args, output_dir
print(f"RTF: {output_audio_dur / vllm_elapsed:.4f}") print(f"RTF: {output_audio_dur / vllm_elapsed:.4f}")
del llm del llm
torch.cuda.empty_cache() torch.accelerator.empty_cache()
gc.collect() gc.collect()
@@ -305,6 +307,12 @@ def parse_args() -> Namespace:
default=None, default=None,
help="CFG alpha for flow-matching guidance (default: use value from stage config, typically 1.2).", help="CFG alpha for flow-matching guidance (default: use value from stage config, typically 1.2).",
) )
parser.add_argument(
"--quantization",
type=str,
default=None,
help="Quantization method (e.g. 'fp8'). Applied to the language model only.",
)
nullify_stage_engine_defaults(parser) nullify_stage_engine_defaults(parser)
return parser.parse_args() return parser.parse_args()
@@ -379,7 +387,7 @@ def main(args: Any) -> None:
f"--num-prompts ({args.num_prompts}) must be divisible by --concurrency ({args.concurrency})" f"--num-prompts ({args.num_prompts}) must be divisible by --concurrency ({args.concurrency})"
) )
torch.cuda.empty_cache() torch.accelerator.empty_cache()
gc.collect() gc.collect()
if args.streaming: if args.streaming:
@@ -142,6 +142,32 @@ def parse_args() -> argparse.Namespace:
action="store_true", action="store_true",
help="Enable layerwise (blockwise) offloading on DiT modules.", help="Enable layerwise (blockwise) offloading on DiT modules.",
) )
parser.add_argument(
"--audio-sample-rate",
type=int,
default=24000,
help="Sample rate for audio output when saved (default: 24000).",
)
parser.add_argument(
"--enable-diffusion-pipeline-profiler",
action="store_true",
help="Enable diffusion pipeline profiler to display stage durations.",
)
parser.add_argument(
"--profiler-config",
type=parse_profiler_config,
default=None,
help='JSON profiler config for torch/cuda profiling, e.g. \'{"profiler":"torch","torch_profiler_dir":"./perf"}\'.',
)
parser.add_argument(
"--quantization",
type=str,
default=None,
choices=["fp8", "mxfp8", "int8", "gguf"],
help="Quantization method for the transformer. mxfp8: W8A8 MXFP8 online quant (NPU). fp8: online FP8 (GPU).",
)
# Distributed and parallel execution
parser.add_argument( parser.add_argument(
"--ulysses-mode", "--ulysses-mode",
type=str, type=str,
@@ -174,41 +200,23 @@ def parse_args() -> argparse.Namespace:
default=1, default=1,
help="Number of GPUs used for tensor parallelism (TP) inside the DiT.", help="Number of GPUs used for tensor parallelism (TP) inside the DiT.",
) )
parser.add_argument(
"--audio-sample-rate",
type=int,
default=24000,
help="Sample rate for audio output when saved (default: 24000).",
)
parser.add_argument( parser.add_argument(
"--vae-patch-parallel-size", "--vae-patch-parallel-size",
type=int, type=int,
default=1, default=1,
help="Number of GPUs used for VAE patch/tile parallelism (decode).", help="Number of GPUs used for VAE patch/tile parallelism (decode).",
) )
parser.add_argument(
"--pipeline-parallel-size",
type=int,
default=1,
help="Number of pipeline parallel stages.",
)
parser.add_argument( parser.add_argument(
"--enable-expert-parallel", "--enable-expert-parallel",
action="store_true", action="store_true",
help="Enable expert parallelism for MoE layers.", help="Enable expert parallelism for MoE layers.",
) )
parser.add_argument(
"--enable-diffusion-pipeline-profiler",
action="store_true",
help="Enable diffusion pipeline profiler to display stage durations.",
)
parser.add_argument(
"--profiler-config",
type=parse_profiler_config,
default=None,
help='JSON profiler config for torch/cuda profiling, e.g. \'{"profiler":"torch","torch_profiler_dir":"./perf"}\'.',
)
parser.add_argument(
"--quantization",
type=str,
default=None,
choices=["fp8", "gguf"],
help="Quantization method for the transformer (fp8 for online FP8 quantization).",
)
parser.add_argument( parser.add_argument(
"--use-hsdp", "--use-hsdp",
action="store_true", action="store_true",
@@ -262,6 +270,7 @@ def main():
cfg_parallel_size=args.cfg_parallel_size, cfg_parallel_size=args.cfg_parallel_size,
tensor_parallel_size=args.tensor_parallel_size, tensor_parallel_size=args.tensor_parallel_size,
vae_patch_parallel_size=args.vae_patch_parallel_size, vae_patch_parallel_size=args.vae_patch_parallel_size,
pipeline_parallel_size=args.pipeline_parallel_size,
enable_expert_parallel=args.enable_expert_parallel, enable_expert_parallel=args.enable_expert_parallel,
) )
@@ -307,7 +316,8 @@ def main():
print( print(
f" Parallel configuration: ulysses_degree={args.ulysses_degree}, ring_degree={args.ring_degree}," f" Parallel configuration: ulysses_degree={args.ulysses_degree}, ring_degree={args.ring_degree},"
f" cfg_parallel_size={args.cfg_parallel_size}, tensor_parallel_size={args.tensor_parallel_size}," f" cfg_parallel_size={args.cfg_parallel_size}, tensor_parallel_size={args.tensor_parallel_size},"
f" vae_patch_parallel_size={args.vae_patch_parallel_size}, enable_expert_parallel={args.enable_expert_parallel}" f" vae_patch_parallel_size={args.vae_patch_parallel_size}, pipeline_parallel_size={args.pipeline_parallel_size},"
f" enable_expert_parallel={args.enable_expert_parallel}"
) )
print(f" Video size: {args.width}x{args.height}") print(f" Video size: {args.width}x{args.height}")
print(f"{'=' * 60}\n") print(f"{'=' * 60}\n")
+19
View File
@@ -68,6 +68,25 @@ vllm serve ByteDance-Seed/BAGEL-7B-MoT --omni --port 8091 --tensor-parallel-size
Or set `tensor_parallel_size` per stage in a custom deploy YAML. Or set `tensor_parallel_size` per stage in a custom deploy YAML.
#### Hybrid Sharded Data Parallel (HSDP)
For larger Bagel deployments on multiple GPUs, you can enable HSDP (Hybrid Sharded Data Parallel) by modifying the stage configuration (for example, [`bagel.yaml`](../../../vllm_omni/deploy/bagel.yaml)). HSDP shards transformer weights across GPUs to reduce per-GPU memory usage.
1. **Enable HSDP**: Set `use_hsdp: true`.
2. **Set shard size**: Set `hsdp_shard_size` to the number of GPUs used for sharding (for example, `4`).
3. **Set replicate size**: Usually keep `hsdp_replicate_size: 1` unless you want replicated HSDP groups.
4. **Set devices**: Specify the comma-separated GPU IDs used by the diffusion stage (for example, `"0,1,2,3"`).
Example configuration for HSDP across 4 GPUs:
```yaml
- stage_id: 1
devices: "0,1,2,3"
parallel_config:
use_hsdp: true
hsdp_shard_size: 4
hsdp_replicate_size: 1
```
### Multi-Node Deployment ### Multi-Node Deployment
Deploy each stage on a **separate node** for better resource utilization. Replace `<ORCHESTRATOR_IP>` with the actual IP address of your orchestrator node. Deploy each stage on a **separate node** for better resource utilization. Replace `<ORCHESTRATOR_IP>` with the actual IP address of your orchestrator node.
@@ -0,0 +1,93 @@
# Covo-Audio-Chat
## Installation
Please refer to [README.md](../../../README.md)
> **Note**
> Covo-Audio code2wav requires `torchdiffeq`. Install it with: `pip install torchdiffeq`
## Run examples (Covo-Audio-Chat)
### Launch the Server
```bash
CUDA_VISIBLE_DEVICES=0 vllm serve tencent/Covo-Audio-Chat --omni \
--trust-remote-code --port 18091
```
> The default deploy config is located at `vllm_omni/deploy/covo_audio.yaml` and is loaded automatically by the model registry — no `--deploy-config` flag needed for default use.
### Send Request
Get into the example folder:
```bash
cd examples/online_serving/covo_audio
```
#### Audio input chat
Using the default audio asset:
```bash
python openai_chat_completion_client.py
```
Using a custom audio file:
```bash
python openai_chat_completion_client.py --audio-path /path/to/audio.wav
```
#### Streaming mode
```bash
python openai_chat_completion_client.py --audio-path /path/to/audio.wav --stream
```
### Command-line Arguments
The Python client supports the following arguments:
- `--prompt` (or `-p`): Text prompt (default: `"请回答这段音频里的问题。"`)
- `--audio-path`: Path to audio file. Supports local file paths or HTTP/HTTPS URLs. Common audio formats: WAV, MP3, OGG, FLAC, M4A. Uses the default audio asset if omitted
- `--output-audio-path` (or `-o`): Output path for generated audio (default: `./audio_0.wav`)
- `--model`: Model name for the API. Auto-detected from server if omitted
- `--stream`: Enable streaming mode
## Pipeline
Covo-Audio-Chat uses a 2-stage pipeline:
- **Stage 0 (fused_thinker_talker):** The 7B LLM generates interleaved text and audio tokens in a single autoregressive pass.
- **Stage 1 (code2wav):** A BigVGAN-based vocoder converts the extracted audio codes into a 24kHz WAV waveform.
## Environment
- GPU: 1x H100 (80 GiB)
- Stage 0 (7B LLM): ~16 GiB VRAM
- Stage 1 (BigVGAN vocoder): ~2 GiB VRAM
## Important: System Prompt
Covo-Audio-Chat requires a specific system prompt to enable interleaved text+audio generation. Without it, Stage 0 produces text-only tokens, and the output audio will be **silent**.
The system prompt must include the instruction:
```
请用文本和音频进行对话,交替生成5个文本token和15个音频token,音频部分使用发音人:default_female
```
The full system prompt is defined in `vllm_omni/model_executor/models/covo_audio/prompt_utils.py` (`COVO_AUDIO_SYSTEM_PROMPT`).
## FAQ
If you encounter `ModuleNotFoundError: No module named 'librosa'`, install it with:
```bash
pip install librosa
```
### Audio output is silent (0.1s, all zeros)
This means Stage 0 did not generate any audio tokens. Most likely causes:
1. **Missing system prompt** -- see the "Important: System Prompt" section above. The model needs the interleaved generation instruction in the system message.
2. **Missing `ignore_eos: true` / `stop_token_ids: [151645]`** in Stage 0 sampling params -- without these, the model stops at `<|endoftext|>` before generating audio tokens.
@@ -0,0 +1,216 @@
"""
Client example for Covo-Audio-Chat with vllm-omni.
Usage
-----
# Start the server first:
# CUDA_VISIBLE_DEVICES=0 vllm serve tencent/Covo-Audio-Chat --omni --trust-remote-code
# Audio input chat (uses default audio asset if --audio-path not provided):
python openai_chat_completion_client.py
python openai_chat_completion_client.py --audio-path /path/to/audio.wav
# Streaming mode:
python openai_chat_completion_client.py --stream
"""
import base64
import os
import sys
import time
from openai import OpenAI
from vllm.assets.audio import AudioAsset
from vllm.utils.argparse_utils import FlexibleArgumentParser
from vllm_omni.model_executor.models.covo_audio.prompt_utils import (
COVO_AUDIO_SYSTEM_PROMPT,
)
openai_api_key = "EMPTY"
openai_api_base = "http://localhost:18091/v1"
client = OpenAI(api_key=openai_api_key, base_url=openai_api_base)
SEED = 42
def encode_base64_content_from_file(file_path: str) -> str:
"""Encode a local file to base64 format."""
with open(file_path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
def get_audio_url(audio_path: str | None) -> str:
"""Convert an audio path to a data URL for the API."""
if not audio_path:
return AudioAsset("mary_had_lamb").url
if audio_path.startswith(("data:", "http://", "https://")):
return audio_path
if not os.path.exists(audio_path):
raise FileNotFoundError(f"Audio file not found: {audio_path}")
ext = audio_path.rsplit(".", 1)[-1].lower() if "." in audio_path else "wav"
mime_map = {
"wav": "audio/wav",
"mp3": "audio/mpeg",
"mpeg": "audio/mpeg",
"ogg": "audio/ogg",
"flac": "audio/flac",
"m4a": "audio/mp4",
}
mime = mime_map.get(ext, "audio/wav")
b64 = encode_base64_content_from_file(audio_path)
return f"data:{mime};base64,{b64}"
def build_audio_messages(audio_path: str | None, prompt: str) -> list[dict]:
"""Build messages for an audio chat request."""
audio_url = get_audio_url(audio_path)
return [
{"role": "system", "content": COVO_AUDIO_SYSTEM_PROMPT},
{
"role": "user",
"content": [
{"type": "audio_url", "audio_url": {"url": audio_url}},
{"type": "text", "text": prompt},
],
},
]
def get_model_name(override: str | None = None) -> str:
"""Return the model name to use in API calls."""
if override:
return override
# Auto-detect from the server
models = client.models.list()
return models.data[0].id
def run(args) -> None:
model_name = get_model_name(args.model)
thinker_params = {
"temperature": 0.0,
"top_p": 1.0,
"top_k": -1,
"max_tokens": 2048,
"seed": SEED,
"detokenize": True,
"repetition_penalty": 1.05,
"stop_token_ids": [151645],
"ignore_eos": True,
}
code2wav_params = {
"temperature": 0.0,
"top_p": 1.0,
"top_k": -1,
"max_tokens": 2048,
"seed": SEED,
"detokenize": False,
"repetition_penalty": 1.05,
}
extra_body = {
"sampling_params_list": [thinker_params, code2wav_params],
}
messages = build_audio_messages(args.audio_path, args.prompt)
start = time.perf_counter()
if not args.stream:
completion = client.chat.completions.create(
messages=messages,
model=model_name,
extra_body=extra_body,
stream=False,
)
elapsed = time.perf_counter() - start
for choice in completion.choices:
if choice.message.audio:
audio_bytes = base64.b64decode(choice.message.audio.data)
out_path = args.output_audio_path
os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
with open(out_path, "wb") as f:
f.write(audio_bytes)
print(f"Audio saved to {out_path} ({len(audio_bytes)} bytes)")
elif choice.message.content:
print("Chat completion output from text:", choice.message.content)
print(f"Elapsed: {elapsed:.2f}s", file=sys.stderr)
else:
stream = client.chat.completions.create(
messages=messages,
model=model_name,
extra_body=extra_body,
stream=True,
)
audio_idx = 0
for chunk in stream:
modality = getattr(chunk, "modality", None)
elapsed = time.perf_counter() - start
for choice in chunk.choices:
content = getattr(choice.delta, "content", None) if hasattr(choice, "delta") else None
if not content:
continue
if modality == "audio":
audio_bytes = base64.b64decode(content)
out_dir = os.path.dirname(args.output_audio_path) or "."
out_path = os.path.join(out_dir, f"audio_{audio_idx}.wav")
os.makedirs(out_dir, exist_ok=True)
with open(out_path, "wb") as f:
f.write(audio_bytes)
print(f"[{elapsed:.2f}s] Audio chunk saved to {out_path}", file=sys.stderr)
audio_idx += 1
elif modality == "text":
print(content, end="", flush=True)
print() # trailing newline
elapsed = time.perf_counter() - start
print(f"Elapsed: {elapsed:.2f}s", file=sys.stderr)
def parse_args():
parser = FlexibleArgumentParser(description="Covo-Audio-Chat client example")
parser.add_argument(
"--prompt",
"-p",
type=str,
default="请回答这段音频里的问题。",
help="Text prompt.",
)
parser.add_argument(
"--audio-path",
type=str,
default=None,
help="Path to audio file. Uses default audio asset if omitted.",
)
parser.add_argument(
"--output-audio-path",
"-o",
type=str,
default="./audio_0.wav",
help="Output path for generated audio.",
)
parser.add_argument(
"--model",
type=str,
default=None,
help="Model name for the API. Auto-detected from server if omitted.",
)
parser.add_argument(
"--stream",
action="store_true",
help="Enable streaming mode.",
)
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
run(args)
Loaded 100 of 623 files, more files were not shown because too many files have changed in this diff. Show more