Skip to content
Draft
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f94a367
test(nightly): add configs and data for the distributed SFT suite
garrett361 Oct 13, 2026
a031f4f
test(nightly): add a distributed SFT functionality suite
garrett361 Oct 13, 2026
785c1d2
test(nightly): run every layout per arch, excluding cells only via skip
garrett361 Oct 13, 2026
fda66dd
fix(nightly): give the tiny GLM-5 config 32 attention heads
garrett361 Oct 13, 2026
9263bfb
test(nightly): name each feature row so arch skips can reference it
garrett361 Oct 13, 2026
714ea74
test(nightly): drop the JUnit XML report from the distributed launcher
garrett361 Oct 13, 2026
748b06b
test(nightly): keep each arch's tokenizer next to its tiny config
garrett361 Oct 13, 2026
f3a9371
docs(nightly): explain the SFT case matrix and -k selection in cases.py
garrett361 Oct 13, 2026
d054545
test(nightly): cover every registered architecture in the distributed…
garrett361 Oct 15, 2026
c3e3b66
fix(nightly): adapt the SFT matrix to Ulysses-only context parallelism
garrett361 Oct 20, 2026
1d101eb
refactor(tests): move the distributed suite and parallel invariance t…
garrett361 Oct 21, 2026
c3689ec
test(regression): retarget the distributed SFT suite to 4-GPU runners
garrett361 Oct 21, 2026
8f3bcca
test(regression): run llama through the ungated unsloth tokenizer mirror
garrett361 Oct 21, 2026
b238808
test(regression): give the tiny GLM-5 and DSv4 configs FlashMLA-compa…
garrett361 Oct 21, 2026
dd39b1f
test(regression): replace the router-dtype row with a fullgraph=True row
garrett361 Oct 22, 2026
133afd4
test(regression): build tiny models from the published HF configs, re…
garrett361 Oct 22, 2026
c7b2bc2
fix(regression): leave the tied lm_head out of tiny checkpoints
garrett361 Oct 22, 2026
4f717e3
test(regression): fail cp2 cases on any graph break, reported apart f…
garrett361 Oct 22, 2026
9220383
test(regression): narrow the tiny MoE checkpoints
garrett361 Oct 28, 2026
c0e7d83
test(regression): cut the SFT matrix, shard it and harden its launches
garrett361 Oct 28, 2026
add08a0
test(regression): drop the eager, AC-off and unfused feature rows
garrett361 Oct 28, 2026
e9c092d
refactor(regression): accumulate shard costs in a defaultdict
garrett361 Oct 28, 2026
8f0d7c6
docs(regression): explain why only qwen3_moe runs feature rows and re…
garrett361 Oct 28, 2026
947efe8
refactor(regression): build an arch's cases with Arch.cases()
garrett361 Oct 28, 2026
27f1e06
refactor(regression): define archs and features inline, derive -k nam…
garrett361 Oct 28, 2026
5c76dba
refactor(regression): define each arch once, with its tiny config and…
garrett361 Oct 28, 2026
0e9b5e1
docs(regression): add a README for the distributed SFT suite
garrett361 Oct 28, 2026
dd0f610
refactor(regression): drop the edge-case guards from SFT runs
garrett361 Oct 28, 2026
daa757a
refactor(regression): split the collection hook into named shard and …
garrett361 Oct 28, 2026
89bd77b
refactor(regression): keep the matrix in archs.py and the run helpers…
garrett361 Oct 28, 2026
a0fd105
fix(regression): keep failures in the suite summary, give runs 900 s
garrett361 Oct 28, 2026
d43cca7
refactor(regression): move the tiny models into a shared tiny_models.py
garrett361 Oct 28, 2026
e18bb96
fix(regression): delete the run dir before the train run
garrett361 Oct 28, 2026
02d109f
test(regression): add a DeepEP feature row on qwen3_moe
garrett361 Oct 28, 2026
f5ba143
test(regression): make GLM-5 and DSv4 sparse attention drop keys
garrett361 Oct 28, 2026
95a0bc8
test(regression): check that a resumed run reproduces the original lo…
garrett361 Oct 28, 2026
48fa885
test(regression): turn off the prime monitor in suite runs
garrett361 Nov 4, 2026
e2ac888
test(regression): drop SFT data samples with personal details
garrett361 Nov 4, 2026
532ca4e
test(regression): drop SFT data samples that credit named people
garrett361 Nov 4, 2026
5432baa
test(regression): drop four more SFT data samples with named people
garrett361 Nov 4, 2026
1aac3c7
test(regression): replace the SFT chat data with truncated math samples
garrett361 Nov 4, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/regression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Regression

# Scheduled regression suites, one job group per suite. Dispatch with `suite`
# to run one of them.
# parallel: test_parallel_invariance.py on the 2xH200 runners
# parallel: distributed/test_parallel_invariance.py on the 2xH200 runners
# eval: every configs/eval/*.toml through `uv run eval` against Prime
# Inference, compared against baselines/eval/. reports/eval/ holds
# the error bars of each baseline. Also runs on PRs that touch the
Expand Down Expand Up @@ -86,7 +86,7 @@ jobs:
WANDB_MODE: offline
HF_TOKEN: ${{ secrets.HF_TOKEN }}
PYTEST_OUTPUT_DIR: /tmp/outputs
run: uv run pytest -vvs tests/regression/test_parallel_invariance.py
run: uv run pytest -vvs tests/regression/distributed/test_parallel_invariance.py
- name: Upload run logs on failure
if: failure()
uses: actions/upload-artifact@v7
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
.codex/
*.ipynb
*.jsonl
!tests/regression/configs/distributed/sft/data/train.jsonl
configs/endpoints.py

# Run artifacts
Expand Down
4 changes: 2 additions & 2 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The test suite is split into three tiers.

- **`tests/unit/`** — fast-running, hermetic tests for isolated logic: config parsing and validation, advantage / loss / scheduler / packer math, individual dataset paths, model-conversion roundtrips, etc. Tests that need a GPU are tagged with the `gpu` marker.
- **`tests/integration/`** — full-stack RL/SFT runs on a tiny model end-to-end through inference + orchestrator + trainer.
- **`tests/regression/`** — nightly regression suites: `test_parallel_invariance.py`, which checks that DP/CP/EP layouts train the same first step as a single GPU, and evals from `configs/eval/` compared against `baselines/eval/`.
- **`tests/regression/`** — nightly regression suites: `distributed/test_parallel_invariance.py`, which checks that DP/CP/EP layouts train the same first step as a single GPU, and evals from `configs/eval/` compared against `baselines/eval/`.

### Running Tests Locally

Expand All @@ -43,7 +43,7 @@ uv run pytest tests/integration/test_reverse_text.py -vvs # one specific scenar
|---|---|---|---|
| [`tests.yml`](https://github.com/PrimeIntellect-ai/prime-rl/blob/main/.github/workflows/tests.yml) | CPU jobs: every PR + push to `main`. `gsm8k_eval`: relevant changes in non-draft PRs, every push to `main`, manual dispatch | CPU: `pytest tests/unit -m "not gpu"`, plus a slim-wheel install check that `prime-rl-configs` imports cleanly without heavy deps (no torch / vllm / transformers / wandb / verifiers / datasets / liger / loguru in `sys.modules`). Separate hosted `gsm8k_eval` job | `ubuntu-latest` |
| [`gpu.yml`](https://github.com/PrimeIntellect-ai/prime-rl/blob/main/.github/workflows/gpu.yml) | On a PR: applying the `gpu-ci` label (Triage or higher). Also every merge to `main` and manual dispatch | Full GPU suite: `pytest tests/unit -m gpu`, plus RL/SFT integration scenarios (`reverse_text`, `reverse_text_sft`, `reverse_text_sft_lora`, `reverse_text_lora`, `reverse_text_moe`, `reverse_text_rl_opd`, `reverse_text_rl_sft`, `alphabet_sort`) with `max-parallel: 5` | Self-hosted `prime-rl-2xh200` k8s scale set (2xH200); `ubuntu-latest` for the trigger check |
| [`regression.yml`](https://github.com/PrimeIntellect-ai/prime-rl/blob/main/.github/workflows/regression.yml) | 03:00 PST daily + manual `workflow_dispatch` (`suite` picks one suite); `eval` also on PRs that touch it | `parallel`: `tests/regression/test_parallel_invariance.py` (layouts that need more than 2 GPUs skip). `eval`: `tests/regression/configs/eval/*.toml` against Prime Inference, compared against `tests/regression/baselines/eval/` | `eval`: `ubuntu-latest`. `parallel`: `prime-rl-2xh200` |
| [`regression.yml`](https://github.com/PrimeIntellect-ai/prime-rl/blob/main/.github/workflows/regression.yml) | 03:00 PST daily + manual `workflow_dispatch` (`suite` picks one suite); `eval` also on PRs that touch it | `parallel`: `tests/regression/distributed/test_parallel_invariance.py` (layouts that need more than 2 GPUs skip). `eval`: `tests/regression/configs/eval/*.toml` against Prime Inference, compared against `tests/regression/baselines/eval/` | `eval`: `ubuntu-latest`. `parallel`: `prime-rl-2xh200` |

Pushes to a PR do not run GPU jobs. To run the GPU suite on a PR's current commit, apply the `gpu-ci` label (drafts included). The workflow removes the label when it starts, so re-apply it to run again after new pushes or to retry. A new trigger cancels the PR's queued or in-flight GPU run. Fork PRs cannot run GPU CI: a maintainer can push the branch to this repo and label that PR. A PR with merge conflicts cannot run it until the conflicts are resolved.

Expand Down
7 changes: 7 additions & 0 deletions scripts/ci/run_distributed_nightly.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
# Run the nightly distributed suite on one node with at least 4 GPUs. Extra args go to pytest, e.g. -k qwen3_moe or
# --shard 2/4.
set -euo pipefail

cd "$(dirname "$0")/../.."
uv run pytest tests/regression/distributed --durations=0 -rfEs "$@"
9 changes: 6 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,13 @@ def _get_wandb_project(wandb_project: str) -> str:
class ProcessResult:
"""Result object containing process information and captured output."""

def __init__(self, process: subprocess.Popen):
def __init__(self, process: subprocess.Popen, timed_out: bool = False):
self.returncode = process.returncode
self.pid = process.pid
self.timed_out = timed_out

def __repr__(self):
return f"ProcessResult(returncode={self.returncode}, pid={self.pid})"
return f"ProcessResult(returncode={self.returncode}, pid={self.pid}, timed_out={self.timed_out})"


@pytest.fixture(scope="module")
Expand All @@ -109,16 +110,18 @@ def run_process() -> Callable[[Command, Environment, int], ProcessResult]:
def _run_process(command: Command, env: Environment = {}, timeout: int | None = None) -> ProcessResult:
"""Run a subprocess with given command and environment with a timeout"""
process = subprocess.Popen(command, env={**os.environ, **env})
timed_out = False
try:
process.wait(timeout=timeout)
except subprocess.TimeoutExpired:
timed_out = True
cleanup_process(process.pid, signal.SIGTERM)
try:
process.wait(timeout=30)
except subprocess.TimeoutExpired:
cleanup_process(process.pid, signal.SIGKILL)
process.wait()

return ProcessResult(process)
return ProcessResult(process, timed_out)

return _run_process
5 changes: 5 additions & 0 deletions tests/regression/configs/distributed/sft/base.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[data]
name = "tests/regression/configs/distributed/sft/data"
batch_size = 16
micro_batch_size = 1
seq_len = 4096
Loading
Loading