diff --git a/docs/mkdocs/hooks/generate_examples.py b/docs/mkdocs/hooks/generate_examples.py
index 6cb601b96..f3f96eb2d 100644
--- a/docs/mkdocs/hooks/generate_examples.py
+++ b/docs/mkdocs/hooks/generate_examples.py
@@ -24,6 +24,16 @@ SERVING_MODE_TITLES = {
"online_serving": "Online serving",
}
+# Generated example pages keep their source-derived URLs even when their
+# navigation category changes. Quantization helpers are feature documentation,
+# not user-serving examples, so keep them under the existing Features /
+# Quantization section instead of creating a second top-level Quantization
+# category under Examples.
+EXAMPLE_NAV_TARGETS = {
+ "quantization": "quantization",
+}
+GENERATED_QUANTIZATION_EXAMPLES_TITLE = "Generated Examples"
+
def load_model_display_names() -> dict[str, str]:
try:
@@ -340,26 +350,58 @@ def update_nav_file(examples: list[Example]):
examples_by_category[category] = []
examples_by_category[category].append(example)
- # Build the new Examples section - start with preserved items
- examples_section = preserved_items.copy()
-
- # Add examples grouped by category, sorted by category name
- for category in sorted(examples_by_category.keys()):
- category_examples = sorted(examples_by_category[category], key=lambda e: e.path.stem)
- category_items = []
- for example in category_examples:
+ def category_items(category: str) -> list[dict[str, str]]:
+ items = []
+ for example in sorted(examples_by_category[category], key=lambda e: e.path.stem):
doc_path = EXAMPLE_DOC_DIR / example.category / f"{example.path.stem}.md"
rel_path = doc_path.relative_to(ROOT_DIR / "docs")
- category_items.append({example.nav_title: rel_path.as_posix()})
+ items.append({example.nav_title: rel_path.as_posix()})
+ return items
- if category_items:
- # Format category name (e.g., "offline_inference" -> "Offline Inference")
+ # Build the new Examples section - start with preserved items. Categories
+ # not explicitly routed elsewhere retain the historical Examples behavior.
+ examples_section = preserved_items.copy()
+ for category in sorted(examples_by_category.keys()):
+ if EXAMPLE_NAV_TARGETS.get(category, "examples") != "examples":
+ continue
+ items = category_items(category)
+ if items:
category_title = fix_case(category.replace("_", " ").title())
- examples_section.append({category_title: category_items})
+ examples_section.append({category_title: items})
# Update the nav structure
nav_list[user_guide_idx]["User Guide"][examples_idx]["Examples"] = examples_section
+ features_content = next(
+ (item["Features"] for item in user_guide_content if isinstance(item, dict) and "Features" in item),
+ None,
+ )
+ quantization_content = None
+ if isinstance(features_content, list):
+ quantization_content = next(
+ (item["Quantization"] for item in features_content if isinstance(item, dict) and "Quantization" in item),
+ None,
+ )
+
+ if isinstance(quantization_content, list):
+ # Remove only the group owned by this hook so repeated builds are
+ # idempotent and hand-authored Quantization navigation is retained.
+ quantization_content[:] = [
+ item
+ for item in quantization_content
+ if not (isinstance(item, dict) and GENERATED_QUANTIZATION_EXAMPLES_TITLE in item)
+ ]
+
+ quantization_examples = examples_by_category.get("quantization")
+ if quantization_examples:
+ if not isinstance(quantization_content, list):
+ logger.warning(
+ "Could not find 'User Guide' -> 'Features' -> 'Quantization' section; "
+ "omitting generated quantization examples from navigation"
+ )
+ else:
+ quantization_content.append({GENERATED_QUANTIZATION_EXAMPLES_TITLE: category_items("quantization")})
+
# Write back to file
nav_data["nav"] = nav_list
with open(NAV_FILE, "w", encoding="utf-8") as f:
diff --git a/docs/models/recipe_coverage.yaml b/docs/models/recipe_coverage.yaml
new file mode 100644
index 000000000..3389bcf98
--- /dev/null
+++ b/docs/models/recipe_coverage.yaml
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: Apache-2.0
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
+
+# This registry is keyed by an exact Hugging Face checkpoint and task. A
+# Supported Models row may cover several checkpoints, but coverage entries must
+# be split whenever the example or recipe differs.
+version: 1
+
+# The registry is incrementally audited. An absent entry means that coverage
+# has not been audited yet; it is not evidence that no recipe exists. Do not
+# infer recipe coverage from the implementation or hardware columns in
+# supported_models.md.
+entries:
+ - model: Qwen/Qwen3-Omni-30B-A3B-Instruct
+ task: omni-serving
+ examples:
+ - docs/user_guide/examples/offline_inference/qwen3_omni.md
+ - docs/user_guide/examples/online_serving/qwen3_omni.md
+ recipe:
+ status: repository
+ repository_path: recipes/Qwen/Qwen3-Omni.md
+ hardware: [nvidia]
+
+ - model: Qwen/Qwen-Image
+ task: text-to-image
+ examples:
+ - docs/user_guide/examples/offline_inference/text_to_image.md
+ - docs/user_guide/examples/online_serving/text_to_image.md
+ recipe:
+ status: published
+ published_url: https://recipes.vllm.ai/Qwen/Qwen-Image
+ repository_path: recipes/Qwen/Qwen-Image.md
+ hardware: [nvidia]
+
+ - model: Qwen/Qwen-Image-2512
+ task: text-to-image
+ examples:
+ - docs/user_guide/examples/offline_inference/text_to_image.md
+ - docs/user_guide/examples/online_serving/text_to_image.md
+ recipe:
+ status: repository
+ repository_path: recipes/Qwen/Qwen-Image-2512.md
+ hardware: [nvidia]
+
+ - model: Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice
+ task: text-to-speech
+ examples:
+ - docs/user_guide/examples/offline_inference/text_to_speech.md
+ - docs/user_guide/examples/online_serving/text_to_speech.md
+ recipe:
+ status: published
+ published_url: https://recipes.vllm.ai/Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice
+ repository_path: recipes/Qwen/Qwen3-TTS.md
+ hardware: [nvidia]
+
+ - model: bosonai/higgs-audio-v3-tts-4b
+ task: text-to-speech
+ examples:
+ - docs/user_guide/examples/online_serving/text_to_speech.md
+ recipe:
+ status: published
+ published_url: https://recipes.vllm.ai/bosonai/higgs-audio-v3-tts-4b
+ repository_path: recipes/BosonAI/Higgs-Audio-V3-TTS.md
+ hardware: [nvidia]
+
+ - model: MiniMaxAI/MiniMax-H3
+ task: video-audio-generation
+ examples: []
+ recipe:
+ status: published
+ published_url: https://recipes.vllm.ai/MiniMaxAI/MiniMax-H3
+ repository_path: recipes/MiniMaxAI/MiniMax-H3.md
+ hardware: [nvidia, amd]
diff --git a/docs/models/supported_models.md b/docs/models/supported_models.md
index 585a22bdc..ab9ae311b 100644
--- a/docs/models/supported_models.md
+++ b/docs/models/supported_models.md
@@ -17,87 +17,121 @@ th {
}
-| Architecture | Models | Example HF Models | NVIDIA GPU | AMD GPU | Ascend NPU | Intel GPU |
-|--------------|--------|-------------------|------------|---------|-----|-----------|
-| `Qwen3OmniMoeForConditionalGeneration` | Qwen3-Omni | `Qwen/Qwen3-Omni-30B-A3B-Instruct` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `Qwen2_5OmniForConditionalGeneration` | Qwen2.5-Omni | `Qwen/Qwen2.5-Omni-7B`, `Qwen/Qwen2.5-Omni-3B` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `MingFlashOmniForConditionalGeneration` + `MingImagePipeline` | Ming-flash-omni-2.0 (omni-speech + imagegen1) | `Jonathan1909/Ming-flash-omni-2.0` | ✅︎ | | | |
-| `BagelForConditionalGeneration` | BAGEL (DiT-only) | `ByteDance-Seed/BAGEL-7B-MoT` | ✅︎ | ✅︎ | | ✅︎ |
-| `InternVLAA1Pipeline` | InternVLA-A1 | `InternRobotics/InternVLA-A1-3B` | ✅︎ | ✅︎ | | |
-| `Gr00tN1d7Pipeline` | GR00T N1.7 | `nvidia/GR00T-N1.7-3B` | ✅︎ | | | |
-| `HunyuanImage3ForCausalMM` | HunyuanImage3.0 (DiT-only) | `tencent/HunyuanImage-3.0`, `tencent/HunyuanImage-3.0-Instruct` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `QwenImagePipeline` | Qwen-Image | `Qwen/Qwen-Image` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `QwenImagePipeline` | Qwen-Image-2512 | `Qwen/Qwen-Image-2512` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `QwenImageEditPipeline` | Qwen-Image-Edit | `Qwen/Qwen-Image-Edit` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `QwenImageEditPlusPipeline` | Qwen-Image-Edit-2509 | `Qwen/Qwen-Image-Edit-2509` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `QwenImageLayeredPipeline` | Qwen-Image-Layered | `Qwen/Qwen-Image-Layered` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `QwenImageEditPlusPipeline` | Qwen-Image-Edit-2511 | `Qwen/Qwen-Image-Edit-2511` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `GlmImagePipeline` | GLM-Image | `zai-org/GLM-Image` | ✅︎ | ✅︎ | | |
-| `ZImagePipeline` | Z-Image | `Tongyi-MAI/Z-Image-Turbo` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `Krea2Pipeline` | Krea 2 (Raw + Turbo) | `krea/Krea-2-Raw`, `krea/Krea-2-Turbo` | ✅︎ | | | |
-| `WanPipeline` | Wan2.1-T2V, Wan2.2-T2V, Wan2.2-TI2V | `Wan-AI/Wan2.1-T2V-1.3B-Diffusers`, `Wan-AI/Wan2.1-T2V-14B-Diffusers`, `Wan-AI/Wan2.2-T2V-A14B-Diffusers`, `Wan-AI/Wan2.2-TI2V-5B-Diffusers` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `WanImageToVideoPipeline` | Wan2.2-I2V | `Wan-AI/Wan2.2-I2V-A14B-Diffusers` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `Cosmos3OmniDiffusersPipeline` | Cosmos3 T2I, T2V, I2V, V2V, T2V with sound, action policy | `nvidia/Cosmos3-Nano`, `nvidia/Cosmos3-Super` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `Wan22S2VPipeline` | Wan2.2-S2V | `Wan-AI/Wan2.2-S2V-14B` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `Wan22VACEPipeline` | Wan2.1-VACE | `Wan-AI/Wan2.1-VACE-1.3B-diffusers`, `Wan-AI/Wan2.1-VACE-14B-diffusers` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `Wan22VACEPipeline` | Wan2.2-VACE | `Pyros13/Wan2.2-VACE-Fun-A14B-Diffusers` | ✅︎ | | | |
-| `LTX2Pipeline` | LTX-2 / LTX-2.3 one-stage T2V and I2V | `Lightricks/LTX-2`, `diffusers/LTX-2.3-Diffusers` | ✅︎ | ✅︎ | | |
-| `LTX2DistilledPipeline` | LTX-2 distilled two-stage T2V and I2V | `rootonchair/LTX-2-19b-distilled` | ✅︎ | ✅︎ | | |
-| `LingBotVideoPipeline` | LingBot-Video dense and MoE T2I, T2V, TI2V | `robbyant/lingbot-video-dense-1.3b`, `robbyant/lingbot-video-moe-30b-a3b` | ✅︎ | | | |
-| `MiniMaxH3Pipeline` | MiniMax H3 T2VA, first/last-keyframe FL2VA, and mixed-reference Ref2VA | `MiniMaxAI/MiniMax-H3` | ✅︎ | | | |
-| `DreamZeroPipeline` | DreamZero-DROID | `GEAR-Dreams/DreamZero-DROID` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `HeliosPipeline`, `HeliosPyramidPipeline` | Helios | `BestWishYsh/Helios-Base`, `BestWishYsh/Helios-Mid`, `BestWishYsh/Helios-Distilled` | ✅︎ | ✅︎ | ✅︎ | |
-| `MagiHumanPipeline` | MagiHuman | `SII-GAIR/daVinci-MagiHuman-Base-1080p` | ✅︎ | ✅︎ | | |
-| `OvisImagePipeline` | Ovis-Image | `OvisAI/Ovis-Image` | ✅︎ | ✅︎ | | ✅︎ |
-| `LongcatImagePipeline` | LongCat-Image | `meituan-longcat/LongCat-Image` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `LongCatImageEditPipeline` | LongCat-Image-Edit | `meituan-longcat/LongCat-Image-Edit` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `BooguImagePipeline` | Boogu-Image | `Boogu/Boogu-Image-0.1-Base`, `Boogu/Boogu-Image-0.1-Edit` | ✅︎ | | | |
-| `StableDiffusionXLPipeline` | Stable-Diffusion-XL | `stabilityai/stable-diffusion-xl-base-1.0` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `StableDiffusion3Pipeline` | Stable-Diffusion-3 | `stabilityai/stable-diffusion-3.5-medium` | ✅︎ | ✅︎ | | ✅︎ |
-| `CosyVoice3Model` | CosyVoice3 | `FunAudioLLM/Fun-CosyVoice3-0.5B-2512` | ✅︎ | ✅︎ | | ✅︎ |
-| `OmniVoiceModel` | OmniVoice | `k2-fsa/OmniVoice` | ✅︎ | | | |
-| `VoxCPM2TalkerForConditionalGeneration` | VoxCPM2 | `openbmb/VoxCPM2` | ✅︎ | | | |
-| `MammothModa2ForConditionalGeneration` | MammothModa2-Preview | `bytedance-research/MammothModa2-Preview` | ✅︎ | ✅︎ | | |
-| `MammothModa2ForConditionalGeneration` | MammothModa2-Dev (AR-only image understanding) | `bytedance-research/MammothModa2-Dev` | ✅︎ | | | |
-| `Flux2KleinPipeline` | FLUX.2-klein | `black-forest-labs/FLUX.2-klein-4B`, `black-forest-labs/FLUX.2-klein-9B` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `FluxKontextPipeline` | FLUX.1-Kontext-dev | `black-forest-labs/FLUX.1-Kontext-dev` | ✅︎ | ✅︎ | | |
-| `FluxPipeline` | FLUX.1-dev | `black-forest-labs/FLUX.1-dev` | ✅︎ | ✅︎ | | ✅︎ |
-| `FluxPipeline` | FLUX.1-schnell | `black-forest-labs/FLUX.1-schnell` | ✅︎ | ✅︎ | | ✅︎ |
-| `OmniGen2Pipeline` | OmniGen2 | `OmniGen2/OmniGen2` | ✅︎ | ✅︎ | | ✅︎ |
-| `StableAudioPipeline` | Stable-Audio-Open | `stabilityai/stable-audio-open-1.0` | ✅︎ | ✅︎ | | ✅︎ |
-| `SoulXSingerPipeline` | SoulX-Singer (SVS) | `Soul-AILab/SoulX-Singer` | ✅︎ | | | |
-| `SoulXSingerSVCPipeline` | SoulX-Singer-SVC | `Soul-AILab/SoulX-Singer` (`model-svc.pt`) | ✅︎ | | | |
-| `AudioXPipeline` | AudioX | `zhangj1an/AudioX` | ✅︎ | ✅︎ | | |
-| `Qwen3TTSForConditionalGeneration` | Qwen3-TTS-12Hz-1.7B-CustomVoice | `Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `Qwen3TTSForConditionalGeneration` | Qwen3-TTS-12Hz-1.7B-VoiceDesign | `Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `Qwen3TTSForConditionalGeneration` | Qwen3-TTS-12Hz-1.7B-Base | `Qwen/Qwen3-TTS-12Hz-1.7B-Base` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-| `MingTTSForConditionalGeneration` | Ming-omni-tts dense 0.5B | `inclusionAI/Ming-omni-tts-0.5B` | ✅︎ | | | |
-| `GLMTTSForConditionalGeneration` | GLM-TTS | `zai-org/GLM-TTS` | ✅︎ | | | |
-| `MossTTSNanoForCausalLM` | MOSS-TTS-Nano | `OpenMOSS-Team/MOSS-TTS-Nano` | ✅︎ | | | |
-| `MossTTSDelayModel` | MOSS-TTS, MOSS-TTSD, MOSS-SoundEffect, MOSS-VoiceGenerator | `OpenMOSS-Team/MOSS-VoiceGenerator` | ✅︎ | | | |
-| `MossTTSRealtime` | MOSS-TTS-Realtime | `OpenMOSS-Team/MOSS-TTS-Realtime` | ✅︎ | | | |
-| `HiggsAudioV2ForConditionalGeneration` | Higgs-Audio v2 | `bosonai/higgs-audio-v2-generation-3B-base` | ✅︎ | | | |
-| `HiggsMultimodalQwen3ForConditionalGeneration` | Higgs-Audio v3 (TTS) | `bosonai/higgs-audio-v3-tts-4b` | ✅︎ | | | |
-| `IndexTTS2TalkerForConditionalGeneration` | IndexTTS-2 | `IndexTeam/IndexTTS-2` | ✅︎ | | | |
-| `NextStep11Pipeline` | NextStep-1.1 | `stepfun-ai/NextStep-1.1` | ✅︎ | ✅︎ | | ✅︎ |
-| `MiMoAudioModel` | MiMo-Audio-7B-Instruct | `XiaomiMiMo/MiMo-Audio-7B-Instruct` | ✅︎ | ✅︎ | | |
-| `MiMoV2ASRForCausalLM` | MiMo-V2.5-ASR | `XiaomiMiMo/MiMo-V2.5-ASR` | ✅︎ | ✅︎ | | |
-| `Flux2Pipeline` | FLUX.2-dev | `black-forest-labs/FLUX.2-dev` | ✅︎ | ✅︎ | | |
-| `FishSpeechSlowARForConditionalGeneration` | Fish Speech S2 Pro | `fishaudio/s2-pro` | ✅︎ | ✅︎ | | ✅︎ |
-| `DreamIDOmniPipeline` | DreamID-Omni | `XuGuo699/DreamID-Omni` | ✅︎ | ✅︎ | | |
-| `SenseNovaU1Pipeline` | SenseNova-U1 (DiT-only) | `SenseNova/SenseNova-U1-8B-MoT` | ✅︎ | | | |
-| `LancePipeline` | Lance | `bytedance-research/Lance` | ✅︎ | | | |
-| `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` | ✅︎ | ✅︎ | | |
-| `VoxtralTTSForConditionalGeneration` | Voxtral TTS | `mistralai/Voxtral-4B-TTS-2603` | ✅︎ | ✅︎ | | |
-| `CovoAudioForConditionalGeneration` | Covo-Audio-Chat | `tencent/Covo-Audio-Chat` | ✅︎ | | | |
-|`DyninOmniForConditionalGeneration` | Dynin-Omni | `snu-aidas/Dynin-Omni` | ✅︎ | | | |
-| `MiniCPMO45OmniForConditionalGeneration` | MiniCPM-o 4.5 | `openbmb/MiniCPM-o-4_5` | ✅︎ | | ✅︎ | |
-| `ErnieImagePipeline` | ERNIE-Image | `baidu/ERNIE-Image`, `baidu/ERNIE-Image-Turbo` | ✅︎ | ✅︎ | ✅︎ | ✅︎ |
-|`HiDreamImagePipeline` | HiDream-I1-Full | `HiDream-ai/HiDream-I1-Full` | ✅︎ | ✅︎ | | |
+| Architecture | Models | Example HF Models | NVIDIA GPU | AMD GPU | Ascend NPU | Intel GPU | Recipe |
+|--------------|--------|-------------------|------------|---------|-----|-----------|--------|
+| `Qwen3OmniMoeForConditionalGeneration` | Qwen3-Omni | `Qwen/Qwen3-Omni-30B-A3B-Instruct` | ✅︎ | | | | [Repository](https://github.com/vllm-project/vllm-omni/blob/main/recipes/Qwen/Qwen3-Omni.md) |
+| `Qwen2_5OmniForConditionalGeneration` | Qwen2.5-Omni | `Qwen/Qwen2.5-Omni-7B`, `Qwen/Qwen2.5-Omni-3B` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `MingFlashOmniForConditionalGeneration` + `MingImagePipeline` | Ming-flash-omni-2.0 (omni-speech + imagegen1) | `Jonathan1909/Ming-flash-omni-2.0` | ✅︎ | | | | — |
+| `BagelForConditionalGeneration` | BAGEL (DiT-only) | `ByteDance-Seed/BAGEL-7B-MoT` | ✅︎ | ✅︎ | | ✅︎ | — |
+| `InternVLAA1Pipeline` | InternVLA-A1 | `InternRobotics/InternVLA-A1-3B` | ✅︎ | ✅︎ | | | — |
+| `Gr00tN1d7Pipeline` | GR00T N1.7 | `nvidia/GR00T-N1.7-3B` | ✅︎ | | | | — |
+| `HunyuanImage3ForCausalMM` | HunyuanImage3.0 (DiT-only) | `tencent/HunyuanImage-3.0`, `tencent/HunyuanImage-3.0-Instruct` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `QwenImagePipeline` | Qwen-Image | `Qwen/Qwen-Image` | ✅︎ | | | | [Published](https://recipes.vllm.ai/Qwen/Qwen-Image) |
+| `QwenImagePipeline` | Qwen-Image-2512 | `Qwen/Qwen-Image-2512` | ✅︎ | | | | [Repository](https://github.com/vllm-project/vllm-omni/blob/main/recipes/Qwen/Qwen-Image-2512.md) |
+| `QwenImageEditPipeline` | Qwen-Image-Edit | `Qwen/Qwen-Image-Edit` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `QwenImageEditPlusPipeline` | Qwen-Image-Edit-2509 | `Qwen/Qwen-Image-Edit-2509` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `QwenImageLayeredPipeline` | Qwen-Image-Layered | `Qwen/Qwen-Image-Layered` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `QwenImageEditPlusPipeline` | Qwen-Image-Edit-2511 | `Qwen/Qwen-Image-Edit-2511` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `GlmImagePipeline` | GLM-Image | `zai-org/GLM-Image` | ✅︎ | ✅︎ | | | — |
+| `ZImagePipeline` | Z-Image | `Tongyi-MAI/Z-Image-Turbo` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `Krea2Pipeline` | Krea 2 (Raw + Turbo) | `krea/Krea-2-Raw`, `krea/Krea-2-Turbo` | ✅︎ | | | | — |
+| `WanPipeline` | Wan2.1-T2V, Wan2.2-T2V, Wan2.2-TI2V | `Wan-AI/Wan2.1-T2V-1.3B-Diffusers`, `Wan-AI/Wan2.1-T2V-14B-Diffusers`, `Wan-AI/Wan2.2-T2V-A14B-Diffusers`, `Wan-AI/Wan2.2-TI2V-5B-Diffusers` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `WanImageToVideoPipeline` | Wan2.2-I2V | `Wan-AI/Wan2.2-I2V-A14B-Diffusers` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `Cosmos3OmniDiffusersPipeline` | Cosmos3 T2I, T2V, I2V, V2V, T2V with sound, action policy | `nvidia/Cosmos3-Nano`, `nvidia/Cosmos3-Super` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `Wan22S2VPipeline` | Wan2.2-S2V | `Wan-AI/Wan2.2-S2V-14B` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `Wan22VACEPipeline` | Wan2.1-VACE | `Wan-AI/Wan2.1-VACE-1.3B-diffusers`, `Wan-AI/Wan2.1-VACE-14B-diffusers` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `Wan22VACEPipeline` | Wan2.2-VACE | `Pyros13/Wan2.2-VACE-Fun-A14B-Diffusers` | ✅︎ | | | | — |
+| `LTX2Pipeline` | LTX-2 / LTX-2.3 one-stage T2V and I2V | `Lightricks/LTX-2`, `diffusers/LTX-2.3-Diffusers` | ✅︎ | ✅︎ | | | — |
+| `LTX2DistilledPipeline` | LTX-2 distilled two-stage T2V and I2V | `rootonchair/LTX-2-19b-distilled` | ✅︎ | ✅︎ | | | — |
+| `LingBotVideoPipeline` | LingBot-Video dense and MoE T2I, T2V, TI2V | `robbyant/lingbot-video-dense-1.3b`, `robbyant/lingbot-video-moe-30b-a3b` | ✅︎ | | | | — |
+| `MiniMaxH3Pipeline` | MiniMax H3 T2VA, first/last-keyframe FL2VA, and mixed-reference Ref2VA | `MiniMaxAI/MiniMax-H3` | ✅︎ | ✅︎ | | | [Published](https://recipes.vllm.ai/MiniMaxAI/MiniMax-H3) |
+| `DreamZeroPipeline` | DreamZero-DROID | `GEAR-Dreams/DreamZero-DROID` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `HeliosPipeline`, `HeliosPyramidPipeline` | Helios | `BestWishYsh/Helios-Base`, `BestWishYsh/Helios-Mid`, `BestWishYsh/Helios-Distilled` | ✅︎ | ✅︎ | ✅︎ | | — |
+| `MagiHumanPipeline` | MagiHuman | `SII-GAIR/daVinci-MagiHuman-Base-1080p` | ✅︎ | ✅︎ | | | — |
+| `OvisImagePipeline` | Ovis-Image | `OvisAI/Ovis-Image` | ✅︎ | ✅︎ | | ✅︎ | — |
+| `LongcatImagePipeline` | LongCat-Image | `meituan-longcat/LongCat-Image` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `LongCatImageEditPipeline` | LongCat-Image-Edit | `meituan-longcat/LongCat-Image-Edit` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `BooguImagePipeline` | Boogu-Image | `Boogu/Boogu-Image-0.1-Base`, `Boogu/Boogu-Image-0.1-Edit` | ✅︎ | | | | — |
+| `StableDiffusionXLPipeline` | Stable-Diffusion-XL | `stabilityai/stable-diffusion-xl-base-1.0` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `StableDiffusion3Pipeline` | Stable-Diffusion-3 | `stabilityai/stable-diffusion-3.5-medium` | ✅︎ | ✅︎ | | ✅︎ | — |
+| `CosyVoice3Model` | CosyVoice3 | `FunAudioLLM/Fun-CosyVoice3-0.5B-2512` | ✅︎ | ✅︎ | | ✅︎ | — |
+| `OmniVoiceModel` | OmniVoice | `k2-fsa/OmniVoice` | ✅︎ | | | | — |
+| `VoxCPM2TalkerForConditionalGeneration` | VoxCPM2 | `openbmb/VoxCPM2` | ✅︎ | | | | — |
+| `MammothModa2ForConditionalGeneration` | MammothModa2-Preview | `bytedance-research/MammothModa2-Preview` | ✅︎ | ✅︎ | | | — |
+| `MammothModa2ForConditionalGeneration` | MammothModa2-Dev (AR-only image understanding) | `bytedance-research/MammothModa2-Dev` | ✅︎ | | | | — |
+| `Flux2KleinPipeline` | FLUX.2-klein | `black-forest-labs/FLUX.2-klein-4B`, `black-forest-labs/FLUX.2-klein-9B` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `FluxKontextPipeline` | FLUX.1-Kontext-dev | `black-forest-labs/FLUX.1-Kontext-dev` | ✅︎ | ✅︎ | | | — |
+| `FluxPipeline` | FLUX.1-dev | `black-forest-labs/FLUX.1-dev` | ✅︎ | ✅︎ | | ✅︎ | — |
+| `FluxPipeline` | FLUX.1-schnell | `black-forest-labs/FLUX.1-schnell` | ✅︎ | ✅︎ | | ✅︎ | — |
+| `OmniGen2Pipeline` | OmniGen2 | `OmniGen2/OmniGen2` | ✅︎ | ✅︎ | | ✅︎ | — |
+| `StableAudioPipeline` | Stable-Audio-Open | `stabilityai/stable-audio-open-1.0` | ✅︎ | ✅︎ | | ✅︎ | — |
+| `SoulXSingerPipeline` | SoulX-Singer (SVS) | `Soul-AILab/SoulX-Singer` | ✅︎ | | | | — |
+| `SoulXSingerSVCPipeline` | SoulX-Singer-SVC | `Soul-AILab/SoulX-Singer` (`model-svc.pt`) | ✅︎ | | | | — |
+| `AudioXPipeline` | AudioX | `zhangj1an/AudioX` | ✅︎ | ✅︎ | | | — |
+| `Qwen3TTSForConditionalGeneration` | Qwen3-TTS-12Hz-1.7B-CustomVoice | `Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice` | ✅︎ | | | | [Published](https://recipes.vllm.ai/Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice) |
+| `Qwen3TTSForConditionalGeneration` | Qwen3-TTS-12Hz-1.7B-VoiceDesign | `Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `Qwen3TTSForConditionalGeneration` | Qwen3-TTS-12Hz-1.7B-Base | `Qwen/Qwen3-TTS-12Hz-1.7B-Base` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+| `MingTTSForConditionalGeneration` | Ming-omni-tts dense 0.5B | `inclusionAI/Ming-omni-tts-0.5B` | ✅︎ | | | | — |
+| `GLMTTSForConditionalGeneration` | GLM-TTS | `zai-org/GLM-TTS` | ✅︎ | | | | — |
+| `MossTTSNanoForCausalLM` | MOSS-TTS-Nano | `OpenMOSS-Team/MOSS-TTS-Nano` | ✅︎ | | | | — |
+| `MossTTSDelayModel` | MOSS-TTS, MOSS-TTSD, MOSS-SoundEffect, MOSS-VoiceGenerator | `OpenMOSS-Team/MOSS-VoiceGenerator` | ✅︎ | | | | — |
+| `MossTTSRealtime` | MOSS-TTS-Realtime | `OpenMOSS-Team/MOSS-TTS-Realtime` | ✅︎ | | | | — |
+| `HiggsAudioV2ForConditionalGeneration` | Higgs-Audio v2 | `bosonai/higgs-audio-v2-generation-3B-base` | ✅︎ | | | | — |
+| `HiggsMultimodalQwen3ForConditionalGeneration` | Higgs-Audio v3 (TTS) | `bosonai/higgs-audio-v3-tts-4b` | ✅︎ | | | | [Published](https://recipes.vllm.ai/bosonai/higgs-audio-v3-tts-4b) |
+| `IndexTTS2TalkerForConditionalGeneration` | IndexTTS-2 | `IndexTeam/IndexTTS-2` | ✅︎ | | | | — |
+| `NextStep11Pipeline` | NextStep-1.1 | `stepfun-ai/NextStep-1.1` | ✅︎ | ✅︎ | | ✅︎ | — |
+| `MiMoAudioModel` | MiMo-Audio-7B-Instruct | `XiaomiMiMo/MiMo-Audio-7B-Instruct` | ✅︎ | ✅︎ | | | — |
+| `MiMoV2ASRForCausalLM` | MiMo-V2.5-ASR | `XiaomiMiMo/MiMo-V2.5-ASR` | ✅︎ | ✅︎ | | | — |
+| `Flux2Pipeline` | FLUX.2-dev | `black-forest-labs/FLUX.2-dev` | ✅︎ | ✅︎ | | | — |
+| `FishSpeechSlowARForConditionalGeneration` | Fish Speech S2 Pro | `fishaudio/s2-pro` | ✅︎ | ✅︎ | | ✅︎ | — |
+| `DreamIDOmniPipeline` | DreamID-Omni | `XuGuo699/DreamID-Omni` | ✅︎ | ✅︎ | | | — |
+| `SenseNovaU1Pipeline` | SenseNova-U1 (DiT-only) | `SenseNova/SenseNova-U1-8B-MoT` | ✅︎ | | | | — |
+| `LancePipeline` | Lance | `bytedance-research/Lance` | ✅︎ | | | | — |
+| `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` | ✅︎ | ✅︎ | | | — |
+| `VoxtralTTSForConditionalGeneration` | Voxtral TTS | `mistralai/Voxtral-4B-TTS-2603` | ✅︎ | ✅︎ | | | — |
+| `CovoAudioForConditionalGeneration` | Covo-Audio-Chat | `tencent/Covo-Audio-Chat` | ✅︎ | | | | — |
+|`DyninOmniForConditionalGeneration` | Dynin-Omni | `snu-aidas/Dynin-Omni` | ✅︎ | | | | — |
+| `MiniCPMO45OmniForConditionalGeneration` | MiniCPM-o 4.5 | `openbmb/MiniCPM-o-4_5` | ✅︎ | | ✅︎ | | — |
+| `ErnieImagePipeline` | ERNIE-Image | `baidu/ERNIE-Image`, `baidu/ERNIE-Image-Turbo` | ✅︎ | ✅︎ | ✅︎ | ✅︎ | — |
+|`HiDreamImagePipeline` | HiDream-I1-Full | `HiDream-ai/HiDream-I1-Full` | ✅︎ | ✅︎ | | | — |
+
+The `Recipe` column links the exact checkpoint/task entry from
+[`recipe_coverage.yaml`](https://github.com/vllm-project/vllm-omni/blob/main/docs/models/recipe_coverage.yaml).
+`Published` is preferred when a corresponding `recipes.vllm.ai` page exists;
+otherwise the link points to the repository recipe. `—` means that the exact
+checkpoint/task mapping has not been audited yet.
+
+For rows with an audited recipe link, the hardware checkmarks reflect hardware
+explicitly documented in that recipe. Other rows retain the implementation
+support metadata until their recipe coverage is audited.
✅︎ indicates the model is supported on that backend. Empty cells mean not listed as supported on that backend.
+## Recipe and Example Coverage
+
+The table above describes implementation and backend support. It does not
+imply that a model has a validated deployment recipe.
+
+Recipe and example coverage is tracked per exact Hugging Face checkpoint and
+task in the machine-readable
+[`recipe_coverage.yaml`](https://github.com/vllm-project/vllm-omni/blob/main/docs/models/recipe_coverage.yaml).
+The registry is populated as coverage is audited and is the source of truth
+for mapping exact checkpoints and tasks to links. Recipe files under
+[`recipes/`](https://github.com/vllm-project/vllm-omni/tree/main/recipes) remain
+the source of truth for commands and validation details. It uses the following
+link precedence:
+
+1. `published`: link to the published `recipes.vllm.ai` page.
+2. `repository`: link to the in-repository recipe until a published page exists.
+3. `no_validated_recipe_yet`: show `No validated recipe yet` only when that
+ result has been explicitly audited.
+4. A checkpoint/task without an entry: coverage has not been audited yet.
+
+Split entries whenever checkpoints, tasks, examples, or validation evidence
+differ. A single Supported Models row may therefore map to multiple registry
+entries.
+
H3 MiniMax H3 AMD GPU support is validated on gfx942 (MI300X) and
gfx950 (MI350) in BF16, using the AITER `FLASH_ATTN` diffusion attention backend.
Other AMD SKUs (e.g. MI325X) are not listed until their
diff --git a/recipes/README.md b/recipes/README.md
index a6015d9b0..005b62ce5 100644
--- a/recipes/README.md
+++ b/recipes/README.md
@@ -19,6 +19,12 @@ add files for this repo. Use one Markdown file per model family by default.
> Individual pages resolve at `https://recipes.vllm.ai//`,
> e.g. [`bosonai/higgs-audio-v3-tts-4b`](https://recipes.vllm.ai/bosonai/higgs-audio-v3-tts-4b).
+The exact checkpoint/task mapping used by the Supported Models documentation
+is tracked in
+[`docs/models/recipe_coverage.yaml`](../docs/models/recipe_coverage.yaml).
+This registry records link precedence; the recipe files in this directory
+remain the source of truth for commands and validation details.
+
Example layout:
```text
diff --git a/tests/docs/test_generate_examples.py b/tests/docs/test_generate_examples.py
index 57d7d4832..34d7045f9 100644
--- a/tests/docs/test_generate_examples.py
+++ b/tests/docs/test_generate_examples.py
@@ -3,8 +3,10 @@
import importlib.util
from pathlib import Path
+from types import SimpleNamespace
import pytest
+import yaml
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
@@ -43,3 +45,113 @@ def test_model_display_names_require_string_mapping(tmp_path, monkeypatch):
with pytest.raises(ValueError, match="MODEL_DISPLAY_NAMES_FILE"):
generate_examples.load_model_display_names()
+
+
+def test_quantization_examples_keep_source_urls_under_feature_navigation(tmp_path, monkeypatch):
+ nav_file = tmp_path / "docs" / ".nav.yml"
+ nav_file.parent.mkdir(parents=True)
+ nav_file.write_text(
+ yaml.safe_dump(
+ {
+ "nav": [
+ {
+ "User Guide": [
+ {
+ "Examples": [
+ "examples/README.md",
+ {"Offline Inference": [{"Old": "user_guide/examples/offline_inference/old.md"}]},
+ ]
+ },
+ {"Features": [{"Quantization": [{"Overview": "user_guide/quantization/overview.md"}]}]},
+ ]
+ }
+ ]
+ },
+ sort_keys=False,
+ ),
+ encoding="utf-8",
+ )
+
+ monkeypatch.setattr(generate_examples, "ROOT_DIR", tmp_path)
+ monkeypatch.setattr(generate_examples, "NAV_FILE", nav_file)
+ monkeypatch.setattr(generate_examples, "EXAMPLE_DOC_DIR", tmp_path / "docs/user_guide/examples")
+
+ quantization_example = SimpleNamespace(
+ category="quantization",
+ path=tmp_path / "examples/quantization/check_modelopt_fp8_export.py",
+ nav_title="Check Modelopt FP8 Export",
+ )
+ offline_example = SimpleNamespace(
+ category="offline_inference",
+ path=tmp_path / "examples/offline_inference/demo.py",
+ nav_title="Demo",
+ )
+
+ generate_examples.update_nav_file([quantization_example, offline_example])
+ generate_examples.update_nav_file([quantization_example, offline_example])
+
+ nav_data = yaml.safe_load(nav_file.read_text(encoding="utf-8"))
+ user_guide = nav_data["nav"][0]["User Guide"]
+ examples = next(item["Examples"] for item in user_guide if "Examples" in item)
+ features = next(item["Features"] for item in user_guide if "Features" in item)
+ quantization = next(item["Quantization"] for item in features if "Quantization" in item)
+
+ assert not any(isinstance(item, dict) and "Quantization" in item for item in examples)
+ generated_examples = [
+ item[generate_examples.GENERATED_QUANTIZATION_EXAMPLES_TITLE]
+ for item in quantization
+ if generate_examples.GENERATED_QUANTIZATION_EXAMPLES_TITLE in item
+ ]
+ assert len(generated_examples) == 1
+ assert generated_examples[0] == [
+ {"Check Modelopt FP8 Export": "user_guide/examples/quantization/check_modelopt_fp8_export.md"}
+ ]
+
+ # A later build without quantization examples must not leave a stale
+ # generated group in the hand-authored Quantization section.
+ generate_examples.update_nav_file([offline_example])
+ nav_data = yaml.safe_load(nav_file.read_text(encoding="utf-8"))
+ user_guide = nav_data["nav"][0]["User Guide"]
+ features = next(item["Features"] for item in user_guide if "Features" in item)
+ quantization = next(item["Quantization"] for item in features if "Quantization" in item)
+ assert not any(
+ isinstance(item, dict) and generate_examples.GENERATED_QUANTIZATION_EXAMPLES_TITLE in item
+ for item in quantization
+ )
+
+
+def test_quantization_examples_are_not_added_to_examples_without_feature_target(tmp_path, monkeypatch, caplog):
+ nav_file = tmp_path / "docs" / ".nav.yml"
+ nav_file.parent.mkdir(parents=True)
+ nav_file.write_text(
+ yaml.safe_dump(
+ {
+ "nav": [
+ {
+ "User Guide": [
+ {"Examples": ["examples/README.md"]},
+ ]
+ }
+ ]
+ },
+ sort_keys=False,
+ ),
+ encoding="utf-8",
+ )
+
+ monkeypatch.setattr(generate_examples, "ROOT_DIR", tmp_path)
+ monkeypatch.setattr(generate_examples, "NAV_FILE", nav_file)
+ monkeypatch.setattr(generate_examples, "EXAMPLE_DOC_DIR", tmp_path / "docs/user_guide/examples")
+
+ quantization_example = SimpleNamespace(
+ category="quantization",
+ path=tmp_path / "examples/quantization/check_modelopt_fp8_export.py",
+ nav_title="Check Modelopt FP8 Export",
+ )
+
+ generate_examples.update_nav_file([quantization_example])
+
+ nav_data = yaml.safe_load(nav_file.read_text(encoding="utf-8"))
+ examples = nav_data["nav"][0]["User Guide"][0]["Examples"]
+ assert not any(isinstance(item, dict) and "Quantization" in item for item in examples)
+ assert "omitting generated quantization examples from navigation" in caplog.text
diff --git a/tests/docs/test_recipe_coverage.py b/tests/docs/test_recipe_coverage.py
new file mode 100644
index 000000000..6425dd4ad
--- /dev/null
+++ b/tests/docs/test_recipe_coverage.py
@@ -0,0 +1,142 @@
+# SPDX-License-Identifier: Apache-2.0
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
+
+import re
+from pathlib import Path
+
+import pytest
+import yaml
+
+pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
+
+ROOT_DIR = Path(__file__).parents[2]
+COVERAGE_PATH = ROOT_DIR / "docs/models/recipe_coverage.yaml"
+SUPPORTED_MODELS_PATH = ROOT_DIR / "docs/models/supported_models.md"
+VALID_STATUSES = {"published", "repository", "no_validated_recipe_yet"}
+VALID_HARDWARE = {"nvidia", "amd", "ascend", "intel"}
+HARDWARE_COLUMNS = {
+ "nvidia": 4,
+ "amd": 5,
+ "ascend": 6,
+ "intel": 7,
+}
+
+
+def load_entries() -> list[dict]:
+ data = yaml.safe_load(COVERAGE_PATH.read_text(encoding="utf-8"))
+ assert data["version"] == 1
+ assert isinstance(data["entries"], list)
+ return data["entries"]
+
+
+def supported_model_ids() -> set[str]:
+ model_ids = set()
+ for line in SUPPORTED_MODELS_PATH.read_text(encoding="utf-8").splitlines():
+ if not line.startswith("|"):
+ continue
+ columns = line.split("|")
+ if len(columns) <= 3:
+ continue
+ model_ids.update(re.findall(r"`([^`]+/[^`]+)`", columns[3]))
+ return model_ids
+
+
+def supported_model_recipe_cells() -> dict[str, str]:
+ recipe_cells = {}
+ in_table = False
+ for line in SUPPORTED_MODELS_PATH.read_text(encoding="utf-8").splitlines():
+ if line.startswith("| Architecture |"):
+ in_table = True
+ continue
+ if in_table and not line.startswith("|"):
+ break
+ if not in_table:
+ continue
+
+ columns = line.split("|")
+ if len(columns) <= 8 or columns[1].strip().startswith("-"):
+ continue
+ for model_id in re.findall(r"`([^`]+/[^`]+)`", columns[3]):
+ recipe_cells[model_id] = columns[8].strip()
+ return recipe_cells
+
+
+def expected_recipe_link(entry: dict) -> str:
+ recipe = entry["recipe"]
+ if recipe["status"] == "published":
+ return recipe["published_url"]
+ if recipe["status"] == "repository":
+ return f"https://github.com/vllm-project/vllm-omni/blob/main/{recipe['repository_path']}"
+ return "No validated recipe yet"
+
+
+def supported_model_hardware_cells() -> dict[str, list[str]]:
+ hardware_cells = {}
+ in_table = False
+ for line in SUPPORTED_MODELS_PATH.read_text(encoding="utf-8").splitlines():
+ if line.startswith("| Architecture |"):
+ in_table = True
+ continue
+ if in_table and not line.startswith("|"):
+ break
+ if not in_table:
+ continue
+
+ columns = line.split("|")
+ if len(columns) <= 8 or columns[1].strip().startswith("-"):
+ continue
+ cells = [columns[index].strip() for index in HARDWARE_COLUMNS.values()]
+ for model_id in re.findall(r"`([^`]+/[^`]+)`", columns[3]):
+ hardware_cells[model_id] = cells
+ return hardware_cells
+
+
+def test_registry_keys_are_exact_supported_checkpoints():
+ entries = load_entries()
+ keys = [(entry["model"], entry["task"]) for entry in entries]
+
+ assert len(keys) == len(set(keys))
+ assert all(entry["model"] in supported_model_ids() for entry in entries)
+
+
+def test_supported_models_render_registry_recipe_links():
+ recipe_cells = supported_model_recipe_cells()
+
+ for entry in load_entries():
+ assert expected_recipe_link(entry) in recipe_cells[entry["model"]]
+
+
+def test_registry_entries_have_explicit_recipe_precedence():
+ for entry in load_entries():
+ assert set(entry) >= {"model", "task", "examples", "recipe"}
+ assert isinstance(entry["examples"], list)
+
+ recipe = entry["recipe"]
+ status = recipe["status"]
+ assert status in VALID_STATUSES
+ assert set(recipe.get("hardware", [])) <= VALID_HARDWARE
+
+ if status == "published":
+ assert recipe["published_url"].startswith("https://recipes.vllm.ai/")
+ elif status == "repository":
+ assert recipe["repository_path"].startswith("recipes/")
+ assert "published_url" not in recipe
+ else:
+ assert "published_url" not in recipe
+ assert "repository_path" not in recipe
+
+ paths = [*entry["examples"], recipe.get("repository_path")]
+ for relative_path in filter(None, paths):
+ path = Path(relative_path)
+ assert not path.is_absolute()
+ assert (ROOT_DIR / path).is_file(), relative_path
+
+
+def test_supported_models_render_recipe_hardware():
+ hardware_cells = supported_model_hardware_cells()
+
+ for entry in load_entries():
+ recipe_hardware = set(entry["recipe"].get("hardware", []))
+ cells = hardware_cells[entry["model"]]
+ for hardware, cell in zip(HARDWARE_COLUMNS, cells, strict=True):
+ assert (cell == "✅︎") == (hardware in recipe_hardware)