Skip to content

Commit c9b1155

Browse files
authored
Normalize .yml to .yaml in modelopt_recipes (#1260)
### What does this PR do? Type of change: Chore Standardize YAML file extensions in `modelopt_recipes/` to `.yaml` for consistency. The existing recipes used a mix of `.yml` (PTQ recipes) and `.yaml` (speculative decoding, model-specific recipes). #### Changes **Renamed files:** - `modelopt_recipes/general/ptq/fp8_default-fp8_kv.yml` → `.yaml` - `modelopt_recipes/general/ptq/nvfp4_default-fp8_kv.yml` → `.yaml` - `modelopt_recipes/general/ptq/nvfp4_experts_only-fp8_kv.yml` → `.yaml` - `modelopt_recipes/general/ptq/nvfp4_mlp_only-fp8_kv.yml` → `.yaml` - `modelopt_recipes/general/ptq/nvfp4_omlp_only-fp8_kv.yml` → `.yaml` **New pre-commit hook:** `normalize-yaml-ext` - `tools/precommit/normalize_yaml_ext.py` — auto-renames `.yml` to `.yaml` for any staged file under `modelopt_recipes/`. Runs before recipe validation so future contributions are caught automatically. **Updated references:** - `tests/unit/recipe/test_loader.py` — built-in recipe paths updated to `.yaml` Note: `load_recipe()` and `load_config()` probe both `.yml` and `.yaml` suffixes, so callers using paths without extensions (e.g., `load_recipe("general/ptq/fp8_default-fp8_kv")`) are unaffected. ### Testing Existing recipe loader tests pass with updated paths. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Standardized recipe file extensions and added an automated pre-commit normalization hook to enforce the convention. * **Tests** * Updated unit tests to reference the new recipe filename convention and ensure consistency with configuration loading. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
1 parent 1619421 commit c9b1155

File tree

9 files changed

+74
-7
lines changed

9 files changed

+74
-7
lines changed

.pre-commit-config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ repos:
5757

5858
- repo: local
5959
hooks:
60+
- id: normalize-yaml-ext
61+
name: normalize .yml to .yaml in required places, right now only yaml files in modelopt_recipes
62+
entry: python tools/precommit/normalize_yaml_ext.py
63+
language: system
64+
files: ^modelopt_recipes/.*\.yml$
65+
6066
- id: check-modelopt-recipes
6167
name: validate modelopt recipes
6268
entry: python tools/precommit/check_modelopt_recipes.py
File renamed without changes.

modelopt_recipes/general/ptq/nvfp4_default-fp8_kv.yml renamed to modelopt_recipes/general/ptq/nvfp4_default-fp8_kv.yaml

File renamed without changes.

modelopt_recipes/general/ptq/nvfp4_default-none_kv_gptq.yml renamed to modelopt_recipes/general/ptq/nvfp4_default-none_kv_gptq.yaml

File renamed without changes.

modelopt_recipes/general/ptq/nvfp4_experts_only-fp8_kv.yml renamed to modelopt_recipes/general/ptq/nvfp4_experts_only-fp8_kv.yaml

File renamed without changes.

modelopt_recipes/general/ptq/nvfp4_mlp_only-fp8_kv.yml renamed to modelopt_recipes/general/ptq/nvfp4_mlp_only-fp8_kv.yaml

File renamed without changes.

modelopt_recipes/general/ptq/nvfp4_omlp_only-fp8_kv.yml renamed to modelopt_recipes/general/ptq/nvfp4_omlp_only-fp8_kv.yaml

File renamed without changes.

tests/unit/recipe/test_loader.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,21 +85,21 @@ def test_load_config_missing_file_raises(tmp_path):
8585

8686
def test_load_recipe_builtin_with_suffix():
8787
"""load_recipe loads a built-in PTQ recipe given the full YAML path."""
88-
recipe = load_recipe("general/ptq/fp8_default-fp8_kv.yml")
88+
recipe = load_recipe("general/ptq/fp8_default-fp8_kv.yaml")
8989
assert recipe.recipe_type == RecipeType.PTQ
9090
assert isinstance(recipe, ModelOptPTQRecipe)
9191
assert recipe.quantize
9292

9393

9494
def test_load_recipe_builtin_without_suffix():
95-
"""load_recipe resolves the .yml suffix automatically."""
95+
"""load_recipe resolves the .yaml suffix automatically."""
9696
recipe = load_recipe("general/ptq/fp8_default-fp8_kv")
9797
assert recipe.recipe_type == RecipeType.PTQ
9898

9999

100100
def test_load_recipe_builtin_description():
101101
"""The description field is loaded from the YAML metadata."""
102-
recipe = load_recipe("general/ptq/fp8_default-fp8_kv.yml")
102+
recipe = load_recipe("general/ptq/fp8_default-fp8_kv.yaml")
103103
assert isinstance(recipe.description, str)
104104
assert len(recipe.description) > 0
105105

@@ -196,10 +196,10 @@ def test_load_recipe_dir_missing_quantize_raises(tmp_path):
196196
@pytest.mark.parametrize(
197197
("yaml_path", "model_cfg_name", "kv_cfg_name"),
198198
[
199-
("general/ptq/fp8_default-fp8_kv.yml", "FP8_DEFAULT_CFG", "FP8_KV_CFG"),
200-
("general/ptq/nvfp4_default-fp8_kv.yml", "NVFP4_DEFAULT_CFG", "FP8_KV_CFG"),
201-
("general/ptq/nvfp4_mlp_only-fp8_kv.yml", "NVFP4_MLP_ONLY_CFG", "FP8_KV_CFG"),
202-
("general/ptq/nvfp4_omlp_only-fp8_kv.yml", "NVFP4_OMLP_ONLY_CFG", "FP8_KV_CFG"),
199+
("general/ptq/fp8_default-fp8_kv.yaml", "FP8_DEFAULT_CFG", "FP8_KV_CFG"),
200+
("general/ptq/nvfp4_default-fp8_kv.yaml", "NVFP4_DEFAULT_CFG", "FP8_KV_CFG"),
201+
("general/ptq/nvfp4_mlp_only-fp8_kv.yaml", "NVFP4_MLP_ONLY_CFG", "FP8_KV_CFG"),
202+
("general/ptq/nvfp4_omlp_only-fp8_kv.yaml", "NVFP4_OMLP_ONLY_CFG", "FP8_KV_CFG"),
203203
],
204204
)
205205
def test_general_ptq_yaml_matches_config_dicts(yaml_path, model_cfg_name, kv_cfg_name):
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""Pre-commit hook: normalize .yml to .yaml in modelopt_recipes/.
17+
18+
Standardizes YAML file extensions to ``.yaml`` for consistency. When a
19+
``.yml`` file is detected, it is renamed to ``.yaml`` and the hook exits
20+
with code 1 so the user can re-stage and commit.
21+
"""
22+
23+
from __future__ import annotations
24+
25+
import os
26+
import sys
27+
from pathlib import Path
28+
29+
30+
def main() -> int:
31+
"""Rename .yml files to .yaml, exit 1 if any were renamed."""
32+
renamed: list[tuple[Path, Path]] = []
33+
collisions: list[tuple[Path, Path]] = []
34+
for f in sys.argv[1:]:
35+
path = Path(f)
36+
if path.suffix == ".yml" and path.is_file():
37+
new_path = path.with_suffix(".yaml")
38+
if new_path.exists():
39+
collisions.append((path, new_path))
40+
continue
41+
os.rename(path, new_path)
42+
renamed.append((path, new_path))
43+
44+
if collisions:
45+
for old, new in collisions:
46+
print(f"ERROR: Cannot rename {old} -> {new} (destination already exists)")
47+
return 1
48+
49+
if renamed:
50+
for old, new in renamed:
51+
print(f"Renamed: {old} -> {new}")
52+
print(
53+
f"\n{len(renamed)} file(s) renamed from .yml to .yaml. "
54+
"Please re-stage the changes and commit again."
55+
)
56+
return 1
57+
return 0
58+
59+
60+
if __name__ == "__main__":
61+
raise SystemExit(main())

0 commit comments

Comments
 (0)