Skip to content

Commit 9de8718

Browse files
author
root
committed
Merge branch 'main' into mchochowski/heterogeneous_moe
Resolve conflict in model_bridge.py by keeping heterogeneous MoE expert layout resolution while adopting main's pg_collection registry API.
2 parents 4bbddb1 + e22cb80 commit 9de8718

864 files changed

Lines changed: 47933 additions & 30428 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
name: verl e2e (weekly)
15+
16+
on:
17+
schedule:
18+
- cron: "0 6 * * 1" # Mondays 06:00 UTC
19+
workflow_dispatch:
20+
inputs:
21+
script:
22+
description: "Run a single script (basename without .sh); empty = all run_*.sh"
23+
required: false
24+
type: string
25+
verl_ref:
26+
description: "verl git ref to check out"
27+
required: false
28+
type: string
29+
default: main
30+
31+
concurrency:
32+
group: ${{ github.workflow }}
33+
cancel-in-progress: false
34+
35+
permissions:
36+
contents: read
37+
38+
jobs:
39+
discover:
40+
runs-on: ubuntu-latest
41+
outputs:
42+
matrix: ${{ steps.scan.outputs.matrix }}
43+
has_scripts: ${{ steps.scan.outputs.has_scripts }}
44+
steps:
45+
- uses: actions/checkout@v6
46+
47+
- name: Scan verl_ci/run_*.sh into a matrix
48+
id: scan
49+
shell: bash -x -e -u -o pipefail {0}
50+
env:
51+
RUNNER_PREFIX: ${{ vars.DEFAULT_RUNNER_PREFIX }}
52+
SINGLE: ${{ github.event.inputs.script }}
53+
run: |
54+
get_header() {
55+
grep -m1 "^# $1=" "$2" | cut -d= -f2
56+
}
57+
58+
entries=""
59+
for f in .github/workflows/verl_ci/run_*.sh; do
60+
[ -f "$f" ] || continue
61+
name=$(basename "$f" .sh)
62+
if [ -n "${SINGLE:-}" ] && [ "$name" != "$SINGLE" ]; then
63+
continue
64+
fi
65+
timeout=$(get_header CI_TIMEOUT "$f"); timeout=${timeout:-60}
66+
gpu_count=$(get_header GPU_COUNT "$f"); gpu_count=${gpu_count:-8}
67+
# shellcheck disable=SC2001 # regex quantifier not expressible via ${var//}
68+
runner=$(echo "${RUNNER_PREFIX}" | sed "s/gpu-x[0-9]*/gpu-x${gpu_count}/")
69+
entries="${entries}{\"script\":\"${name}\",\"timeout\":${timeout},\"runner\":\"${runner}\"},"
70+
done
71+
72+
matrix="{\"include\":[${entries%,}]}"
73+
echo "matrix=${matrix}" | tee -a "$GITHUB_OUTPUT"
74+
if [ -n "$entries" ]; then
75+
echo "has_scripts=true" | tee -a "$GITHUB_OUTPUT"
76+
else
77+
echo "has_scripts=false" | tee -a "$GITHUB_OUTPUT"
78+
fi
79+
80+
verl-e2e:
81+
needs: [discover]
82+
if: needs.discover.outputs.has_scripts == 'true'
83+
strategy:
84+
fail-fast: false
85+
max-parallel: 3
86+
matrix: ${{ fromJSON(needs.discover.outputs.matrix) }}
87+
runs-on: ${{ matrix.runner }}
88+
name: ${{ matrix.script }}
89+
timeout-minutes: ${{ matrix.timeout }}
90+
env:
91+
CONTAINER: verl_${{ github.run_id }}_${{ matrix.script }}
92+
VERL_IMAGE: ${{ vars.DEFAULT_CONTAINER_REGISTRY }}/dockerhub/verlai/verl:vllm020.dev1
93+
VERL_REF: ${{ github.event.inputs.verl_ref || 'main' }}
94+
TEST_DATA_PATH: ${{ vars.DEFAULT_TEST_DATA_PATH }}
95+
VERL_DATA_ROOT: /home/TestData/verl_ci
96+
steps:
97+
- name: Checkout
98+
uses: actions/checkout@v6
99+
100+
- name: Start verl container
101+
shell: bash -x -e -u -o pipefail {0}
102+
run: |
103+
docker rm -f "$CONTAINER" || true
104+
docker run \
105+
--rm -d \
106+
--name "$CONTAINER" \
107+
--runtime=nvidia --gpus all \
108+
--shm-size=64g \
109+
--env HF_HUB_OFFLINE=1 \
110+
--env VERL_DATA_ROOT="$VERL_DATA_ROOT" \
111+
--volume "${TEST_DATA_PATH}:/home/TestData" \
112+
--volume "${GITHUB_WORKSPACE}:/opt/Megatron-Bridge" \
113+
--workdir /workspace \
114+
"$VERL_IMAGE" \
115+
bash -c "sleep $(( ${{ matrix.timeout }} * 60 + 300 ))"
116+
117+
- name: Run ${{ matrix.script }}
118+
shell: bash -x -e -u -o pipefail {0}
119+
run: |
120+
docker exec \
121+
-e SCRIPT_PATH="/opt/Megatron-Bridge/.github/workflows/verl_ci/${{ matrix.script }}.sh" \
122+
-e VERL_REF="$VERL_REF" \
123+
-e VERL_DATA_ROOT="$VERL_DATA_ROOT" \
124+
"$CONTAINER" bash -euxo pipefail -c '
125+
export HOME=/tmp/verl_home
126+
mkdir -p "$HOME"
127+
# Reproduce verl runner layout: $HOME/models/<org>/<name> + $HOME/models/hf_data/gsm8k
128+
ln -sfn "$VERL_DATA_ROOT/models" "$HOME/models"
129+
git clone https://github.com/verl-project/verl.git /workspace/verl
130+
cd /workspace/verl
131+
git checkout "$VERL_REF"
132+
git rev-parse HEAD
133+
bash "$SCRIPT_PATH"
134+
'
135+
136+
- name: Cleanup container
137+
if: always()
138+
shell: bash
139+
run: docker rm -f "$CONTAINER" || true
140+
141+
notify:
142+
needs: [discover, verl-e2e]
143+
if: failure()
144+
runs-on: ubuntu-latest
145+
steps:
146+
- name: Notify Slack on failure
147+
env:
148+
SLACK_WEBHOOK: ${{ secrets.SLACK_CI_CHANNEL_WEBHOOK }}
149+
SLACK_WEBHOOK_ADMIN: <!subteam^${{ secrets.SLACK_TEAM_GROUP_ID }}>
150+
GITHUB_RUN_ID: ${{ github.run_id }}
151+
GITHUB_REPOSITORY: ${{ github.repository }}
152+
run: |
153+
curl -X POST \
154+
-H 'Content-type: application/json' \
155+
--data "{\"text\":\":rotating_light: <https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}|verl e2e (weekly)> has failing leg(s). Please review.\n\ncc ${SLACK_WEBHOOK_ADMIN}\"}" \
156+
"$SLACK_WEBHOOK"
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Megatron-Bridge vLLM PPO CI suites
2+
3+
This directory provides standalone entry points for three jobs currently defined
4+
across
5+
[`e2e_ppo_trainer_megatron_vllm.yml`](https://github.com/verl-project/verl/blob/main/.github/workflows/e2e_ppo_trainer_megatron_vllm.yml)
6+
and
7+
[`e2e_ppo_trainer_megatron_vllm_2.yml`](https://github.com/verl-project/verl/blob/main/.github/workflows/e2e_ppo_trainer_megatron_vllm_2.yml).
8+
The existing verl source files and workflows do not need to be changed to use them.
9+
Only variants using the official NVIDIA NeMo Megatron-Bridge backend are included.
10+
11+
| Existing CI job | Included Megatron-Bridge variants | Standalone entry point |
12+
| --- | --- | --- |
13+
| `e2e_ppo_trainer_megatron-deepseek` | LoRA, save and resume | `bash run_megatron_deepseek.sh` |
14+
| `e2e_ppo_trainer_megatron-qwen3` | Dense | `bash run_megatron_qwen3.sh` |
15+
| `e2e_ppo_trainer_megatron-moe-expert-parallel` | MoE EP=4, FP8 rollout, and LoRA EP=2 | `bash run_megatron_moe_expert_parallel.sh` |
16+
17+
Each script reproduces the relevant Megatron-Bridge command order from its original
18+
job: dependency installation, GSM8K preprocessing, selected training variants,
19+
checkpoint boundaries, and final checkpoint cleanup. Every training invocation
20+
uses `USE_MBRIDGE=True` and `VANILLA_MBRIDGE=False`, directly or through an
21+
explicit shared environment array. Run a script from the verl repository root in
22+
an isolated CI job. Do not run multiple scripts concurrently in the same checkout,
23+
Python environment, home directory, or set of GPUs.
24+
25+
## Runner requirements
26+
27+
- One Linux node with 8 NVIDIA L20 GPUs for each active script. The shared trainer
28+
config uses one node and 8 GPUs, and the expert-parallel topology consumes all 8.
29+
- A 60-minute timeout per script, matching the existing jobs.
30+
- The tested container image:
31+
`verl-ci-cn-beijing.cr.volces.com/verlai/verl:vllm020.dev1`. Its preinstalled
32+
CUDA, Python, PyTorch, vLLM, Ray, Megatron build dependencies, and system
33+
libraries are part of the test environment. Several installs use `--no-deps`,
34+
so a generic CUDA image is not an equivalent replacement.
35+
- An NVIDIA host driver compatible with the image, NCCL peer communication across
36+
all 8 GPUs, enough host RAM and shared memory for CPU offload, and usable local
37+
loopback ports for Ray.
38+
- A pip-writable Python environment plus writable repository root, `${HOME}`,
39+
`/tmp`, and enough storage for package/model caches, datasets, generated dummy
40+
models, and checkpoints. The workflows do not define numeric CPU, RAM, disk, or
41+
shared-memory minima; use the existing `L20x8` runner profile as the baseline.
42+
- A checkout with full history (`fetch-depth: 0`). All commands must run from the
43+
repository root because requirements, configs, launchers, and `checkpoints/` use
44+
repository-relative paths.
45+
46+
## Preloaded models and data
47+
48+
The shared trainer script does not download models. Preload the public snapshots
49+
at these exact paths:
50+
51+
```text
52+
${HOME}/models/deepseek-ai/deepseek-coder-1.3b-instruct
53+
${HOME}/models/Qwen/Qwen3-0.6B
54+
${HOME}/models/Qwen/Qwen3-30B-A3B-Instruct-2507
55+
${HOME}/models/Skywork/Skywork-Reward-V2-Llama-3.2-1B
56+
```
57+
58+
The MoE suite creates a dummy model, but still needs the Qwen3-30B snapshot for
59+
tokenizer and base artifacts. Start with no stale
60+
`${HOME}/dummy_models/Qwen/Qwen3-30B-A3B-Instruct-2507` directory, or validate that
61+
it was generated from the current dummy-model config. All three suites enable the
62+
Skywork reward model.
63+
64+
Make the raw GSM8K dataset loadable from `${HOME}/models/hf_data/gsm8k`. Each
65+
script preprocesses it to:
66+
67+
```text
68+
${HOME}/data/gsm8k/train.parquet
69+
${HOME}/data/gsm8k/test.parquet
70+
```
71+
72+
## Network and environment
73+
74+
The runner must be able to pull the tested image and reach its Python package index
75+
and GitHub. The scripts install Megatron-Bridge and Megatron-LM at run time.
76+
Because the requirements files and the ModelOpt lower bound are not all immutable
77+
pins, retain the exact image and capture `pip3 list` when investigating a
78+
reproducibility failure.
79+
80+
No external service container is needed. Ray runs inside the job, and every
81+
training invocation first stops any previous local Ray runtime.
82+
83+
The DeepSeek script preserves checkpoints between its Megatron-Bridge LoRA
84+
save/resume phases. DeepSeek and Qwen3 remove stale checkpoints before their
85+
retained variants; MoE retains the workflow's cleanup boundary before its LoRA
86+
variant. Every script has an exit trap that removes the repository-local
87+
`checkpoints/` directory after success or failure. No artifacts are uploaded, so
88+
a zero script exit status is the CI pass condition.
89+
90+
## Preflight checklist
91+
92+
Before starting a CI job, verify:
93+
94+
- exactly 8 CUDA devices are visible;
95+
- every required model and raw-data path is readable;
96+
- the workspace, Python environment, `${HOME}/data`, `${HOME}/dummy_models`, and
97+
`/tmp` are writable;
98+
- `python3`, `pip3`, `git`, and `ray` are available; and
99+
- localhost traffic and NCCL communication are not routed through the proxy.
100+
101+
## NVIDIA CI integration
102+
103+
These scripts are discovered and run weekly by
104+
[`.github/workflows/verl-e2e-weekly.yml`](../verl-e2e-weekly.yml). That workflow
105+
globs every `run_*.sh` in this directory into a parallel matrix (one 8-GPU job per
106+
script), so adding a new `run_*.sh` here is picked up automatically with no
107+
workflow edit. The workflow reads two tuning headers from each script:
108+
109+
- `# CI_TIMEOUT=<minutes>` — per-leg `timeout-minutes` (default 60).
110+
- `# GPU_COUNT=<n>` — selects the N-GPU runner label (default 8).
111+
112+
In NVIDIA CI the L20x8 runner profile above is served by an 8×H100 node, and the
113+
verl image is mirrored into the internal ECR registry. Required models and the raw
114+
GSM8K dataset are pre-staged into the shared test-data volume, so each leg runs
115+
offline (`HF_HUB_OFFLINE=1`); the workflow reproduces the `${HOME}/models` and
116+
`${HOME}/models/hf_data/gsm8k` layout above via symlinks before invoking a script.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env bash
2+
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
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+
# CI_TIMEOUT=60
17+
# GPU_COUNT=8
18+
set -xeuo pipefail
19+
20+
readonly TRAINER_SCRIPT="tests/special_e2e/run_ppo_trainer_megatron.sh"
21+
readonly MODEL_NAME="deepseek-ai/deepseek-coder-1.3b-instruct"
22+
23+
cleanup() {
24+
rm -rf checkpoints
25+
}
26+
trap cleanup EXIT
27+
28+
## Use our checkout
29+
pip3 install git+https://github.com/NVIDIA-NeMo/Megatron-Bridge.git@main --no-deps --no-build-isolation
30+
pip3 install git+https://github.com/NVIDIA/Megatron-LM.git@main --no-deps --no-build-isolation
31+
pip3 install "nvidia-modelopt[torch]>=0.37.0"
32+
## cd to verl checkout root
33+
pip3 install -r requirements-test.txt
34+
pip3 install -r requirements.txt
35+
pip3 install --no-deps -e .
36+
pip3 install math-verify
37+
38+
python3 examples/data_preprocess/gsm8k.py --local_dataset_path "${HOME}/models/hf_data/gsm8k"
39+
40+
cleanup
41+
42+
ray stop --force
43+
ALL_OFFLOAD=True \
44+
SAVE_FREQ=1 \
45+
MODEL_ID="${MODEL_NAME}" \
46+
COMMON_PP=4 \
47+
LORA_RANK=8 \
48+
COMMON_VPP=null \
49+
COMMON_CP=1 \
50+
USE_MBRIDGE=True \
51+
VANILLA_MBRIDGE=False \
52+
VALUE_VANILLA_MBRIDGE=False \
53+
USE_DIST_CKPT=False \
54+
bash "${TRAINER_SCRIPT}"
55+
56+
ray stop --force
57+
RESUME_MODE=auto \
58+
MODEL_ID="${MODEL_NAME}" \
59+
TOTAL_TRAIN_STEPS=2 \
60+
SAVE_FREQ=1 \
61+
COMMON_PP=4 \
62+
LORA_RANK=8 \
63+
COMMON_VPP=null \
64+
COMMON_CP=1 \
65+
USE_MBRIDGE=True \
66+
VANILLA_MBRIDGE=False \
67+
VALUE_VANILLA_MBRIDGE=False \
68+
USE_DIST_CKPT=False \
69+
bash "${TRAINER_SCRIPT}"

0 commit comments

Comments
 (0)