Compare commits

..
Author SHA1 Message Date
Hongsheng Liu 29d2f157db Merge origin/main to resolve TTS ratchet conflict
Signed-off-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-08-10 15:29:25 +08:00
Hongsheng Liu ddcc050a94 ci: ratchet tts adapter branch budget
Signed-off-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-08-10 14:28:32 +08:00
Hongsheng Liu 50f4e675c1 docs: remove quantization example sources
Signed-off-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-08-10 14:13:30 +08:00
Hongsheng Liu 159dceaf65 docs: remove recipe coverage registry
Signed-off-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-08-10 12:47:40 +08:00
Hongsheng Liu d10bf78c60 docs: preserve quantization URLs and recipe coverage
Signed-off-by: Hongsheng Liu <liuhongsheng4@huawei.com>
2026-08-10 12:41:11 +08:00
8 changed files with 182 additions and 190 deletions

No files matched your search

+14 -4
View File
@@ -16,21 +16,31 @@ nav:
- Examples: - Examples:
- examples/README.md - examples/README.md
- Offline Inference: - Offline Inference:
- GLM-Image Multistage End-to-End Inference: user_guide/examples/offline_inference/glm_image.md
- HunyuanImage-3.0 Image-to-Text Inference: user_guide/examples/offline_inference/hunyuan_image3.md
- Image-To-Image: user_guide/examples/offline_inference/image_to_image.md - Image-To-Image: user_guide/examples/offline_inference/image_to_image.md
- Image-To-Video: user_guide/examples/offline_inference/image_to_video.md - Image-To-Video: user_guide/examples/offline_inference/image_to_video.md
- Speech-To-Video: user_guide/examples/offline_inference/speech_to_video.md - InternVLA-A1: user_guide/examples/offline_inference/internvla_a1.md
- MiMo-Audio: user_guide/examples/offline_inference/mimo_audio.md
- Qwen2.5-Omni: user_guide/examples/offline_inference/qwen2_5_omni.md
- Qwen3-Omni: user_guide/examples/offline_inference/qwen3_omni.md
- Text-To-Audio: user_guide/examples/offline_inference/text_to_audio.md - Text-To-Audio: user_guide/examples/offline_inference/text_to_audio.md
- Text-To-Image: user_guide/examples/offline_inference/text_to_image.md - Text-To-Image: user_guide/examples/offline_inference/text_to_image.md
- Text-To-Speech: user_guide/examples/offline_inference/text_to_speech.md - Text-To-Speech: user_guide/examples/offline_inference/text_to_speech.md
- Text-To-Video: user_guide/examples/offline_inference/text_to_video.md - Text-To-Video: user_guide/examples/offline_inference/text_to_video.md
- X-To-Text: user_guide/examples/offline_inference/x_to_text.md
- X-To-Video-Audio: user_guide/examples/offline_inference/x_to_video_audio.md - X-To-Video-Audio: user_guide/examples/offline_inference/x_to_video_audio.md
- Online Serving: - Online Serving:
- BAGEL-7B-MoT: user_guide/examples/online_serving/bagel.md
- vLLM-Omni Helm Chart: user_guide/examples/online_serving/chart-helm.md
- Diffusers Backend Adapter: user_guide/examples/online_serving/diffusers_pipeline_adapter.md
- GLM-Image Online Serving: user_guide/examples/online_serving/glm_image.md
- Image-To-Image: user_guide/examples/online_serving/image_to_image.md - Image-To-Image: user_guide/examples/online_serving/image_to_image.md
- Image-To-Video: user_guide/examples/online_serving/image_to_video.md - Image-To-Video: user_guide/examples/online_serving/image_to_video.md
- Speech-To-Video: user_guide/examples/online_serving/speech_to_video.md - MiMo-Audio: user_guide/examples/online_serving/mimo_audio.md
- Text-To-Image: user_guide/examples/online_serving/text_to_image.md - Qwen2.5-Omni: user_guide/examples/online_serving/qwen2_5_omni.md
- Qwen3-Omni: user_guide/examples/online_serving/qwen3_omni.md
- Text-To-Speech: user_guide/examples/online_serving/text_to_speech.md - Text-To-Speech: user_guide/examples/online_serving/text_to_speech.md
- Text-To-Image: user_guide/examples/online_serving/text_to_image.md
- Text-To-Video: user_guide/examples/online_serving/text_to_video.md - Text-To-Video: user_guide/examples/online_serving/text_to_video.md
- General: - General:
- usage/* - usage/*
+43 -26
View File
@@ -24,22 +24,15 @@ SERVING_MODE_TITLES = {
"online_serving": "Online serving", "online_serving": "Online serving",
} }
# Keep the Examples navigation focused on reusable modality/task entry points. # Generated example pages keep their source-derived URLs even when their
# Model-specific scripts remain available under ``examples/`` and are surfaced # navigation category changes. Quantization helpers are feature documentation,
# from these shared pages instead of getting one page per model. # not user-serving examples, so keep them under the existing Features /
GENERAL_EXAMPLE_SLUGS = frozenset( # Quantization section instead of creating a second top-level Quantization
{ # category under Examples.
"image_to_image", EXAMPLE_NAV_TARGETS = {
"image_to_video", "quantization": "quantization",
"speech_to_video", }
"text_to_audio", GENERATED_QUANTIZATION_EXAMPLES_TITLE = "Generated Examples"
"text_to_image",
"text_to_speech",
"text_to_video",
"x_to_text",
"x_to_video_audio",
}
)
def load_model_display_names() -> dict[str, str]: def load_model_display_names() -> dict[str, str]:
@@ -63,13 +56,6 @@ def load_model_display_names() -> dict[str, str]:
MODEL_DISPLAY_NAMES = load_model_display_names() MODEL_DISPLAY_NAMES = load_model_display_names()
def is_general_example(example: "Example") -> bool:
"""Return whether an example should get a generated serving page."""
if example.category not in SERVING_MODE_TITLES:
return True
return example.path.stem in GENERAL_EXAMPLE_SLUGS
def fix_case(text: str) -> str: def fix_case(text: str) -> str:
subs = { subs = {
"api": "API", "api": "API",
@@ -372,9 +358,12 @@ def update_nav_file(examples: list[Example]):
items.append({example.nav_title: rel_path.as_posix()}) items.append({example.nav_title: rel_path.as_posix()})
return items return items
# Build the new Examples section - start with preserved items. # Build the new Examples section - start with preserved items. Categories
# not explicitly routed elsewhere retain the historical Examples behavior.
examples_section = preserved_items.copy() examples_section = preserved_items.copy()
for category in sorted(examples_by_category.keys()): for category in sorted(examples_by_category.keys()):
if EXAMPLE_NAV_TARGETS.get(category, "examples") != "examples":
continue
items = category_items(category) items = category_items(category)
if items: if items:
category_title = fix_case(category.replace("_", " ").title()) category_title = fix_case(category.replace("_", " ").title())
@@ -383,6 +372,36 @@ def update_nav_file(examples: list[Example]):
# Update the nav structure # Update the nav structure
nav_list[user_guide_idx]["User Guide"][examples_idx]["Examples"] = examples_section 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 # Write back to file
nav_data["nav"] = nav_list nav_data["nav"] = nav_list
with open(NAV_FILE, "w", encoding="utf-8") as f: with open(NAV_FILE, "w", encoding="utf-8") as f:
@@ -413,8 +432,6 @@ def on_startup(command: Literal["build", "gh-deploy", "serve"], dirty: bool):
for path in category.glob("*/*.md"): for path in category.glob("*/*.md"):
examples.append(Example(path.parent, category.stem)) examples.append(Example(path.parent, category.stem))
examples = [example for example in examples if is_general_example(example)]
# Generate the example documentation # Generate the example documentation
for example in sorted(examples, key=lambda e: e.path.stem): for example in sorted(examples, key=lambda e: e.path.stem):
example_name = f"{example.path.stem}.md" example_name = f"{example.path.stem}.md"
+15 -128
View File
@@ -4,17 +4,6 @@ This recipe uses online FP8 weight quantization, tiled VAE decode, and a single
resident partition. GB10 is a unified-memory platform, so unlike the discrete-GPU resident partition. GB10 is a unified-memory platform, so unlike the discrete-GPU
recipes it uses **no offload of any kind** — see the capacity note below. recipes it uses **no offload of any kind** — see the capacity note below.
Validated on:
- Host: DGX Spark (GB10), aarch64
- GPUs: 1 (unified memory)
- Driver: <FILL_DRIVER_VERSION>
- vLLM: 0.26.0
- vLLM-Omni: `main` at `e1aa6eae75c460cd1893bc320546e81e66973831`
- Workloads: T2VA and Ref2VA, 960x576, `duration=8.0`, 50 steps, `flow_shift=12`,
`seed=1101`
## Capacity requirements ## Capacity requirements
| Resource | DGX Spark (GB10) | | Resource | DGX Spark (GB10) |
@@ -22,8 +11,7 @@ Validated on:
| Unified memory | 128 GB LPDDR5X (~121 GiB usable) | | Unified memory | 128 GB LPDDR5X (~121 GiB usable) |
| GPUs | 1 | | GPUs | 1 |
| Checkpoint storage | 135 GiB per partition | | Checkpoint storage | 135 GiB per partition |
| Measured peak allocator high-water, T2VA (FP8) | 97.7 GiB | | Measured peak allocator high-water (FP8) | 97.7 GiB |
| Measured peak allocator high-water, Ref2VA (FP8) | 102.8 GiB |
`FL2VA` and `Ref2VA` are separate 135 GiB checkpoint partitions. Start one server `FL2VA` and `Ref2VA` are separate 135 GiB checkpoint partitions. Start one server
at a time. at a time.
@@ -41,21 +29,9 @@ math relative to the RTX and datacenter recipes:
fit in 121 GiB. Online FP8 quantizes the DiT only (62 GiB to ~31 GiB); the fit in 121 GiB. Online FP8 quantizes the DiT only (62 GiB to ~31 GiB); the
Qwen3-VL text encoder and both VAEs stay BF16. Qwen3-VL text encoder and both VAEs stay BF16.
With ~97.7 GiB of the pool held by the allocator at peak, T2VA leaves roughly With ~97.7 GiB of the pool held by the allocator at peak, there is roughly 23 GiB
23 GiB of headroom for the OS, page cache, and any other process on the box. of headroom for the OS, page cache, and any other process on the box. Treat this
Ref2VA is tighter on both counts. The allocator high-water rises to 102.8 GiB, platform as single-tenant while a request is in flight.
and the allocator figure understates true pool usage.
Sampling `/proc/meminfo` at 1 Hz across a 50-step Ref2VA request on a host with
`MemTotal: 127600524 kB` (121.7 GiB), `MemAvailable` started at 8.1 GiB, fell to
a trough of **7.4 GiB**, and recovered to 8.9 GiB. At the trough the pool held
roughly **114.3 GiB** — 11.4 GiB more than the 102.8 GiB the allocator reports,
which is the CUDA context, non-PyTorch allocations, and page cache the header
does not count.
Treat this platform as strictly single-tenant for Ref2VA. There is no room for a
second process, and adding reference images, reference videos, or a larger output
shape will run into the OOM killer.
## One GB10: 960x576, 8 seconds ## One GB10: 960x576, 8 seconds
@@ -118,52 +94,6 @@ ffprobe -v error -show_entries \
Both requests return `200 OK` with an MP4 body: H.264 video at the requested Both requests return `200 OK` with an MP4 body: H.264 video at the requested
geometry plus a 32 kHz stereo AAC track. geometry plus a 32 kHz stereo AAC track.
## One GB10: Ref2VA, 960x576, 8 seconds
`FL2VA` and `Ref2VA` are separate 135 GiB partitions and only one fits at a time.
Stop the FL2VA server before starting this one.
```bash
CUDA_VISIBLE_DEVICES=0 \
FLASHINFER_DISABLE_VERSION_CHECK=1 \
VLLM_WORKER_MULTIPROC_METHOD=spawn \
VLLM_OMNI_VIDEO_SYNC_TIMEOUT=14400 \
vllm serve /path/to/MiniMax-H3/Ref2VA \
--omni --trust-remote-code --host 0.0.0.0 --port 8000 \
--init-timeout 3600 \
--num-gpus 1 --tensor-parallel-size 1 --text-encoder-tp-size 1 \
--usp 1 --ring 1 --vae-patch-parallel-size 1 \
--vae-parallel-mode tile --vae-use-tiling \
--quantization fp8 --enforce-eager \
--diffusion-attention-backend CUDNN_ATTN
```
The 7200 s timeout used for T2VA is too short here: a 50-step Ref2VA request
takes over an hour on this platform. Use 14400 s.
Unlike `t2va`, `ref2va` defaults to a 16:9 output ratio, so `aspect_ratio` can be
omitted when `width` and `height` are supplied.
```bash
curl --fail-with-body -sS -X POST http://127.0.0.1:8000/v1/videos/sync \
-D ref2va-headers.txt \
-w 'client_e2e_s=%{time_total}\n' \
--max-time 14400 \
-F 'input_reference=@/path/to/reference.jpg;type=image/jpeg' \
-F 'prompt=A hand rests on a wooden table in warm evening light, then slowly opens and turns toward the camera, with quiet room ambience.' \
-F 'width=960' \
-F 'height=576' \
-F 'fps=24' \
-F 'num_inference_steps=50' \
-F 'flow_shift=12' \
-F 'seed=1101' \
-F 'extra_params={"task":"ref2va","duration":8.0,"audio_flow_shift":3.0}' \
-o h3-gb10-ref2va.mp4
```
The response is `200 OK` with an MP4 body: H.264 at the requested geometry plus a
32 kHz stereo AAC track. `ffprobe` reports 8.032 s for `duration=8.0`.
## Measured latency ## Measured latency
Single GB10, FP8, eager, cuDNN attention, tiled VAE, one request at a time: Single GB10, FP8, eager, cuDNN attention, tiled VAE, one request at a time:
@@ -172,69 +102,32 @@ Single GB10, FP8, eager, cuDNN attention, tiled VAE, one request at a time:
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| T2VA, 4 s, smoke shape | 10 | — | — | 8.68 s | — | 0.49 s | 95.9 s | — | | T2VA, 4 s, smoke shape | 10 | — | — | 8.68 s | — | 0.49 s | 95.9 s | — |
| T2VA, 8 s, 960x576 | 50 | 0.25 s | 2088 s | 42.61 s | 70 s | 4.84 s | 2169.4 s (36 min 9 s) | 97.7 GiB | | T2VA, 8 s, 960x576 | 50 | 0.25 s | 2088 s | 42.61 s | 70 s | 4.84 s | 2169.4 s (36 min 9 s) | 97.7 GiB |
| Ref2VA, 8 s, 960x576, one image (3 runs) | 10 | 8.43-10.94 s | 665.5-748.8 s | 66.55-74.88 s | 67.9-69.8 s | 3.63 s | 770.3-861.1 s | 102.8 GiB |
| Ref2VA, 8 s, 960x576, one image | 50 | 9.66 s | 4039.6 s | 80.79 s | 69.4 s | 3.63 s | 4157.0 s (69 min 17 s) | 102.8 GiB |
How these were measured: How these were measured:
- Stage times come from `--enable-diffusion-pipeline-profiler`, read from the - Stage times come from `--enable-diffusion-pipeline-profiler`, read from the
`X-Stage-Durations` response header of `/v1/videos/sync`. The keys are `X-Stage-Durations` response header of `/v1/videos/sync`.
`MiniMaxH3Pipeline.encode_prompt` (text encode),
`MiniMaxH3Pipeline.diffuse` (denoise), and `MiniMaxH3Pipeline.decode`
(VAE decode).
- MP4 mux is not a header field. It is the
`Video response encoding (MP4 bytes)` line in the server log.
- Peak memory is the `X-Peak-Memory-MB` header, which reports the CUDA allocator - Peak memory is the `X-Peak-Memory-MB` header, which reports the CUDA allocator
high-water mark (`torch.cuda.max_memory_reserved`) for the request. It excludes high-water mark (`torch.cuda.max_memory_reserved`) for the request. It excludes
the CUDA context and non-PyTorch allocations, so true pool usage is somewhat the CUDA context and non-PyTorch allocations, so true pool usage is somewhat
higher. `nvidia-smi --query-gpu=memory.used` is not usable on GB10 — unified higher. `nvidia-smi --query-gpu=memory.used` is not usable on GB10 — unified
memory has no discrete VRAM to report and the field returns `[N/A]`. See the memory has no discrete VRAM to report and the field returns `[N/A]`.
capacity section for a `/proc/meminfo` cross-check. - Text encode and VAE decode were measured on a separate 10-step run at the same
- For T2VA, text encode and VAE decode were measured on a separate 10-step run at 960x576 / 8 s geometry. Both are independent of step count, so they transfer to
the same 960x576 / 8 s geometry. Both are independent of step count, so they the 50-step row unchanged.
transfer to the 50-step row unchanged. The Ref2VA MP4 mux figure transfers the
same way, from the 10-step run at identical output geometry and duration.
- Ref2VA peak memory is byte-identical between the 10-step and 50-step runs
(`X-Peak-Memory-MB: 105318.000` in both), confirming that peak is set by output
geometry and reference input rather than step count.
- Stage times do not sum exactly to end-to-end. Queueing, result transfer, and - Stage times do not sum exactly to end-to-end. Queueing, result transfer, and
engine bookkeeping sit outside the profiled stages. engine bookkeeping sit outside the profiled stages.
Denoise dominates at 96% of end-to-end, so step count and geometry are the only Denoise dominates at 96% of end-to-end, so step count and geometry are the only
knobs that materially change the wall clock on this platform. knobs that materially change the wall clock on this platform.
Ref2VA costs 1.92x the wall clock of T2VA at the same geometry and step count: Repeated identical 10-step requests varied by up to 23% in denoise time
4157.0 s versus 2169.4 s. Text encoding shows the most dramatic ratio — 9.66 s
versus 0.25 s, 39x — but at 0.2% of end-to-end it is not what makes Ref2VA slow.
The cost is in denoise. A single reference image expands the Qwen3-VL
presentation to 6883 tokens (logged as `MiniMax H3 ref2va Qwen presentation:
6883 tokens, 1 reference images`), and those tokens sit in the attention context
of every denoise step. Per-step time rises from 42.61 s to 80.79 s, and that
1.90x multiplies across all 50 steps. VAE decode is unchanged at ~69 s, as
expected for an identical output shape.
Repeated identical 10-step T2VA requests varied by up to 23% in denoise time
(397 s to 490 s) on a machine that had already been under sustained load. Treat (397 s to 490 s) on a machine that had already been under sustained load. Treat
these as single-run measurements on one machine, not a throughput guarantee, and these as single-run measurements on one machine, not a throughput guarantee, and
expect the slower end of that range for back-to-back jobs. expect the slower end of that range for back-to-back jobs.
Ref2VA per-step time is load-dependent in the same way. On an idle machine the
first 10-step request ran at 66.55 s per step. The 50-step request that followed
occupied the box for 69 consecutive minutes and averaged 80.79 s per step. Two
further 10-step requests taken immediately afterwards settled at 74.88 s and
74.31 s — 12.5% slower than the idle run, and within 0.8% of each other, so this
is a stable thermal state rather than measurement noise.
Plan for 81 s per step when sizing a full 50-step Ref2VA job; 66 s per step is
only reachable on a cold machine and does not hold for the length of one request.
Ref2VA text encode converged downward across the four runs (10.94 s, 9.66 s,
8.52 s, 8.43 s) as first-touch costs fell away; treat ~8.5 s as steady state.
VAE decode was stable at 67.9-69.8 s throughout.
Set `VLLM_OMNI_VIDEO_SYNC_TIMEOUT` well above the expected request time — the Set `VLLM_OMNI_VIDEO_SYNC_TIMEOUT` well above the expected request time — the
default 1800 s is shorter than a 50-step run on this platform, and 7200 s is default 1800 s is shorter than a 50-step run on this platform.
shorter than a 50-step Ref2VA run.
## Known limitations ## Known limitations
@@ -246,17 +139,11 @@ shorter than a 50-step Ref2VA run.
- `t2va` rejects requests without an explicit `aspect_ratio`. When `width` and - `t2va` rejects requests without an explicit `aspect_ratio`. When `width` and
`height` are both supplied they take precedence and `aspect_ratio` only has to `height` are both supplied they take precedence and `aspect_ratio` only has to
name one of the supported ratios. name one of the supported ratios.
- Sustained load degrades throughput. See the variance notes above. - Sustained load degrades throughput. See the variance note above.
- Only one partition is resident. For Ref2VA, stop the FL2VA server and restart - Only one partition is resident. For Ref2VA, stop the FL2VA server and restart
with `/path/to/MiniMax-H3/Ref2VA`. Ref2VA with one reference image is measured the same command with `/path/to/MiniMax-H3/Ref2VA`. Ref2VA reference video
above; reference videos and additional images push the token count higher and count and prompt length increase activation memory; begin with one request at
were not measured. With `MemAvailable` bottoming out at 7.4 GiB during a a time and keep the short edge at 576.
single-image request, there is very little room to grow — re-measure before
adding references or raising the output shape.
- Ref2VA requires a vLLM-Omni build newer than `v0.26.0`. See the version note
under **Validated on**: FP8 weight loading and image-only Ref2VA are both
broken on `release/v0.26.0`.suggestion is `v0.26.1`.
- Online FP8 is incompatible with layerwise offload — the offload path produces a - Online FP8 is incompatible with layerwise offload — the offload path produces a
weight stride the Cutlass FP8 kernel rejects. This is not a practical weight stride the Cutlass FP8 kernel rejects. This is not a practical
restriction here since offload is unusable on GB10 anyway. restriction here since offload is unusable on GB10 anyway.
```
@@ -69,7 +69,6 @@ def test_h3_fused_rope_matches_reference_and_preserves_unrotated_dims():
attention = object.__new__(MiniMaxH3Attention) attention = object.__new__(MiniMaxH3Attention)
nn.Module.__init__(attention) nn.Module.__init__(attention)
attention.rot_dim = 96
attention.rope = RotaryEmbedding(is_neox_style=True, half_head_dim=False) attention.rope = RotaryEmbedding(is_neox_style=True, half_head_dim=False)
attention.rope._forward_method = attention.rope.forward_native attention.rope._forward_method = attention.rope.forward_native
+108 -5
View File
@@ -6,6 +6,7 @@ from pathlib import Path
from types import SimpleNamespace from types import SimpleNamespace
import pytest import pytest
import yaml
pytestmark = [pytest.mark.core_model, pytest.mark.cpu] pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
@@ -46,9 +47,111 @@ def test_model_display_names_require_string_mapping(tmp_path, monkeypatch):
generate_examples.load_model_display_names() generate_examples.load_model_display_names()
def test_only_general_serving_examples_get_generated_pages(): def test_quantization_examples_keep_source_urls_under_feature_navigation(tmp_path, monkeypatch):
general_example = SimpleNamespace(category="offline_inference", path=Path("text_to_image")) nav_file = tmp_path / "docs" / ".nav.yml"
model_example = SimpleNamespace(category="offline_inference", path=Path("qwen3_omni")) 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",
)
assert generate_examples.is_general_example(general_example) monkeypatch.setattr(generate_examples, "ROOT_DIR", tmp_path)
assert not generate_examples.is_general_example(model_example) 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
-14
View File
@@ -68,7 +68,6 @@ class RMSNorm(CustomOp):
def __init__(self, hidden_size: int, eps: float = 1e-6, dtype: torch.dtype = torch.float32) -> None: def __init__(self, hidden_size: int, eps: float = 1e-6, dtype: torch.dtype = torch.float32) -> None:
super().__init__() super().__init__()
self.weight = nn.Parameter(torch.ones(hidden_size, dtype=dtype)) self.weight = nn.Parameter(torch.ones(hidden_size, dtype=dtype))
self.hidden_size = hidden_size
self.variance_epsilon = eps self.variance_epsilon = eps
def _forward_fused(self, x: torch.Tensor) -> torch.Tensor: def _forward_fused(self, x: torch.Tensor) -> torch.Tensor:
@@ -108,19 +107,6 @@ class RMSNorm(CustomOp):
except Exception: except Exception:
return self.forward_native(x) return self.forward_native(x)
def forward_musa(
self,
x: torch.Tensor,
) -> torch.Tensor:
# Preserve the aten::rms_norm graph so dynamic Inductor can fuse the
# H3 Q/K norm with its inline RoPE path on MUSA.
return F.rms_norm(
x,
(self.hidden_size,),
self.weight,
self.variance_epsilon,
)
def forward_npu( def forward_npu(
self, self,
x: torch.Tensor, x: torch.Tensor,
+1 -10
View File
@@ -193,16 +193,7 @@ class RotaryEmbedding(CustomOp):
cos: torch.Tensor, cos: torch.Tensor,
sin: torch.Tensor, sin: torch.Tensor,
) -> torch.Tensor: ) -> torch.Tensor:
# Keep the full-dimension NeoX form inline for MUSA compilation. return self.forward_native(x, cos, sin)
# Other layouts use the shared native implementation.
if self.half_head_dim or self.interleaved:
return self.forward_native(x, cos, sin)
if cos.dim() == 3:
cos, sin = cos[0], sin[0]
cos = cos.unsqueeze(-2)
sin = sin.unsqueeze(-2)
x1, x2 = x.chunk(2, dim=-1)
return x * cos + torch.cat((-x2, x1), dim=-1) * sin
def forward_native( def forward_native(
self, self,
@@ -333,7 +333,6 @@ class MiniMaxH3Attention(nn.Module):
) )
self.num_heads = self.qkv_proj.num_heads self.num_heads = self.qkv_proj.num_heads
self.num_kv_heads = self.qkv_proj.num_kv_heads self.num_kv_heads = self.qkv_proj.num_kv_heads
self.rot_dim = 6 * arch.rope_inv_freq_len
self.q_norm = _norm(arch.attention_head_dim, eps=arch.qk_norm_eps) self.q_norm = _norm(arch.attention_head_dim, eps=arch.qk_norm_eps)
self.k_norm = _norm(arch.attention_head_dim, eps=arch.qk_norm_eps) self.k_norm = _norm(arch.attention_head_dim, eps=arch.qk_norm_eps)
self.rope = RotaryEmbedding(is_neox_style=True, half_head_dim=False) self.rope = RotaryEmbedding(is_neox_style=True, half_head_dim=False)
@@ -366,7 +365,7 @@ class MiniMaxH3Attention(nn.Module):
x: [T, heads, head_dim]; freqs: [T, rot_dim]. In the unfused path, cos/sin x: [T, heads, head_dim]; freqs: [T, rot_dim]. In the unfused path, cos/sin
are cast to the activation dtype before the elementwise math. are cast to the activation dtype before the elementwise math.
""" """
rot_dim = self.rot_dim rot_dim = freqs.shape[-1]
x_rot, x_pass = x[..., :rot_dim], x[..., rot_dim:] x_rot, x_pass = x[..., :rot_dim], x[..., rot_dim:]
cos = torch.cos(freqs).to(x.dtype) # [T, rot_dim] cos = torch.cos(freqs).to(x.dtype) # [T, rot_dim]
sin = torch.sin(freqs).to(x.dtype) sin = torch.sin(freqs).to(x.dtype)