Files
Model-Optimizer/.github/workflows/unit_tests.yml
T
Chad Voegele d3fe8117ff Add ModelOpt agent plugin marketplace (#2025)
### What does this PR do?

Type of change: new feature

Packages the existing ModelOpt agent skills as installable Codex and
Claude plugins:

- Adds a repo-scoped Codex marketplace and Claude-compatible
marketplace.
- Adds the canonical `plugins/modelopt/` plugin tree and manifests.
- Moves the skill tree into the plugin and keeps `.agents/skills` as a
compatibility symlink.
- Adds a minimal `common` placeholder skill required by Codex
validation.
- Documents installation from this repository.

### Usage

```bash
codex plugin marketplace add NVIDIA/Model-Optimizer
```

Then open `/plugins`, select the `modelopt` marketplace, and install
`modelopt`.

For Claude Code:

```bash
claude plugin marketplace add https://github.com/NVIDIA/Model-Optimizer.git
claude plugin install modelopt@modelopt
```

### Testing

- Codex plugin validator
- `claude plugin validate . --strict`
- `claude plugin validate plugins/modelopt --strict`

1. Install the marketplace plugin with Codex and Claude from an
unrelated temporary workspace.
2. Exercise packaged evaluation helpers, a day-0 gate, and the shared
remote helper from that workspace.
3. Run `uv run --frozen --extra dev python -m pytest -q
plugins/modelopt/skills/day0-release/tests/test_gates.py
plugins/modelopt/skills/benchmark-model-kernels/tests`.
4. Run pre-commit hooks for all changed files.

### Before your PR is "*Ready for review*"

- Is this change backward compatible?: ✅
- If you copied code from any other sources or added a new PIP
dependency, did you follow guidance in `CONTRIBUTING.md`: N/A
- Did you write any new necessary tests?: ✅ — added a plugin-path
validator; existing focused skill tests and installed-plugin smoke tests
pass.
- Did you update Changelog?: N/A — agent tooling and distribution only.
- Did you get Claude approval on this PR?: N/A

### Additional Information

Skills remain available through `.agents/skills`; bundled helpers are
packaged under the plugin and resolved from `$SKILL_DIR` so installed
workflows do not depend on the current workspace.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added installable ModelOpt plugins for Claude Code and Codex.
* Added skills for PTQ, deployment, evaluation, monitoring, debugging,
benchmarking, MLflow access, EAGLE3 workflows, and release management.
* Added deployment helpers, evaluation recipes, checkpoint validation,
and release-gating tools.
* **Documentation**
* Expanded setup, credential, SLURM, benchmarking, deployment,
evaluation, troubleshooting, and workspace guidance.
* Added installation instructions and updated agent-skill discovery
guidance.
* **Maintenance**
* Updated skill references and compatibility links for reliable use
across supported plugin environments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Chad Voegele <cvoegele@nvidia.com>
2026-08-12 11:33:17 -05:00

213 lines
7.9 KiB
YAML

name: Unit tests
on:
pull_request:
branches: [main, release/*, feature/*]
push:
branches: [main, release/*, feature/*]
paths:
- ".github/workflows/unit_tests.yml"
- "modelopt/**"
- "noxfile.py"
- "pyproject.toml"
- "tests/unit/**"
- "tools/launcher/**"
- "tools/mcp/**"
- "tools/resource_monitor.py"
- ".agents/skills/**"
- "plugins/modelopt/skills/**"
schedule:
- cron: "0 0 * * *" # Nightly
workflow_dispatch:
# On-demand
concurrency:
# Cancel previous runs if new commit is pushed
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
check-file-changes:
runs-on: ubuntu-latest
outputs:
any_changed: ${{ steps.changed.outputs.any_modified || steps.non-pr.outputs.any_changed }}
steps:
- id: non-pr
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
run: echo "any_changed=true" >> $GITHUB_OUTPUT
- if: github.event_name == 'pull_request'
uses: actions/checkout@v6
with:
fetch-depth: 0
- if: github.event_name == 'pull_request'
name: Check for changes in test-relevant paths
id: changed
uses: step-security/changed-files@v47.0.5
with:
files: |
.github/workflows/unit_tests.yml
modelopt/**
modelopt_recipes/**
noxfile.py
pyproject.toml
tests/unit/**
tests/_test_utils/**
tools/launcher/**
tools/mcp/**
tools/resource_monitor.py
.agents/skills/**
plugins/modelopt/skills/**
linux:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/ubuntu-setup
- name: Run unit tests
env:
COVERAGE_PROCESS_START: ${{ github.workspace }}/pyproject.toml
COVERAGE_FILE: ${{ github.workspace }}/.coverage
run: pip install nox uv && nox -s "unit-3.12(torch_213, tf_latest)"
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unit
fail_ci_if_error: true
# Skip GPG/SHASUM integrity check of the Codecov CLI: its key import
# intermittently fails (codecov/codecov-action#1876), which would
# otherwise hard-fail this required job on a transient infra blip.
skip_validation: true
verbose: true
windows:
if: needs.check-file-changes.outputs.any_changed == 'true'
needs: [linux, check-file-changes]
runs-on: windows-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Run unit tests (without coverage)
run: pip install nox uv && nox -s "unit-3.12(torch_213, tf_latest)"
multi-version:
if: needs.check-file-changes.outputs.any_changed == 'true'
needs: [linux, check-file-changes]
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
# Default torch (2.13) across the other supported Python versions
- {nox_session: "unit-3.10(torch_213, tf_latest)", python_version: "3.10"}
- {nox_session: "unit-3.11(torch_213, tf_latest)", python_version: "3.11"}
- {nox_session: "unit-3.13(torch_213, tf_latest)", python_version: "3.13"}
- {nox_session: "unit-3.14(torch_213, tf_latest)", python_version: "3.14"}
# Older torch versions on the default Python (3.12) for back-compat.
- {nox_session: "unit-3.12(torch_28, tf_latest)", python_version: "3.12"}
- {nox_session: "unit-3.12(torch_29, tf_latest)", python_version: "3.12"}
- {nox_session: "unit-3.12(torch_210, tf_latest)", python_version: "3.12"}
- {nox_session: "unit-3.12(torch_211, tf_latest)", python_version: "3.12"}
- {nox_session: "unit-3.12(torch_212, tf_latest)", python_version: "3.12"}
# Minimum supported transformers on the default torch.
- {nox_session: "unit-3.12(torch_213, tf_min)", python_version: "3.12"}
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/ubuntu-setup
with:
python-version: ${{ matrix.python_version }}
- name: Run unit tests
run: pip install nox uv && nox -s "${{ matrix.nox_session }}"
partial-install:
if: needs.check-file-changes.outputs.any_changed == 'true'
needs: [linux, check-file-changes]
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
test-env: [onnx, torch]
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/ubuntu-setup
- name: Run unit tests
run: pip install nox uv && nox -s "partial_unit(subset='${{ matrix.test-env }}')"
launcher:
if: needs.check-file-changes.outputs.any_changed == 'true'
needs: [linux, check-file-changes]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Run launcher tests
working-directory: tools/launcher
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
uv venv .venv
uv pip install -e . pytest
uv run python3 -m pytest -v
mcp:
if: needs.check-file-changes.outputs.any_changed == 'true'
needs: [linux, check-file-changes]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Run modelopt-mcp tests
working-directory: tools/mcp
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
uv venv .venv
# Install the sibling launcher package first; it's a runtime
# dep declared in tools/mcp/pyproject.toml as `modelopt-launcher`
# but uv resolves the source via [tool.uv.sources] to a local
# editable path. -e on both packages keeps the install cheap
# and matches the dev-mode install in tools/mcp/README.md.
uv pip install -e ../launcher
uv pip install -e . pytest
uv run python3 -m pytest -v
skills:
if: needs.check-file-changes.outputs.any_changed == 'true'
needs: [linux, check-file-changes]
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Run skill gate tests
# Skill gate tests are stdlib-only and hermetic (no GPU/cluster/network),
# so they run in their own lightweight job rather than the main unit lane.
# Override addopts to drop the repo's coverage/instafail plugins (not installed here).
run: |
pip install pytest
python -m pytest plugins/modelopt/skills/ -o addopts="" -p no:cacheprovider -v
unit-pr-required-check:
# Run even if some jobs are skipped
if: ${{ github.event_name == 'pull_request' && always() }}
needs: [check-file-changes, linux, windows, multi-version, partial-install, launcher, mcp, skills]
runs-on: ubuntu-latest
steps:
- name: Required unit tests did not succeed
if: >-
${{ needs.linux.result != 'success' || (needs.check-file-changes.outputs.any_changed == 'true' && (
needs.windows.result != 'success' ||
needs.multi-version.result != 'success' ||
needs.partial-install.result != 'success' ||
needs.launcher.result != 'success' ||
needs.mcp.result != 'success' ||
needs.skills.result != 'success'
)) }}
run: exit 1