slime runs CPU tests when a PR is opened or updated, when code is pushed to main, and when the workflow is triggered manually. GPU end-to-end tests are triggered by PR labels and run real Megatron training and SGLang rollout on self-hosted machines. For routine changes, start with CPU tests and select GPU tests that cover the affected behavior.
| Trigger | CI job | Environment | Coverage |
|---|---|---|---|
| Automatic | cpu-unittest |
CPU | Argument validation, batch scheduling, metrics, rewards, samples, checkpoint utilities, and extension interfaces. |
| Automatic | agent-adapter-test |
CPU | Agent adapters, with the required model-provider SDKs installed. |
run-ci-sglang-config |
e2e-test-sglang-config |
CPU/GPU | SGLang deployment configuration, including multiple models, engine layouts, and fault recovery with memory offload. |
run-ci-megatron |
e2e-test-megatron |
GPU | Megatron training, including dense models, MoE, PPO, MTP, OPD, fully async rollout, PD/Mooncake, and debug replay. |
run-ci-precision |
e2e-test-precision |
CPU/GPU | Numerical precision and consistency across parallel configurations. |
run-ci-ckpt |
e2e-test-ckpt |
GPU | Checkpoint saving and loading, including CPU/GPU optimizer states and async saves. |
run-ci-image |
e2e-test-image |
GPU | The same tests as run-ci-megatron, using the slimerl/slime-test:latest image. |
run-ci-changed |
e2e-test-changed |
CPU/GPU | Only added or modified tests, with GPU counts taken from each file's NUM_GPUS. |
You can also run the workflow manually through workflow_dispatch on the GitHub Actions page. Manual runs execute the registered CPU and GPU jobs. Pushes to main automatically run only the CPU jobs.
The workflow is defined in .github/workflows/pr-test.yml, generated from .github/workflows/pr-test.yml.j2. To change test lists or trigger conditions, edit the template and regenerate the workflow.
CPU jobs run on GitHub-hosted ubuntu-latest runners. They install CPU PyTorch and test dependencies, then execute python tests/<test_file>.py. They do not use Docker or acquire GPUs.
cpu-unittest checks:
- Megatron arguments and Hugging Face model configuration;
- DP/CP batch scheduling and loss invariance under CP partitioning;
- metric reporting and distributed aggregation;
- reward calculation for math, GPQA, F1, DeepScaler, and DAPO;
Samplebehavior, rollout validation, and agent trajectory merging;- Hugging Face checkpoint saving and interface contracts for custom rollout, generation functions, and runtime hooks.
Agent adapter tests run in a separate agent-adapter-test job because they also require SDKs such as openai, openai-agents, and anthropic.
CPU test entries marked with straw: true install the latest straw-queue wheel from PyPI. They do not need a Rust toolchain or the Straw source repository. test_optional_straw.py deliberately runs without Straw to check that default data transport still works and that explicitly selecting Straw produces a clear installation hint.
Useful local commands:
python tests/test_agent/test_trajectory_manager_branching.py
python -m pytest tests/test_megatron_argument_validation.py tests/plugin_contracts/test_plugin_generate_contracts.pyGPU jobs run on self-hosted machines. Each test follows these steps:
- Start a Docker container, usually
slimerl/slime:latest; image validation usesslimerl/slime-test:latest. - Install the latest
straw-queuewheel from PyPI and the current slime checkout withpip install -e . --no-deps. - Acquire the required GPUs with
tests/ci/gpu_lock_exec.py --count <num_gpus>. - Execute
python tests/<test_file>.py.
Test files usually use prepare() to download models and datasets, then execute() to build training arguments and call U.execute_train(...).
With the run-ci-changed label, CI finds added or modified tests/test_*.py and tests/plugin_contracts/test_*.py files relative to origin/main and creates a test job for each file.
The GPU count comes from the file's top-level NUM_GPUS = <N>. Without this declaration, CI requests 8 GPUs. CPU-only tests should therefore declare:
NUM_GPUS = 0These jobs still run in Docker on self-hosted machines, but do not acquire GPUs when NUM_GPUS = 0.
Straw end-to-end tests explicitly set --rollout-data-transport straw, so local runs, run-ci-changed, and the fixed test list use the same transport. Coverage includes:
- R3:
test_qwen3_30B_A3B_r3.pyandtest_moonlight_16B_A3B_r3.py. - SC:
test_qwen2.5_0.5B_score_centering.py, checking both top-k and top-p data. - Fully async rollout, fanout, PPO, MTP, PD/Mooncake, distributed SGLang, fault recovery with mixed memory offload, debug replay, and continued rollout after releasing training resources.
- Checkpoint saving and loading: phases share a Straw storage pool to check queue and training-state recovery.
test_straw_checkpoint_fork.pyalso checks step selection, repeated rollback, automatic branch selection, and debug replay.
R3, SC, and fully async tests also enable online GC. Ordinary Straw tests use isolated temporary directories and clean them up afterward. Single-host GPU tests use the local filesystem profile; multi-host JuiceFS durability needs separate validation. Other end-to-end tests continue to cover Ray object-store and NIXL transport.
test_straw_fully_async_recovery.py is an automatic CPU integration test. It uses SIGKILL to terminate a job with two local Ray nodes, then starts a new process to recover from the same filesystem queue. It tests both enabled and disabled online GC, using small R3/SC payloads and deterministic inference and reward fixtures. After installing a compatible Straw wheel, run it locally with:
PYTHONPATH=. python tests/test_straw_fully_async_recovery.pytest_qwen2.5_0.5B_pipeline_rl.py runs fully async rollout and three actual GRPO training steps on 4 GPUs. It checks whether the same HTTP request continues generating across weight updates and whether training changes the policy weights.
The fixed test list covers three configurations: --flush-cache-interval 0 with NCCL or full disk weight synchronization, and --flush-cache-interval 2 with NCCL for periodic cache flushing. The CPU test test_pipeline_rl.py checks the flush schedule and SGLang control requests. These tests check functionality, not learning quality or throughput gains.
test_qwen2.5_0.5B_training_recovery.py uses 4 GPUs and two successive training jobs on the same Ray cluster. The first uses TP=1 and deliberately triggers a real CUDA OOM. After verifying that serving still responds when the job exits, it resubmits training with TP=2, which also changes the DP size.
The test checks that healthy SGLang processes, routers, and GPU placements are reused; replayed batch contents match; and training scheduler progress, finite nonzero gradients, and the final checkpoint are correct. The fixed test list includes:
| Data storage | RolloutManager state | Checks |
|---|---|---|
| Straw with online GC | Remains alive | Reconnect trainers and replay completed training batches that were not checkpointed. |
| Straw with online GC | Killed after failure | Reconnect a new manager to the original serving cluster and replay the same batches. |
| Straw with a model/optimizer checkpoint and Megatron YAML configuration | Killed during training | Restore from the checkpoint and check configuration and recovery state. |
| Rollout debug files | Killed after failure | Restore data from debug files and reconnect a new manager to the original serving cluster. |
| Straw with disk-delta weight synchronization | Killed after failure | Publish restored weights as a new full baseline, then continue delta updates. |
| Straw with PD/Mooncake serving | Killed after failure | Wedge the prefill actor, replace it within the reset timeout, and retain the healthy decode actor. |
test_qwen3_30B_A3B_training_recovery.py uses 8 GPUs for the same OOM/checkpoint/manager-loss workflow with a MoE model, R3, and stateless Adam. It omits optimizer tensors while checking scheduler progress, compares persisted routing bytes across the TP/DP change, and completes training after recovery. The dense cases cover ordinary Adam with optimizer checkpoints.
Internal serving health checks are enabled with or without the compatibility flag --use-fault-tolerance. CPU coverage includes configuration and checkpoint boundaries in test_training_recovery.py, lost disk-update replies in test_disk_delta_recovery.py, and real Ray manager SIGKILL or conversion-reply loss in test_rollout_manager_recovery.py.
test_qwen2.5_0.5B_rollout_health.py stops a real SGLang HTTP server just before rollout completes, leaving its router registration intact. Two four-GPU cases either retain or kill the corresponding Ray actor. They check bounded rollout completion, deregistration before training, engine recovery at weight update, and a final training checkpoint.
Both cases omit --use-fault-tolerance and set the background interval and initial wait to 600 seconds. This verifies that rollout-completion checks run immediately without waiting for background checks.
Follow nearby examples and place tests under tests/test_*.py, tests/utils/test_*.py, or tests/plugin_contracts/test_*.py. Declare a top-level NUM_GPUS = 0 if the file will be run by run-ci-changed.
CI executes test files directly, so pytest files need an entry point:
if __name__ == "__main__":
raise SystemExit(pytest.main([__file__]))To run a test automatically, register it in the cpu-unittest or agent-adapter-test list in .github/workflows/pr-test.yml.j2, then regenerate the workflow.
- Create
tests/test_<your_test_name>.pyusing the existingprepare()/execute()structure. - Declare the required GPU count with a top-level
NUM_GPUS = <N>. - Download models and datasets in
prepare(). - Build arguments and call
U.execute_train(...)inexecute(). - Register the test in the appropriate GPU job in
.github/workflows/pr-test.yml.j2, then regenerate the workflow.
Example:
import os
import slime.utils.external_utils.command_utils as U
MODEL_NAME = "Qwen2.5-0.5B-Instruct"
MODEL_TYPE = "qwen2.5-0.5B"
NUM_GPUS = 4
def prepare():
U.exec_command("mkdir -p /root/models /root/datasets")
U.exec_command(f"hf download Qwen/{MODEL_NAME} --local-dir /root/models/{MODEL_NAME}")
def execute():
# Build argument strings and call U.execute_train(...)
...
if __name__ == "__main__":
prepare()
for proxy_var in ("http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY"):
os.environ.pop(proxy_var, None)
execute()Do not edit the generated .github/workflows/pr-test.yml directly. After editing .github/workflows/pr-test.yml.j2, run:
python .github/workflows/generate_github_workflows.pyCommit both the template and the generated workflow file.
- Argument parsing, rewards, batch scheduling, samples, trajectories, or extension interfaces: start with the corresponding CPU tests.
- SGLang deployment or engine layouts: add
run-ci-sglang-config. - Megatron training, loss, checkpoint conversion, or model training configurations: add
run-ci-megatron. For numerical or checkpoint changes, also addrun-ci-precisionorrun-ci-ckptas needed. - Docker images or dependencies: add
run-ci-image. It runs the full Megatron test list and uses more GPU time. - Added or modified tests: add
run-ci-changedto validate the changed test files directly.