Files
Shengliang Xu c9b11559f1 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>
2026-04-14 17:49:51 -07:00

62 lines
2.0 KiB
Python

# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Pre-commit hook: normalize .yml to .yaml in modelopt_recipes/.
Standardizes YAML file extensions to ``.yaml`` for consistency. When a
``.yml`` file is detected, it is renamed to ``.yaml`` and the hook exits
with code 1 so the user can re-stage and commit.
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
def main() -> int:
"""Rename .yml files to .yaml, exit 1 if any were renamed."""
renamed: list[tuple[Path, Path]] = []
collisions: list[tuple[Path, Path]] = []
for f in sys.argv[1:]:
path = Path(f)
if path.suffix == ".yml" and path.is_file():
new_path = path.with_suffix(".yaml")
if new_path.exists():
collisions.append((path, new_path))
continue
os.rename(path, new_path)
renamed.append((path, new_path))
if collisions:
for old, new in collisions:
print(f"ERROR: Cannot rename {old} -> {new} (destination already exists)")
return 1
if renamed:
for old, new in renamed:
print(f"Renamed: {old} -> {new}")
print(
f"\n{len(renamed)} file(s) renamed from .yml to .yaml. "
"Please re-stage the changes and commit again."
)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())