Skip to content

Add a general composable $import system for YAML configs, and use it to implement composable recipes#1253

Merged
shengliangxu merged 60 commits intomainfrom
shengliangx/composable-recipes
May 6, 2026
Merged

Add a general composable $import system for YAML configs, and use it to implement composable recipes#1253
shengliangxu merged 60 commits intomainfrom
shengliangx/composable-recipes

Conversation

@shengliangxu
Copy link
Copy Markdown
Collaborator

@shengliangxu shengliangxu commented Apr 14, 2026

What does this PR do?

Type of change: New feature

Adds a general composable YAML config loading layer for ModelOpt configs and recipes. YAML remains the source of truth for configuration data, while Python/Pydantic-compatible types provide schema validation at load time. This PR uses that loader to de-duplicate PTQ recipes, introduce reusable config snippets/presets, and start migrating selected hardcoded quantization presets to YAML.

Problem

  1. Built-in PTQ recipes duplicated numeric format definitions, KV-cache entries, and the default quantizer exclusion list.
  2. YAML snippets were reusable only by convention; they did not declare or validate the schema they were meant to satisfy.
  3. Loading YAML-backed quantization presets from modelopt.torch.quantization.config could not depend on modelopt.recipe without creating circular imports.
  4. Directory-format recipes exposed import-resolution details in the recipe loader and used recipe.yaml plus nested metadata: in a way that made metadata handling inconsistent.

Solution

Shared YAML config loader

  • Adds modelopt.torch.opt.config_loader as the low-level loader used by both modelopt.recipe and modelopt.torch.quantization.config.
  • Keeps the public modelopt.recipe.load_config() entry point, while removing the private modelopt/recipe/_config_loader.py shim.
  • Handles YAML loading, built-in/filesystem path resolution, suffix probing, ExMy conversion for num_bits / scale_bits, $import expansion, and schema validation.
  • Lives below modelopt.recipe in the dependency graph to avoid circular imports from quantization config code.

Composable $import system

Recipes and snippets can declare an imports mapping, then reference entries with {$import: name}.

$import semantics:

  • Dict value: replaced with the imported dict. Multiple imports are supported with ordered precedence; inline keys override imported keys.
  • List entry: schema-driven behavior for strongly typed lists. If the snippet schema matches the containing list type, the imported list is spliced. If the snippet schema matches the list element type, the imported element is appended. Other schema combinations are rejected.
  • Multi-document YAML: supports snippets that need an imports header plus a list body.
  • Recursive and scoped: snippets can import other snippets; import names are scoped per file.
  • Cycle detection: circular imports report a clear error.

Snippet schema validation

  • Every reusable snippet referenced through imports must declare a # modelopt-schema: ... preamble.
  • Snippets are validated after nested imports are resolved.
  • Schema paths are restricted to the modelopt. package and may be Pydantic models, TypedDict classes, or explicitly typed container aliases such as list[QuantizerCfgEntry].
  • Untyped list imports are rejected so list append/splice behavior stays strongly typed.

Recipe model and directory recipe cleanup

  • ModelOptRecipeBase now owns a metadata: RecipeMetadataConfig field.
  • ModelOptPTQRecipe is the PTQ recipe schema; the overlapping YAML-specific PTQ config class was removed.
  • Directory recipes now use metadata.yaml / metadata.yml for top-level metadata fields, plus section files such as quantize.yaml.
  • Directory recipe loading now delegates import resolution to load_config() instead of manually using raw config loading.

Config snippet and preset library

Adds reusable snippets under modelopt_recipes/configs/:

  • numerics/: fp8, nvfp4, nvfp4_static
  • ptq/units/: base_disable_all, default_disabled_quantizers, w8a8_fp8_fp8, w4a4_nvfp4_nvfp4, kv_fp8, kv_fp8_cast, kv_nvfp4_cast
  • ptq/presets/: YAML presets for FP8_DEFAULT_CFG and FP8_KV_CFG

FP8_DEFAULT_CFG and FP8_KV_CFG now load from YAML presets via load_config().

Recipe migration and naming

  • General PTQ recipes now use shared imports instead of repeating the same quantizer fragments inline.
  • General PTQ recipe paths were renamed to KV-first naming, for example:
    • general/ptq/fp8_default-fp8_kv -> general/ptq/fp8_default-kv_fp8
    • general/ptq/fp8_default-fp8_cast_kv -> general/ptq/fp8_default-kv_fp8_cast
    • general/ptq/nvfp4_default-none_kv_gptq -> general/ptq/nvfp4_default-kv_none-gptq
    • general/ptq/nvfp4_default-nvfp4_cast_kv -> general/ptq/nvfp4_default-kv_nvfp4_cast
  • Example docs and examples/llm_ptq/hf_ptq.py --recipe help text were updated to use the new paths.

Pre-commit and documentation

  • Recipe validation accepts $import entries and handles directory recipes using metadata.yaml.
  • The recipe validation hook skips modelopt_recipes/configs/ because those files are reusable snippets, not full recipes.
  • docs/source/guides/10_recipes.rst now documents imports, schema modelines, list append/splice semantics, built-in snippets, built-in recipe paths, directory recipes, and the current recipe data model.

Backward compatibility

  • Existing inline YAML recipes without $import continue to load.
  • modelopt.recipe.load_config() remains public.
  • The built-in recipe path renames are user-visible; callers should update recipe path strings to the KV-first names listed above.

Testing

  • pytest tests/unit/recipe/test_loader.py -q - 90 passed
  • python tools/precommit/check_modelopt_recipes.py ... for the renamed built-in PTQ recipes
  • pre-commit run mypy --files modelopt/onnx/llm_export_utils/quantization_utils.py
  • python -m py_compile examples/llm_ptq/hf_ptq.py
  • git diff --check

Before your PR is "Ready for review"

  • Is this change backward compatible?: Partially. Loader/API behavior is compatible for existing inline YAML recipes, but built-in recipe path names were renamed to KV-first paths.
  • Did you write any new necessary tests?: Yes.
  • Did you update Changelog?: Yes.

@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot Bot commented Apr 14, 2026

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
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a composable YAML imports system (imports + $import) and a new centralized config loader with raw-load + import resolution; updates recipe loading to resolve imports, refactors many PTQ recipes to use shared snippet files, adds numeric/unit presets and tests, and updates pre-commit to exclude modelopt_recipes/configs/.

Changes

Cohort / File(s) Summary
Config loader core
modelopt/torch/opt/config_loader.py, modelopt/recipe/_config_loader.py, modelopt/recipe/loader.py
Introduce a new config loader with _load_raw_config, _resolve_imports, and load_config; replace the old local loader with re-exports and update recipe loader to raw-load then resolve imports (including validation of mapping shapes).
Quantization defaults integration
modelopt/torch/quantization/config.py
Replace inline FP8/kv default dicts with load_config(...) calls to YAML presets and add dict[str, Any] annotations.
Reusable numerics & units
modelopt_recipes/configs/numerics/*.yaml, modelopt_recipes/configs/ptq/units/*, modelopt_recipes/configs/ptq/units/README.md
Add numeric snippets (fp8, nvfp4, nvfp4_static) and PTQ unit fragments (base_disable_all, default_disabled_quantizers, fp8_kv, w8a8_fp8_fp8, w4a4_nvfp4_nvfp4) for composition via $import.
Presets & recipes
modelopt_recipes/configs/ptq/presets/*, modelopt_recipes/general/ptq/*.yaml
Add preset fragments and refactor PTQ recipes to use imports + $import, replacing many inline quantizer entries with imported unit fragments.
Documentation & tooling
docs/source/guides/10_recipes.rst, CHANGELOG.rst, .pre-commit-config.yaml, modelopt_recipes/configs/ptq/presets/README.md
Document the imports/$import system, update CHANGELOG, add README for presets, and update pre-commit hook to exclude modelopt_recipes/configs/.
Tests & validator
tests/unit/recipe/test_loader.py, tools/precommit/check_modelopt_recipes.py
Add extensive unit tests covering import resolution (list/dict, multi-document, recursive/circular, scoping, builtin snippets) and update pre-commit checker to accept/validate "$import" entries in quant_cfg.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant L as modelopt.recipe.loader
    participant R as _load_raw_config
    participant S as _resolve_imports
    participant FS as Files / Builtins

    U->>L: load_recipe(path)
    L->>R: _load_raw_config(path)
    R->>FS: read YAML (file or builtin)
    FS-->>R: raw documents
    R-->>L: parsed raw config (may include imports)
    alt imports present
        L->>S: _resolve_imports(parsed_config)
        S->>R: _load_raw_config(import_path)
        R->>FS: read imported snippet
        FS-->>R: snippet content
        R-->>S: snippet (dict/list/_list_content)
        S->>S: merge / splice / detect circular refs
        S-->>L: resolved config
    end
    L-->>U: final resolved recipe
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main change: introducing a composable $import system for YAML configs and using it to implement composable recipes, which is the primary objective of the entire changeset.
Docstring Coverage ✅ Passed Docstring coverage is 96.43% which is sufficient. The required threshold is 80.00%.
Security Anti-Patterns ✅ Passed PR introduces secure YAML config loading with safe_load() methods and composable imports, no unsafe code patterns or new dependencies detected.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch shengliangx/composable-recipes

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 14, 2026

PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-05-06 00:01 UTC

Commit description:

Introduce an import mechanism that lets recipe YAML files reference reusable
config snippets by name, reducing duplication across recipes.

Syntax:
  imports:
    fp8: configs/numerics/fp8
    base_disable_all: configs/ptq/base_disable_all

  quant_cfg:
    - base_disable_all # string entry → replaced with imported dict or spliced
list
    - quantizer_name: '*weight_quantizer'
      cfg: fp8 # string cfg → replaced with imported dict

Features:
- Dict-based imports (keys are names, values are config paths) — no name conflicts
- Three resolution modes: string cfg value, string list entry (dict), string list entry (list
 splice)
- Recursive resolution with circular import detection
- Path resolution via load_config (built-in library first, then filesystem)
- Works with both single-file and directory recipe formats

New reusable config snippets (modelopt_recipes/configs/):
- numerics/fp8.yml, nvfp4_dynamic.yml, nvfp4_static.yml
- ptq/base_disable_all.yaml, default_disabled_quantizers.yaml

All 6 built-in PTQ recipes converted to use imports, reducing each by ~30 lines.

Pre-commit hook updated to skip configs/ directory and allow string entries in
quant_cfg. load_config() now accepts YAML lists for list-valued snippets.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
@shengliangxu shengliangxu force-pushed the shengliangx/composable-recipes branch from ea910ae to 82d5a12 Compare April 15, 2026 21:10
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
@shengliangxu shengliangxu changed the title Add composable $import system for recipe YAML configs Add a general composable $import system for YAML configs, and refactor some flat YAML config files into composable format Apr 15, 2026
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
it is not in use, so safe to change

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
these classes are not in use outside of the codebase so safe to change

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
@shengliangxu shengliangxu requested a review from a team as a code owner May 5, 2026 00:09
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…e-recipes

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Comment thread modelopt_recipes/configs/ptq/units/README.md Outdated
Copy link
Copy Markdown
Contributor

@realAsma realAsma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks awesome!

Copy link
Copy Markdown
Collaborator

@cjluo-nv cjluo-nv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread examples/llm_ptq/hf_ptq.py Outdated
Comment thread examples/llm_ptq/README.md Outdated
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
shengliangxu and others added 3 commits May 5, 2026 13:38
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
@shengliangxu shengliangxu requested a review from a team as a code owner May 5, 2026 21:24
@shengliangxu shengliangxu merged commit f34f488 into main May 6, 2026
71 of 76 checks passed
@shengliangxu shengliangxu deleted the shengliangx/composable-recipes branch May 6, 2026 00:01
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.

7 participants