Skip to content

Reuse a whole recipe via $import, deprecate recipe_type, and backfill the recipes behind NVIDIA's published checkpoints - #2376

Draft
shengliangxu wants to merge 22 commits into
mainfrom
shengliangx/batch-backfill-recipe
Draft

Reuse a whole recipe via $import, deprecate recipe_type, and backfill the recipes behind NVIDIA's published checkpoints#2376
shengliangxu wants to merge 22 commits into
mainfrom
shengliangx/batch-backfill-recipe

Conversation

@shengliangxu

@shengliangxu shengliangxu commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Type of change: new feature (recipe loading + model recipes) with two bug fixes

Two things, the second built on the first:

  1. A recipe can now reuse another recipe whole. A top-level $import brings in the imported recipe's entire body; keys given alongside it override the imported ones. metadata.recipe_type becomes optional and is deprecated along the way.
  2. modelopt_recipes/models/ backfills the recipes behind already-published checkpoints — the NVIDIA-Nemotron-3 releases, plus three aliases for releases an existing general recipe already covers. Nothing here proposes a new quantization scheme: each entry records, in the repo, the scheme a checkpoint NVIDIA has already shipped was built with, so it is reachable from that checkpoint's hub path.

This is the first of several backfill batches. It carries the loader and doc changes plus the checkpoints whose source model is NVIDIA's own. The rest follow one source-model org per PR — Qwen, deepseek-ai, meta-llama, google, moonshotai, zai-org, microsoft, MiniMaxAI, mistralai, meta-models — so each set of recipes can be validated on its own rather than in one 160-file review.

Declaring what kind of recipe a file is

load_recipe read metadata.recipe_type out of the raw YAML before resolving imports, because it needs the schema class to hand to load_config. That made the field impossible to inherit, so a recipe reusing another had to restate a line it could only have copied.

It is now optional, and the loader takes the first of these that answers:

  1. a # modelopt-schema: comment naming the recipe's schema class,
  2. metadata.recipe_typedeprecated; still read and still honoured, so a recipe outside this repo keeps working unchanged,
  3. the recipe it delegates to via a top-level $import.

Whatever a recipe does state must be true, in both directions. A schema comment contradicting a recipe_type is rejected, and so is a recipe importing a different kind of recipe — that used to surface as whatever pydantic made of, say, an eagle section spliced into a PTQ schema.

The concrete recipe classes carry a RECIPE_TYPE ClassVar as the single source of truth and fill metadata.recipe_type in from it, so load_config validates a recipe identically with or without an explicit schema_type.

Only a recipe that another file imports needs the schema comment — that is what $import resolution requires to validate the payload. A recipe nothing imports needs nothing. Since recipe_type is now deprecated, the sweep here drops it from all 77 shipped recipes that carried it and gives 72 of them the schema comment instead (5 already had one), so nothing in-tree depends on the deprecated field.

How the backfilled recipes were derived

Each release's hf_quant_config.json gives the quantized-module list and the formats; two things it cannot express were read out of the published weights directly:

  • Cast vs. calibrated KV cache. hf_quant_config.json records only FP8. use_constant_amax pins amax to the E4M3 max, so a cast KV cache exports k_scale == 1.0 exactly — sampling one k_scale is what separates kv_fp8_cast from kv_fp8, and it is how both Nemotron KV mismatches below surfaced.
  • Layers the model class never builds. A checkpoint declaring num_hidden_layers: 61 with num_nextn_predict_layers: 1 ships model.layers.61, the MTP block, which the HF class does not instantiate. Those tensors are outside the quantized model, so they say nothing about what a recipe should match.

Two limits on what this can establish. It says nothing about calibrationhf_quant_config.json does not record whether a release used max, MSE, or something else, so the calibration in each backfilled recipe below reflects what the recipe is meant to do, not a property recovered from the checkpoint. And a checkpoint's tensor names are not the loaded model's module names: transformers/conversion_mapping.py rewrites keys on load, so each pattern below was checked against the modeling code rather than against the checkpoint. nemotron_h's rules (backbone.model., per-expert up_proj/down_proj fused into 3-D parameters) leave the *mixer.* patterns intact, and the omni-modal releases load through trust_remote_code, where no rewriting applies.

Recipes backfilled

  • NVIDIA-Nemotron-3-Nano-30B-A3B-BF16/ptq/fp8_moe_mamba-kv_fp8_cast — MoE experts and the Mamba in_proj/out_proj quantized, except on layers 4, 11, 18, 25, 32 and 41: the Mamba layer immediately preceding each of the six attention layers, which the release leaves BF16. There is deliberately no NVFP4 sibling — see below.
  • NVIDIA-Nemotron-3-Super-120B-A12B-BF16/ptq/fp8_moe_mamba-kv_fp8_cast — the FP8 sibling of the existing NVFP4 mirrors; uniform, with no per-layer carve-out.
  • Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16/ptq/{fp8-kv_fp8_cast,nvfp4_experts-fp8_rest-kv_fp8_cast} — the omni-modal releases. The NVFP4 one is mixed: routed experts NVFP4, shared experts / Mamba in-out / attention o_proj only FP8, q/k/v BF16. Both add explicit disables for the RADIO vision tower, the Conformer sound encoder and the modality projectors, none of which match a standard exclusion.
  • Three aliases for the Llama-Nemotron FP8 releases (Llama-3_1-Nemotron-Ultra-253B-v1, Llama-3_3-Nemotron-Super-49B-v1, -v1_5). Those releases are uniform FP8 W8A8 with an FP8 KV cache and exclude only lm_head, which general/ptq/fp8_default-kv_fp8_cast already covers, so each is a five-line delegation to it rather than a copied body.

What the model cards say about method

A backfilled recipe claims only what its released checkpoint's model card supports. Checking all twelve NVIDIA-source releases these recipes target turned up one that a PTQ recipe cannot reproduce: nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-NVFP4, whose card says under Stage 4 that "After PTQ, Quantization-Aware Distillation (QAD) was applied for further accuracy recovery" (arXiv 2601.20088). QAD leaves the quantization layout untouched and retrains the weights, so a PTQ recipe can match every module and format and still not produce the release — and no structural check can detect that. tools/launcher/examples/ agrees: that family ships megatron_lm_qad.yaml and an offline_kd_qad.yaml keyed to the NVFP4 checkpoint itself. So that release is left un-backfilled. The FP8 sibling is kept: its card documents Stage 4 as plain post-training quantization with exactly the selective scope the recipe encodes.

Two releases are confirmed PTQ by their cards (Nano-30B FP8; Lightning-30B NVFP4, whose card names the Four-Over-Six recipe and its 1000-sample/32k calibration set). The remaining cards state no method either way.

One fix

  • The Nemotron-3 Super and Ultra NVFP4 recipes quantized the MTP block on the Megatron-Core path, where it is a live model.mtp submodule their broad *mixer.* patterns matched into, contrary to their own descriptions. Hugging Face runs were unaffected — NemotronHPreTrainedModel sets _keys_to_ignore_on_load_unexpected = [r"mtp.*"] and builds no MTP module.

Left for you, not changed here. Those same two recipes also calibrate the KV cache where their releases used a constant amax (published k_scale is exactly 1.0), and a handful of Mamba in_proj / out_proj / latent-proj layers differ from the released map. Altering a validated recipe's numerics is your call, so both are noted rather than edited.

Usage

python examples/hf_ptq/hf_ptq.py \
    --pyt_ckpt_path nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16 \
    --recipe models/nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16/ptq/nvfp4_moe_mamba-kv_fp8_cast \
    --export_path <output>

A recipe that reuses another whole — the shape the aliases and the follow-up batches use:

imports:
  base: general/ptq/nvfp4_default-kv_fp8_cast

$import: base
metadata:
  description: What this checkpoint uses the base recipe for.

Testing

  • tests/unit/recipe/test_loader.py — 18 new cases covering every behaviour change: whole-recipe reuse with no metadata at all; kind resolution from each of the three sources, from a delegation chain and from a $import list; a delegation cycle failing with ValueError rather than recursing; peek_declared_schema including a comment placed below the first YAML line; recipe_type being optional, filled per class, and rejected when it contradicts; a directory recipe still requiring it; and delegating across kinds being an error, parametrized over both ways of declaring the kind.
  • test_shipped_modelopt_schema_comments_are_in_the_preamble_parse_modelopt_schema stops at the first non-comment line, so a comment written after metadata: is silently ignored. This catches that shape using the parser's own regex.
  • tests/unit/recipe/test_recipe_docs.py — unchanged checks, now also covering the three alias folders, which must be listed in ptq.md like every other models/<org>/<model_id> entry.
  • Full tests/unit/recipe/ — 387 passed, 13 failed. The 13 failures are pre-existing and environment-only: they reproduce identically on an unmodified main checkout in the same venv, where a broken transformer_engine .so makes mtq.quantize raise. They are test_glm_5_3_recipe, test_minimax_m3_recipe, test_qwen3_8_recipe, four test_qwen_vision_recipe cases and six test_step3p7_recipes cases.
  • pre-commit run --all-files — all hooks pass except Regenerate examples/llm_qat/ARGUMENTS.md, which fails with ModuleNotFoundError: No module named 'torch' in its own hook environment and is untouched here.

Not covered by any of this: numerics. Nothing here asserts accuracy, or that running one of these recipes reproduces a released checkpoint's weights. The recipes were derived from the released quantization layout, but this PR does not claim — and does not test — bit-for-bit reproduction.

Before your PR is "Ready for review"

  • Is this change backward compatible?: ✅ — metadata.recipe_type is still read and honoured for recipes outside this repo, the schema comments are inert for direct loads, the loader change only relaxes a check, and the two edits to existing recipes bring them in line with the checkpoints they already claim to mirror.
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: ✅ — no new dependencies.
  • Did you write any new necessary tests?: ✅
  • Did you update Changelog?: ✅ — two feature entries, one deprecation, one bug fix under 0.48.0
  • Did you get Claude approval on this PR?: ❌ — not yet run

🤖 Generated with Claude Code

shengliangxu and others added 12 commits September 10, 2026 03:37
Pairs every model in the Inference Optimized Checkpoints collection with the
recipe that reproduces it, and makes that pairing a test rather than a claim.

`tools/recipe_backfill/` scans each released checkpoint into a per-module
quantization map -- from its `hf_quant_config.json`, its exported scale tensors
and, for the oldest exports, the safetensors headers read with range requests --
and replays each recipe's `quant_cfg` over those module names the way
`set_quantizer_by_cfg` does, then diffs. The snapshot is checked in, so the
check runs offline with no Hub access and no weights.

Of the 96 published checkpoints, 55 turned out to be reproduced by an existing
general or `huggingface/<model_type>` recipe and needed no new file. The rest
are covered by:

New general recipes (KV cache left unquantized, matching how several releases
ship): `nvfp4_default-kv_fp16`, `fp8_default-kv_fp16`, `nvfp4_mlp_only-kv_fp16`,
`nvfp4_experts_only-kv_fp16`.

New architecture recipes: `huggingface/deepseek_v4/ptq/{nvfp4,w4a16_nvfp4}_experts_only`
(DeepSeek's native layout, where `Expert` keeps w1/w2/w3 as parameters);
`huggingface/qwen3_next/ptq/nvfp4_omlp_linear_attn-kv_fp8_cast` (omlp scope
extended to the gated-delta output projection);
`huggingface/qwen3_5/ptq/nvfp4-fp8_attn-kv_none` (W4A4 twin of the W4A16
recipe); `huggingface/phi4mm/ptq/{fp8,nvfp4}-kv_none` (LoRA base layers only).

New checkpoint mirrors: K-EXAONE-2.0-750B-A37B (experts NVFP4 on interior
layers 5-74 only), NVIDIA-Nemotron-3-Nano-30B-A3B (Mamba projections quantized
except on the six layers preceding an attention layer),
NVIDIA-Nemotron-3-Super-120B-A12B FP8, Nemotron-3-Nano-Omni-30B-A3B-Reasoning
(FP8 and mixed NVFP4), Qwen3.5-397B-A17B V2, and Step-3.7-Flash.

Three fixes to existing recipes, each found by the new check:

* `huggingface/minimax_m3_vl/ptq/mxfp8_nvfp4_experts` matched only
  `*mlp.experts*`, but the HF classes name the MoE block `block_sparse_moe`, so
  the routed experts fell through to MXFP8 and the recipe did not reproduce
  nvidia/MiniMax-M3-NVFP4. Adds the `block_sparse_moe.experts` pair its sibling
  `nvfp4_experts_only.yaml` already had, plus a `patch_merge_mlp` disable.
* The Nemotron-3 Super and Ultra recipes quantized the MTP block, which their
  own descriptions say stays BF16 and which the releases ship unquantized.
* `models/deepseek-ai/DeepSeek-V4-Pro-0813` now imports the shared
  `deepseek_v4` snippet instead of repeating the family's layer map.

The Super and Ultra recipes remain approximate mirrors of their AutoQuant
releases -- a few Mamba in/out_proj layers differ, and the releases used a
constant-amax KV cache where the recipes calibrate one. Both are recorded as
`approximate` with the residual spelled out rather than silently changed, since
altering a validated recipe's numerics is the maintainers' call.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Most released checkpoints are reproduced by a portable recipe with no
checkpoint-specific changes, so they had no entry in the `models/` tier and
could only be found through the index doc. They now all have one, keyed by their
source model's hub path -- as a thin **alias** that delegates its whole body to
the recipe that reproduces it:

    imports:
      base: general/ptq/nvfp4_default-kv_fp8_cast

    $import: base
    metadata:
      recipe_type: ptq
      description: meta-llama/Llama-3.1-8B-Instruct as published in ...

Nothing is duplicated: the alias inherits `quantize` -- algorithm and every
`quant_cfg` entry -- from the base, and editing the base changes every alias
pointing at it. 73 aliases across 53 source models.

Making that work needed two changes:

* `load_recipe` rejected any recipe whose raw YAML had no body section, which a
  delegating recipe cannot have -- the body arrives during import resolution. It
  now allows a top-level `$import` to supply it; pydantic still rejects the
  result if the import supplies no body, which `test_load_recipe_delegating_alias_still_needs_a_body`
  pins.
* An imported file must declare a `modelopt-schema`, so every full recipe under
  `general/`, `huggingface/` and `timm/` now declares one. This is inert for
  direct loads -- `load_recipe` always passes an explicit schema -- and only
  makes the file importable.

The aliases are generated by `tools/recipe_backfill/render_aliases.py` from
`recipe_map.json`, so their headers cannot drift from the mapping, and
`published_checkpoints.md` now shows each checkpoint's own recipe path with the
recipe it aliases. `test_every_model_specific_ptq_dir_is_mentioned` now accepts
either doc: an aliased checkpoint has nothing to say in the PTQ guide but is
always in the generated index, while a folder that deviates from a portable
scheme still belongs in ptq.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The release's card lists nvidia/DeepSeek-V4-Flash-NVFP4 as its base_model, which
put the alias under an already-quantized checkpoint. The recipe runs against the
original deepseek-ai/DeepSeek-V4-Flash weights, so the alias now sits there next
to its W4A4 sibling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…guide

Adds the four KV-unquantized general recipes to the built-in table, a section on
delegating a recipe's body with a top-level $import, and a note that every
published checkpoint now has an entry under models/ -- an alias when a portable
recipe reproduces it, a mirror when it deviates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
A recipe had to state `metadata.recipe_type` in its raw YAML, because the loader
reads it before resolving imports in order to pick the schema class. That made
the field impossible to inherit, so every checkpoint alias restated a line it
could only ever have copied from the recipe it imports.

`metadata.recipe_type` is now optional, and the loader takes the first of these
that answers:

1. a `# modelopt-schema:` comment naming the recipe's schema class,
2. `metadata.recipe_type`,
3. the recipe it delegates to via a top-level `$import`.

Stating more than one is allowed; `ModelOptRecipeBase` rejects a recipe whose
`recipe_type` contradicts its schema class rather than letting the file mean two
different things. The concrete recipe classes now carry a `RECIPE_TYPE` ClassVar
that is the single source of truth, and fill `metadata.recipe_type` in from it --
so `load_config` on a recipe validates the same either way, and the four
`_metadata_field(...)` defaults no longer bake in a type each.

Only a recipe that another file **imports** needs the schema comment, since that
is what `$import` resolution requires to validate the payload. The comment is
therefore back to the 10 pre-existing recipes an alias actually imports rather
than the 47 of the previous commit -- the other 37 had no importer, and giving
them one silently changed what untyped `load_config` returns for files unrelated
to this work.

Alias recipes now state neither `recipe_type` nor a schema comment: an alias is
just an import plus a description.

Also fixes a latent bug from the previous commit: `_parse_modelopt_schema` stops
at the first non-comment line, so a schema comment written after `metadata:` is
silently ignored. Four files had one -- they were all in the 37 being reverted,
but `test_shipped_modelopt_schema_comments_are_in_the_preamble` now catches the
shape rather than relying on that.

`tools/precommit/check_modelopt_recipes.py` recognises recipes by schema comment
too, so a recipe that omits `recipe_type` does not silently stop being validated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
A top-level `$import` takes over the whole body, so the importing recipe and the
imported one have to be the same kind. Nothing checked that: a PTQ recipe
importing an EAGLE one spliced an `eagle` section into a PTQ schema and surfaced
as whatever pydantic made of the unknown field, with no hint that the two recipes
disagreed about what they were.

The kinds are now compared as *declared* on both sides -- schema comment or
`metadata.recipe_type`, whichever each uses -- and a mismatch is an error naming
both kinds and both files.

Together with the existing schema-vs-recipe_type check this gives one rule:
whatever a recipe states about its kind must be true, whether it states it once,
twice, or inherits it across an import.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…th tests

`metadata.recipe_type` is now deprecated in recipe YAML: a recipe says what kind
it is with a `# modelopt-schema:` comment, or by delegating to a recipe that
does. It is still read and still honoured, so no existing recipe changes, but
new recipes leave it out -- the 18 recipes this branch adds now declare their
schema instead, and the checkpoint aliases state neither.

Tests for every behaviour change to the recipe subsystem:

* whole-recipe reuse -- a recipe that is nothing but an import inherits
  `quantize` *and* `metadata` verbatim, which is the two-line form of an alias;
* kind resolution from each of the three sources, from a delegation chain, and
  from a `$import` list; a non-recipe schema comment falling through to
  `metadata.recipe_type`; and no declaration at all being an error;
* a delegation cycle failing with a ValueError rather than recursing forever;
* `peek_declared_schema`: reads the preamble, returns None when absent, returns
  None for a comment below the first YAML line, rejects two declarations;
* `RecipeMetadataConfig.recipe_type` being optional, every recipe class filling
  it from its own `RECIPE_TYPE`, and a contradicting value being rejected;
* a directory recipe still requiring `recipe_type`, since its `metadata.yml`
  has no comment preamble to read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…-backfill-recipe

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>

# Conflicts:
#	CHANGELOG.rst
main added `huggingface/step3p7/ptq/` in #2202, which overlaps the checkpoint
mirror this branch wrote for `stepfun-ai/Step-3.7-Flash-NVFP4`. Checked against
the release's own per-module map, their experts-only recipe matches it on every
module and differs in one thing only: the release calibrated its KV scales,
where that recipe pins them to a constant amax (the published `k_scale` is
0.104, not 1.0). Their `nvfp4_mlp_only-kv_fp8` has the right KV mode but also
quantizes the dense MLP of layers 0-2, nine modules the release leaves BF16.

So the released layout is a KV variant of a portable scheme, not a
checkpoint-specific deviation, and belongs in the architecture tier. Adds
`huggingface/step3p7/ptq/nvfp4_experts_only-kv_fp8` -- their recipe with the
calibrated KV unit -- and turns the mirror into an alias of it, dropping 40
lines that restated their wildcards.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The Inference Optimized Checkpoints collection also carries partner-published
releases -- LGAI-EXAONE, stepfun-ai, thinkingmachines, black-forest-labs -- and
this backfill was writing recipes for two of them as if they were NVIDIA's.

Scope is now NVIDIA's own releases. The scan still records the partner
checkpoints, so the data is there if that ever changes, but they are listed
under `unmapped` with the publisher as the reason rather than given recipes.

Removes the K-EXAONE-2.0-750B-A37B checkpoint mirror, and the Step-3.7-Flash
alias together with the `step3p7/ptq/nvfp4_experts_only-kv_fp8` recipe added in
the previous commit purely to reproduce that release -- `huggingface/step3p7/`
is back to exactly what #2202 landed.

88 checkpoints mapped, 8 unmapped, 73 aliases.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The full backfill is too large to validate in one review: 88 checkpoints across
eleven source-model orgs. Split so each org can be checked on its own.

This branch keeps the parts that are shared or not org-specific -- the loader
and config changes, the tooling and its snapshot, the tests, the four
KV-unquantized `general/ptq/*-kv_fp16` recipes, the fixes to existing recipes,
and the checkpoints whose source model is NVIDIA's own. The remaining 77 move to
`shengliangx/batch-backfill-recipe-<org>`, one branch per source org, each
adding only its own `models/<org>/` recipes and the architecture recipes those
depend on (`deepseek_v4`, `phi4mm`, `qwen3_next`, the `qwen3_5` W4A4 variant).

Two consequences worth knowing:

* The 10 one-line `# modelopt-schema:` annotations stay here even though only
  one of them is imported by anything on this branch. They are what makes a
  recipe importable at all, and every org branch needs some subset of them --
  landing them once beats the same edit in six branches.
* Coverage is now deliberately partial, so the "every scanned checkpoint is
  accounted for" test is replaced by one that catches the opposite: a map entry
  naming a checkpoint the collection no longer has. `verify_recipes.py` prints
  the uncovered checkpoints instead of failing on them.

`models/deepseek-ai/DeepSeek-V4-Pro-0813` reverts to main's version here, since
the rewrite that made it a wrapper imports the `deepseek_v4` snippet that leaves
with its org.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://NVIDIA.github.io/Model-Optimizer/pr-preview/pr-2376/

Built to branch gh-pages at 2026-09-11 20:59 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.27778% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.99%. Comparing base (c5d1065) to head (33861e0).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
modelopt/recipe/loader.py 86.95% 6 Missing ⚠️
modelopt/recipe/config.py 95.45% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2376      +/-   ##
==========================================
- Coverage   71.29%   70.99%   -0.31%     
==========================================
  Files         590      590              
  Lines       64583    65001     +418     
==========================================
+ Hits        46046    46149     +103     
- Misses      18537    18852     +315     
Flag Coverage Δ
unit 57.75% <90.27%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

shengliangxu and others added 5 commits September 11, 2026 00:42
The recipe-alias docstring still showed `recipe_type: ptq` in its example, which
is exactly the line an alias no longer needs and which is now deprecated. It also
described the feature as delegating a body, when what it does is give an existing
recipe a second name.

Reframed around that: the minimal form is the import and nothing else, resolving
to the imported recipe whole -- body, algorithm and metadata alike. Overrides are
shown second, as the optional part, with the note that an override replaces a
top-level key rather than merging into it.

Also fixes `_load_recipe_from_file`, which still claimed a recipe must contain
`metadata` with at least `recipe_type`, and marks `metadata.recipe_type`
deprecated where `_peek_recipe_type` lists it.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Nothing on this branch used them: no checkpoint here maps to a `-kv_fp16`
recipe, and the paragraph justifying them cited three releases that are not in
this change. They move to the batches whose checkpoints actually need them --
fp8_default, nvfp4_default and nvfp4_experts_only to the Qwen batch,
nvfp4_mlp_only to deepseek-ai.

The `kv_fp16` KV-mode bullet stays, since `nvfp4_weight_only-kv_fp16` predates
this branch, but no longer points at checkpoints that are not here.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Every recipe in modelopt_recipes now declares its kind with a
`# modelopt-schema:` comment naming its schema class, and carries no
`metadata.recipe_type`. 77 files: 62 PTQ, 8 auto_quantize, 6 DFlash, 1 EAGLE.
The checkpoint aliases declare nothing at all -- they inherit the kind from the
recipe they import.

The comment has to sit in the comment preamble, because
`_parse_modelopt_schema` stops at the first non-comment line; 20 files already
had one and it is re-placed there rather than assumed correct.
`test_shipped_modelopt_schema_comments_are_in_the_preamble` fails on a comment
the parser cannot see, so this cannot regress silently.

`huggingface/vit/ptq/fp8.yaml` had nothing in `metadata` but `recipe_type`,
so it gains the description it should have had.

The loader still reads `recipe_type`, so a recipe outside this repo keeps
working unchanged. Only a directory-format recipe's `metadata.yml` still needs
it, having no preamble to read.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The tool replayed each recipe's quant_cfg over a released checkpoint's
per-module quantization map and reported a match, but it never compared the
calibration algorithm: _numerics_key keyed only on num_bits, block size and
type, scale_bits and four_over_six, and the scanned snapshot carried no
calibration field at all. `algorithm: max` and `algorithm: {method: mse, ...}`
therefore passed identically, as did AWQ variants whose pre_quant_scale tensors
were never tracked. A "verified to reproduce this checkpoint" claim it cannot
actually make is worse than no claim, so drop the tool, the generated
published_checkpoints.md index, and the test that asserted the aliases were
current.

The three checkpoint aliases stay, now as hand-maintained files: they are
ordinary recipes that delegate to general/ptq/fp8_default-kv_fp8_cast so the
recipe is reachable from a released checkpoint's hub path, and their prose says
what the release's scheme is rather than claiming a verified reproduction. They
are listed in ptq.md alongside the checkpoint mirrors, which is also what keeps
test_every_model_specific_ptq_dir_is_mentioned satisfied.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
… names

The block_sparse_moe patterns I added to mxfp8_nvfp4_experts were derived from
the published checkpoint's tensor names, but quant_cfg patterns are replayed
over the *loaded* model by set_quantizer_by_cfg, and transformers rewrites keys
on load. For minimax_m3_vl (transformers/conversion_mapping.py) the rules are:

    WeightRenaming(r"\.block_sparse_moe\.", r"\.mlp\.")
    WeightConverter([".experts.*.w1.weight", ".experts.*.w3.weight"]
                    -> ".experts.gate_up_proj", [MergeModulelist, Concatenate])
    WeightRenaming(r"^patch_merge_mlp\.linear_1\."
                    -> "model.multi_modal_projector.merge_linear_1.")

So block_sparse_moe does not exist in memory -- MiniMaxM3VLDecoderLayer names
the MoE block `mlp` and MiniMaxM3VLExperts holds fused gate_up_proj/down_proj
parameters. `*mlp.experts*` was already the correct pattern and the experts were
never falling through to MXFP8; the recipe was not broken. tests/unit/recipe/
test_minimax_m3_recipe.py asserts exactly this layout and passes without the
change. Likewise patch_merge_mlp loads as multi_modal_projector.merge_linear_*,
which default_disabled_quantizers already excludes via `*multi_modal_projector*`,
so that disable was dead too. Both entries are removed and the changelog entry
claiming a fix is dropped -- there was no bug.

The MTP disables survive, but for a narrower reason than stated. The HF
nemotron_h class sets _keys_to_ignore_on_load_unexpected = [r"mtp.*"] and builds
no MTP module, so `mtp.*` is inert there; the block is only live on the
Megatron-Core path, as `model.mtp`. The comments now say that instead of naming
an `mtp.layers.*.mixer.*` path that was never verified, and the changelog entry
is scoped to Megatron-Core. The copy in fp8_moe_mamba-kv_fp8_cast is dropped
outright: that recipe imports default_disabled_quantizers, which already carries
`mtp.*`, and nothing after it re-enables the block.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
@shengliangxu shengliangxu changed the title Reuse a whole recipe via $import, and record which recipe reproduces each published NVIDIA checkpoint Reuse a whole recipe via $import, deprecate recipe_type, and add the NVIDIA-source checkpoint recipes Sep 11, 2026
nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-NVFP4's model card says, under Stage 4:
"After PTQ, Quantization-Aware Distillation (QAD) was applied for further accuracy
recovery", and cites arxiv 2601.20088. QAD keeps the quantization layout and
retrains the weights, so a PTQ recipe can reproduce which modules are quantized in
which format and still not produce the released checkpoint -- and no structural
check can tell the difference. Shipping it under models/ as a mirror of that
release overstates what running it gives you.

tools/launcher/examples corroborates the card: the Nano-30B family has
megatron_lm_qad.yaml and an offline_kd_qad.yaml keyed to the NVFP4 checkpoint
itself, alongside the PTQ pipelines.

The FP8 sibling stays. Its card documents Stage 4 as plain post-training
quantization with the same selective scope the recipe encodes, and mentions QAD
nowhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
@shengliangxu shengliangxu changed the title Reuse a whole recipe via $import, deprecate recipe_type, and add the NVIDIA-source checkpoint recipes Reuse a whole recipe via $import, deprecate recipe_type, and backfill the recipes behind NVIDIA's published checkpoints Sep 11, 2026
shengliangxu and others added 4 commits September 11, 2026 19:09
The entry read "Add checkpoint recipes", which suggests new quantization schemes
are being proposed here. They are not: every entry records the scheme a
checkpoint NVIDIA has already published was built with, so it is reachable from
that checkpoint's own hub path instead of only from the general tier. Reword to
say so, and state the bar a release has to clear -- its model card has to support
a post-training quantization recipe, which is why the one release documented as
PTQ-then-QAD is left out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The three checkpoint aliases each carried a paragraph explaining how a top-level
$import works -- that it brings in the imported body, that only metadata is
local, that you edit the base recipe to change the result. That is documentation
of the recipe format, not of the recipe, and it belongs in
modelopt_recipes/models/README.md, where it already is. Repeating it in every
alias means it has to be maintained in every alias.

The headers now say what the recipe is: the same recipe as
general/ptq/fp8_default-kv_fp8_cast, recorded at the checkpoint's hub path, FP8
W8A8 with an FP8 KV cache in cast mode, and why no checkpoint-specific body is
needed -- the release excludes only lm_head, which the general recipe's standard
exclusions already cover.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>

# Conflicts:
#	tests/unit/recipe/test_loader.py
Remove the Llama-3.1/3.3-Nemotron FP8 checkpoint-mirror alias recipes
(Llama-3_1-Nemotron-Ultra-253B-v1, Llama-3_3-Nemotron-Super-49B-v1,
Llama-3_3-Nemotron-Super-49B-v1_5) -- these base models are too old to
backfill. Also drop the now-example-less 'Aliases' section from ptq.md
and de-link the reference to it.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant