Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LICENSE @NVIDIA/modelopt-setup-codeowners
LICENSE_HEADER @NVIDIA/modelopt-setup-codeowners
pyproject.toml @NVIDIA/modelopt-setup-codeowners
SECURITY.md @NVIDIA/modelopt-setup-codeowners
tox.ini @NVIDIA/modelopt-setup-codeowners
noxfile.py @NVIDIA/modelopt-setup-codeowners
uv.lock @NVIDIA/modelopt-setup-codeowners

# Library
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/code_quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ jobs:
with:
extra_args: --results=verified,unknown
- name: Run code quality checks
run: pip install tox && tox -e pre-commit-all
run: pip install nox uv && nox -s pre_commit_all
16 changes: 5 additions & 11 deletions .github/workflows/gpu_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ jobs:
files: |
.github/workflows/gpu_tests.yml
modelopt/**
noxfile.py
pyproject.toml
tests/gpu/**
tests/gpu_megatron/**
tests/gpu_regression/**
tests/gpu_trtllm/**
tests/gpu_regression/**
examples/speculative_decoding/**
examples/dataset/**
modelopt_recipes/general/speculative_decoding/**
tools/launcher/**
pyproject.toml
tox.ini
fail_on_initial_diff_error: true
wait-checks:
needs: [check-file-changes]
Expand Down Expand Up @@ -101,14 +101,8 @@ jobs:
COVERAGE_PROCESS_START: ${{ github.workspace }}/pyproject.toml
COVERAGE_FILE: ${{ github.workspace }}/.coverage
run: |
# nemo containers use uv venvs which is not compatible with tox-current-env, so run tests directly
if [[ "${{ matrix.example }}" == "gpu_megatron" ]]; then
python -m pip install -e .[hf,dev-test]
python -m pytest tests/gpu_megatron --cov
else
python -m pip install tox tox-current-env
COV_ARGS="--cov" python -m tox -e cuda13-${{ matrix.example }} --current-env
fi
pip install nox
nox -s ${{ matrix.example }}
- name: Upload GPU coverage to Codecov
uses: codecov/codecov-action@v5
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v6
- uses: ./.github/actions/ubuntu-setup
- name: Build docs
run: pip install tox && tox -e build-docs
run: pip install nox uv && nox -s docs
- name: Upload docs artifact
if: github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v4
Expand All @@ -47,7 +47,7 @@ jobs:
- uses: ./.github/actions/ubuntu-setup
- name: Build docs
if: github.event.action != 'closed'
run: pip install tox && tox -e build-docs
run: pip install nox uv && nox -s docs
- name: Deploy / remove PR preview
uses: rossjrw/pr-preview-action@v1
with:
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/ubuntu-setup
- name: Install dependencies
run: pip install tox
- name: Run basic unit tests
run: tox -e py312-torch29-tf_latest-unit
run: pip install nox uv && nox -s "unit-3.12(torch_211, tf_latest)"
- name: Build Wheel
run: |
tox -e build-wheel
nox -s build_wheel
echo "WHEEL_PATH=$(find dist -name "*.whl" | head -n 1)" >> $GITHUB_ENV
- name: Upload GitHub Release Artifact
env:
Expand Down
20 changes: 11 additions & 9 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ on:
paths:
- ".github/workflows/unit_tests.yml"
- "modelopt/**"
- "tests/unit/**"
- "noxfile.py"
- "pyproject.toml"
- "tox.ini"
- "tests/unit/**"
- "tools/launcher/**"
schedule:
- cron: "0 0 * * *" # Nightly
Expand All @@ -38,7 +38,10 @@ jobs:
- uses: actions/checkout@v6
- uses: ./.github/actions/ubuntu-setup
- name: Run unit tests
run: pip install tox && COV_ARGS="--cov" tox -e py312-torch211-tf_latest-unit
env:
COVERAGE_PROCESS_START: ${{ github.workspace }}/pyproject.toml
COVERAGE_FILE: ${{ github.workspace }}/.coverage
run: pip install nox uv && nox -s "unit-3.12(torch_211, tf_latest)"
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
Expand All @@ -57,8 +60,7 @@ jobs:
with:
python-version: "3.12"
- name: Run unit tests (without coverage)
# Some issues with torch 2.10 on Windows, so using 2.9 for now
run: pip install tox && tox -e py312-torch29-tf_latest-unit
run: pip install nox uv && nox -s "unit-3.12(torch_211, tf_latest)"
multi-py:
if: github.event_name == 'pull_request'
needs: [linux]
Expand All @@ -74,7 +76,7 @@ jobs:
with:
python-version: "3.${{ matrix.py }}"
- name: Run unit tests
run: pip install tox && tox -e py3${{ matrix.py }}-torch211-tf_latest-unit
run: pip install nox uv && nox -s "unit-3.${{ matrix.py }}(torch_211, tf_latest)"
multi-torch:
if: github.event_name == 'pull_request'
needs: [linux]
Expand All @@ -88,7 +90,7 @@ jobs:
- uses: actions/checkout@v6
- uses: ./.github/actions/ubuntu-setup
- name: Run unit tests
run: pip install tox && tox -e py312-torch${{ matrix.torch }}-tf_latest-unit
run: pip install nox uv && nox -s "unit-3.12(torch_${{ matrix.torch }}, tf_latest)"
multi-transformers:
if: github.event_name == 'pull_request'
needs: [linux]
Expand All @@ -102,7 +104,7 @@ jobs:
- uses: actions/checkout@v6
- uses: ./.github/actions/ubuntu-setup
- name: Run unit tests
run: pip install tox && tox -e py312-torch211-tf_${{ matrix.tf }}-unit
run: pip install nox uv && nox -s "unit-3.12(torch_211, tf_min)"
launcher:
if: github.event_name == 'pull_request'
needs: [linux]
Expand Down Expand Up @@ -133,7 +135,7 @@ jobs:
- uses: actions/checkout@v6
- uses: ./.github/actions/ubuntu-setup
- name: Run unit tests
run: pip install tox && tox -e py312-partial-unit-${{ matrix.test-env }}
run: pip install nox uv && nox -s "partial_unit(subset='${{ matrix.test-env }}')"
unit-pr-required-check:
# Run even if some jobs are skipped
if: ${{ github.event_name == 'pull_request' && always() }}
Expand Down
1 change: 0 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
".mypy_cache": true,
".pytest_cache": true,
".ruff_cache": true,
".tox": true,
"**/__pycache__/**": true,
"**/*.pyc": true,
"**/runs": true,
Expand Down
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ Primarily Python codebase with optional C++/CUDA extensions supporting PyTorch,
| Pattern match | `pytest tests/unit -k "test_quantize"` |
| Lint + format (all files) | `pre-commit run --all-files` |
| Lint (diff only) | `pre-commit run --from-ref origin/main --to-ref HEAD` |
| Run via tox (CPU unit) | `tox -e py312-torch210-tf_latest-unit` |
| Build docs | `tox -e build-docs` |
| Build wheel | `tox -e build-wheel` |
| Run via nox (CPU unit) | `nox -s "unit-3.12(torch_211, tf_latest)"` |
| Build docs | `nox -s docs` |
| Build wheel | `nox -s build_wheel` |

## Architecture

Expand Down Expand Up @@ -104,7 +104,7 @@ A **recipe** is a declarative YAML specification of an optimization configuratio
| `modelopt_recipes/general/ptq/` | Built-in PTQ recipe YAML files |
| `pyproject.toml` | Optional dependency groups (`[onnx]`, `[hf]`, `[all]`, `[dev]`); ruff, mypy, pytest, bandit, and coverage config |
| `.pre-commit-config.yaml` | Pre-commit hooks (ruff, mypy, clang-format, license headers) |
| `tox.ini` | Test environment definitions |
| `noxfile.py` | Test session definitions |

## Design Patterns

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ We use [pytest](https://docs.pytest.org/) for all tests. For any new features /
- `tests/gpu_trtllm`: Fast GPU-based unit tests for the core ModelOpt library for TensorRT-LLM features. In most cases, they should not take more than a few seconds to run.
- `tests/examples`: Integration tests for ModelOpt examples. They should not take more than a few minutes to run. Please refer to [example test README](./tests/examples/README.md) for more details.

Please refer to [tox.ini](./tox.ini) for more details on how to run the tests and their dependencies.
Please refer to [noxfile.py](./noxfile.py) for more details on how to run the tests and their dependencies.

## ✍️ Signing your work

Expand Down
193 changes: 193 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# 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.
"""Nox session definitions for testing, linting, docs, and wheel builds.

Usage:
python -m pip install nox uv # install nox and uv (once)
nox -l # list all sessions
nox -s gpu_megatron # run a GPU session (inside container)
nox -s "unit-3.12(torch_211, tf_latest)" # run a specific unit test combination
nox -s "unit-3.12(torch_211, tf_latest)" -R # force-recreate venv (e.g. after dep changes)
COVERAGE_PROCESS_START=pyproject.toml nox -s "unit-3.12(torch_211, tf_latest)" # with coverage
"""

import glob
import os
import shutil

import nox

nox.options.default_venv_backend = "uv" if shutil.which("uv") else "virtualenv"
nox.options.envdir = "/tmp/.nox"
nox.options.reuse_existing_virtualenvs = True

TORCH_VERSIONS = {
"torch_28": "torchvision~=0.23.0",
"torch_29": "torchvision~=0.24.0",
"torch_210": "torchvision~=0.25.0",
"torch_211": "torchvision~=0.26.0",
}

TRANSFORMERS_VERSIONS = {
"tf_latest": None,
"tf_min": "transformers~=4.56.0",
}


def _cov_args():
"""Return --cov when COVERAGE_PROCESS_START is set (CI only)."""
return ["--cov"] if os.environ.get("COVERAGE_PROCESS_START") else []


# ─── CPU unit tests ───────────────────────────────────────────────────────────
@nox.session(python=["3.10", "3.11", "3.12", "3.13"])
@nox.parametrize("tf_ver", [nox.param(k, id=k) for k in TRANSFORMERS_VERSIONS])
@nox.parametrize("torch_ver", [nox.param(k, id=k) for k in TORCH_VERSIONS])
def unit(session, torch_ver, tf_ver):
"""Unit tests — parametrized over torch and transformers versions."""
session.install(TORCH_VERSIONS[torch_ver], "-e", ".[all,dev-test]")
tf_pin = TRANSFORMERS_VERSIONS[tf_ver]
if tf_pin:
session.install(tf_pin)
session.run("python", "-m", "pytest", "tests/unit", *_cov_args())


@nox.session(python="3.12")
@nox.parametrize("subset", ["onnx", "torch", "torch_deploy"])
def partial_unit(session, subset):
"""Unit tests with partial installs."""
if subset == "onnx":
session.install("torchvision~=0.26.0", ".[onnx,dev-test]")
session.run("python", "-m", "pytest", "tests/unit/onnx")
elif subset == "torch":
session.install("megatron-core", ".[dev-test]")
session.run(
"python",
"-m",
"pytest",
"tests/unit/torch",
"--ignore=tests/unit/torch/deploy",
"--ignore=tests/unit/torch/puzzletron",
)
else: # torch_deploy
session.install(".[onnx,dev-test]")
session.run("python", "-m", "pytest", "tests/unit/torch/deploy")


# ─── GPU sessions (run inside containers — no new venv) ──────────────────────
# `venv_backend="none"` skips creating a new venv so the session runs directly in the container's
# existing Python environment (e.g. /opt/venv in NeMo) instead of an isolated one.
# Use `python -m pip/pytest` to ensure the container's active venv Python is used,
# not a stale PATH entry (e.g. NeMo container has pip → /usr/local/bin/pip but python → /opt/venv/bin/python).
# Container: nvcr.io/nvidia/pytorch:26.01-py3 or later
@nox.session(venv_backend="none")
def gpu(session):
# tests/gpu/_extensions/test_onnx_extensions.py fails for newer containers
# until https://github.com/tbenthompson/cppimport/pull/98
session.run(
"python",
"-m",
"pip",
"install",
"--no-build-isolation",
"git+https://github.com/Dao-AILab/fast-hadamard-transform.git",
)
session.run("python", "-m", "pip", "install", "-e", ".[all,dev-test]")
session.run("python", "-m", "pip", "uninstall", "-y", "cupy-cuda12x")
session.run("python", "-m", "pip", "install", "cupy-cuda13x")
session.run(
"python",
"-m",
"pip",
"install",
"--no-build-isolation",
"git+https://github.com/state-spaces/mamba.git",
"git+https://github.com/Dao-AILab/causal-conv1d.git",
)
session.run("python", "-m", "pytest", "tests/gpu", *_cov_args())


# Container: nvcr.io/nvidia/nemo:26.02 or later
@nox.session(venv_backend="none")
def gpu_megatron(session):
session.run("python", "-m", "pip", "install", "-e", ".[hf,dev-test]")
session.run("python", "-m", "pytest", "tests/gpu_megatron", *_cov_args())


# Container: nvcr.io/nvidia/pytorch:26.01-py3 or later
@nox.session(venv_backend="none")
def gpu_regression(session):
session.run("python", "-m", "pip", "install", "-e", ".[hf,dev-test]")
session.run("python", "-m", "pytest", "tests/gpu_regression", *_cov_args())


# Container: nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc10 or later
@nox.session(venv_backend="none")
def gpu_trtllm(session):
session.run("python", "-m", "pip", "install", "-e", ".[hf,dev-test]")
session.run("python", "-m", "pytest", "tests/gpu_trtllm", *_cov_args())


# ─── Code quality ─────────────────────────────────────────────────────────────
@nox.session
def pre_commit_all(session):
session.install("-e", ".[all,dev-lint]")
session.run("pre-commit", "run", "--all-files", "--show-diff-on-failure")


@nox.session
def pre_commit_diff(session):
session.install("-e", ".[all,dev-lint]")
session.run("pre-commit", "run", "--from-ref", "origin/main", "--to-ref", "HEAD")


# ─── Docs ─────────────────────────────────────────────────────────────────────
@nox.session
def docs(session):
session.install("-e", ".[all,dev-docs]")
shutil.rmtree("docs/build", ignore_errors=True)
shutil.rmtree("docs/source/reference/generated", ignore_errors=True)
with session.chdir("docs"):
session.run(
"sphinx-build",
"source",
"build/html",
"--fail-on-warning",
"--show-traceback",
"--keep-going",
)


@nox.session
def docs_debug(session):
session.install("-e", ".[all,dev-docs]")
shutil.rmtree("docs/build", ignore_errors=True)
shutil.rmtree("docs/source/reference/generated", ignore_errors=True)
with session.chdir("docs"):
session.run("sphinx-autobuild", "source", "build/html", "--host", "0.0.0.0")


# ─── Wheel build ──────────────────────────────────────────────────────────────
@nox.session
def build_wheel(session):
shutil.rmtree("build", ignore_errors=True)
session.install("twine")
session.run("pip", "wheel", "--no-deps", "--wheel-dir=dist", ".")
wheels = glob.glob("dist/*.whl")
session.run("twine", "check", *wheels)
(modelopt_wheel,) = glob.glob("dist/nvidia_modelopt-*.whl")
session.install(modelopt_wheel, "-f", "dist")
with session.chdir("dist"):
session.run("python", "-c", "import modelopt; print(modelopt.__version__)")
Loading
Loading