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/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Make sure you read and follow the [Security Best Practices](https://github.com/N
- Is this change backward compatible?: ✅ / ❌ / N/A <!--- If ❌, explain why. -->
- If you copied code from any other sources or added a new PIP dependency, did you follow guidance in `CONTRIBUTING.md`: ✅ / ❌ / N/A <!--- Mandatory -->
- Did you write any new necessary tests?: ✅ / ❌ / N/A <!--- Mandatory for new features or examples. -->
- Did you update [Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?: ✅ / ❌ / N/A <!--- Only for new features, API changes, critical bug fixes or backward incompatible changes. -->
- Did you update [Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?: ✅ / ❌ / N/A <!--- Only for new features, backward breaking changes, deprecations, or fixes for critical bugs present in previous releases. -->
- Did you get Claude approval on this PR?: ✅ / ❌ / N/A <!--- Run `/claude review`. NVIDIA org members can self-trigger for complex changes; orthogonal to CodeRabbit. -->

### Additional Information
Expand Down
38 changes: 38 additions & 0 deletions .github/actions/pr-merge-base/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: PR merge base
description: >
Resolve the commit to diff a copied PR branch against: the merge base of the PR's head and its
target branch. Outputs are empty for non-PR triggers, which have no diff to inspect.
Requires a prior actions/checkout with fetch-depth 0.

outputs:
merge_base:
description: Commit to use as the base for changed-file comparisons.
value: ${{ steps.calculate-merge-base.outputs.merge-base }}
head_sha:
description: The PR's head commit.
value: ${{ steps.pr-shas.outputs.head_sha }}

runs:
using: composite
steps:
- if: startsWith(github.ref, 'refs/heads/pull-request/')
id: get-pr-info
uses: nv-gha-runners/get-pr-info@main
Comment thread
kevalmorabia97 marked this conversation as resolved.
# Extract SHAs from pr-info JSON via shell to avoid fromJSON on potentially-empty outputs
- if: startsWith(github.ref, 'refs/heads/pull-request/')
id: pr-shas
shell: bash
env:
PR_INFO: ${{ steps.get-pr-info.outputs.pr-info }}
run: |
echo "head_sha=$(echo "$PR_INFO" | jq -r '.head.sha')" >> $GITHUB_OUTPUT
echo "base_sha=$(echo "$PR_INFO" | jq -r '.base.sha')" >> $GITHUB_OUTPUT
# Get commit from the target branch that is present in the PR to use as base for changed files
- if: startsWith(github.ref, 'refs/heads/pull-request/')
id: calculate-merge-base
shell: bash
run: |
# Assign first: piping git into tee would mask a merge-base failure behind tee's exit
# status and emit an empty base, silently changing which lanes run.
merge_base=$(git merge-base "${{ steps.pr-shas.outputs.base_sha }}" "${{ steps.pr-shas.outputs.head_sha }}")
echo "merge-base=$merge_base" | tee --append "${GITHUB_OUTPUT}"
45 changes: 22 additions & 23 deletions .github/workflows/_pr_gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ on:
description: "Newline-separated list of file patterns to watch for changes"
required: true
type: string
files_ignore:
description: "Newline-separated patterns to ignore; defaults to docs-only files no test runs"
required: false
type: string
default: |
**.ipynb
**.md
**.png
**.rst
Comment thread
kevalmorabia97 marked this conversation as resolved.
outputs:
any_changed:
description: "Whether any relevant files changed"
Expand All @@ -16,7 +25,7 @@ jobs:
check-file-changes:
runs-on: ubuntu-latest
outputs:
any_changed: ${{ steps.changed-tests.outputs.any_changed || steps.non-pr.outputs.any_changed }}
any_changed: ${{ steps.changed-tests.outputs.any_modified || steps.non-pr.outputs.any_changed }}
steps:
# For non-PR triggers (schedule, workflow_dispatch), always run tests
- id: non-pr
Expand All @@ -27,39 +36,29 @@ jobs:
with:
fetch-depth: 0
- if: startsWith(github.ref, 'refs/heads/pull-request/')
id: get-pr-info
uses: nv-gha-runners/get-pr-info@main
# Extract SHAs from pr-info JSON via shell to avoid fromJSON on potentially-empty outputs
- if: startsWith(github.ref, 'refs/heads/pull-request/')
id: pr-shas
env:
PR_INFO: ${{ steps.get-pr-info.outputs.pr-info }}
run: |
echo "head_sha=$(echo "$PR_INFO" | jq -r '.head.sha')" >> $GITHUB_OUTPUT
echo "base_sha=$(echo "$PR_INFO" | jq -r '.base.sha')" >> $GITHUB_OUTPUT
# Get commit from main branch that is present in the PR to use as base for changed files
- if: startsWith(github.ref, 'refs/heads/pull-request/')
id: calculate-merge-base
run: |
(echo -n "merge-base="; git merge-base "${{ steps.pr-shas.outputs.base_sha }}" "${{ steps.pr-shas.outputs.head_sha }}") | tee --append "${GITHUB_OUTPUT}"
id: base
uses: ./.github/actions/pr-merge-base
- if: startsWith(github.ref, 'refs/heads/pull-request/')
name: Check for changes in test-relevant directories
id: changed-tests
uses: step-security/changed-files@v46.0.5
with:
base_sha: ${{ steps.calculate-merge-base.outputs.merge-base }}
sha: ${{ steps.pr-shas.outputs.head_sha }}
base_sha: ${{ steps.base.outputs.merge_base }}
sha: ${{ steps.base.outputs.head_sha }}
files: ${{ inputs.files }}
files_ignore: ${{ inputs.files_ignore }}
fail_on_initial_diff_error: true
wait-checks:
needs: [check-file-changes]
if: >-
startsWith(github.ref, 'refs/heads/pull-request/') &&
needs.check-file-changes.outputs.any_changed == 'true'
uses: ./.github/workflows/_wait_for_checks.yml
runs-on: ubuntu-latest
permissions:
checks: read
secrets: inherit
with:
match_pattern: "^linux$" # Wait for Unit tests / linux (DCO is a prerequisite of linux)
delay: 300s
steps:
- uses: poseidon/wait-for-status-checks@v0.6.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
match_pattern: "^linux$" # Wait for Unit tests / linux
delay: 300s
29 changes: 0 additions & 29 deletions .github/workflows/_wait_for_checks.yml

This file was deleted.

5 changes: 4 additions & 1 deletion .github/workflows/code_quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ jobs:
- name: Secret Scanning
uses: trufflesecurity/trufflehog@v3.90.5
with:
extra_args: --results=verified,unknown
# Exclude lob: its `(live|test)_[a-zA-Z0-9_]{35}` pattern matches any pytest function
# whose name happens to be exactly 35 characters after `test_`, and it reports them as
# verified. Only nightly sees them, since it scans all history while PRs scan their diff.
extra_args: --results=verified,unknown --exclude-detectors=lob
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- name: Run code quality checks
run: pip install nox uv && nox -s pre_commit_all
141 changes: 98 additions & 43 deletions .github/workflows/example_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,49 +18,121 @@ concurrency:
# a comma-separated list of example names whose test failures should be non-blocking, e.g. "torch_trt,llm_qat"

jobs:
# One changed-files pass decides which lanes run. Lane granularity, not per-example: a job's
# `if` cannot read `matrix`, so gating an individual example would need a job per example.
pr-gate:
uses: ./.github/workflows/_pr_gate.yml
runs-on: ubuntu-latest
permissions:
checks: read
secrets: inherit
with:
files: |
.github/actions/cache-extensions/**
.github/workflows/_example_tests_runner.yml
.github/workflows/example_tests.yml
examples/**
modelopt/**
pyproject.toml
tests/examples/**
contents: read
outputs:
any_changed: ${{ steps.lanes.outputs.any_changed }}
torch: ${{ steps.lanes.outputs.torch }}
trtllm: ${{ steps.lanes.outputs.trtllm }}
megatron: ${{ steps.lanes.outputs.megatron }}
onnx: ${{ steps.lanes.outputs.onnx }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- if: startsWith(github.ref, 'refs/heads/pull-request/')
id: base
uses: ./.github/actions/pr-merge-base
- if: startsWith(github.ref, 'refs/heads/pull-request/')
id: changed
uses: step-security/changed-files@v46.0.5
with:
base_sha: ${{ steps.base.outputs.merge_base }}
sha: ${{ steps.base.outputs.head_sha }}
fail_on_initial_diff_error: true
files_ignore: |
**.ipynb
**.md
**.png
**.rst
# `common` runs every lane. Each lane lists the example directories it covers, plus any
# extra directory those tests reach into (e.g. hf_ptq's script runs lm_eval from
# ../llm_eval, and the speculative_decoding test drives hf_ptq).
files_yaml: |
common:
- .github/actions/**
- .github/workflows/_example_tests_runner.yml
- .github/workflows/example_tests.yml
- modelopt/**
- modelopt_recipes/**
- pyproject.toml
- tests/_test_utils/**
- tests/conftest.py
- tests/examples/conftest.py
torch:
- "{examples,tests/examples}/{llm_distill,llm_qat,llm_sparsity,specdec_bench,speculative_decoding}/**"
- examples/dataset/** # data prep for llm_qat and speculative_decoding
- examples/hf_ptq/** # the speculative_decoding test drives hf_ptq
trtllm:
- "{examples,tests/examples}/{gpt-oss,hf_ptq,llm_eval}/**"
- examples/dataset/** # data prep for gpt-oss
megatron:
- "{examples,tests/examples}/megatron_bridge/**"
onnx:
- "{examples,tests/examples}/{diffusers,torch_onnx,torch_trt}/**"
- examples/onnx_ptq/** # torch_trt reuses onnx_ptq
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- id: lanes
env:
# Nightly and on-demand runs have no diff to inspect, so they run everything.
NON_PR: ${{ !startsWith(github.ref, 'refs/heads/pull-request/') }}
COMMON: ${{ steps.changed.outputs.common_any_modified }}
TORCH: ${{ steps.changed.outputs.torch_any_modified }}
TRTLLM: ${{ steps.changed.outputs.trtllm_any_modified }}
MEGATRON: ${{ steps.changed.outputs.megatron_any_modified }}
ONNX: ${{ steps.changed.outputs.onnx_any_modified }}
run: |
any=false
for lane in TORCH TRTLLM MEGATRON ONNX; do
if [[ "$NON_PR" == "true" || "$COMMON" == "true" || "${!lane}" == "true" ]]; then
run=true
any=true
else
run=false
fi
echo "${lane,,}=$run" | tee --append "$GITHUB_OUTPUT"
done
echo "any_changed=$any" | tee --append "$GITHUB_OUTPUT"
- name: Wait for unit tests before spending GPU runners
if: ${{ steps.lanes.outputs.any_changed == 'true' && startsWith(github.ref, 'refs/heads/pull-request/') }}
uses: poseidon/wait-for-status-checks@v0.6.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
match_pattern: "^linux$" # Wait for Unit tests / linux
delay: 300s

##### PyTorch Example Tests #####
torch:
needs: [pr-gate]
if: needs.pr-gate.outputs.any_changed == 'true'
if: needs.pr-gate.outputs.torch == 'true'
strategy:
fail-fast: false
matrix:
example: [gpt-oss, llm_distill, llm_qat, llm_sparsity, specdec_bench, speculative_decoding]
example: [llm_distill, llm_qat, llm_sparsity, specdec_bench, speculative_decoding]
uses: ./.github/workflows/_example_tests_runner.yml
permissions:
contents: read
secrets: inherit
with:
docker_image: "nvcr.io/nvidia/pytorch:26.06-py3"
docker_image: "nvcr.io/nvidia/pytorch:26.07-py3"
example: ${{ matrix.example }}
timeout_minutes: 30
pip_install_extras: "[hf,dev-test]"
runner: ${{ startsWith(github.ref, 'refs/heads/pull-request/') && 'linux-amd64-gpu-rtxpro6000-latest-1' || 'linux-amd64-gpu-rtxpro6000-latest-2' }}
allow_failure: ${{ contains(format(',{0},', vars.ALLOW_FAILURE_EXAMPLE_TESTS), format(',{0},', matrix.example)) }}

##### TensorRT-LLM Example Tests (pr/non-pr split: non-pr runs extra eval examples) #####
trtllm-pr:
##### TensorRT-LLM Example Tests #####
trtllm:
needs: [pr-gate]
if: startsWith(github.ref, 'refs/heads/pull-request/') && needs.pr-gate.outputs.any_changed == 'true'
if: needs.pr-gate.outputs.trtllm == 'true'
strategy:
fail-fast: false
matrix:
example: [hf_ptq]
example: [gpt-oss, hf_ptq, llm_eval]
uses: ./.github/workflows/_example_tests_runner.yml
permissions:
contents: read
Expand All @@ -69,30 +141,13 @@ jobs:
docker_image: "nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc20"
example: ${{ matrix.example }}
pip_install_extras: "[hf,dev-test]"
runner: linux-amd64-gpu-rtxpro6000-latest-1
allow_failure: ${{ contains(format(',{0},', vars.ALLOW_FAILURE_EXAMPLE_TESTS), format(',{0},', matrix.example)) }}

trtllm-non-pr:
if: ${{ !startsWith(github.ref, 'refs/heads/pull-request/') }}
strategy:
fail-fast: false
matrix:
example: [llm_eval, hf_ptq]
uses: ./.github/workflows/_example_tests_runner.yml
permissions:
contents: read
secrets: inherit
with:
docker_image: "nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc20"
example: ${{ matrix.example }}
pip_install_extras: "[hf,dev-test]"
runner: linux-amd64-gpu-rtxpro6000-latest-2
runner: ${{ startsWith(github.ref, 'refs/heads/pull-request/') && 'linux-amd64-gpu-rtxpro6000-latest-1' || 'linux-amd64-gpu-rtxpro6000-latest-2' }}
allow_failure: ${{ contains(format(',{0},', vars.ALLOW_FAILURE_EXAMPLE_TESTS), format(',{0},', matrix.example)) }}

##### Megatron Example Tests #####
megatron:
needs: [pr-gate]
if: needs.pr-gate.outputs.any_changed == 'true'
if: needs.pr-gate.outputs.megatron == 'true'
uses: ./.github/workflows/_example_tests_runner.yml
permissions:
contents: read
Expand All @@ -108,7 +163,7 @@ jobs:
##### ONNX/TensorRT Example Tests #####
onnx:
needs: [pr-gate]
if: needs.pr-gate.outputs.any_changed == 'true'
if: needs.pr-gate.outputs.onnx == 'true'
strategy:
fail-fast: false
matrix:
Expand All @@ -132,16 +187,16 @@ jobs:
example-pr-required-check:
# Run even if example tests are skipped
if: ${{ startsWith(github.ref, 'refs/heads/pull-request/') && always() }}
needs: [pr-gate, torch, trtllm-pr, megatron, onnx]
needs: [pr-gate, torch, trtllm, megatron, onnx]
runs-on: ubuntu-latest
steps:
- name: Required example tests did not succeed
if: |
needs.pr-gate.result != 'success' ||
(needs.pr-gate.outputs.any_changed == 'true' && (
needs.torch.result != 'success' ||
needs.trtllm-pr.result != 'success' ||
needs.megatron.result != 'success' ||
needs.onnx.result != 'success'
!contains(fromJSON('["success", "skipped"]'), needs.torch.result) ||
!contains(fromJSON('["success", "skipped"]'), needs.trtllm.result) ||
!contains(fromJSON('["success", "skipped"]'), needs.megatron.result) ||
!contains(fromJSON('["success", "skipped"]'), needs.onnx.result)
))
run: exit 1
6 changes: 5 additions & 1 deletion .github/workflows/gpu_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ jobs:
secrets: inherit
with:
files: |
.github/actions/cache-extensions/**
.github/actions/**
.github/workflows/_pr_gate.yml
.github/workflows/gpu_tests.yml
modelopt/**
modelopt_recipes/**
noxfile.py
pyproject.toml
tests/_test_utils/**
tests/conftest.py
tests/gpu/**
tests/gpu_megatron/**
tests/gpu_trtllm/**
Expand Down
Loading
Loading