ABO12138 commited on
Commit
f5b2db8
·
verified ·
1 Parent(s): 915cd83

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .claude/settings.local.json +21 -0
  2. .gitattributes +2 -0
  3. .sii/ide-port +1 -0
  4. 0519_sft_test/data/convert_to_parquet.py +154 -0
  5. 0519_sft_test/data/train.parquet +3 -0
  6. 0519_sft_test/data/val.parquet +3 -0
  7. 0519_sft_test/run_sft_qwen3vl_8b.sh +75 -0
  8. 0520_rej_test/README.md +69 -0
  9. 0520_rej_test/data/build_prompts.py +135 -0
  10. 0520_rej_test/reward/__pycache__/reward_fn.cpython-312.pyc +0 -0
  11. 0520_rej_test/reward/reward_fn.py +34 -0
  12. 0520_rej_test/reward/score.py +80 -0
  13. 0520_rej_test/run_pipeline.sh +43 -0
  14. 0520_rej_test/scripts/run_generate.sh +48 -0
  15. 0520_rej_test/scripts/run_sft.sh +20 -0
  16. 0520_rej_test/select/topk_to_sft.py +148 -0
  17. data.tar.gz +3 -0
  18. pip_list.txt +206 -0
  19. requirements.txt +132 -0
  20. verl.tar.gz +3 -0
  21. verl/.agent/skills/issue/SKILL.md +47 -0
  22. verl/.agent/skills/pr/SKILL.md +33 -0
  23. verl/.gemini/config.yaml +10 -0
  24. verl/.git-blame-ignore-revs +13 -0
  25. verl/.github/CODEOWNERS +25 -0
  26. verl/.github/ISSUE_TEMPLATE/bug-report.yml +65 -0
  27. verl/.github/ISSUE_TEMPLATE/config.yml +2 -0
  28. verl/.github/ISSUE_TEMPLATE/feature-request.yml +32 -0
  29. verl/.github/PULL_REQUEST_TEMPLATE.md +41 -0
  30. verl/.github/dependabot.yml +9 -0
  31. verl/.github/workflows/README.md +73 -0
  32. verl/.github/workflows/check-pr-title.yml +58 -0
  33. verl/.github/workflows/cpu_unit_tests.yml +117 -0
  34. verl/.github/workflows/doc.yml +101 -0
  35. verl/.github/workflows/docker-build-ascend-a2-qwen3_5.yml +123 -0
  36. verl/.github/workflows/docker-build-ascend-a2.yml +281 -0
  37. verl/.github/workflows/docker-build-ascend-a3-qwen3_5.yml +123 -0
  38. verl/.github/workflows/docker-build-ascend-a3.yml +281 -0
  39. verl/.github/workflows/docker-build-ascend-sglang-a2.yml +84 -0
  40. verl/.github/workflows/docker-build-ascend-sglang-a3.yml +84 -0
  41. verl/.github/workflows/e2e_ascend.yml +215 -0
  42. verl/.github/workflows/e2e_fully_async_policy.yml +169 -0
  43. verl/.github/workflows/e2e_fully_async_policy_ascend.yml +169 -0
  44. verl/.github/workflows/e2e_fully_async_policy_opd.yml +105 -0
  45. verl/.github/workflows/e2e_fully_async_policy_trtllm.yml +171 -0
  46. verl/.github/workflows/e2e_one_step_off_policy.yml +169 -0
  47. verl/.github/workflows/e2e_one_step_off_policy_ascend.yml +170 -0
  48. verl/.github/workflows/e2e_ppo_grpo_trainer_trtllm.yml +289 -0
  49. verl/.github/workflows/e2e_ppo_trainer.yml +78 -0
  50. verl/.github/workflows/e2e_ppo_trainer_megatron_sglang.yml +202 -0
.claude/settings.local.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(env)",
5
+ "Bash(curl -s -o /dev/null -w \"HTTP %{http_code} time=%{time_total}s\\\\n\" --max-time 5 http://nexus.sii.shaipower.online/repository/pypi/simple/)",
6
+ "Bash(curl -s -o /dev/null -w \"HTTPS %{http_code} time=%{time_total}s\\\\n\" --max-time 5 https://nexus.sii.shaipower.online/repository/pypi/simple/)",
7
+ "Bash(curl -s -o /dev/null -w \"HTTP %{http_code} time=%{time_total}s\\\\n\" --max-time 15 http://nexus.sii.shaipower.online/repository/pypi/simple/)",
8
+ "Bash(python3 -c \"import sys, json; d=json.loads\\(sys.stdin.read\\(\\)\\); print\\(json.dumps\\(d, indent=2, ensure_ascii=False\\)[:3000]\\)\")",
9
+ "Bash(python3 -c ' *)",
10
+ "Bash(nvidia-smi)",
11
+ "Bash(python3 *)",
12
+ "Read(//root/miniconda3/envs/**)",
13
+ "Bash(conda env *)",
14
+ "Bash(/root/miniconda3/envs/verl/bin/python *)",
15
+ "Bash(/root/miniconda3/envs/verl/bin/pip list *)",
16
+ "Bash(chmod +x *)",
17
+ "Bash(/root/miniconda3/envs/verl/bin/pip show *)",
18
+ "Bash(grep -E \"\\\\.\\(py|sh\\)$\")"
19
+ ]
20
+ }
21
+ }
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ verl/core.778099 filter=lfs diff=lfs merge=lfs -text
37
+ verl/verl/trainer/ppo/__pycache__/core_algos.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
.sii/ide-port ADDED
@@ -0,0 +1 @@
 
 
1
+ 30003
0519_sft_test/data/convert_to_parquet.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Convert ind_v4 SFT JSONL into a verl-compatible parquet for MultiTurnSFTDataset.
2
+ #
3
+ # Source format (per line):
4
+ # {
5
+ # "messages": [
6
+ # {"role": "user", "content": "<image>\n..."},
7
+ # {"role": "assistant", "content": "", "tool_calls": [{"id": "...", "type": "function",
8
+ # "function": {"name": "...",
9
+ # "arguments": "{...}"}}]}
10
+ # ],
11
+ # "images": ["/abs/path/to/img.png"],
12
+ # "tools": [{"type": "function", "function": {...}}],
13
+ # "metadata": {...}
14
+ # }
15
+ #
16
+ # Target parquet columns (verl MultiTurnSFTDataset):
17
+ # - messages: list[dict]
18
+ # - images: list[{"bytes": <png bytes>}] (loadable by qwen_vl_utils.fetch_image)
19
+ # - tools: list[dict]
20
+
21
+ import argparse
22
+ import json
23
+ import os
24
+ import random
25
+ from pathlib import Path
26
+
27
+ import pandas as pd
28
+
29
+
30
+ def load_jsonl(path):
31
+ rows = []
32
+ with open(path, "r", encoding="utf-8") as f:
33
+ for line in f:
34
+ line = line.strip()
35
+ if line:
36
+ rows.append(json.loads(line))
37
+ return rows
38
+
39
+
40
+ def read_image_bytes(path: str) -> bytes:
41
+ with open(path, "rb") as f:
42
+ return f.read()
43
+
44
+
45
+ def normalize_messages(messages):
46
+ out = []
47
+ for m in messages:
48
+ nm = {"role": m["role"], "content": m.get("content", "") or ""}
49
+ if "tool_calls" in m and m["tool_calls"]:
50
+ tcs = []
51
+ for tc in m["tool_calls"]:
52
+ fn = tc.get("function", {})
53
+ args = fn.get("arguments", "")
54
+ if not isinstance(args, str):
55
+ args = json.dumps(args, ensure_ascii=False)
56
+ tcs.append(
57
+ {
58
+ "id": tc.get("id", ""),
59
+ "type": tc.get("type", "function"),
60
+ "function": {
61
+ "name": fn.get("name", ""),
62
+ "arguments": args,
63
+ },
64
+ }
65
+ )
66
+ nm["tool_calls"] = tcs
67
+ out.append(nm)
68
+ return out
69
+
70
+
71
+ def convert(rows, image_root: str | None, max_samples: int = -1):
72
+ if max_samples > 0:
73
+ rows = rows[:max_samples]
74
+ out = []
75
+ for row in rows:
76
+ messages = normalize_messages(row["messages"])
77
+
78
+ images = []
79
+ for img_path in row.get("images", []) or []:
80
+ p = img_path
81
+ if image_root and not os.path.isabs(p):
82
+ p = os.path.join(image_root, p)
83
+ images.append({"bytes": read_image_bytes(p)})
84
+
85
+ tools = row.get("tools", []) or []
86
+
87
+ out.append(
88
+ {
89
+ "messages": messages,
90
+ "images": images,
91
+ "tools": tools,
92
+ }
93
+ )
94
+ return out
95
+
96
+
97
+ def main():
98
+ parser = argparse.ArgumentParser()
99
+ parser.add_argument(
100
+ "--src",
101
+ default="/inspire/qb-ilm2/project/26summer-camp-21/26210880/0518_dataBuilder_liubw"
102
+ "/in_domain/output/ind_v4/sft_rl/sft_train.jsonl",
103
+ help="Path to source SFT JSONL.",
104
+ )
105
+ parser.add_argument(
106
+ "--image_root",
107
+ default=None,
108
+ help="Root for relative image paths. JSONL already has absolute paths, leave None.",
109
+ )
110
+ parser.add_argument(
111
+ "--out_dir",
112
+ default=str(Path(__file__).resolve().parent),
113
+ help="Directory to write {train,val}.parquet.",
114
+ )
115
+ parser.add_argument("--val_ratio", type=float, default=0.02)
116
+ parser.add_argument("--max_samples", type=int, default=-1)
117
+ parser.add_argument("--seed", type=int, default=42)
118
+ args = parser.parse_args()
119
+
120
+ out_dir = Path(args.out_dir)
121
+ out_dir.mkdir(parents=True, exist_ok=True)
122
+
123
+ rows = load_jsonl(args.src)
124
+ print(f"loaded {len(rows)} rows from {args.src}")
125
+
126
+ rng = random.Random(args.seed)
127
+ rng.shuffle(rows)
128
+
129
+ if args.max_samples > 0:
130
+ rows = rows[: args.max_samples]
131
+ print(f"truncated to {len(rows)} samples")
132
+
133
+ n_val = max(1, int(len(rows) * args.val_ratio)) if args.val_ratio > 0 else 0
134
+ val_rows = rows[:n_val]
135
+ train_rows = rows[n_val:]
136
+ print(f"split: train={len(train_rows)}, val={len(val_rows)}")
137
+
138
+ train_records = convert(train_rows, args.image_root)
139
+ val_records = convert(val_rows, args.image_root) if val_rows else []
140
+
141
+ train_df = pd.DataFrame(train_records)
142
+ train_path = out_dir / "train.parquet"
143
+ train_df.to_parquet(train_path, index=False)
144
+ print(f"wrote {train_path} ({len(train_df)} rows)")
145
+
146
+ if val_records:
147
+ val_df = pd.DataFrame(val_records)
148
+ val_path = out_dir / "val.parquet"
149
+ val_df.to_parquet(val_path, index=False)
150
+ print(f"wrote {val_path} ({len(val_df)} rows)")
151
+
152
+
153
+ if __name__ == "__main__":
154
+ main()
0519_sft_test/data/train.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e61cc170fa7c579bb897cfca7ec2058ead67bfbab7e3c7830b80211958737d4c
3
+ size 131141435
0519_sft_test/data/val.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7741860c3e5aadef4589d9306a95495c232b91798e4f1cc3ddcde799b03b48e7
3
+ size 2522091
0519_sft_test/run_sft_qwen3vl_8b.sh ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # SFT for Qwen3-VL-8B-Thinking on the ind_v4 dataset (verl FSDP engine).
3
+ #
4
+ # Usage:
5
+ # bash run_sft_qwen3vl_8b.sh # default 8 GPUs
6
+ # NUM_TRAINERS=4 bash run_sft_qwen3vl_8b.sh # override GPU count
7
+ # SP_SIZE=2 NUM_TRAINERS=8 bash run_sft_qwen3vl_8b.sh
8
+ set -xeuo pipefail
9
+
10
+ WORKDIR=$(cd "$(dirname "$0")" && pwd)
11
+
12
+ MODEL_PATH=${MODEL_PATH:-/inspire/qb-ilm2/project/26summer-camp-21/26210880/0518_test_liubw/models/Qwen3-VL-8B-Thinking}
13
+ TRAIN_FILES=${TRAIN_FILES:-${WORKDIR}/data/train.parquet}
14
+ VAL_FILES=${VAL_FILES:-${WORKDIR}/data/val.parquet}
15
+
16
+ NUM_TRAINERS=${NUM_TRAINERS:-8}
17
+ SP_SIZE=${SP_SIZE:-2} # ulysses sequence parallel
18
+ FSDP_SIZE=${FSDP_SIZE:--1}
19
+ FSDP_STRATEGY=${FSDP_STRATEGY:-fsdp2}
20
+
21
+ TRAIN_BATCH_SIZE=${TRAIN_BATCH_SIZE:-32}
22
+ MICRO_BATCH_SIZE_PER_GPU=${MICRO_BATCH_SIZE_PER_GPU:-1}
23
+ MAX_LENGTH=${MAX_LENGTH:-8192}
24
+ MAX_TOKEN_LEN_PER_GPU=${MAX_TOKEN_LEN_PER_GPU:-16384}
25
+ TOTAL_EPOCHS=${TOTAL_EPOCHS:-3}
26
+ LR=${LR:-1e-5}
27
+
28
+ PROJECT_NAME=${PROJECT_NAME:-ind_v4_sft}
29
+ EXPERIMENT_NAME=${EXPERIMENT_NAME:-qwen3vl-8b-thinking-fsdp${FSDP_STRATEGY}-sp${SP_SIZE}}
30
+ SAVE_DIR=${SAVE_DIR:-${WORKDIR}/checkpoints/${PROJECT_NAME}/${EXPERIMENT_NAME}}
31
+ LOGGER=${LOGGER:-"['console']"} # set to "['console','wandb']" once wandb is logged in
32
+ RESUME_MODE=${RESUME_MODE:-auto}
33
+
34
+ mkdir -p "${SAVE_DIR}"
35
+
36
+ cd /inspire/qb-ilm2/project/26summer-camp-21/26210880/0519_verl/verl
37
+
38
+ torchrun --standalone --nnodes=1 --nproc-per-node="${NUM_TRAINERS}" \
39
+ -m verl.trainer.sft_trainer \
40
+ data.train_files="${TRAIN_FILES}" \
41
+ data.val_files="${VAL_FILES}" \
42
+ data.train_batch_size="${TRAIN_BATCH_SIZE}" \
43
+ data.micro_batch_size_per_gpu="${MICRO_BATCH_SIZE_PER_GPU}" \
44
+ data.max_length="${MAX_LENGTH}" \
45
+ data.max_token_len_per_gpu="${MAX_TOKEN_LEN_PER_GPU}" \
46
+ data.use_dynamic_bsz=True \
47
+ data.pad_mode=no_padding \
48
+ data.truncation=right \
49
+ data.messages_key=messages \
50
+ data.tools_key=tools \
51
+ data.ignore_input_ids_mismatch=True \
52
+ model.path="${MODEL_PATH}" \
53
+ model.use_remove_padding=True \
54
+ engine=fsdp \
55
+ optim=fsdp \
56
+ optim.lr="${LR}" \
57
+ optim.lr_warmup_steps_ratio=0.03 \
58
+ optim.weight_decay=0.1 \
59
+ optim.betas="[0.9,0.95]" \
60
+ optim.clip_grad=1.0 \
61
+ optim.min_lr_ratio=0.1 \
62
+ optim.warmup_style=cosine \
63
+ engine.ulysses_sequence_parallel_size="${SP_SIZE}" \
64
+ engine.strategy="${FSDP_STRATEGY}" \
65
+ engine.fsdp_size="${FSDP_SIZE}" \
66
+ trainer.default_local_dir="${SAVE_DIR}" \
67
+ trainer.project_name="${PROJECT_NAME}" \
68
+ trainer.experiment_name="${EXPERIMENT_NAME}" \
69
+ trainer.total_epochs="${TOTAL_EPOCHS}" \
70
+ trainer.logger="${LOGGER}" \
71
+ trainer.save_freq=500 \
72
+ trainer.test_freq=200 \
73
+ trainer.max_ckpt_to_keep=3 \
74
+ trainer.resume_mode="${RESUME_MODE}" \
75
+ checkpoint.save_contents=[model,optimizer,extra]
0520_rej_test/README.md ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 0520_rej_test — Offline RAFT/RFT framework
2
+
3
+ verl 主仓库**没有现成的 reward-based 拒绝采样训练循环**:
4
+ - `algorithm.filter_groups` 只有 dataclass,没有 trainer 消费
5
+ - `algorithm.rollout_correction` 是 IS-based 拒绝,依据是重要性比值,不是 reward
6
+
7
+ 这里搭的是经典 **RAFT (Reward-rAnked Fine-Tuning)** —— 完全离线、字面意义上的拒绝采样:
8
+ 对当前模型采样 N 条 → reward 打分 → 取 top-K → 喂给 SFT。
9
+
10
+ ```
11
+ rl_train.jsonl
12
+ ↓ build_prompts.py
13
+ prompts.parquet
14
+ ↓ Stage A: verl.trainer.main_generation_server (vLLM, n=N)
15
+ gen.parquet
16
+ ↓ Stage B: reward/score.py + reward/reward_fn.py (STUB)
17
+ scored.parquet
18
+ ↓ Stage C: select/topk_to_sft.py
19
+ raft_train.parquet
20
+ ↓ Stage D: scripts/run_sft.sh -> 0519_sft_test/run_sft_qwen3vl_8b.sh
21
+ checkpoints/
22
+ ```
23
+
24
+ ## 现状
25
+ - 框架完整可执行;
26
+ - **`reward/reward_fn.py` 是 stub**,固定 `compute_reward(prompt, response, metadata, tools, ...)` 签名,永远返回 `0.0`,等后续实现;
27
+ - 不动 `0519_sft_test/` 任何文件,Stage D 通过环境变量复用其 SFT 脚本。
28
+
29
+ ## 使用
30
+
31
+ 整体跑:
32
+ ```bash
33
+ bash run_pipeline.sh
34
+ ```
35
+
36
+ 只跑某一段:
37
+ ```bash
38
+ STAGE=A_pre bash run_pipeline.sh # 只生成 prompts.parquet
39
+ STAGE=A bash run_pipeline.sh # 跑 vLLM 采样
40
+ STAGE=B bash run_pipeline.sh # 打分
41
+ STAGE=C bash run_pipeline.sh # 选 top-K + 转 SFT 格式
42
+ STAGE=D bash run_pipeline.sh # SFT
43
+ ```
44
+
45
+ Smoke 试跑(4 条 prompt × 2 个 sample):
46
+ ```bash
47
+ MAX_SAMPLES=4 N_SAMPLES=2 RESPONSE_LENGTH=512 STAGE=A_pre bash run_pipeline.sh
48
+ MAX_SAMPLES=4 N_SAMPLES=2 RESPONSE_LENGTH=512 STAGE=A bash run_pipeline.sh
49
+ STAGE=B bash run_pipeline.sh
50
+ TOP_K=1 STAGE=C bash run_pipeline.sh
51
+ ```
52
+
53
+ ## 关键依赖
54
+
55
+ | 用途 | 路径 |
56
+ |---|---|
57
+ | Stage A 入口 | `verl/trainer/main_generation_server.py` |
58
+ | 多模态 chat template | `verl/workers/rollout/schemas.py` |
59
+ | SFT 目标 schema | `verl/utils/dataset/multiturn_sft_dataset.py` |
60
+ | SFT 启动脚本(被复用) | `0519_sft_test/run_sft_qwen3vl_8b.sh` |
61
+ | 模型 | `0518_test_liubw/models/Qwen3-VL-8B-Thinking` |
62
+ | RL 原始数据 | `0518_dataBuilder_liubw/.../sft_rl/rl_train.jsonl` |
63
+
64
+ ## 设计要点
65
+
66
+ - **图像如何进 vLLM?** verl 的 `main_generation_server` 直接把 `prompt` 列里的 OpenAI 格式 messages 转给 vLLM 的 chat completions 接口 —— 我们把每张图编成 `data:image/png;base64,...` URL 塞进 multimodal content block。Stage C 再从 `image_paths` 重新读字节进 SFT parquet。
67
+ - **tools 怎么走?** `main_generation_server` 不读 `tools` 列;`tools` 的 JSON schema 当作文本前缀拼进 user turn。模型是 Qwen3-VL-Thinking,训练时见过 `<tool_call>{...}</tool_call>` 输出格式,会自然产出。
68
+ - **怎么把模型输出还原成结构化 tool_calls?** Stage C 用正则提 `<tool_call>` 块 → `json.loads` → 构造 OpenAI tool_call dict(`arguments` 强制成字符串,与 `0519_sft_test/data/convert_to_parquet.py` 一致)。解析失败的样本直接丢。
69
+ - **SFT schema 完全和 0519_sft_test 一致**,所以可以直接复用其 dataset 加载 + chat template 验证路径。
0520_rej_test/data/build_prompts.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Build prompts.parquet for Stage A (verl.trainer.main_generation_server).
2
+
3
+ Source: ind_v4 rl_train.jsonl with shape
4
+ {messages, images, tools, metadata}
5
+
6
+ main_generation_server expects a single `prompt` column whose value is an
7
+ OpenAI-style chat-completions `messages` list. It does NOT read `tools` or
8
+ `images` columns directly — both must be baked into `prompt`.
9
+
10
+ Encoding:
11
+ - `images[i]` is read from disk and embedded as a base64 data-URL inside
12
+ a multimodal content block on the user turn.
13
+ - `tools` (the JSON spec) is rendered as a textual preamble prepended to
14
+ the user turn so that even without server-side tool injection the model
15
+ sees the tool name + schema. The model is Qwen3-VL-Thinking, which is
16
+ trained to emit `<tool_call>{...}</tool_call>` blocks — Stage C will
17
+ parse them back out.
18
+
19
+ Extra columns kept on the parquet so downstream stages can use them:
20
+ - `metadata`: original metadata dict (test_points, expression_numpy, ...)
21
+ - `image_paths`: original image paths (so Stage C can re-read bytes
22
+ without going through the data-URL).
23
+ - `tools_orig`: original tools spec (for Stage C's SFT reconstruction).
24
+ """
25
+
26
+ import argparse
27
+ import base64
28
+ import json
29
+ import mimetypes
30
+ import os
31
+ import random
32
+ from pathlib import Path
33
+
34
+ import pandas as pd
35
+
36
+
37
+ def load_jsonl(path):
38
+ rows = []
39
+ with open(path, "r", encoding="utf-8") as f:
40
+ for line in f:
41
+ line = line.strip()
42
+ if line:
43
+ rows.append(json.loads(line))
44
+ return rows
45
+
46
+
47
+ def img_to_data_url(path: str) -> str:
48
+ mime, _ = mimetypes.guess_type(path)
49
+ if mime is None:
50
+ mime = "image/png"
51
+ with open(path, "rb") as f:
52
+ b64 = base64.b64encode(f.read()).decode("ascii")
53
+ return f"data:{mime};base64,{b64}"
54
+
55
+
56
+ def render_tools_preamble(tools: list[dict]) -> str:
57
+ if not tools:
58
+ return ""
59
+ lines = ["You have access to the following tools. Reply via a single "
60
+ "<tool_call>{...}</tool_call> block whose JSON contains "
61
+ "`name` and `arguments`."]
62
+ for t in tools:
63
+ lines.append(json.dumps(t, ensure_ascii=False))
64
+ return "\n".join(lines) + "\n\n"
65
+
66
+
67
+ def build_prompt(row: dict) -> list[dict]:
68
+ """Construct OpenAI multimodal `messages` from one raw RL sample."""
69
+ user_text = row["messages"][0]["content"]
70
+ if user_text.startswith("<image>\n"):
71
+ user_text = user_text[len("<image>\n") :]
72
+ elif user_text.startswith("<image>"):
73
+ user_text = user_text[len("<image>") :]
74
+
75
+ preamble = render_tools_preamble(row.get("tools") or [])
76
+ text_block = preamble + user_text
77
+
78
+ content = []
79
+ for img_path in row.get("images") or []:
80
+ content.append(
81
+ {"type": "image_url",
82
+ "image_url": {"url": img_to_data_url(img_path)}}
83
+ )
84
+ content.append({"type": "text", "text": text_block})
85
+
86
+ return [{"role": "user", "content": content}]
87
+
88
+
89
+ def main():
90
+ parser = argparse.ArgumentParser()
91
+ parser.add_argument(
92
+ "--src",
93
+ default="/inspire/qb-ilm2/project/26summer-camp-21/26210880"
94
+ "/0518_dataBuilder_liubw/in_domain/output/ind_v4/sft_rl/rl_train.jsonl",
95
+ )
96
+ parser.add_argument(
97
+ "--out",
98
+ default=str(Path(__file__).resolve().parent / "prompts.parquet"),
99
+ )
100
+ parser.add_argument("--max_samples", type=int, default=-1)
101
+ parser.add_argument("--shuffle", action="store_true")
102
+ parser.add_argument("--seed", type=int, default=42)
103
+ args = parser.parse_args()
104
+
105
+ rows = load_jsonl(args.src)
106
+ print(f"loaded {len(rows)} rows from {args.src}")
107
+
108
+ if args.shuffle:
109
+ rng = random.Random(args.seed)
110
+ rng.shuffle(rows)
111
+
112
+ if args.max_samples > 0:
113
+ rows = rows[: args.max_samples]
114
+ print(f"truncated to {len(rows)}")
115
+
116
+ records = []
117
+ for row in rows:
118
+ records.append(
119
+ {
120
+ "prompt": build_prompt(row),
121
+ "metadata": row.get("metadata", {}),
122
+ "image_paths": row.get("images") or [],
123
+ "tools_orig": row.get("tools") or [],
124
+ }
125
+ )
126
+
127
+ out_path = Path(args.out)
128
+ out_path.parent.mkdir(parents=True, exist_ok=True)
129
+ df = pd.DataFrame(records)
130
+ df.to_parquet(out_path, index=False)
131
+ print(f"wrote {out_path} ({len(df)} rows)")
132
+
133
+
134
+ if __name__ == "__main__":
135
+ main()
0520_rej_test/reward/__pycache__/reward_fn.cpython-312.pyc ADDED
Binary file (1.4 kB). View file
 
0520_rej_test/reward/reward_fn.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Stage B reward function.
2
+
3
+ This is a STUB. The signature is fixed; downstream `score.py` calls
4
+ `compute_reward(...)` row-wise by keyword. Implement the body later.
5
+
6
+ Inputs (kwargs):
7
+ prompt: list[dict] # OpenAI chat-completions messages used at generation
8
+ response: str # one model completion (raw text)
9
+ metadata: dict # original sample metadata (test_points, expression_numpy, ...)
10
+ tools: list[dict] # original `tools` JSON spec
11
+ index: int # sample index (for logging)
12
+ sample: int # which of the N samples (0..N-1)
13
+
14
+ Returns:
15
+ float in [0, 1] -- higher is better
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+
21
+ def compute_reward(
22
+ *,
23
+ prompt: list[dict],
24
+ response: str,
25
+ metadata: dict,
26
+ tools: list[dict],
27
+ index: int = 0,
28
+ sample: int = 0,
29
+ ) -> float:
30
+ # TODO: parse <tool_call>{"name": "submit_expression", "arguments": "..."}</tool_call>
31
+ # eval expression at metadata["test_points"], compute MSE-based reward.
32
+ # Return 0.0 if parse fails or eval errors out.
33
+ del prompt, response, metadata, tools, index, sample
34
+ return 0.0
0520_rej_test/reward/score.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Stage B driver: read gen.parquet, score each (prompt, response) pair via
2
+ reward_fn.compute_reward, write scored.parquet with a new `scores` column
3
+ (parallel to `responses`).
4
+
5
+ Reward fn lives in reward/reward_fn.py and can be swapped via --reward_fn.
6
+ """
7
+
8
+ import argparse
9
+ import importlib.util
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ import pandas as pd
14
+
15
+
16
+ def load_reward_fn(path: str, name: str = "compute_reward"):
17
+ spec = importlib.util.spec_from_file_location("reward_fn_module", path)
18
+ mod = importlib.util.module_from_spec(spec)
19
+ sys.modules["reward_fn_module"] = mod
20
+ assert spec.loader is not None
21
+ spec.loader.exec_module(mod)
22
+ fn = getattr(mod, name)
23
+ return fn
24
+
25
+
26
+ def main():
27
+ parser = argparse.ArgumentParser()
28
+ parser.add_argument(
29
+ "--gen",
30
+ default=str(Path(__file__).resolve().parent.parent / "data" / "gen.parquet"),
31
+ )
32
+ parser.add_argument(
33
+ "--out",
34
+ default=str(Path(__file__).resolve().parent.parent / "data" / "scored.parquet"),
35
+ )
36
+ parser.add_argument(
37
+ "--reward_fn",
38
+ default=str(Path(__file__).resolve().parent / "reward_fn.py"),
39
+ )
40
+ parser.add_argument("--reward_name", default="compute_reward")
41
+ args = parser.parse_args()
42
+
43
+ df = pd.read_parquet(args.gen)
44
+ print(f"loaded {len(df)} rows from {args.gen}; columns: {list(df.columns)}")
45
+
46
+ if "responses" not in df.columns:
47
+ raise SystemExit("expected `responses` column from Stage A; not found.")
48
+
49
+ compute_reward = load_reward_fn(args.reward_fn, args.reward_name)
50
+
51
+ all_scores = []
52
+ for idx, row in df.iterrows():
53
+ prompt = row["prompt"]
54
+ responses = list(row["responses"])
55
+ metadata = row.get("metadata", {}) or {}
56
+ tools = list(row.get("tools_orig", []) or [])
57
+ scores = []
58
+ for s_idx, resp in enumerate(responses):
59
+ score = compute_reward(
60
+ prompt=prompt,
61
+ response=resp,
62
+ metadata=metadata,
63
+ tools=tools,
64
+ index=int(idx),
65
+ sample=s_idx,
66
+ )
67
+ scores.append(float(score))
68
+ all_scores.append(scores)
69
+
70
+ df = df.copy()
71
+ df["scores"] = all_scores
72
+
73
+ out_path = Path(args.out)
74
+ out_path.parent.mkdir(parents=True, exist_ok=True)
75
+ df.to_parquet(out_path, index=False)
76
+ print(f"wrote {out_path} ({len(df)} rows)")
77
+
78
+
79
+ if __name__ == "__main__":
80
+ main()
0520_rej_test/run_pipeline.sh ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # End-to-end RAFT pipeline: build prompts -> generate -> score -> top-K -> SFT.
3
+ # Each stage is overrideable via env vars and idempotent.
4
+ set -xeuo pipefail
5
+
6
+ WORKDIR=$(cd "$(dirname "$0")" && pwd)
7
+ PY=${PY:-python}
8
+
9
+ MAX_SAMPLES=${MAX_SAMPLES:--1}
10
+ N_SAMPLES=${N_SAMPLES:-8}
11
+ TOP_K=${TOP_K:-1}
12
+
13
+ STAGE=${STAGE:-all} # all | A | B | C | D
14
+
15
+ if [[ "${STAGE}" == "all" || "${STAGE}" == "A_pre" || "${STAGE}" == "A" ]]; then
16
+ "${PY}" "${WORKDIR}/data/build_prompts.py" \
17
+ --out "${WORKDIR}/data/prompts.parquet" \
18
+ --max_samples "${MAX_SAMPLES}"
19
+ fi
20
+
21
+ if [[ "${STAGE}" == "all" || "${STAGE}" == "A" ]]; then
22
+ N_SAMPLES="${N_SAMPLES}" \
23
+ PROMPTS="${WORKDIR}/data/prompts.parquet" \
24
+ OUT="${WORKDIR}/data/gen.parquet" \
25
+ bash "${WORKDIR}/scripts/run_generate.sh"
26
+ fi
27
+
28
+ if [[ "${STAGE}" == "all" || "${STAGE}" == "B" ]]; then
29
+ "${PY}" "${WORKDIR}/reward/score.py" \
30
+ --gen "${WORKDIR}/data/gen.parquet" \
31
+ --out "${WORKDIR}/data/scored.parquet"
32
+ fi
33
+
34
+ if [[ "${STAGE}" == "all" || "${STAGE}" == "C" ]]; then
35
+ "${PY}" "${WORKDIR}/select/topk_to_sft.py" \
36
+ --scored "${WORKDIR}/data/scored.parquet" \
37
+ --out "${WORKDIR}/data/raft_train.parquet" \
38
+ --k "${TOP_K}"
39
+ fi
40
+
41
+ if [[ "${STAGE}" == "all" || "${STAGE}" == "D" ]]; then
42
+ bash "${WORKDIR}/scripts/run_sft.sh"
43
+ fi
0520_rej_test/scripts/run_generate.sh ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Stage A: rollout N completions per prompt with vLLM, via verl's
3
+ # main_generation_server.
4
+ #
5
+ # Usage:
6
+ # bash run_generate.sh # full prompts.parquet
7
+ # PROMPTS=/path/to/sub.parquet OUT=/tmp/gen.parquet \
8
+ # N_SAMPLES=2 bash run_generate.sh # smoke
9
+ set -xeuo pipefail
10
+
11
+ WORKDIR=$(cd "$(dirname "$0")/.." && pwd)
12
+ VERL_ROOT=/inspire/qb-ilm2/project/26summer-camp-21/26210880/0519_verl/verl
13
+
14
+ MODEL_PATH=${MODEL_PATH:-/inspire/qb-ilm2/project/26summer-camp-21/26210880/0518_test_liubw/models/Qwen3-VL-8B-Thinking}
15
+ PROMPTS=${PROMPTS:-${WORKDIR}/data/prompts.parquet}
16
+ OUT=${OUT:-${WORKDIR}/data/gen.parquet}
17
+
18
+ NNODES=${NNODES:-1}
19
+ NGPUS_PER_NODE=${NGPUS_PER_NODE:-8}
20
+ ROLLOUT_TP=${ROLLOUT_TP:-2}
21
+ ROLLOUT_GPU_MEM_UTIL=${ROLLOUT_GPU_MEM_UTIL:-0.85}
22
+
23
+ PROMPT_LENGTH=${PROMPT_LENGTH:-4096}
24
+ RESPONSE_LENGTH=${RESPONSE_LENGTH:-4096}
25
+ N_SAMPLES=${N_SAMPLES:-8}
26
+ TEMPERATURE=${TEMPERATURE:-1.0}
27
+ TOP_P=${TOP_P:-0.95}
28
+
29
+ mkdir -p "$(dirname "${OUT}")"
30
+
31
+ cd "${VERL_ROOT}"
32
+
33
+ python3 -m verl.trainer.main_generation_server \
34
+ trainer.nnodes="${NNODES}" \
35
+ trainer.n_gpus_per_node="${NGPUS_PER_NODE}" \
36
+ data.train_files="${PROMPTS}" \
37
+ data.prompt_key=prompt \
38
+ +data.output_path="${OUT}" \
39
+ actor_rollout_ref.model.path="${MODEL_PATH}" \
40
+ actor_rollout_ref.model.trust_remote_code=True \
41
+ actor_rollout_ref.rollout.name=vllm \
42
+ actor_rollout_ref.rollout.temperature="${TEMPERATURE}" \
43
+ actor_rollout_ref.rollout.top_p="${TOP_P}" \
44
+ actor_rollout_ref.rollout.prompt_length="${PROMPT_LENGTH}" \
45
+ actor_rollout_ref.rollout.response_length="${RESPONSE_LENGTH}" \
46
+ actor_rollout_ref.rollout.tensor_model_parallel_size="${ROLLOUT_TP}" \
47
+ actor_rollout_ref.rollout.gpu_memory_utilization="${ROLLOUT_GPU_MEM_UTIL}" \
48
+ actor_rollout_ref.rollout.n="${N_SAMPLES}" "$@"
0520_rej_test/scripts/run_sft.sh ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Stage D wrapper: re-use 0519_sft_test/run_sft_qwen3vl_8b.sh against the
3
+ # RAFT-mined parquet, without modifying the original script.
4
+ set -xeuo pipefail
5
+
6
+ WORKDIR=$(cd "$(dirname "$0")/.." && pwd)
7
+
8
+ TRAIN_FILES=${TRAIN_FILES:-${WORKDIR}/data/raft_train.parquet}
9
+ VAL_FILES=${VAL_FILES:-${WORKDIR}/data/raft_val.parquet}
10
+
11
+ PROJECT_NAME=${PROJECT_NAME:-ind_v4_raft}
12
+ EXPERIMENT_NAME=${EXPERIMENT_NAME:-qwen3vl-8b-thinking-raft}
13
+ SAVE_DIR=${SAVE_DIR:-${WORKDIR}/checkpoints/${PROJECT_NAME}/${EXPERIMENT_NAME}}
14
+
15
+ TRAIN_FILES="${TRAIN_FILES}" \
16
+ VAL_FILES="${VAL_FILES}" \
17
+ PROJECT_NAME="${PROJECT_NAME}" \
18
+ EXPERIMENT_NAME="${EXPERIMENT_NAME}" \
19
+ SAVE_DIR="${SAVE_DIR}" \
20
+ bash /inspire/qb-ilm2/project/26summer-camp-21/26210880/0519_verl/0519_sft_test/run_sft_qwen3vl_8b.sh "$@"
0520_rej_test/select/topk_to_sft.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Stage C: scored.parquet -> raft_train.parquet (verl SFT schema).
2
+
3
+ For each row pick the top-K responses by score, parse Qwen3-VL-Thinking
4
+ `<tool_call>{...}</tool_call>` blocks back into structured tool_calls, and
5
+ emit one SFT example per (row, kept response). Output schema matches
6
+ `verl/utils/dataset/multiturn_sft_dataset.py`:
7
+ - messages: [user_turn, assistant_turn_with_tool_calls]
8
+ - images: [{"bytes": <png>}] (re-read from row["image_paths"])
9
+ - tools: original tools spec
10
+ """
11
+
12
+ import argparse
13
+ import json
14
+ import os
15
+ import re
16
+ from pathlib import Path
17
+
18
+ import pandas as pd
19
+
20
+ TOOL_CALL_RE = re.compile(r"<tool_call>\s*(.*?)\s*</tool_call>", re.DOTALL)
21
+
22
+
23
+ def read_image_bytes(path: str) -> bytes:
24
+ with open(path, "rb") as f:
25
+ return f.read()
26
+
27
+
28
+ def parse_tool_calls(text: str) -> tuple[str, list[dict]]:
29
+ """Strip <tool_call>...</tool_call> blocks out of `text`.
30
+
31
+ Returns:
32
+ (residual_text_with_blocks_removed, list_of_tool_call_dicts)
33
+
34
+ Each tool_call dict is OpenAI-style:
35
+ {"id": "...", "type": "function",
36
+ "function": {"name": "...", "arguments": "<json string>"}}
37
+ Failed JSON parses make the whole sample invalid (caller should drop).
38
+ """
39
+ tool_calls = []
40
+ for i, m in enumerate(TOOL_CALL_RE.finditer(text)):
41
+ raw = m.group(1).strip()
42
+ try:
43
+ obj = json.loads(raw)
44
+ except json.JSONDecodeError:
45
+ return text, []
46
+ name = obj.get("name", "")
47
+ args = obj.get("arguments", obj.get("parameters", {}))
48
+ if not isinstance(args, str):
49
+ args = json.dumps(args, ensure_ascii=False)
50
+ tool_calls.append(
51
+ {
52
+ "id": f"call_{i}",
53
+ "type": "function",
54
+ "function": {"name": name, "arguments": args},
55
+ }
56
+ )
57
+ residual = TOOL_CALL_RE.sub("", text).strip()
58
+ return residual, tool_calls
59
+
60
+
61
+ def reconstruct_user_turn(prompt_messages: list[dict]) -> dict:
62
+ """The prompt for generation was multimodal (image_url + text). For SFT
63
+ we replace it with the conventional `<image>\\n<text>` Qwen format —
64
+ this matches the existing 0519_sft_test/data parquet exactly. The image
65
+ bytes are attached separately via the `images` column.
66
+ """
67
+ user_msg = prompt_messages[0]
68
+ text_parts = []
69
+ for blk in user_msg["content"]:
70
+ if isinstance(blk, dict) and blk.get("type") == "text":
71
+ text_parts.append(blk["text"])
72
+ text = "\n".join(text_parts)
73
+ return {"role": "user", "content": "<image>\n" + text}
74
+
75
+
76
+ def main():
77
+ parser = argparse.ArgumentParser()
78
+ parser.add_argument(
79
+ "--scored",
80
+ default=str(Path(__file__).resolve().parent.parent / "data" / "scored.parquet"),
81
+ )
82
+ parser.add_argument(
83
+ "--out",
84
+ default=str(Path(__file__).resolve().parent.parent / "data" / "raft_train.parquet"),
85
+ )
86
+ parser.add_argument("--k", type=int, default=1, help="Top-K responses per prompt.")
87
+ parser.add_argument("--min_score", type=float, default=None)
88
+ parser.add_argument("--keep_above_mean", action="store_true",
89
+ help="Keep responses scoring strictly above the per-row mean.")
90
+ args = parser.parse_args()
91
+
92
+ df = pd.read_parquet(args.scored)
93
+ print(f"loaded {len(df)} rows from {args.scored}")
94
+
95
+ out_records = []
96
+ n_dropped_parse = 0
97
+ n_dropped_score = 0
98
+ for _, row in df.iterrows():
99
+ responses = list(row["responses"])
100
+ scores = list(row["scores"])
101
+ assert len(responses) == len(scores)
102
+
103
+ ranked = sorted(zip(scores, responses), key=lambda x: x[0], reverse=True)
104
+ if args.keep_above_mean:
105
+ mean_s = sum(scores) / max(1, len(scores))
106
+ ranked = [(s, r) for s, r in ranked if s > mean_s]
107
+ if args.min_score is not None:
108
+ ranked = [(s, r) for s, r in ranked if s >= args.min_score]
109
+ ranked = ranked[: args.k]
110
+
111
+ if not ranked:
112
+ n_dropped_score += 1
113
+ continue
114
+
115
+ user_turn = reconstruct_user_turn(list(row["prompt"]))
116
+ tools = list(row.get("tools_orig", []) or [])
117
+ image_paths = list(row.get("image_paths", []) or [])
118
+ images = [{"bytes": read_image_bytes(p)} for p in image_paths]
119
+
120
+ for score, resp in ranked:
121
+ content_text, tool_calls = parse_tool_calls(resp)
122
+ if not tool_calls:
123
+ n_dropped_parse += 1
124
+ continue
125
+ assistant_turn = {
126
+ "role": "assistant",
127
+ "content": content_text or "",
128
+ "tool_calls": tool_calls,
129
+ }
130
+ out_records.append(
131
+ {
132
+ "messages": [user_turn, assistant_turn],
133
+ "images": images,
134
+ "tools": tools,
135
+ }
136
+ )
137
+
138
+ print(f"kept {len(out_records)} sft examples; "
139
+ f"dropped {n_dropped_parse} (parse), {n_dropped_score} (score filter)")
140
+
141
+ out_path = Path(args.out)
142
+ out_path.parent.mkdir(parents=True, exist_ok=True)
143
+ pd.DataFrame(out_records).to_parquet(out_path, index=False)
144
+ print(f"wrote {out_path}")
145
+
146
+
147
+ if __name__ == "__main__":
148
+ main()
data.tar.gz ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:13d6c907cdce09739e63e32f40977e608f6afe6169b85bb58938b9df2616b3f8
3
+ size 391661196
pip_list.txt ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py 2.4.0
2
+ accelerate 1.12.0
3
+ aiohappyeyeballs 2.6.1
4
+ aiohttp 3.13.3
5
+ aiohttp-cors 0.8.1
6
+ aiosignal 1.4.0
7
+ annotated-doc 0.0.4
8
+ annotated-types 0.7.0
9
+ antlr4-python3-runtime 4.9.3
10
+ anyio 4.12.1
11
+ astor 0.8.1
12
+ async-timeout 5.0.1
13
+ attrs 25.4.0
14
+ av 16.1.0
15
+ blake3 1.0.8
16
+ cachetools 6.2.5
17
+ cbor2 5.8.0
18
+ certifi 2026.1.4
19
+ cffi 2.0.0
20
+ charset-normalizer 3.4.4
21
+ click 8.3.1
22
+ cloudpickle 3.1.2
23
+ codetiming 1.4.0
24
+ colorful 0.5.8
25
+ compressed-tensors 0.11.0
26
+ cupy-cuda12x 13.6.0
27
+ datasets 4.5.0
28
+ depyf 0.19.0
29
+ dill 0.4.0
30
+ diskcache 5.6.3
31
+ distlib 0.4.0
32
+ distro 1.9.0
33
+ dnspython 2.8.0
34
+ einops 0.8.2
35
+ email-validator 2.3.0
36
+ exceptiongroup 1.3.1
37
+ fastapi 0.128.0
38
+ fastapi-cli 0.0.20
39
+ fastapi-cloud-cli 0.11.0
40
+ fastar 0.8.0
41
+ fastrlock 0.8.3
42
+ filelock 3.20.3
43
+ flash_attn 2.8.3+cu12torch28cxx11abitrue
44
+ frozendict 2.4.7
45
+ frozenlist 1.8.0
46
+ fsspec 2025.10.0
47
+ gguf 0.17.1
48
+ gitdb 4.0.12
49
+ GitPython 3.1.46
50
+ google-api-core 2.29.0
51
+ google-auth 2.47.0
52
+ googleapis-common-protos 1.72.0
53
+ grpcio 1.76.0
54
+ h11 0.16.0
55
+ hf-xet 1.2.0
56
+ httpcore 1.0.9
57
+ httptools 0.7.1
58
+ httpx 0.28.1
59
+ huggingface-hub 0.36.0
60
+ idna 3.11
61
+ importlib_metadata 8.7.1
62
+ interegular 0.3.3
63
+ Jinja2 3.1.6
64
+ jiter 0.12.0
65
+ jsonschema 4.26.0
66
+ jsonschema-specifications 2025.9.1
67
+ lark 1.2.2
68
+ liger_kernel 0.6.4
69
+ llguidance 0.7.30
70
+ llvmlite 0.44.0
71
+ lm-format-enforcer 0.11.3
72
+ Markdown 3.10.1
73
+ markdown-it-py 4.0.0
74
+ MarkupSafe 3.0.3
75
+ mathruler 0.1.0
76
+ mdurl 0.1.2
77
+ mistral_common 1.8.8
78
+ mpmath 1.3.0
79
+ msgpack 1.1.2
80
+ msgspec 0.20.0
81
+ multidict 6.7.1
82
+ multiprocess 0.70.18
83
+ networkx 3.4.2
84
+ ninja 1.13.0
85
+ numba 0.61.2
86
+ numpy 2.2.6
87
+ nvidia-cublas-cu12 12.6.4.1
88
+ nvidia-cuda-cupti-cu12 12.6.80
89
+ nvidia-cuda-nvrtc-cu12 12.6.77
90
+ nvidia-cuda-runtime-cu12 12.6.77
91
+ nvidia-cudnn-cu12 9.10.2.21
92
+ nvidia-cufft-cu12 11.3.0.4
93
+ nvidia-cufile-cu12 1.11.1.6
94
+ nvidia-curand-cu12 10.3.7.77
95
+ nvidia-cusolver-cu12 11.7.1.2
96
+ nvidia-cusparse-cu12 12.5.4.2
97
+ nvidia-cusparselt-cu12 0.7.1
98
+ nvidia-nccl-cu12 2.27.3
99
+ nvidia-nvjitlink-cu12 12.6.85
100
+ nvidia-nvtx-cu12 12.6.77
101
+ omegaconf 2.3.0
102
+ openai 2.15.0
103
+ openai-harmony 0.0.8
104
+ opencensus 0.11.4
105
+ opencensus-context 0.1.3
106
+ opencv-python-headless 4.13.0.90
107
+ opentelemetry-api 1.39.1
108
+ opentelemetry-exporter-prometheus 0.60b1
109
+ opentelemetry-proto 1.39.1
110
+ opentelemetry-sdk 1.39.1
111
+ opentelemetry-semantic-conventions 0.60b1
112
+ orjson 3.11.5
113
+ outlines_core 0.2.11
114
+ packaging 25.0
115
+ pandas 2.3.3
116
+ partial-json-parser 0.2.1.1.post7
117
+ peft 0.18.1
118
+ pillow 12.1.0
119
+ pip 25.3
120
+ platformdirs 4.5.1
121
+ prometheus_client 0.24.1
122
+ prometheus-fastapi-instrumentator 7.1.0
123
+ propcache 0.4.1
124
+ proto-plus 1.27.0
125
+ protobuf 6.33.4
126
+ psutil 7.2.1
127
+ py-cpuinfo 9.0.0
128
+ py-spy 0.4.1
129
+ pyarrow 23.0.0
130
+ pyasn1 0.6.2
131
+ pyasn1_modules 0.4.2
132
+ pybase64 1.4.3
133
+ pycountry 24.6.1
134
+ pycparser 3.0
135
+ pydantic 2.12.5
136
+ pydantic_core 2.41.5
137
+ pydantic-extra-types 2.11.0
138
+ pydantic-settings 2.12.0
139
+ Pygments 2.19.2
140
+ pylatexenc 2.10
141
+ python-dateutil 2.9.0.post0
142
+ python-dotenv 1.2.1
143
+ python-json-logger 4.0.0
144
+ python-multipart 0.0.22
145
+ pytz 2025.2
146
+ pyvers 0.1.0
147
+ PyYAML 6.0.3
148
+ pyzmq 27.1.0
149
+ qwen-vl-utils 0.0.14
150
+ ray 2.53.0
151
+ referencing 0.37.0
152
+ regex 2026.1.15
153
+ requests 2.32.5
154
+ rich 14.3.1
155
+ rich-toolkit 0.17.1
156
+ rignore 0.7.6
157
+ rpds-py 0.30.0
158
+ rsa 4.9.1
159
+ safetensors 0.7.0
160
+ scipy 1.15.3
161
+ sentencepiece 0.2.1
162
+ sentry-sdk 2.50.0
163
+ setproctitle 1.3.7
164
+ setuptools 80.9.0
165
+ shellingham 1.5.4
166
+ six 1.17.0
167
+ smart_open 7.5.0
168
+ smmap 5.0.2
169
+ sniffio 1.3.1
170
+ soundfile 0.13.1
171
+ soxr 1.0.0
172
+ starlette 0.50.0
173
+ sympy 1.14.0
174
+ tensorboard 2.20.0
175
+ tensorboard-data-server 0.7.2
176
+ tensordict 0.10.0
177
+ tiktoken 0.12.0
178
+ tokenizers 0.22.2
179
+ tomli 2.4.0
180
+ torch 2.8.0+cu126
181
+ torchaudio 2.8.0+cu126
182
+ torchdata 0.11.0
183
+ torchvision 0.23.0+cu126
184
+ tqdm 4.67.1
185
+ transformers 4.57.0
186
+ triton 3.4.0
187
+ typer 0.21.1
188
+ typing_extensions 4.15.0
189
+ typing-inspection 0.4.2
190
+ tzdata 2025.3
191
+ urllib3 2.6.3
192
+ uvicorn 0.40.0
193
+ uvloop 0.22.1
194
+ virtualenv 20.36.1
195
+ vllm 0.11.0
196
+ wandb 0.24.0
197
+ watchfiles 1.1.1
198
+ websockets 16.0
199
+ Werkzeug 3.1.5
200
+ wheel 0.45.1
201
+ wrapt 2.0.1
202
+ xformers 0.0.32.post1
203
+ xgrammar 0.1.25
204
+ xxhash 3.6.0
205
+ yarl 1.22.0
206
+ zipp 3.23.0
requirements.txt ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated from pip_list.txt — exact version pinning
2
+ # Note:
3
+ # - torch / torchaudio / torchvision carry the +cu124 local tag;
4
+ # install with: pip install --index-url https://download.pytorch.org/whl/cu124 ...
5
+ # - conda / mamba / libmambapy / menuinst / libarchive-c are normally managed
6
+ # by conda, not pip. Remove them if installing into a non-conda env.
7
+ # - pip / setuptools / wheel are bootstrap packages; pin only if you intend to
8
+ # control their versions explicitly.
9
+
10
+ absl-py==2.4.0
11
+ annotated-doc==0.0.4
12
+ anyio==4.13.0
13
+ archspec==0.2.3
14
+ asttokens==2.4.1
15
+ astunparse==1.6.3
16
+ attrs==24.2.0
17
+ beautifulsoup4==4.12.3
18
+ blessed==1.33.0
19
+ boltons==24.0.0
20
+ Brotli==1.1.0
21
+ certifi==2024.8.30
22
+ cffi==1.17.1
23
+ chardet==5.2.0
24
+ charset-normalizer==3.4.0
25
+ click==8.3.3
26
+ colorama==0.4.6
27
+ cycler==0.12.1
28
+ decorator==5.1.1
29
+ distro==1.9.0
30
+ dnspython==2.7.0
31
+ exceptiongroup==1.2.2
32
+ executing==2.1.0
33
+ expecttest==0.2.1
34
+ filelock==3.16.1
35
+ fonttools==4.62.1
36
+ frozendict==2.4.6
37
+ fsspec==2024.10.0
38
+ gpustat==1.1.1
39
+ grpcio==1.80.0
40
+ h11==0.16.0
41
+ h2==4.1.0
42
+ hf-xet==1.5.0
43
+ hpack==4.0.0
44
+ httpcore==1.0.9
45
+ httpx==0.28.1
46
+ huggingface-hub==1.14.0
47
+ hyperframe==6.0.1
48
+ hypothesis==6.115.5
49
+ idna==3.10
50
+ importlib-resources==6.4.5
51
+ ipython==8.29.0
52
+ jedi==0.19.1
53
+ Jinja2==3.1.4
54
+ jsonpatch==1.33
55
+ jsonpointer==3.0.0
56
+ jsonschema==4.23.0
57
+ jsonschema-specifications==2024.10.1
58
+ kiwisolver==1.5.0
59
+ libarchive-c==5.1
60
+ lief==0.14.1
61
+ lintrunner==0.12.5
62
+ Markdown==3.10.2
63
+ markdown-it-py==4.2.0
64
+ MarkupSafe==3.0.2
65
+ matplotlib==3.10.9
66
+ matplotlib-inline==0.1.7
67
+ mdurl==0.1.2
68
+ modelscope==1.35.2
69
+ more-itertools==10.5.0
70
+ mpmath==1.3.0
71
+ networkx==3.4.2
72
+ ninja==1.11.1.1
73
+ numpy<2
74
+ nvitop==1.6.2
75
+ optree==0.13.0
76
+ packaging==24.1
77
+ parso==0.8.4
78
+ pexpect==4.9.0
79
+ pickleshare==0.7.5
80
+ pillow==10.2.0
81
+ pip==24.2
82
+ pkginfo==1.11.2
83
+ pkgutil-resolve-name==1.3.10
84
+ platformdirs==4.3.6
85
+ pluggy==1.5.0
86
+ prompt-toolkit==3.0.48
87
+ protobuf==7.34.1
88
+ psutil==6.1.0
89
+ ptyprocess==0.7.0
90
+ pure-eval==0.2.3
91
+ pyarrow==24.0.0
92
+ pycosat==0.6.6
93
+ pycparser==2.22
94
+ Pygments==2.18.0
95
+ pyparsing==3.3.2
96
+ PySocks==1.7.1
97
+ python-dateutil==2.9.0.post0
98
+ python-etcd==0.4.5
99
+ pytz==2024.2
100
+ PyYAML==6.0.2
101
+ referencing==0.35.1
102
+ requests==2.32.3
103
+ rich==15.0.0
104
+ rpds-py==0.20.0
105
+ ruamel.yaml==0.18.6
106
+ ruamel.yaml.clib==0.2.8
107
+ setuptools==72.1.0
108
+ shellingham==1.5.4
109
+ six==1.16.0
110
+ sortedcontainers==2.4.0
111
+ soupsieve==2.5
112
+ stack-data==0.6.2
113
+ sympy==1.13.1
114
+ tensorboard==2.20.0
115
+ tensorboard-data-server==0.7.2
116
+ torch==2.5.1+cu124
117
+ torchaudio==2.5.1+cu124
118
+ torchelastic==0.2.2
119
+ torchvision==0.20.1+cu124
120
+ tqdm==4.66.5
121
+ traitlets==5.14.3
122
+ triton==3.1.0
123
+ truststore==0.9.2
124
+ typer==0.25.1
125
+ types-dataclasses==0.6.6
126
+ typing-extensions==4.12.2
127
+ urllib3==2.2.3
128
+ wcwidth==0.6.0
129
+ Werkzeug==3.1.8
130
+ wheel==0.44.0
131
+ zipp==3.20.2
132
+ zstandard==0.23.0
verl.tar.gz ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ee07dc56371e123a4f5afcd399a6b1b4624ce8af1edac0f8512ef1d98f5bced9
3
+ size 5809181481
verl/.agent/skills/issue/SKILL.md ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: issue
3
+ description: Create or update a GitHub issue following verl project conventions.
4
+ user_invocable: true
5
+ ---
6
+
7
+ When the user asks to create or update an issue, follow these steps:
8
+
9
+ ### 1. Gather Context
10
+
11
+ Read the following to understand available issue types and their required fields:
12
+
13
+ - [`bug-report.yml`](.github/ISSUE_TEMPLATE/bug-report.yml)
14
+ - [`feature-request.yml`](.github/ISSUE_TEMPLATE/feature-request.yml)
15
+
16
+ If updating an existing issue, read its current title, body, labels, and comments first.
17
+
18
+ ### 2. Determine Issue Type
19
+
20
+ Based on the user's description, select the appropriate template:
21
+
22
+ - **Bug report** ([`bug-report.yml`](.github/ISSUE_TEMPLATE/bug-report.yml)) — something is broken or behaves unexpectedly
23
+ - **Feature request** ([`feature-request.yml`](.github/ISSUE_TEMPLATE/feature-request.yml)) — a new capability or enhancement
24
+ - **Blank issue** — if neither template fits
25
+
26
+ ### 3. Compose the Issue
27
+
28
+ Fill in the template fields based on information from the user and the codebase. For bug reports, run `python scripts/diagnose.py` to gather system info if possible.
29
+
30
+ When updating, ensure the title and body still accurately reflect the current state of the issue.
31
+
32
+ ### 4. Check for Duplicates
33
+
34
+ Search for existing issues before creating:
35
+
36
+ ```
37
+ gh issue list --repo verl-project/verl --state open --search "<keywords>"
38
+ ```
39
+
40
+ If a duplicate exists, inform the user instead of creating a new one.
41
+
42
+ ### 5. Create or Update the Issue
43
+
44
+ - **Create**: add `good first issue` and/or `call for contribution` labels if the issue is straightforward and suitable for new contributors.
45
+ - **Update**: update title, body, and labels as needed.
46
+
47
+ Return the issue URL when done.
verl/.agent/skills/pr/SKILL.md ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: pr
3
+ description: Create or update a pull request following verl project conventions.
4
+ user_invocable: true
5
+ ---
6
+
7
+ When the user asks to create or update a PR, follow these steps:
8
+
9
+ ### 1. Gather Context
10
+
11
+ Read the following and understand the current branch's changes compared to main:
12
+
13
+ - [`CONTRIBUTING.md`](CONTRIBUTING.md)
14
+ - [`PULL_REQUEST_TEMPLATE.md`](.github/PULL_REQUEST_TEMPLATE.md)
15
+
16
+ If a PR already exists for this branch, also read its current title, body, and review comments.
17
+
18
+ ### 2. Compose PR Title and Body
19
+
20
+ Follow the PR template strictly for both title format and body sections. Only check checklist boxes for steps that have actually been completed.
21
+
22
+ When updating, ensure the title and body still accurately reflect **all** changes on the branch, not just the latest commit.
23
+
24
+ ### 3. Pre-submit Checks
25
+
26
+ Run pre-commit and fix any issues before creating or pushing.
27
+
28
+ ### 4. Create or Update the PR
29
+
30
+ - **Create**: target `main` by default unless the user specifies otherwise.
31
+ - **Update**: push new commits and update the title and body if the scope has changed. **Read the current PR title and body first** and incorporate any edits the user may have made directly on GitHub — never overwrite with a version generated from scratch.
32
+
33
+ Return the PR URL when done.
verl/.gemini/config.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ have_fun: false
2
+ code_review:
3
+ disable: false
4
+ comment_severity_threshold: HIGH
5
+ max_review_comments: -1
6
+ pull_request_opened:
7
+ help: false
8
+ summary: false
9
+ code_review: true
10
+ ignore_patterns: []
verl/.git-blame-ignore-revs ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Local uasge: git config blame.ignoreRevsFile .git-blame-ignore-revs
2
+
3
+ # [dev] feat: immigrate from yapf & pylint to ruff based on pre-commit
4
+ # Changed 268 files, +10k/-9k lines. This is the biggest formatter change.
5
+ b00f77d8559b48d57a33c0132a5ba1c81891a536
6
+
7
+ # [ci] refactor: reduce ruff line-length from 300 to 120
8
+ # Changed 238 files, +6k/-1k lines. Global formatting change.
9
+ 00a10a8ef389556f957a2f36132b2358fd6a109f
10
+
11
+ # [Lint] fix: linting errors in all files
12
+ # Changed 179 files, +1k/-3k lines. Global lint fix.
13
+ 8e5ad4688a13de81727c014a3c2e2fb26324bc20
verl/.github/CODEOWNERS ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /docs @eric-haibin-lin @zhaochenyang20 @hongpeng-guo
2
+ /docs/amd_tutorial @yushengsu-thu
3
+ /docs/slang_multiturn @zhaochenyang20 @SwordFaith
4
+ /docs/ascend_tutorial @wucong25
5
+
6
+ /third_party/sglang @zhaochenyang20 @SwordFaith
7
+ /third_party/vllm @PeterSH6 @wuxibin89
8
+
9
+ /examples/grpo_trainer @vermouth1992 @PeterSH6 @tardis-key @wucong25 @ji-huazhong
10
+
11
+ /verl/single_controller @zw0610 @wuxibin89 @hongpeng-guo
12
+ /verl/trainer @eric-haibin-lin @vermouth1992 @tongyx361 @PeterSH6
13
+ /verl/models/mcore @ISEEKYAN @vermouth1992
14
+ /verl/models/transformers @vermouth1992 @PeterSH6 @tardis-key @wucong25 @ji-huazhong
15
+ /verl/workers/engine @eric-haibin-lin @vermouth1992 @ZihengJiang @ArronHZG
16
+ /verl/workers/roles @eric-haibin-lin @vermouth1992 @ZihengJiang
17
+ /verl/workers/engine/fsdp @eric-haibin-lin @vermouth1992 @ZihengJiang
18
+ /verl/workers/rollout/vllm_rollout @wuxibin89 @PeterSH6 @chenhaiq @ArronHZG
19
+ /verl/workers/rollout/sglang_rollout @zhaochenyang20 @SwordFaith @chenhaiq @ArronHZG
20
+ /verl/workers/engine/megatron @ISEEKYAN @vermouth1992
21
+ /verl/experimental @wuxibin89 @ArronHZG
22
+
23
+ /tests/single_controller @zw0610 @wuxibin89
24
+ /tests/trainer @eric-haibin-lin @vermouth1992 @tongyx361 @PeterSH6
25
+ /tests/workers/rollout/vllm_rollout @wuxibin89 @PeterSH6 @chenhaiq
verl/.github/ISSUE_TEMPLATE/bug-report.yml ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # modified from https://github.com/huggingface/transformers/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml?plain=1
2
+ name: "\U0001F41B Bug Report"
3
+ description: Submit a bug report to help us improve verl
4
+ labels: [ "bug" ]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Thanks for taking the time to fill out this bug report! 🤗
10
+
11
+ - type: textarea
12
+ id: system-info
13
+ attributes:
14
+ label: System Info
15
+ description: Please share your system info with us. You can run the command `python scripts/diagnose.py` and copy-paste its output below.
16
+ placeholder: verl version, platform, python version, ...
17
+ validations:
18
+ required: true
19
+
20
+ - type: checkboxes
21
+ id: information-scripts-examples
22
+ attributes:
23
+ label: Information
24
+ description: 'The problem arises when using:'
25
+ options:
26
+ - label: "The official example scripts"
27
+ - label: "My own modified scripts"
28
+
29
+ - type: checkboxes
30
+ id: information-tasks
31
+ attributes:
32
+ label: Tasks
33
+ description: "The tasks I am working on are:"
34
+ options:
35
+ - label: "An officially supported task in the `examples` folder (such as GLUE/SQuAD, ...)"
36
+ - label: "My own task or dataset (give details below)"
37
+
38
+ - type: textarea
39
+ id: reproduction
40
+ validations:
41
+ required: true
42
+ attributes:
43
+ label: Reproduction
44
+ description: |
45
+ Please provide a code sample that reproduces the problem you ran into. It can be a Colab link or just a code snippet.
46
+ Please include relevant config information with your code.
47
+ If you have code snippets, error messages, stack traces please provide them here as well.
48
+ Important! Use code tags to correctly format your code. See https://help.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks#syntax-highlighting
49
+ Do not use screenshots, as they are hard to read and (more importantly) don't allow others to copy-and-paste your code.
50
+
51
+ placeholder: |
52
+ Steps to reproduce the behavior:
53
+
54
+ 1.
55
+ 2.
56
+ 3.
57
+
58
+
59
+ - type: textarea
60
+ id: expected-behavior
61
+ validations:
62
+ required: true
63
+ attributes:
64
+ label: Expected behavior
65
+ description: "A clear and concise description of what you would expect to happen."
verl/.github/ISSUE_TEMPLATE/config.yml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ blank_issues_enabled: true
2
+ version: 0.1
verl/.github/ISSUE_TEMPLATE/feature-request.yml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # modified from https://github.com/huggingface/transformers/blob/main/.github/ISSUE_TEMPLATE/feature-request.yml?plain=1
2
+ name: "\U0001F680 Feature request"
3
+ description: Submit a proposal/request for a new verl feature
4
+ labels: [ "Feature request" ]
5
+ body:
6
+ - type: textarea
7
+ id: feature-request
8
+ validations:
9
+ required: true
10
+ attributes:
11
+ label: Feature request
12
+ description: |
13
+ A clear and concise description of the feature proposal. Please provide a link to the paper and code in case they exist.
14
+
15
+ - type: textarea
16
+ id: motivation
17
+ validations:
18
+ required: true
19
+ attributes:
20
+ label: Motivation
21
+ description: |
22
+ Please outline the motivation for the proposal. Is your feature request related to a problem? e.g., I'm always frustrated when [...]. If this is related to another GitHub issue, please link here too.
23
+
24
+
25
+ - type: textarea
26
+ id: contribution
27
+ validations:
28
+ required: true
29
+ attributes:
30
+ label: Your contribution
31
+ description: |
32
+ Is there any way that you could help, e.g. by submitting a PR? Make sure to read the CONTRIBUTING.MD [readme](https://github.com/verl-project/verl/blob/main/CONTRIBUTING.md)
verl/.github/PULL_REQUEST_TEMPLATE.md ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### What does this PR do?
2
+
3
+ > Add **concise** overview of what this PR aims to achieve or accomplish. Reference related GitHub issues and PRs that help with the review.
4
+
5
+ ### Checklist Before Starting
6
+
7
+ - [ ] Search for similar PRs. Paste at least one query link here: ...
8
+ - [ ] Format the PR title as `[{modules}] {type}: {description}` (This will be checked by the CI)
9
+ - `{modules}` include `fsdp`, `megatron`, `veomni`, `sglang`, `vllm`, `rollout`, `trainer`, `ci`, `training_utils`, `recipe`, `hardware`, `deployment`, `ray`, `worker`, `single_controller`, `misc`, `perf`, `model`, `algo`, `env`, `tool`, `ckpt`, `doc`, `data`, `cfg`, `reward`, `fully_async`, `one_step_off`
10
+ - If this PR involves multiple modules, separate them with `,` like `[megatron, fsdp, doc]`
11
+ - `{type}` is in `feat`, `fix`, `refactor`, `chore`, `test`
12
+ - If this PR breaks any API (CLI arguments, config, function signature, etc.), add `[BREAKING]` to the beginning of the title.
13
+ - Example: `[BREAKING][fsdp, megatron] feat: dynamic batching`
14
+
15
+ ### Test
16
+
17
+ > For changes that can not be tested by CI (e.g., algorithm implementation, new model support), validate by experiment(s) and show results like training curve plots, evaluation results, etc.
18
+
19
+ ### API and Usage Example
20
+
21
+ > Demonstrate how the API changes if any, and provide usage example(s) if possible.
22
+
23
+ ```python
24
+ # Add code snippet or script demonstrating how to use this
25
+ ```
26
+
27
+ ### Design & Code Changes
28
+
29
+ > Demonstrate the high-level design if this PR is complex, and list the specific changes.
30
+
31
+ ### Checklist Before Submitting
32
+
33
+ > [!IMPORTANT]
34
+ > Please check all the following items before requesting a review, otherwise the reviewer might deprioritize this PR for review.
35
+
36
+ - [ ] Read the [Contribute Guide](https://github.com/verl-project/verl/blob/main/CONTRIBUTING.md).
37
+ - [ ] Apply [pre-commit checks](https://github.com/verl-project/verl/blob/main/CONTRIBUTING.md#code-linting-and-formatting): `pre-commit install && pre-commit run --all-files --show-diff-on-failure --color=always`
38
+ - [ ] Add / Update [the documentation](https://github.com/verl-project/verl/tree/main/docs).
39
+ - [ ] Add unit or end-to-end test(s) to [the CI workflow](https://github.com/verl-project/verl/tree/main/.github/workflows) to cover all the code. If not feasible, explain why: ...
40
+ - [ ] Once your PR is ready for CI, send a message in [the `ci-request` channel](https://verl-project.slack.com/archives/C091TCESWB1) in [the `verl` Slack workspace](https://join.slack.com/t/verl-project/shared_invite/zt-3855yhg8g-CTkqXu~hKojPCmo7k_yXTQ). (If not accessible, please try [the Feishu group (飞书群)](https://applink.larkoffice.com/client/chat/chatter/add_by_link?link_token=772jd4f1-cd91-441e-a820-498c6614126a).)
41
+ - [ ] If your PR is related to the `recipe` submodule, please also update the reference to the submodule commit via `git submodule update --remote` or `cd recipe && git pull origin main`.
verl/.github/dependabot.yml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ## Enabled the dependabot to check the dependencies of the project
2
+ ## Dependabot will open pull requests to update dependencies automatically
3
+
4
+ version: 2
5
+ updates:
6
+ - package-ecosystem: pip
7
+ directory: "/"
8
+ schedule:
9
+ interval: weekly
verl/.github/workflows/README.md ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### Adding a New Workflow
2
+
3
+ When adding a new workflow for continuous integration (CI), you have two runner options: a fixed runner or a machine from the vemlp.
4
+
5
+ - **Fixed Runner**: To use a fixed runner, specify it in your workflow using the `runs-on` keyword, like `runs-on: [L20x8]`.
6
+ - **Vemlp Runner**: Opting for a Vemlp machine allows you to launch tasks elastically.
7
+
8
+ Here is a template to assist you. This template is designed for using Vemlp machines. Currently, for each workflow, you need to create a `setup` and a `cleanup` job. When using this template, the main parts you need to modify are the `IMAGE` environment variable and the specific `job steps`.
9
+
10
+ ```yaml
11
+ name: Your Default Workflow
12
+
13
+ on:
14
+ push:
15
+ branches:
16
+ - main
17
+ - v0.*
18
+ pull_request:
19
+ branches:
20
+ - main
21
+ - v0.*
22
+ paths:
23
+ - "**/*.py"
24
+ - ".github/workflows/template.yml"
25
+
26
+ concurrency:
27
+ group: ${{ github.workflow }}-${{ github.ref }}
28
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
29
+
30
+ permissions:
31
+ contents: read
32
+
33
+ env:
34
+ IMAGE: "your vemlp image" # e.g. "verl-ci-cn-beijing.cr.volces.com/verlai/verl:sgl059.dev2"
35
+ DYNAMIC_RUNNER_URL: "https://sd10g3clalm04ug7alq90.apigateway-cn-beijing.volceapi.com/runner" # public veFaas api
36
+
37
+ jobs:
38
+ setup:
39
+ if: github.repository_owner == 'verl-project'
40
+ runs-on: ubuntu-latest
41
+ outputs:
42
+ runner-label: ${{ steps.create-runner.outputs.runner-label }}
43
+ task-id: ${{ steps.create-runner.outputs.task-id }}
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+ - id: create-runner
47
+ uses: volcengine/vemlp-github-runner@v1
48
+ with:
49
+ mode: "create"
50
+ faas-url: "${{ env.DYNAMIC_RUNNER_URL }}"
51
+ image: "${{ env.DEFAULT_IMAGE }}"
52
+
53
+ your_job:
54
+ needs: setup
55
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'default-runner' }}"]
56
+ steps:
57
+ xxxx # your jobs
58
+
59
+ cleanup:
60
+ runs-on: ubuntu-latest
61
+ needs: [setup, your_job]
62
+ if: always()
63
+ steps:
64
+ - id: destroy-runner
65
+ uses: volcengine/vemlp-github-runner@v1
66
+ with:
67
+ mode: "destroy"
68
+ faas-url: "${{ env.DYNAMIC_RUNNER_URL }}"
69
+ task-id: "${{ needs.setup.outputs.task-id }}"
70
+ ```
71
+
72
+ ### Model and Dataset
73
+ To avoid CI relies on network, we pre-download dataset on a NFS on the CI machine. The path for models are \${HOME}/models and the path for dataset is \${HOME}/models/hf_data.
verl/.github/workflows/check-pr-title.yml ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+
33
+ on:
34
+ pull_request:
35
+ types: [opened, edited, synchronize]
36
+
37
+ jobs:
38
+ check-title:
39
+ runs-on: ubuntu-latest
40
+ steps:
41
+ - name: Checkout code
42
+ uses: actions/checkout@v4
43
+
44
+ - name: Set up Python
45
+ uses: actions/setup-python@v5
46
+ with:
47
+ python-version: '3.11'
48
+
49
+ - name: Run PR title checker
50
+ run: python3 tests/special_sanity/check_pr_title.py
51
+ env:
52
+ PR_TITLE: ${{ github.event.pull_request.title }}
53
+
54
+ - name: Run PR description checker
55
+ run: python3 tests/special_sanity/check_pr_description.py
56
+ env:
57
+ PR_TITLE: ${{ github.event.pull_request.title }}
58
+ GITHUB_EVENT_PATH: ${{ github.event_path }}
verl/.github/workflows/cpu_unit_tests.yml ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+ name: cpu_unit_tests
33
+
34
+ on:
35
+ # Trigger the workflow on push or pull request,
36
+ # but only for the main branch
37
+ push:
38
+ branches:
39
+ - main
40
+ - v0.*
41
+ pull_request:
42
+ branches:
43
+ - main
44
+ - v0.*
45
+ paths:
46
+ - "**/*.py"
47
+ - "!tests/special_sanity/**"
48
+ - .github/workflows/cpu_unit_tests.yml
49
+
50
+ # Cancel jobs on the same ref if a new one is triggered
51
+ concurrency:
52
+ group: ${{ github.workflow }}-${{ github.ref }}
53
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
54
+
55
+ # Declare permissions just read content.
56
+ permissions:
57
+ contents: read
58
+
59
+ env:
60
+ IMAGE: "verl-ci-cn-beijing.cr.volces.com/verlai/verl:vllm018.dev1"
61
+ DYNAMIC_RUNNER_ENDPOINT: "https://sd10g3clalm04ug7alq90.apigateway-cn-beijing.volceapi.com/runner"
62
+
63
+ jobs:
64
+ setup:
65
+ if: github.repository_owner == 'verl-project'
66
+ runs-on: ubuntu-latest
67
+ outputs:
68
+ runner-label: ${{ steps.create-runner.outputs.runner-label }}
69
+ mlp-task-id: ${{ steps.create-runner.outputs.mlp-task-id }}
70
+ steps:
71
+ - uses: actions/checkout@v4
72
+ - id: create-runner
73
+ uses: volcengine/vemlp-github-runner@v1
74
+ with:
75
+ mode: "create"
76
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
77
+ mlp-image: "${{ env.IMAGE }}"
78
+
79
+ cpu_unit_tests:
80
+ needs: setup
81
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
82
+ timeout-minutes: 20 # Increase this timeout value as needed
83
+ env:
84
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
85
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
86
+ HF_ENDPOINT: "https://hf-mirror.com"
87
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
88
+ TORCH_COMPILE_DISABLE: 1
89
+ TORCHINDUCTOR_DISABLE: 1
90
+ steps:
91
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
92
+ with:
93
+ fetch-depth: 0
94
+ - name: Install the current repository
95
+ run: |
96
+ pip3 install -r requirements-test.txt
97
+ pip3 install --no-deps -e .
98
+ - name: Download datasets
99
+ run: |
100
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k
101
+ python3 examples/data_preprocess/geo3k.py --local_dataset_path ${HOME}/models/hf_data/hiyouga/geometry3k
102
+ - name: Running CPU unit tests
103
+ run: |
104
+ echo '[pytest]' > pytest.ini
105
+ echo 'python_files = *_on_cpu.py' >> pytest.ini
106
+ pytest -s -x --asyncio-mode=auto tests/
107
+ cleanup:
108
+ runs-on: ubuntu-latest
109
+ needs: [setup, cpu_unit_tests]
110
+ if: always()
111
+ steps:
112
+ - id: destroy-runner
113
+ uses: volcengine/vemlp-github-runner@v1
114
+ with:
115
+ mode: "destroy"
116
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
117
+ mlp-task-id: "${{ needs.setup.outputs.mlp-task-id }}"
verl/.github/workflows/doc.yml ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+
33
+ name: doc_test
34
+
35
+ on:
36
+ # Trigger the workflow on push or pull request,
37
+ # but only for the main branch
38
+ push:
39
+ branches:
40
+ - main
41
+ - v0.*
42
+ pull_request:
43
+ branches:
44
+ - main
45
+ - v0.*
46
+ paths:
47
+ - "**/*.py"
48
+ - "docs/**"
49
+ - .github/workflows/doc.yml
50
+
51
+ # Cancel jobs on the same ref if a new one is triggered
52
+ concurrency:
53
+ group: ${{ github.workflow }}-${{ github.ref }}
54
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
55
+
56
+ # Declare permissions just read content.
57
+ permissions:
58
+ contents: read # for checkout
59
+ pages: write # for deploy-pages
60
+ id-token: write # for deploy-pages
61
+
62
+ jobs:
63
+ doc_test:
64
+ runs-on: ubuntu-latest
65
+ timeout-minutes: 5 # Increase this timeout value as needed
66
+ strategy:
67
+ matrix:
68
+ python-version: ["3.10"]
69
+ steps:
70
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
71
+ - name: Set up Python ${{ matrix.python-version }}
72
+ uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
73
+ with:
74
+ python-version: ${{ matrix.python-version }}
75
+ - name: Install the current repository
76
+ run: |
77
+ pip3 install -r requirements-test.txt
78
+ pip3 install --no-deps -e .
79
+ pip install -r docs/requirements-docs.txt
80
+
81
+ - name: Run doc make html
82
+ run: |
83
+ cd docs
84
+ make clean
85
+ make html SPHINXOPTS="--keep-going -w _build/sphinx.log"
86
+ if grep -q ": ERROR:" _build/sphinx.log; then
87
+ echo "🚨 Sphinx doc build contained ERRORs - see _build/sphinx.log"
88
+ exit 1
89
+ fi
90
+ if grep -q "WARNING: document isn't included in any toctree" _build/sphinx.log; then
91
+ echo "🚨 Sphinx doc build contained WARNING. Please include newly added docs in index.rst. See _build/sphinx.log for details"
92
+ exit 1
93
+ fi
94
+ if grep -q "WARNING: Inline emphasis" _build/sphinx.log; then
95
+ echo "🚨 Sphinx doc build contained WARNING. Please check inline emphasis is correct. See _build/sphinx.log for details"
96
+ exit 1
97
+ fi
98
+ if grep -q "WARNING: Definition list ends without a blank line" _build/sphinx.log; then
99
+ echo "🚨 Sphinx doc build contained WARNING. Please check if the indentation is correct. See _build/sphinx.log for details"
100
+ exit 1
101
+ fi
verl/.github/workflows/docker-build-ascend-a2-qwen3_5.yml ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: docker-build-ascend-a2-qwen3_5
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ env:
7
+ QUAY_REPO: quay.io/ascend/verl
8
+
9
+ jobs:
10
+ build-push-digest:
11
+ name: build-${{ matrix.tag }}
12
+ if: ${{ github.repository_owner == 'verl-project' }}
13
+ runs-on: ${{ matrix.runner }}
14
+ permissions:
15
+ packages: write
16
+ contents: read
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ include:
21
+ - arch: linux/amd64
22
+ runner: ubuntu-latest
23
+ tag: amd64
24
+ - arch: linux/arm64
25
+ runner: ubuntu-22.04-arm
26
+ tag: arm64
27
+ steps:
28
+ - name: Remove unnecessary parts in github actions runners to free up disk space
29
+ uses: jlumbroso/free-disk-space@v1.3.1
30
+ with:
31
+ tool-cache: true
32
+
33
+ - name: Checkout code
34
+ uses: actions/checkout@v4
35
+
36
+ - name: Set up Python
37
+ uses: actions/setup-python@v5
38
+ with:
39
+ python-version: "3.11"
40
+
41
+ - name: Login to Quay.io
42
+ uses: docker/login-action@v3
43
+ with:
44
+ registry: quay.io
45
+ username: ${{ secrets.QUAY_USERNAME }}
46
+ password: ${{ secrets.QUAY_PASSWORD }}
47
+
48
+ - name: Set up Docker Buildx
49
+ uses: docker/setup-buildx-action@v3
50
+ with:
51
+ install: true
52
+ driver: docker-container
53
+ use: true
54
+
55
+ - name: Clean Docker cache before build
56
+ run: |
57
+ docker system prune -a -f --volumes || true
58
+
59
+ - name: Build and push by digest
60
+ uses: docker/build-push-action@v6
61
+ id: build
62
+ with:
63
+ context: .
64
+ platforms: ${{ matrix.arch }}
65
+ file: ./docker/ascend/Dockerfile.ascend_8.5.2_a2_qwen3-5
66
+ push: true
67
+ # 关键:push-by-digest=true 只推送镜像层,不创建 tag
68
+ outputs: type=image,name=${{ env.QUAY_REPO }},push-by-digest=true,name-canonical=true,push=true
69
+ provenance: false
70
+ cache-from: type=gha
71
+ cache-to: type=gha,mode=max
72
+ build-args: |
73
+ BUILDKIT_INLINE_CACHE=1
74
+
75
+ - name: Export digest
76
+ run: |
77
+ mkdir -p ${{ runner.temp }}/digests
78
+ digest="${{ steps.build.outputs.digest }}"
79
+ touch "${{ runner.temp }}/digests/${digest#sha256:}"
80
+
81
+ - name: Upload digest
82
+ uses: actions/upload-artifact@v4
83
+ with:
84
+ name: digests-${{ matrix.tag }}
85
+ path: ${{ runner.temp }}/digests/*
86
+ if-no-files-found: error
87
+ retention-days: 1
88
+
89
+ merge-image:
90
+ name: merge
91
+ runs-on: ubuntu-latest
92
+ needs: build-push-digest
93
+ steps:
94
+ - name: Download digests
95
+ uses: actions/download-artifact@v4
96
+ with:
97
+ path: ${{ runner.temp }}/digests
98
+ pattern: digests-*
99
+ merge-multiple: true
100
+
101
+ - name: Login to Quay.io
102
+ uses: docker/login-action@v3
103
+ with:
104
+ registry: quay.io
105
+ username: ${{ secrets.QUAY_USERNAME }}
106
+ password: ${{ secrets.QUAY_PASSWORD }}
107
+
108
+ - name: Set up Docker Buildx
109
+ uses: docker/setup-buildx-action@v3
110
+
111
+ - name: Merge and push multi-arch image
112
+ env:
113
+ IMAGE: ${{ env.QUAY_REPO }}
114
+ run: |
115
+ # 读取所有 digest 文件,构建 IMAGE@sha256:DIGEST 格式
116
+ DIGESTS=$(printf "$IMAGE@sha256:%s " $(ls ${{ runner.temp }}/digests))
117
+
118
+ echo "Digests: $DIGESTS"
119
+
120
+ # 创建多架构 manifest
121
+ docker buildx imagetools create \
122
+ -t "$IMAGE:verl-8.5.2-910b-ubuntu22.04-py3.11-qwen3-5" \
123
+ $DIGESTS
verl/.github/workflows/docker-build-ascend-a2.yml ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: docker-build-ascend-a2
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches: ["main"]
7
+ paths:
8
+ - "docker/ascend/Dockerfile.ascend_8.5.0_a2"
9
+ - "docker/ascend/Dockerfile.ascend_8.5.0_a2_v0.7.1"
10
+ - ".github/workflows/docker-build-ascend-a2.yml"
11
+
12
+ release:
13
+ types: [published]
14
+ schedule:
15
+ - cron: "0 16 * * *"
16
+
17
+ env:
18
+ QUAY_REPO: quay.io/ascend/verl
19
+
20
+ jobs:
21
+ # ============================================================
22
+ # 第一阶段:分架构构建并推送 digest(main 镜像)
23
+ # ============================================================
24
+ build-push-digest:
25
+ name: build-${{ matrix.tag }}
26
+ if: ${{ github.event_name != 'pull_request' && github.repository_owner == 'verl-project' }}
27
+ concurrency:
28
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.tag }}
29
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
30
+ runs-on: ${{ matrix.runner }}
31
+ permissions:
32
+ packages: write
33
+ contents: read
34
+ strategy:
35
+ fail-fast: false
36
+ matrix:
37
+ include:
38
+ - arch: linux/amd64
39
+ runner: ubuntu-latest
40
+ tag: amd64
41
+ - arch: linux/arm64
42
+ runner: ubuntu-22.04-arm
43
+ tag: arm64
44
+ outputs:
45
+ tag: ${{ steps.version.outputs.tag }}
46
+ steps:
47
+ - name: Remove unnecessary parts in github actions runners to free up disk space
48
+ uses: jlumbroso/free-disk-space@v1.3.1
49
+ with:
50
+ tool-cache: true
51
+
52
+ - name: Checkout code
53
+ uses: actions/checkout@v4
54
+
55
+ - name: Set up Python
56
+ uses: actions/setup-python@v5
57
+ with:
58
+ python-version: "3.11"
59
+
60
+ - name: Get base image name and tag
61
+ id: base_image
62
+ run: |
63
+ BASE_IMAGE_FULL=$(grep '^FROM' ./docker/ascend/Dockerfile.ascend_8.5.0_a2 | head -1 | cut -d' ' -f2)
64
+ echo "Base image full: $BASE_IMAGE_FULL"
65
+ BASE_IMAGE_TAG=$(echo "$BASE_IMAGE_FULL" | cut -d':' -f2)
66
+ echo "Base image tag: $BASE_IMAGE_TAG"
67
+ NEW_IMAGE_NAME="verl-$BASE_IMAGE_TAG"
68
+ echo "New image name: $NEW_IMAGE_NAME"
69
+ echo "base_image_tag=$BASE_IMAGE_TAG" >> "$GITHUB_OUTPUT"
70
+ echo "new_image_name=$NEW_IMAGE_NAME" >> "$GITHUB_OUTPUT"
71
+
72
+ - name: Get image tag
73
+ id: version
74
+ run: |
75
+ BRANCH_NAME=$(echo "${{ github.ref }}" | sed 's/refs\/heads\///g' | sed 's/[^a-zA-Z0-9._-]/_/g')
76
+ if [ "${{ github.event_name }}" = "release" ]; then
77
+ echo "tag=${{ steps.base_image.outputs.new_image_name }}-${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
78
+ elif [ "$BRANCH_NAME" = "main" ]; then
79
+ echo "tag=${{ steps.base_image.outputs.new_image_name }}-latest" >> "$GITHUB_OUTPUT"
80
+ fi
81
+
82
+ - name: Set up Docker Buildx
83
+ uses: docker/setup-buildx-action@v3
84
+ with:
85
+ install: true
86
+ driver: docker-container
87
+ use: true
88
+
89
+ - name: Login to Quay.io
90
+ uses: docker/login-action@v3
91
+ with:
92
+ registry: quay.io
93
+ username: ${{ secrets.QUAY_USERNAME }}
94
+ password: ${{ secrets.QUAY_PASSWORD }}
95
+
96
+ - name: Clean Docker cache before build
97
+ run: |
98
+ docker system prune -a -f --volumes || true
99
+
100
+ - name: Build and push by digest (main image)
101
+ uses: docker/build-push-action@v6
102
+ id: build_main
103
+ with:
104
+ context: .
105
+ platforms: ${{ matrix.arch }}
106
+ file: ./docker/ascend/Dockerfile.ascend_8.5.0_a2
107
+ push: true
108
+ outputs: type=image,name=${{ env.QUAY_REPO }},push-by-digest=true,name-canonical=true,push=true
109
+ provenance: false
110
+ cache-from: type=gha
111
+ cache-to: type=gha,mode=max
112
+ build-args: |
113
+ BUILDKIT_INLINE_CACHE=1
114
+
115
+ - name: Export digest (main image)
116
+ run: |
117
+ mkdir -p ${{ runner.temp }}/digests
118
+ digest="${{ steps.build_main.outputs.digest }}"
119
+ touch "${{ runner.temp }}/digests/main-${digest#sha256:}"
120
+
121
+ - name: Upload digest (main image)
122
+ uses: actions/upload-artifact@v4
123
+ with:
124
+ name: digests-main-${{ matrix.tag }}
125
+ path: ${{ runner.temp }}/digests/*
126
+ if-no-files-found: error
127
+ retention-days: 1
128
+
129
+ # ============================================================
130
+ # 第一阶段(v0.7.1):分架构构建并推送 digest
131
+ # ============================================================
132
+ build-push-digest-v071:
133
+ name: build-v0.7.1-${{ matrix.tag }}
134
+ if: ${{ github.event_name != 'pull_request' && github.repository_owner == 'verl-project' }}
135
+ concurrency:
136
+ group: ${{ github.workflow }}-${{ github.ref }}-v0.7.1-${{ matrix.tag }}
137
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
138
+ runs-on: ${{ matrix.runner }}
139
+ permissions:
140
+ packages: write
141
+ contents: read
142
+ strategy:
143
+ fail-fast: false
144
+ matrix:
145
+ include:
146
+ - arch: linux/amd64
147
+ runner: ubuntu-latest
148
+ tag: amd64
149
+ - arch: linux/arm64
150
+ runner: ubuntu-22.04-arm
151
+ tag: arm64
152
+ steps:
153
+ - name: Remove unnecessary parts in github actions runners to free up disk space
154
+ uses: jlumbroso/free-disk-space@v1.3.1
155
+ with:
156
+ tool-cache: true
157
+
158
+ - name: Checkout code
159
+ uses: actions/checkout@v4
160
+
161
+ - name: Set up Python
162
+ uses: actions/setup-python@v5
163
+ with:
164
+ python-version: "3.11"
165
+
166
+ - name: Set up Docker Buildx
167
+ uses: docker/setup-buildx-action@v3
168
+ with:
169
+ install: true
170
+ driver: docker-container
171
+ use: true
172
+
173
+ - name: Login to Quay.io
174
+ uses: docker/login-action@v3
175
+ with:
176
+ registry: quay.io
177
+ username: ${{ secrets.QUAY_USERNAME }}
178
+ password: ${{ secrets.QUAY_PASSWORD }}
179
+
180
+ - name: Clean Docker cache before build
181
+ run: |
182
+ docker system prune -a -f --volumes || true
183
+
184
+ - name: Build and push by digest (v0.7.1 image)
185
+ uses: docker/build-push-action@v6
186
+ id: build_v071
187
+ with:
188
+ context: .
189
+ platforms: ${{ matrix.arch }}
190
+ file: ./docker/ascend/Dockerfile.ascend_8.5.0_a2_v0.7.1
191
+ push: true
192
+ outputs: type=image,name=${{ env.QUAY_REPO }},push-by-digest=true,name-canonical=true,push=true
193
+ provenance: false
194
+ cache-from: type=gha
195
+ cache-to: type=gha,mode=max
196
+ build-args: |
197
+ BUILDKIT_INLINE_CACHE=1
198
+
199
+ - name: Export digest (v0.7.1 image)
200
+ run: |
201
+ mkdir -p ${{ runner.temp }}/digests-v071
202
+ digest="${{ steps.build_v071.outputs.digest }}"
203
+ touch "${{ runner.temp }}/digests-v071/v071-${digest#sha256:}"
204
+
205
+ - name: Upload digest (v0.7.1 image)
206
+ uses: actions/upload-artifact@v4
207
+ with:
208
+ name: digests-v071-${{ matrix.tag }}
209
+ path: ${{ runner.temp }}/digests-v071/*
210
+ if-no-files-found: error
211
+ retention-days: 1
212
+
213
+ # ============================================================
214
+ # 第二阶段:合并多架构 manifest(main 镜像)
215
+ # ============================================================
216
+ merge-image:
217
+ name: merge
218
+ runs-on: ubuntu-latest
219
+ needs: build-push-digest
220
+ steps:
221
+ - name: Download digests (main image)
222
+ uses: actions/download-artifact@v4
223
+ with:
224
+ path: ${{ runner.temp }}/digests-main
225
+ pattern: digests-main-*
226
+ merge-multiple: true
227
+
228
+ - name: Login to Quay.io
229
+ uses: docker/login-action@v3
230
+ with:
231
+ registry: quay.io
232
+ username: ${{ secrets.QUAY_USERNAME }}
233
+ password: ${{ secrets.QUAY_PASSWORD }}
234
+
235
+ - name: Set up Docker Buildx
236
+ uses: docker/setup-buildx-action@v3
237
+
238
+ - name: Merge and push multi-arch image (main)
239
+ env:
240
+ IMAGE: ${{ env.QUAY_REPO }}
241
+ run: |
242
+ DIGESTS=$(printf "$IMAGE@sha256:%s " $(ls ${{ runner.temp }}/digests-main | sed 's/^main-//'))
243
+ echo "Main image digests: $DIGESTS"
244
+ docker buildx imagetools create \
245
+ -t "$IMAGE:${{ needs.build-push-digest.outputs.tag }}" \
246
+ $DIGESTS
247
+
248
+ # ============================================================
249
+ # 第二阶段(v0.7.1):合并多架构 manifest
250
+ # ============================================================
251
+ merge-image-v071:
252
+ name: merge-v0.7.1
253
+ runs-on: ubuntu-latest
254
+ needs: build-push-digest-v071
255
+ steps:
256
+ - name: Download digests (v0.7.1 image)
257
+ uses: actions/download-artifact@v4
258
+ with:
259
+ path: ${{ runner.temp }}/digests-v071
260
+ pattern: digests-v071-*
261
+ merge-multiple: true
262
+
263
+ - name: Login to Quay.io
264
+ uses: docker/login-action@v3
265
+ with:
266
+ registry: quay.io
267
+ username: ${{ secrets.QUAY_USERNAME }}
268
+ password: ${{ secrets.QUAY_PASSWORD }}
269
+
270
+ - name: Set up Docker Buildx
271
+ uses: docker/setup-buildx-action@v3
272
+
273
+ - name: Merge and push multi-arch image (v0.7.1)
274
+ env:
275
+ IMAGE: ${{ env.QUAY_REPO }}
276
+ run: |
277
+ DIGESTS=$(printf "$IMAGE@sha256:%s " $(ls ${{ runner.temp }}/digests-v071 | sed 's/^v071-//'))
278
+ echo "v0.7.1 image digests: $DIGESTS"
279
+ docker buildx imagetools create \
280
+ -t "$IMAGE:verl-8.5.0-910b-ubuntu22.04-py3.11-v0.7.1" \
281
+ $DIGESTS
verl/.github/workflows/docker-build-ascend-a3-qwen3_5.yml ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: docker-build-ascend-a3-qwen3_5
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ env:
7
+ QUAY_REPO: quay.io/ascend/verl
8
+
9
+ jobs:
10
+ build-push-digest:
11
+ name: build-${{ matrix.tag }}
12
+ if: ${{ github.repository_owner == 'verl-project' }}
13
+ runs-on: ${{ matrix.runner }}
14
+ permissions:
15
+ packages: write
16
+ contents: read
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ include:
21
+ - arch: linux/amd64
22
+ runner: ubuntu-latest
23
+ tag: amd64
24
+ - arch: linux/arm64
25
+ runner: ubuntu-22.04-arm
26
+ tag: arm64
27
+ steps:
28
+ - name: Remove unnecessary parts in github actions runners to free up disk space
29
+ uses: jlumbroso/free-disk-space@v1.3.1
30
+ with:
31
+ tool-cache: true
32
+
33
+ - name: Checkout code
34
+ uses: actions/checkout@v4
35
+
36
+ - name: Set up Python
37
+ uses: actions/setup-python@v5
38
+ with:
39
+ python-version: "3.11"
40
+
41
+ - name: Login to Quay.io
42
+ uses: docker/login-action@v3
43
+ with:
44
+ registry: quay.io
45
+ username: ${{ secrets.QUAY_USERNAME }}
46
+ password: ${{ secrets.QUAY_PASSWORD }}
47
+
48
+ - name: Set up Docker Buildx
49
+ uses: docker/setup-buildx-action@v3
50
+ with:
51
+ install: true
52
+ driver: docker-container
53
+ use: true
54
+
55
+ - name: Clean Docker cache before build
56
+ run: |
57
+ docker system prune -a -f --volumes || true
58
+
59
+ - name: Build and push by digest
60
+ uses: docker/build-push-action@v6
61
+ id: build
62
+ with:
63
+ context: .
64
+ platforms: ${{ matrix.arch }}
65
+ file: ./docker/ascend/Dockerfile.ascend_8.5.2_a3_qwen3-5
66
+ push: true
67
+ # 关键:push-by-digest=true 只推送镜像层,不创建 tag
68
+ outputs: type=image,name=${{ env.QUAY_REPO }},push-by-digest=true,name-canonical=true,push=true
69
+ provenance: false
70
+ cache-from: type=gha
71
+ cache-to: type=gha,mode=max
72
+ build-args: |
73
+ BUILDKIT_INLINE_CACHE=1
74
+
75
+ - name: Export digest
76
+ run: |
77
+ mkdir -p ${{ runner.temp }}/digests
78
+ digest="${{ steps.build.outputs.digest }}"
79
+ touch "${{ runner.temp }}/digests/${digest#sha256:}"
80
+
81
+ - name: Upload digest
82
+ uses: actions/upload-artifact@v4
83
+ with:
84
+ name: digests-${{ matrix.tag }}
85
+ path: ${{ runner.temp }}/digests/*
86
+ if-no-files-found: error
87
+ retention-days: 1
88
+
89
+ merge-image:
90
+ name: merge
91
+ runs-on: ubuntu-latest
92
+ needs: build-push-digest
93
+ steps:
94
+ - name: Download digests
95
+ uses: actions/download-artifact@v4
96
+ with:
97
+ path: ${{ runner.temp }}/digests
98
+ pattern: digests-*
99
+ merge-multiple: true
100
+
101
+ - name: Login to Quay.io
102
+ uses: docker/login-action@v3
103
+ with:
104
+ registry: quay.io
105
+ username: ${{ secrets.QUAY_USERNAME }}
106
+ password: ${{ secrets.QUAY_PASSWORD }}
107
+
108
+ - name: Set up Docker Buildx
109
+ uses: docker/setup-buildx-action@v3
110
+
111
+ - name: Merge and push multi-arch image
112
+ env:
113
+ IMAGE: ${{ env.QUAY_REPO }}
114
+ run: |
115
+ # 读取所有 digest 文件,构建 IMAGE@sha256:DIGEST 格式
116
+ DIGESTS=$(printf "$IMAGE@sha256:%s " $(ls ${{ runner.temp }}/digests))
117
+
118
+ echo "Digests: $DIGESTS"
119
+
120
+ # 创建多架构 manifest
121
+ docker buildx imagetools create \
122
+ -t "$IMAGE:verl-8.5.2-a3-ubuntu22.04-py3.11-qwen3-5" \
123
+ $DIGESTS
verl/.github/workflows/docker-build-ascend-a3.yml ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: docker-build-ascend-a3
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches: ["main"]
7
+ paths:
8
+ - "docker/ascend/Dockerfile.ascend_8.5.0_a3"
9
+ - "docker/ascend/Dockerfile.ascend_8.5.0_a3_v0.7.1"
10
+ - ".github/workflows/docker-build-ascend-a3.yml"
11
+
12
+ release:
13
+ types: [published]
14
+ schedule:
15
+ - cron: "0 19 * * *"
16
+
17
+ env:
18
+ QUAY_REPO: quay.io/ascend/verl
19
+
20
+ jobs:
21
+ # ============================================================
22
+ # 第一阶段:分架构构建并推送 digest(main 镜像)
23
+ # ============================================================
24
+ build-push-digest:
25
+ name: build-${{ matrix.tag }}
26
+ if: ${{ github.event_name != 'pull_request' && github.repository_owner == 'verl-project' }}
27
+ concurrency:
28
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.tag }}
29
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
30
+ runs-on: ${{ matrix.runner }}
31
+ permissions:
32
+ packages: write
33
+ contents: read
34
+ strategy:
35
+ fail-fast: false
36
+ matrix:
37
+ include:
38
+ - arch: linux/amd64
39
+ runner: ubuntu-latest
40
+ tag: amd64
41
+ - arch: linux/arm64
42
+ runner: ubuntu-22.04-arm
43
+ tag: arm64
44
+ outputs:
45
+ tag: ${{ steps.version.outputs.tag }}
46
+ steps:
47
+ - name: Remove unnecessary parts in github actions runners to free up disk space
48
+ uses: jlumbroso/free-disk-space@v1.3.1
49
+ with:
50
+ tool-cache: true
51
+
52
+ - name: Checkout code
53
+ uses: actions/checkout@v4
54
+
55
+ - name: Set up Python
56
+ uses: actions/setup-python@v5
57
+ with:
58
+ python-version: "3.11"
59
+
60
+ - name: Get base image name and tag
61
+ id: base_image
62
+ run: |
63
+ BASE_IMAGE_FULL=$(grep '^FROM' ./docker/ascend/Dockerfile.ascend_8.5.0_a3 | head -1 | cut -d' ' -f2)
64
+ echo "Base image full: $BASE_IMAGE_FULL"
65
+ BASE_IMAGE_TAG=$(echo "$BASE_IMAGE_FULL" | cut -d':' -f2)
66
+ echo "Base image tag: $BASE_IMAGE_TAG"
67
+ NEW_IMAGE_NAME="verl-$BASE_IMAGE_TAG"
68
+ echo "New image name: $NEW_IMAGE_NAME"
69
+ echo "base_image_tag=$BASE_IMAGE_TAG" >> "$GITHUB_OUTPUT"
70
+ echo "new_image_name=$NEW_IMAGE_NAME" >> "$GITHUB_OUTPUT"
71
+
72
+ - name: Get image tag
73
+ id: version
74
+ run: |
75
+ BRANCH_NAME=$(echo "${{ github.ref }}" | sed 's/refs\/heads\///g' | sed 's/[^a-zA-Z0-9._-]/_/g')
76
+ if [ "${{ github.event_name }}" = "release" ]; then
77
+ echo "tag=${{ steps.base_image.outputs.new_image_name }}-${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
78
+ elif [ "$BRANCH_NAME" = "main" ]; then
79
+ echo "tag=${{ steps.base_image.outputs.new_image_name }}-latest" >> "$GITHUB_OUTPUT"
80
+ fi
81
+
82
+ - name: Set up Docker Buildx
83
+ uses: docker/setup-buildx-action@v3
84
+ with:
85
+ install: true
86
+ driver: docker-container
87
+ use: true
88
+
89
+ - name: Login to Quay.io
90
+ uses: docker/login-action@v3
91
+ with:
92
+ registry: quay.io
93
+ username: ${{ secrets.QUAY_USERNAME }}
94
+ password: ${{ secrets.QUAY_PASSWORD }}
95
+
96
+ - name: Clean Docker cache before build
97
+ run: |
98
+ docker system prune -a -f --volumes || true
99
+
100
+ - name: Build and push by digest (main image)
101
+ uses: docker/build-push-action@v6
102
+ id: build_main
103
+ with:
104
+ context: .
105
+ platforms: ${{ matrix.arch }}
106
+ file: ./docker/ascend/Dockerfile.ascend_8.5.0_a3
107
+ push: true
108
+ outputs: type=image,name=${{ env.QUAY_REPO }},push-by-digest=true,name-canonical=true,push=true
109
+ provenance: false
110
+ cache-from: type=gha
111
+ cache-to: type=gha,mode=max
112
+ build-args: |
113
+ BUILDKIT_INLINE_CACHE=1
114
+
115
+ - name: Export digest (main image)
116
+ run: |
117
+ mkdir -p ${{ runner.temp }}/digests
118
+ digest="${{ steps.build_main.outputs.digest }}"
119
+ touch "${{ runner.temp }}/digests/main-${digest#sha256:}"
120
+
121
+ - name: Upload digest (main image)
122
+ uses: actions/upload-artifact@v4
123
+ with:
124
+ name: digests-main-${{ matrix.tag }}
125
+ path: ${{ runner.temp }}/digests/*
126
+ if-no-files-found: error
127
+ retention-days: 1
128
+
129
+ # ============================================================
130
+ # 第一阶段(v0.7.1):分架构构建并推送 digest
131
+ # ============================================================
132
+ build-push-digest-v071:
133
+ name: build-v0.7.1-${{ matrix.tag }}
134
+ if: ${{ github.event_name != 'pull_request' && github.repository_owner == 'verl-project' }}
135
+ concurrency:
136
+ group: ${{ github.workflow }}-${{ github.ref }}-v0.7.1-${{ matrix.tag }}
137
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
138
+ runs-on: ${{ matrix.runner }}
139
+ permissions:
140
+ packages: write
141
+ contents: read
142
+ strategy:
143
+ fail-fast: false
144
+ matrix:
145
+ include:
146
+ - arch: linux/amd64
147
+ runner: ubuntu-latest
148
+ tag: amd64
149
+ - arch: linux/arm64
150
+ runner: ubuntu-22.04-arm
151
+ tag: arm64
152
+ steps:
153
+ - name: Remove unnecessary parts in github actions runners to free up disk space
154
+ uses: jlumbroso/free-disk-space@v1.3.1
155
+ with:
156
+ tool-cache: true
157
+
158
+ - name: Checkout code
159
+ uses: actions/checkout@v4
160
+
161
+ - name: Set up Python
162
+ uses: actions/setup-python@v5
163
+ with:
164
+ python-version: "3.11"
165
+
166
+ - name: Set up Docker Buildx
167
+ uses: docker/setup-buildx-action@v3
168
+ with:
169
+ install: true
170
+ driver: docker-container
171
+ use: true
172
+
173
+ - name: Login to Quay.io
174
+ uses: docker/login-action@v3
175
+ with:
176
+ registry: quay.io
177
+ username: ${{ secrets.QUAY_USERNAME }}
178
+ password: ${{ secrets.QUAY_PASSWORD }}
179
+
180
+ - name: Clean Docker cache before build
181
+ run: |
182
+ docker system prune -a -f --volumes || true
183
+
184
+ - name: Build and push by digest (v0.7.1 image)
185
+ uses: docker/build-push-action@v6
186
+ id: build_v071
187
+ with:
188
+ context: .
189
+ platforms: ${{ matrix.arch }}
190
+ file: ./docker/ascend/Dockerfile.ascend_8.5.0_a3_v0.7.1
191
+ push: true
192
+ outputs: type=image,name=${{ env.QUAY_REPO }},push-by-digest=true,name-canonical=true,push=true
193
+ provenance: false
194
+ cache-from: type=gha
195
+ cache-to: type=gha,mode=max
196
+ build-args: |
197
+ BUILDKIT_INLINE_CACHE=1
198
+
199
+ - name: Export digest (v0.7.1 image)
200
+ run: |
201
+ mkdir -p ${{ runner.temp }}/digests-v071
202
+ digest="${{ steps.build_v071.outputs.digest }}"
203
+ touch "${{ runner.temp }}/digests-v071/v071-${digest#sha256:}"
204
+
205
+ - name: Upload digest (v0.7.1 image)
206
+ uses: actions/upload-artifact@v4
207
+ with:
208
+ name: digests-v071-${{ matrix.tag }}
209
+ path: ${{ runner.temp }}/digests-v071/*
210
+ if-no-files-found: error
211
+ retention-days: 1
212
+
213
+ # ============================================================
214
+ # 第二阶段:合并多架构 manifest(main 镜像)
215
+ # ============================================================
216
+ merge-image:
217
+ name: merge
218
+ runs-on: ubuntu-latest
219
+ needs: build-push-digest
220
+ steps:
221
+ - name: Download digests (main image)
222
+ uses: actions/download-artifact@v4
223
+ with:
224
+ path: ${{ runner.temp }}/digests-main
225
+ pattern: digests-main-*
226
+ merge-multiple: true
227
+
228
+ - name: Login to Quay.io
229
+ uses: docker/login-action@v3
230
+ with:
231
+ registry: quay.io
232
+ username: ${{ secrets.QUAY_USERNAME }}
233
+ password: ${{ secrets.QUAY_PASSWORD }}
234
+
235
+ - name: Set up Docker Buildx
236
+ uses: docker/setup-buildx-action@v3
237
+
238
+ - name: Merge and push multi-arch image (main)
239
+ env:
240
+ IMAGE: ${{ env.QUAY_REPO }}
241
+ run: |
242
+ DIGESTS=$(printf "$IMAGE@sha256:%s " $(ls ${{ runner.temp }}/digests-main | sed 's/^main-//'))
243
+ echo "Main image digests: $DIGESTS"
244
+ docker buildx imagetools create \
245
+ -t "$IMAGE:${{ needs.build-push-digest.outputs.tag }}" \
246
+ $DIGESTS
247
+
248
+ # ============================================================
249
+ # 第二阶段(v0.7.1):合并多架构 manifest
250
+ # ============================================================
251
+ merge-image-v071:
252
+ name: merge-v0.7.1
253
+ runs-on: ubuntu-latest
254
+ needs: build-push-digest-v071
255
+ steps:
256
+ - name: Download digests (v0.7.1 image)
257
+ uses: actions/download-artifact@v4
258
+ with:
259
+ path: ${{ runner.temp }}/digests-v071
260
+ pattern: digests-v071-*
261
+ merge-multiple: true
262
+
263
+ - name: Login to Quay.io
264
+ uses: docker/login-action@v3
265
+ with:
266
+ registry: quay.io
267
+ username: ${{ secrets.QUAY_USERNAME }}
268
+ password: ${{ secrets.QUAY_PASSWORD }}
269
+
270
+ - name: Set up Docker Buildx
271
+ uses: docker/setup-buildx-action@v3
272
+
273
+ - name: Merge and push multi-arch image (v0.7.1)
274
+ env:
275
+ IMAGE: ${{ env.QUAY_REPO }}
276
+ run: |
277
+ DIGESTS=$(printf "$IMAGE@sha256:%s " $(ls ${{ runner.temp }}/digests-v071 | sed 's/^v071-//'))
278
+ echo "v0.7.1 image digests: $DIGESTS"
279
+ docker buildx imagetools create \
280
+ -t "$IMAGE:verl-8.5.0-a3-ubuntu22.04-py3.11-v0.7.1" \
281
+ $DIGESTS
verl/.github/workflows/docker-build-ascend-sglang-a2.yml ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: docker-build-ascend-sglang-a2
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches: ["main"]
7
+ paths:
8
+ - "docker/ascend/Dockerfile.ascend.sglang_8.5.0_a2"
9
+ - ".github/workflows/docker-build-ascend-sglang-a2.yml"
10
+ release:
11
+ types: [published]
12
+ schedule:
13
+ - cron: "0 16 * * *"
14
+
15
+ jobs:
16
+ build-ascend-sglang-image-a2:
17
+ if: ${{ github.event_name != 'pull_request' && github.repository_owner == 'verl-project' }}
18
+ runs-on: ubuntu-latest
19
+ concurrency:
20
+ group: ${{ github.workflow }}-${{ github.ref }}-build-ascend-sglang-image-a2
21
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
22
+ steps:
23
+ - name: Remove unnecessary parts in github actions runners to free up disk space
24
+ uses: jlumbroso/free-disk-space@v1.3.1
25
+ with:
26
+ tool-cache: true
27
+
28
+ - name: Checkout code
29
+ uses: actions/checkout@v4
30
+
31
+ - name: Set up Python
32
+ uses: actions/setup-python@v5
33
+ with:
34
+ python-version: "3.11"
35
+
36
+ - name: Get base image name and tag
37
+ id: base_image
38
+ run: |
39
+ BASE_IMAGE_FULL=$(grep '^FROM' ./docker/ascend/Dockerfile.ascend.sglang_8.5.0_a2 | head -1 | cut -d' ' -f2)
40
+ echo "Base image full: $BASE_IMAGE_FULL"
41
+ BASE_IMAGE_TAG=$(echo "$BASE_IMAGE_FULL" | cut -d':' -f2)
42
+ echo "Base image tag: $BASE_IMAGE_TAG"
43
+ NEW_IMAGE_NAME="verl-sglang-$BASE_IMAGE_TAG"
44
+ echo "New image name: $NEW_IMAGE_NAME"
45
+ echo "base_image_tag=$BASE_IMAGE_TAG" >> "$GITHUB_OUTPUT"
46
+ echo "new_image_name=$NEW_IMAGE_NAME" >> "$GITHUB_OUTPUT"
47
+
48
+ - name: Get image tag
49
+ id: version
50
+ run: |
51
+ BRANCH_NAME=$(echo "${{ github.ref }}" | sed 's/refs\/heads\///g' | sed 's/[^a-zA-Z0-9._-]/_/g')
52
+ if [ "${{ github.event_name }}" = "release" ]; then
53
+ echo "tag=${{ steps.base_image.outputs.new_image_name }}-${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
54
+ elif [ "$BRANCH_NAME" = "main" ]; then
55
+ echo "tag=${{ steps.base_image.outputs.new_image_name }}-latest" >> "$GITHUB_OUTPUT"
56
+ fi
57
+
58
+ - name: Set up Docker Buildx
59
+ uses: docker/setup-buildx-action@v3
60
+
61
+ - name: Login to Quay.io
62
+ uses: docker/login-action@v3
63
+ with:
64
+ registry: quay.io
65
+ username: ${{ secrets.QUAY_USERNAME }}
66
+ password: ${{ secrets.QUAY_PASSWORD }}
67
+
68
+ - name: Clean Docker cache before build
69
+ run: |
70
+ docker system prune -a -f --volumes || true
71
+
72
+ - name: Build and push images Quay
73
+ uses: docker/build-push-action@v6
74
+ with:
75
+ context: .
76
+ platforms: linux/amd64,linux/arm64
77
+ file: ./docker/ascend/Dockerfile.ascend.sglang_8.5.0_a2
78
+ push: true
79
+ tags: |
80
+ quay.io/ascend/verl:${{ steps.version.outputs.tag }}
81
+ cache-from: type=gha
82
+ cache-to: type=gha,mode=max
83
+ build-args: |
84
+ BUILDKIT_INLINE_CACHE=1
verl/.github/workflows/docker-build-ascend-sglang-a3.yml ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: docker-build-ascend-sglang-a3
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches: ["main"]
7
+ paths:
8
+ - "docker/ascend/Dockerfile.ascend.sglang_8.5.0_a3"
9
+ - ".github/workflows/docker-build-ascend-sglang-a3.yml"
10
+ release:
11
+ types: [published]
12
+ schedule:
13
+ - cron: "0 16 * * *"
14
+
15
+ jobs:
16
+ build-ascend-sglang-image-a3:
17
+ if: ${{ github.event_name != 'pull_request' && github.repository_owner == 'verl-project' }}
18
+ runs-on: ubuntu-latest
19
+ concurrency:
20
+ group: ${{ github.workflow }}-${{ github.ref }}-build-ascend-sglang-image-a3
21
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
22
+ steps:
23
+ - name: Remove unnecessary parts in github actions runners to free up disk space
24
+ uses: jlumbroso/free-disk-space@v1.3.1
25
+ with:
26
+ tool-cache: true
27
+
28
+ - name: Checkout code
29
+ uses: actions/checkout@v4
30
+
31
+ - name: Set up Python
32
+ uses: actions/setup-python@v5
33
+ with:
34
+ python-version: "3.11"
35
+
36
+ - name: Get base image name and tag
37
+ id: base_image
38
+ run: |
39
+ BASE_IMAGE_FULL=$(grep '^FROM' ./docker/ascend/Dockerfile.ascend.sglang_8.5.0_a3 | head -1 | cut -d' ' -f2)
40
+ echo "Base image full: $BASE_IMAGE_FULL"
41
+ BASE_IMAGE_TAG=$(echo "$BASE_IMAGE_FULL" | cut -d':' -f2)
42
+ echo "Base image tag: $BASE_IMAGE_TAG"
43
+ NEW_IMAGE_NAME="verl-sglang-$BASE_IMAGE_TAG"
44
+ echo "New image name: $NEW_IMAGE_NAME"
45
+ echo "base_image_tag=$BASE_IMAGE_TAG" >> "$GITHUB_OUTPUT"
46
+ echo "new_image_name=$NEW_IMAGE_NAME" >> "$GITHUB_OUTPUT"
47
+
48
+ - name: Get image tag
49
+ id: version
50
+ run: |
51
+ BRANCH_NAME=$(echo "${{ github.ref }}" | sed 's/refs\/heads\///g' | sed 's/[^a-zA-Z0-9._-]/_/g')
52
+ if [ "${{ github.event_name }}" = "release" ]; then
53
+ echo "tag=${{ steps.base_image.outputs.new_image_name }}-${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
54
+ elif [ "$BRANCH_NAME" = "main" ]; then
55
+ echo "tag=${{ steps.base_image.outputs.new_image_name }}-latest" >> "$GITHUB_OUTPUT"
56
+ fi
57
+
58
+ - name: Set up Docker Buildx
59
+ uses: docker/setup-buildx-action@v3
60
+
61
+ - name: Login to Quay.io
62
+ uses: docker/login-action@v3
63
+ with:
64
+ registry: quay.io
65
+ username: ${{ secrets.QUAY_USERNAME }}
66
+ password: ${{ secrets.QUAY_PASSWORD }}
67
+
68
+ - name: Clean Docker cache before build
69
+ run: |
70
+ docker system prune -a -f --volumes || true
71
+
72
+ - name: Build and push images Quay
73
+ uses: docker/build-push-action@v6
74
+ with:
75
+ context: .
76
+ platforms: linux/amd64,linux/arm64
77
+ file: ./docker/ascend/Dockerfile.ascend.sglang_8.5.0_a3
78
+ push: true
79
+ tags: |
80
+ quay.io/ascend/verl:${{ steps.version.outputs.tag }}
81
+ cache-from: type=gha
82
+ cache-to: type=gha,mode=max
83
+ build-args: |
84
+ BUILDKIT_INLINE_CACHE=1
verl/.github/workflows/e2e_ascend.yml ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+ name: e2e_ascend
33
+
34
+ on:
35
+ # Trigger the workflow on push or pull request,
36
+ # but only for the main branch
37
+ push:
38
+ branches:
39
+ - main
40
+ - v0.*
41
+ pull_request:
42
+ branches:
43
+ - main
44
+ paths:
45
+ - ".github/workflows/e2e_ascend.yml"
46
+ - "examples/data_preprocess/**"
47
+ - "examples/grpo_trainer/**"
48
+ - "examples/ppo_trainer/**"
49
+ - "examples/sft/**"
50
+ - "verl/experimental/one_step_off_policy/**"
51
+ - "tests/special_npu/**"
52
+ - "tests/special_sanity/check_device_api_usage.py"
53
+ - "verl/**"
54
+ - "pyproject.toml"
55
+ - "requirements-npu.txt"
56
+ - "setup.py"
57
+
58
+ # Cancel jobs on the same ref if a new one is triggered
59
+ concurrency:
60
+ group: ${{ github.workflow }}-${{ github.ref }}
61
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
62
+
63
+ permissions:
64
+ contents: read
65
+
66
+ jobs:
67
+ llm_rl_job:
68
+ if: github.repository_owner == 'verl-project'
69
+ name: E2E Ascend testing for RL training scenarios of LLM models
70
+ runs-on: linux-aarch64-a3-8
71
+ timeout-minutes: 120
72
+ container:
73
+ image: swr.cn-southwest-2.myhuaweicloud.com/modelfoundry/ascend-ci/verl/verl:verl-8.5.0-a3-ubuntu22.04-py3.11-latest
74
+ options: >-
75
+ --shm-size 16g
76
+ env:
77
+ HF_ENDPOINT: "https://hf-mirror.com"
78
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
79
+ steps:
80
+ - name: Check npu and CANN info
81
+ run: |
82
+ cat /usr/local/Ascend/ascend-toolkit/latest/"$(uname -i)"-linux/ascend_toolkit_install.info
83
+ npu-smi info
84
+ - name: Check initial pip list from image
85
+ run: |
86
+ pip list
87
+ - name: Checkout verl-project/verl repo
88
+ uses: actions/checkout@v4
89
+ with:
90
+ fetch-depth: 0
91
+ clean: true
92
+ - name: Install the current repository
93
+ run: |
94
+ pip install --no-deps -e .
95
+ - name: Check final pip list
96
+ run: |
97
+ pip list
98
+ - name: Preprocess gsm8k dataset
99
+ run: |
100
+ python examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/.cache/datasets/openai/gsm8k
101
+ - name: Running gsm8k e2e training tests with PPO on ASCEND NPU (FSDP backend)
102
+ run: |
103
+ ray stop --force
104
+ bash tests/special_npu/run_qwen3_06b_ppo.sh
105
+ rm -rf $HOME/ckpts
106
+ - name: Running gsm8k e2e training tests with GRPO on ASCEND NPU (FSDP backend)
107
+ run: |
108
+ ray stop --force
109
+ bash tests/special_npu/run_qwen2_5_05b_grpo.sh
110
+ rm -rf $HOME/ckpts
111
+ - name: Running gsm8k e2e training tests with GRPO on ASCEND NPU (MindSpeed backend)
112
+ run: |
113
+ ray stop --force
114
+ USE_DIST_CKPT=True bash tests/special_npu/run_qwen2_5_05b_grpo_mindspeed.sh
115
+ rm -rf $HOME/dist_ckpt/qwen2_5_05b_grpo_mindspeed
116
+ rm -rf $HOME/ckpts
117
+ - name: Running gsm8k e2e training tests with GRPO on ASCEND NPU (MindSpeed backend, MoE Model)
118
+ run: |
119
+ ray stop --force
120
+ USE_DIST_CKPT=True USE_DUMMY_MODEL=True DUMMY_MODEL_CONFIG_PATH=tests/special_e2e/ppo_trainer/expert_parallel/qwen3moe_minimal.json DUMMY_MODEL_PATH=$HOME/dist_ckpt/qwen3_30b_grpo_mindspeed bash tests/special_npu/run_qwen3_30b_grpo_mindspeed.sh
121
+
122
+ engine_mindspeed_llm_rl_job:
123
+ if: github.repository_owner == 'verl-project'
124
+ name: E2E Ascend testing for RL training scenarios of LLM models using MindSpeed_LLM engine
125
+ runs-on: linux-aarch64-a3-8
126
+ timeout-minutes: 120
127
+ container:
128
+ image: swr.cn-southwest-2.myhuaweicloud.com/modelfoundry/ascend-ci/verl/verl:verl-sglang-8.3.rc1-a3-ubuntu22.04-py3.11-latest
129
+ options: >-
130
+ --shm-size 16g
131
+ env:
132
+ HF_ENDPOINT: "https://hf-mirror.com"
133
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
134
+ steps:
135
+ - name: Check npu and CANN info
136
+ run: |
137
+ cat /usr/local/Ascend/ascend-toolkit/latest/"$(uname -i)"-linux/ascend_toolkit_install.info
138
+ npu-smi info
139
+ - name: Check initial pip list from image
140
+ run: |
141
+ pip list
142
+ - name: Checkout verl-project/verl repo
143
+ uses: actions/checkout@v4
144
+ with:
145
+ fetch-depth: 0
146
+ clean: true
147
+ - name: Install the current repository
148
+ run: |
149
+ pip install --no-deps --no-build-isolation -e .
150
+ - name: Check final pip list
151
+ run: |
152
+ pip list
153
+ - name: Configure related dependencies
154
+ run: |
155
+ git clone --depth 1 --branch core_v0.12.1 https://github.com/NVIDIA/Megatron-LM.git /Megatron-LM
156
+ rm -rf /MindSpeed
157
+ git clone https://gitcode.com/ascend/MindSpeed.git /MindSpeed
158
+ git clone https://gitcode.com/ascend/MindSpeed-LLM.git /MindSpeed-LLM
159
+ - name: Preprocess gsm8k dataset
160
+ run: |
161
+ python examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/.cache/datasets/openai/gsm8k
162
+ - name: Running gsm8k e2e training tests with GRPO on ASCEND NPU (MindSpeedLLM backend)
163
+ run: |
164
+ ray stop --force
165
+ export PYTHONPATH=$PYTHONPATH:/Megatron-LM
166
+ export PYTHONPATH=$PYTHONPATH:/MindSpeed
167
+ export PYTHONPATH=$PYTHONPATH:/MindSpeed-LLM
168
+ bash tests/special_npu/run_qwen3_8b_grpo_mindspeedllm.sh
169
+ - name: Running gsm8k e2e training tests with GRPO on ASCEND NPU (MindSpeedLLM backend, MoE Model)
170
+ run: |
171
+ ray stop --force
172
+ export PYTHONPATH=$PYTHONPATH:/Megatron-LM
173
+ export PYTHONPATH=$PYTHONPATH:/MindSpeed
174
+ export PYTHONPATH=$PYTHONPATH:/MindSpeed-LLM
175
+ USE_DIST_CKPT=True USE_DUMMY_MODEL=True DUMMY_MODEL_CONFIG_PATH=tests/special_e2e/ppo_trainer/expert_parallel/qwen3moe_minimal.json DUMMY_MODEL_PATH=$HOME/dist_ckpt/qwen3_30b_grpo_mindspeedllm bash tests/special_npu/run_qwen3_30b_grpo_mindspeedllm.sh
176
+
177
+ vlm_rl_job:
178
+ if: github.repository_owner == 'verl-project'
179
+ name: E2E Ascend testing for RL training scenarios of VLM models
180
+ runs-on: linux-aarch64-a3-8
181
+ timeout-minutes: 120
182
+ container:
183
+ image: swr.cn-southwest-2.myhuaweicloud.com/modelfoundry/ascend-ci/verl/verl:verl-8.5.0-a3-ubuntu22.04-py3.11-latest
184
+ options: >-
185
+ --shm-size 16g
186
+ env:
187
+ HF_ENDPOINT: "https://hf-mirror.com"
188
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
189
+ steps:
190
+ - name: Check npu and CANN info
191
+ run: |
192
+ cat /usr/local/Ascend/ascend-toolkit/latest/"$(uname -i)"-linux/ascend_toolkit_install.info
193
+ npu-smi info
194
+ - name: Check initial pip list from image
195
+ run: |
196
+ pip list
197
+ - name: Checkout verl-project/verl repo
198
+ uses: actions/checkout@v4
199
+ with:
200
+ fetch-depth: 0
201
+ clean: true
202
+ - name: Install the current repository
203
+ run: |
204
+ pip install --no-deps -e .
205
+ - name: Check final pip list
206
+ run: |
207
+ pip list
208
+ - name: Preprocess geo3k dataset
209
+ run: |
210
+ python examples/data_preprocess/geo3k.py --local_dataset_path ${HOME}/.cache/datasets/hiyouga/geometry3k
211
+ - name: Running geo3k e2e training tests with GRPO on ASCEND NPU
212
+ run: |
213
+ ray stop --force
214
+ bash tests/special_npu/run_qwen2_5_vl_3b_npu.sh
215
+ rm -rf $HOME/ckpts
verl/.github/workflows/e2e_fully_async_policy.yml ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+ name: e2e_fully_async_policy
33
+
34
+ on:
35
+ # Trigger the workflow on push or pull request,
36
+ # but only for the main branch
37
+ # For push, for now only anti-patterns are specified so it is more conservative
38
+ # and achieves higher coverage.
39
+ push:
40
+ branches:
41
+ - main
42
+ - v0.*
43
+ paths:
44
+ - "**/*.py"
45
+ - "!**/*.md"
46
+ - "!**/*.sh"
47
+ # Other entrypoints
48
+ - "!examples/*trainer*"
49
+ - "!tests/**"
50
+ - "!verl/trainer/main_*.py"
51
+ - "!verl/trainer/fsdp_sft_trainer.py"
52
+ - "verl/experimental/fully_async_policy"
53
+ pull_request:
54
+ branches:
55
+ - main
56
+ - v0.*
57
+ paths:
58
+ - "**/*.py"
59
+ - "!**/*.md"
60
+ - "!**/*.sh"
61
+ # Other entrypoints
62
+ - "!examples/**"
63
+ - "!tests/**"
64
+ - "!verl/trainer/main_*.py"
65
+ - "!verl/trainer/fsdp_sft_trainer.py"
66
+ # Home
67
+ - "verl/experimental/fully_async_policy"
68
+ # Entrypoints
69
+ - ".github/workflows/e2e_fully_async_policy.yml"
70
+ - "examples/data_preprocess/gsm8k.py"
71
+ - "tests/special_e2e/run_fully_async_policy.sh"
72
+
73
+ # Cancel jobs on the same ref if a new one is triggered
74
+ concurrency:
75
+ group: ${{ github.workflow }}-${{ github.ref }}
76
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
77
+
78
+ # Declare permissions just read content.
79
+ permissions:
80
+ contents: read
81
+
82
+ env:
83
+ IMAGE: "verl-ci-cn-beijing.cr.volces.com/verlai/verl:vllm018.dev1"
84
+ DYNAMIC_RUNNER_ENDPOINT: "https://sd10g3clalm04ug7alq90.apigateway-cn-beijing.volceapi.com/runner"
85
+
86
+ jobs:
87
+ setup:
88
+ if: github.repository_owner == 'verl-project'
89
+ runs-on: ubuntu-latest
90
+ outputs:
91
+ runner-label: ${{ steps.create-runner.outputs.runner-label }}
92
+ mlp-task-id: ${{ steps.create-runner.outputs.mlp-task-id }}
93
+ steps:
94
+ - uses: actions/checkout@v4
95
+ - id: create-runner
96
+ uses: volcengine/vemlp-github-runner@v1
97
+ with:
98
+ mode: "create"
99
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
100
+ mlp-image: "${{ env.IMAGE }}"
101
+
102
+ # Test FSDP2 strategy
103
+ e2e_fully_async_policy_fsdp2:
104
+ needs: setup
105
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
106
+ timeout-minutes: 10 # Increase timeout for async training
107
+ env:
108
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
109
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
110
+ HF_ENDPOINT: "https://hf-mirror.com"
111
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
112
+ ACTOR_STRATEGY: "fsdp2"
113
+ steps:
114
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
115
+ with:
116
+ fetch-depth: 0
117
+ - name: Install the current repository
118
+ run: |
119
+ pip3 install -r requirements-test.txt
120
+ pip3 install --no-deps -e .
121
+ pip3 install cupy-cuda12x==13.6.0
122
+ - name: Prepare GSM8K dataset
123
+ run: |
124
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k
125
+ - name: Running the E2E test with fully_async_policy algorithm (FSDP2)
126
+ run: |
127
+ ray stop --force
128
+ bash tests/special_e2e/run_fully_async_policy.sh
129
+
130
+ # Test Megatron strategy
131
+ e2e_fully_async_policy_megatron:
132
+ needs: setup
133
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
134
+ timeout-minutes: 10 # Increase timeout for async training
135
+ env:
136
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
137
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
138
+ HF_ENDPOINT: "https://hf-mirror.com"
139
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
140
+ ACTOR_STRATEGY: "megatron"
141
+ steps:
142
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
143
+ with:
144
+ fetch-depth: 0
145
+ - name: Install the current repository
146
+ run: |
147
+ pip3 install -r requirements-test.txt
148
+ pip3 install --no-deps -e .
149
+ pip3 install cupy-cuda12x==13.6.0
150
+ pip3 install git+https://github.com/ISEEKYAN/mbridge.git@main --no-deps --no-build-isolation
151
+ - name: Prepare GSM8K dataset
152
+ run: |
153
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k
154
+ - name: Running the E2E test with fully_async_policy algorithm (Megatron)
155
+ run: |
156
+ ray stop --force
157
+ bash tests/special_e2e/run_fully_async_policy.sh
158
+
159
+ cleanup:
160
+ runs-on: ubuntu-latest
161
+ needs: [setup, e2e_fully_async_policy_fsdp2]
162
+ if: always()
163
+ steps:
164
+ - id: destroy-runner
165
+ uses: volcengine/vemlp-github-runner@v1
166
+ with:
167
+ mode: "destroy"
168
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
169
+ mlp-task-id: "${{ needs.setup.outputs.mlp-task-id }}"
verl/.github/workflows/e2e_fully_async_policy_ascend.yml ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+ name: e2e_fully_async_policy_ascend
33
+
34
+ on:
35
+ # Trigger the workflow on push or pull request,
36
+ # but only for the main branch
37
+ # For push, for now only anti-patterns are specified so it is more conservative
38
+ # and achieves higher coverage.
39
+ push:
40
+ branches:
41
+ - main
42
+ - v0.*
43
+ paths:
44
+ - "**/*.py"
45
+ - "!**/*.md"
46
+ - "!**/*.sh"
47
+ # Other entrypoints
48
+ - "!examples/*trainer*"
49
+ - "!tests/**"
50
+ - "!verl/trainer/main_*.py"
51
+ - "!verl/trainer/fsdp_sft_trainer.py"
52
+ - "verl/experimental/fully_async_policy"
53
+ pull_request:
54
+ branches:
55
+ - main
56
+ - v0.*
57
+ paths:
58
+ - "**/*.py"
59
+ - "!**/*.md"
60
+ - "!**/*.sh"
61
+ # Other entrypoints
62
+ - "!examples/**"
63
+ - "!tests/**"
64
+ - "!verl/trainer/main_*.py"
65
+ - "!verl/trainer/fsdp_sft_trainer.py"
66
+ # Home
67
+ - "verl/experimental/fully_async_policy"
68
+ # Entrypoints
69
+ - ".github/workflows/e2e_fully_async_policy_ascend.yml"
70
+ - "examples/data_preprocess/gsm8k.py"
71
+ - "tests/special_e2e/run_fully_async_policy.sh"
72
+
73
+ # Cancel jobs on the same ref if a new one is triggered
74
+ concurrency:
75
+ group: ${{ github.workflow }}-${{ github.ref }}
76
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
77
+
78
+ # Declare permissions just read content.
79
+ permissions:
80
+ contents: read
81
+
82
+ jobs:
83
+ # Test FSDP2 strategy
84
+ e2e_fully_async_policy_fsdp2_ascend:
85
+ if: github.repository_owner == 'verl-project'
86
+ runs-on: linux-aarch64-a3-8
87
+ timeout-minutes: 60 # Increase this timeout value as needed
88
+ container:
89
+ image: swr.cn-southwest-2.myhuaweicloud.com/modelfoundry/ascend-ci/verl/verl:verl-8.5.0-a3-ubuntu22.04-py3.11-latest
90
+ options: >-
91
+ --shm-size 16g
92
+ env:
93
+ HF_ENDPOINT: "https://hf-mirror.com"
94
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
95
+ ACTOR_STRATEGY: "fsdp2"
96
+ device_name: "npu"
97
+ steps:
98
+ - name: Check npu and CANN info
99
+ run: |
100
+ cat /usr/local/Ascend/ascend-toolkit/latest/"$(uname -i)"-linux/ascend_toolkit_install.info
101
+ npu-smi info
102
+ - name: Check initial pip list from image
103
+ run: |
104
+ pip list
105
+ - name: Checkout verl-project/verl repo
106
+ uses: actions/checkout@v4
107
+ with:
108
+ fetch-depth: 0
109
+ clean: true
110
+ - name: Install the current repository
111
+ run: |
112
+ pip install --no-deps -e .
113
+ - name: Check final pip list
114
+ run: |
115
+ pip list
116
+ - name: Prepare weights
117
+ run: |
118
+ ln -s /root/.cache/models ~/models
119
+ - name: Prepare GSM8K dataset
120
+ run: |
121
+ python examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/.cache/datasets/openai/gsm8k
122
+ - name: Running the E2E test with fully_async_policy algorithm (FSDP2)
123
+ run: |
124
+ ray stop --force
125
+ bash tests/special_e2e/run_fully_async_policy.sh
126
+
127
+ # Test Megatron strategy
128
+ e2e_fully_async_policy_megatron_ascend:
129
+ if: github.repository_owner == 'verl-project'
130
+ runs-on: linux-aarch64-a3-8
131
+ timeout-minutes: 60 # Increase this timeout value as needed
132
+ container:
133
+ image: swr.cn-southwest-2.myhuaweicloud.com/modelfoundry/ascend-ci/verl/verl:verl-8.5.0-a3-ubuntu22.04-py3.11-latest
134
+ options: >-
135
+ --shm-size 16g
136
+ env:
137
+ HF_ENDPOINT: "https://hf-mirror.com"
138
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
139
+ ACTOR_STRATEGY: "megatron"
140
+ device_name: "npu"
141
+ steps:
142
+ - name: Check npu and CANN info
143
+ run: |
144
+ cat /usr/local/Ascend/ascend-toolkit/latest/"$(uname -i)"-linux/ascend_toolkit_install.info
145
+ npu-smi info
146
+ - name: Check initial pip list from image
147
+ run: |
148
+ pip list
149
+ - name: Checkout verl-project/verl repo
150
+ uses: actions/checkout@v4
151
+ with:
152
+ fetch-depth: 0
153
+ clean: true
154
+ - name: Install the current repository
155
+ run: |
156
+ pip install --no-deps -e .
157
+ - name: Check final pip list
158
+ run: |
159
+ pip list
160
+ - name: Prepare weights
161
+ run: |
162
+ ln -s /root/.cache/models ~/models
163
+ - name: Prepare GSM8K dataset
164
+ run: |
165
+ python examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/.cache/datasets/openai/gsm8k
166
+ - name: Running the E2E test with fully_async_policy algorithm (Megatron)
167
+ run: |
168
+ ray stop --force
169
+ bash tests/special_e2e/run_fully_async_policy.sh
verl/.github/workflows/e2e_fully_async_policy_opd.yml ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: e2e_fully_async_policy_opd
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - v0.*
8
+ paths:
9
+ - "**/*.py"
10
+ - "!**/*.md"
11
+ - "!**/*.sh"
12
+ - "!examples/*trainer*"
13
+ - "!tests/**"
14
+ - "!verl/trainer/main_*.py"
15
+ - "!verl/trainer/fsdp_sft_trainer.py"
16
+ - "verl/experimental/fully_async_policy"
17
+ - "verl/trainer/distillation"
18
+ pull_request:
19
+ branches:
20
+ - main
21
+ - v0.*
22
+ paths:
23
+ - "**/*.py"
24
+ - "!**/*.md"
25
+ - "!**/*.sh"
26
+ - "!examples/**"
27
+ - "!tests/**"
28
+ - "!verl/trainer/main_*.py"
29
+ - "!verl/trainer/fsdp_sft_trainer.py"
30
+ - "verl/experimental/fully_async_policy"
31
+ - "verl/trainer/distillation"
32
+ - ".github/workflows/e2e_fully_async_policy_opd.yml"
33
+ - "examples/data_preprocess/gsm8k.py"
34
+ - "examples/data_preprocess/geo3k.py"
35
+ - "tests/special_e2e/run_fully_async_policy_opd.sh"
36
+
37
+ # Cancel jobs on the same ref if a new one is triggered
38
+ concurrency:
39
+ group: ${{ github.workflow }}-${{ github.ref }}
40
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
41
+
42
+ # Declare permissions just read content.
43
+ permissions:
44
+ contents: read
45
+
46
+ env:
47
+ IMAGE: "verl-ci-cn-beijing.cr.volces.com/verlai/verl:vllm018.dev1"
48
+ DYNAMIC_RUNNER_ENDPOINT: "https://sd10g3clalm04ug7alq90.apigateway-cn-beijing.volceapi.com/runner"
49
+
50
+ jobs:
51
+ setup:
52
+ if: github.repository_owner == 'verl-project'
53
+ runs-on: ubuntu-latest
54
+ outputs:
55
+ runner-label: ${{ steps.create-runner.outputs.runner-label }}
56
+ mlp-task-id: ${{ steps.create-runner.outputs.mlp-task-id }}
57
+ steps:
58
+ - uses: actions/checkout@v4
59
+ - id: create-runner
60
+ uses: volcengine/vemlp-github-runner@v1
61
+ with:
62
+ mode: "create"
63
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
64
+ mlp-image: "${{ env.IMAGE }}"
65
+
66
+ # Test Multi-Teacher OPD with Megatron strategy
67
+ e2e_fully_async_policy_opd_megatron:
68
+ needs: setup
69
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
70
+ timeout-minutes: 30
71
+ env:
72
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
73
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
74
+ HF_ENDPOINT: "https://hf-mirror.com"
75
+ HF_HUB_ENABLE_HF_TRANSFER: "0"
76
+ steps:
77
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
78
+ with:
79
+ fetch-depth: 0
80
+ - name: Install the current repository
81
+ run: |
82
+ pip3 install -r requirements-test.txt
83
+ pip3 install --no-deps -e .
84
+ pip3 install cupy-cuda12x==13.6.0
85
+ pip3 install --force-reinstall --no-deps --no-build-isolation git+https://github.com/ISEEKYAN/mbridge.git@main
86
+ - name: Prepare datasets
87
+ run: |
88
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k
89
+ python3 examples/data_preprocess/geo3k.py --local_dataset_path ${HOME}/models/hf_data/hiyouga/geometry3k/ --local_save_dir ${HOME}/data/geo3k
90
+ - name: Running the E2E test with fully_async_policy + Multi-Teacher OPD (Megatron)
91
+ run: |
92
+ ray stop --force
93
+ bash tests/special_e2e/run_fully_async_policy_opd.sh
94
+
95
+ cleanup:
96
+ runs-on: ubuntu-latest
97
+ needs: [setup, e2e_fully_async_policy_opd_megatron]
98
+ if: always()
99
+ steps:
100
+ - id: destroy-runner
101
+ uses: volcengine/vemlp-github-runner@v1
102
+ with:
103
+ mode: "destroy"
104
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
105
+ mlp-task-id: "${{ needs.setup.outputs.mlp-task-id }}"
verl/.github/workflows/e2e_fully_async_policy_trtllm.yml ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+ name: e2e_fully_async_policy_trtllm
33
+
34
+ on:
35
+ # Trigger the workflow on push or pull request,
36
+ # but only for the main branch
37
+ push:
38
+ branches:
39
+ - main
40
+ - v0.*
41
+ paths:
42
+ - "**/*.py"
43
+ - "!**/*.md"
44
+ - "!**/*.sh"
45
+ - "!examples/*trainer*"
46
+ - "!tests/**"
47
+ - "!verl/trainer/main_*.py"
48
+ - "!verl/trainer/fsdp_sft_trainer.py"
49
+ - "verl/workers/rollout/trtllm_rollout/**"
50
+ - "verl/experimental/fully_async_policy/**"
51
+ - "verl/checkpoint_engine/**"
52
+ pull_request:
53
+ branches:
54
+ - main
55
+ - v0.*
56
+ paths:
57
+ - "verl/workers/rollout/trtllm_rollout/**"
58
+ - "verl/experimental/fully_async_policy/**"
59
+ - "verl/checkpoint_engine/**"
60
+ - ".github/workflows/e2e_fully_async_policy_trtllm.yml"
61
+ - "tests/special_e2e/run_fully_async_policy.sh"
62
+ - "examples/data_preprocess/gsm8k.py"
63
+
64
+ # Cancel jobs on the same ref if a new one is triggered
65
+ concurrency:
66
+ group: ${{ github.workflow }}-${{ github.ref }}
67
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
68
+
69
+ # Declare permissions just read content.
70
+ permissions:
71
+ contents: read
72
+
73
+ env:
74
+ IMAGE: "verl-ci-cn-beijing.cr.volces.com/verlai/verl:trtllm1.3.0rc14"
75
+ DYNAMIC_RUNNER_ENDPOINT: "https://sd10g3clalm04ug7alq90.apigateway-cn-beijing.volceapi.com/runner"
76
+
77
+ jobs:
78
+ setup:
79
+ if: github.repository_owner == 'verl-project'
80
+ runs-on: ubuntu-latest
81
+ outputs:
82
+ runner-label: ${{ steps.create-runner.outputs.runner-label }}
83
+ mlp-task-id: ${{ steps.create-runner.outputs.mlp-task-id }}
84
+ steps:
85
+ - uses: actions/checkout@v4
86
+ - id: create-runner
87
+ uses: volcengine/vemlp-github-runner@v1
88
+ with:
89
+ mode: "create"
90
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
91
+ mlp-image: "${{ env.IMAGE }}"
92
+
93
+ trtllm_async_unit_tests:
94
+ needs: setup
95
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
96
+ timeout-minutes: 30
97
+ env:
98
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
99
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
100
+ HF_ENDPOINT: "https://hf-mirror.com"
101
+ HF_HUB_ENABLE_HF_TRANSFER: "0"
102
+ TORCH_CUDA_ARCH_LIST: "7.5;8.0;8.9;9.0;10.0;12.0+PTX"
103
+ steps:
104
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
105
+ with:
106
+ fetch-depth: 0
107
+ - name: Reinstall FlashInfer for runtime GPU arch
108
+ run: |
109
+ unset TORCH_CUDA_ARCH_LIST
110
+ pip3 install --force-reinstall --no-deps flashinfer-python
111
+ - name: Install the current repository
112
+ run: |
113
+ pip3 install pytest-asyncio
114
+ pip3 install -r requirements-test.txt
115
+ pip3 install --no-deps -e .
116
+ - name: Run TRT-LLM async unit tests
117
+ run: |
118
+ unset TORCH_CUDA_ARCH_LIST
119
+ export TRTLLM_ENABLE_PDL=0
120
+ export TRTLLM_TEST_MODEL_PATH_ROOT="${HOME}/models"
121
+ ray stop --force
122
+ pytest -v -s --durations=20 \
123
+ tests/workers/rollout/rollout_trtllm/test_trtllm_abort.py
124
+
125
+ # Megatron multi-replica: 4 rollout GPUs split into 2 replicas × TP=2.
126
+ e2e_fully_async_policy_trtllm_megatron_multi_replica:
127
+ needs: setup
128
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
129
+ timeout-minutes: 20
130
+ env:
131
+ HTTP_PROXY: ${{ secrets.PROXY_HTTP }}
132
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
133
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
134
+ HF_ENDPOINT: "https://hf-mirror.com"
135
+ HF_HUB_ENABLE_HF_TRANSFER: "0"
136
+ ACTOR_STRATEGY: "megatron"
137
+ ROLLOUT_NAME: "trtllm"
138
+ GEN_TP: "2" # 4 rollout GPUs / TP=2 = 2 replicas
139
+ steps:
140
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
141
+ with:
142
+ fetch-depth: 0
143
+ - name: Install the current repository
144
+ run: |
145
+ pip3 install -r requirements-test.txt
146
+ pip3 install --no-deps -e .
147
+ pip3 install cupy-cuda12x==14.0.1
148
+ - name: Prepare GSM8K dataset
149
+ run: |
150
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k
151
+ - name: Running TRT-LLM fully-async E2E test with Megatron (multi-replica, 2×TP=2)
152
+ run: |
153
+ ray stop --force
154
+ bash tests/special_e2e/run_fully_async_policy.sh \
155
+ data.max_response_length=128 \
156
+ actor_rollout_ref.rollout.n=2 \
157
+ actor_rollout_ref.rollout.gpu_memory_utilization=0.1 \
158
+ actor_rollout_ref.actor.ppo_mini_batch_size=4 \
159
+ actor_rollout_ref.actor.ppo_max_token_len_per_gpu=1536
160
+
161
+ cleanup:
162
+ runs-on: ubuntu-latest
163
+ needs: [setup, trtllm_async_unit_tests, e2e_fully_async_policy_trtllm_megatron_multi_replica]
164
+ if: always()
165
+ steps:
166
+ - id: destroy-runner
167
+ uses: volcengine/vemlp-github-runner@v1
168
+ with:
169
+ mode: "destroy"
170
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
171
+ mlp-task-id: "${{ needs.setup.outputs.mlp-task-id }}"
verl/.github/workflows/e2e_one_step_off_policy.yml ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+ name: e2e_one_step_off_policy
33
+
34
+ on:
35
+ # Trigger the workflow on push or pull request,
36
+ # but only for the main branch
37
+ # For push, for now only anti-patterns are specified so it is more conservative
38
+ # and achieves higher coverage.
39
+ push:
40
+ branches:
41
+ - main
42
+ - v0.*
43
+ paths:
44
+ - "**/*.py"
45
+ - "!**/*.md"
46
+ - "!**/*.sh"
47
+ # Other entrypoints
48
+ - "!examples/*trainer*"
49
+ - "!tests/**"
50
+ - "!verl/trainer/main_*.py"
51
+ - "!verl/trainer/fsdp_sft_trainer.py"
52
+ - "verl/experimental/one_step_off_policy"
53
+ pull_request:
54
+ branches:
55
+ - main
56
+ - v0.*
57
+ paths:
58
+ - "**/*.py"
59
+ - "!**/*.md"
60
+ - "!**/*.sh"
61
+ # Other entrypoints
62
+ - "!examples/**"
63
+ - "!tests/**"
64
+ - "!verl/trainer/main_*.py"
65
+ - "!verl/trainer/fsdp_sft_trainer.py"
66
+ # Home
67
+ - "verl/experimental/one_step_off_policy"
68
+ # Entrypoints
69
+ - ".github/workflows/e2e_one_step_off_policy.yml"
70
+ - "examples/data_preprocess/gsm8k.py"
71
+ - "tests/special_e2e/run_one_step_off_policy.sh"
72
+
73
+ # Cancel jobs on the same ref if a new one is triggered
74
+ concurrency:
75
+ group: ${{ github.workflow }}-${{ github.ref }}
76
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
77
+
78
+ # Declare permissions just read content.
79
+ permissions:
80
+ contents: read
81
+
82
+ env:
83
+ IMAGE: "verl-ci-cn-beijing.cr.volces.com/verlai/verl:vllm018.dev1"
84
+ DYNAMIC_RUNNER_ENDPOINT: "https://sd10g3clalm04ug7alq90.apigateway-cn-beijing.volceapi.com/runner"
85
+
86
+ jobs:
87
+ setup:
88
+ if: github.repository_owner == 'verl-project'
89
+ runs-on: ubuntu-latest
90
+ outputs:
91
+ runner-label: ${{ steps.create-runner.outputs.runner-label }}
92
+ mlp-task-id: ${{ steps.create-runner.outputs.mlp-task-id }}
93
+ steps:
94
+ - uses: actions/checkout@v4
95
+ - id: create-runner
96
+ uses: volcengine/vemlp-github-runner@v1
97
+ with:
98
+ mode: "create"
99
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
100
+ mlp-image: "${{ env.IMAGE }}"
101
+
102
+ # Test FSDP2 strategy
103
+ e2e_one_step_off_policy_fsdp2:
104
+ needs: setup
105
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
106
+ timeout-minutes: 10 # Increase timeout for async training
107
+ env:
108
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
109
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
110
+ HF_ENDPOINT: "https://hf-mirror.com"
111
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
112
+ ACTOR_STRATEGY: "fsdp2"
113
+ steps:
114
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
115
+ with:
116
+ fetch-depth: 0
117
+ - name: Install the current repository
118
+ run: |
119
+ pip3 install -r requirements-test.txt
120
+ pip3 install --no-deps -e .
121
+ pip3 install cupy-cuda12x==13.6.0
122
+ - name: Prepare GSM8K dataset
123
+ run: |
124
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k
125
+ - name: Running the E2E test with one_step_off_policy algorithm (FSDP2)
126
+ run: |
127
+ ray stop --force
128
+ bash tests/special_e2e/run_one_step_off_policy.sh
129
+
130
+ # Test Megatron strategy
131
+ e2e_one_step_off_policy_megatron:
132
+ needs: setup
133
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
134
+ timeout-minutes: 10 # Increase timeout for async training
135
+ env:
136
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
137
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
138
+ HF_ENDPOINT: "https://hf-mirror.com"
139
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
140
+ ACTOR_STRATEGY: "megatron"
141
+ steps:
142
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
143
+ with:
144
+ fetch-depth: 0
145
+ - name: Install the current repository
146
+ run: |
147
+ pip3 install -r requirements-test.txt
148
+ pip3 install --no-deps -e .
149
+ pip3 install cupy-cuda12x==13.6.0
150
+ - name: Prepare GSM8K dataset
151
+ run: |
152
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k
153
+ - name: Running the E2E test with one_step_off_policy algorithm (Megatron)
154
+ run: |
155
+ ray stop --force
156
+ bash tests/special_e2e/run_one_step_off_policy.sh
157
+
158
+ cleanup:
159
+ runs-on: ubuntu-latest
160
+ needs:
161
+ [setup, e2e_one_step_off_policy_fsdp2, e2e_one_step_off_policy_megatron]
162
+ if: always()
163
+ steps:
164
+ - id: destroy-runner
165
+ uses: volcengine/vemlp-github-runner@v1
166
+ with:
167
+ mode: "destroy"
168
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
169
+ mlp-task-id: "${{ needs.setup.outputs.mlp-task-id }}"
verl/.github/workflows/e2e_one_step_off_policy_ascend.yml ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+ name: e2e_one_step_off_policy_ascend
33
+
34
+ on:
35
+ # Trigger the workflow on push or pull request,
36
+ # but only for the main branch
37
+ # For push, for now only anti-patterns are specified so it is more conservative
38
+ # and achieves higher coverage.
39
+ push:
40
+ branches:
41
+ - main
42
+ - v0.*
43
+ paths:
44
+ - "**/*.py"
45
+ - "!**/*.md"
46
+ - "!**/*.sh"
47
+ # Other entrypoints
48
+ - "!examples/*trainer*"
49
+ - "!tests/**"
50
+ - "!verl/trainer/main_*.py"
51
+ - "!verl/trainer/fsdp_sft_trainer.py"
52
+ - "verl/experimental/one_step_off_policy"
53
+ pull_request:
54
+ branches:
55
+ - main
56
+ - v0.*
57
+ paths:
58
+ - "**/*.py"
59
+ - "!**/*.md"
60
+ - "!**/*.sh"
61
+ # Other entrypoints
62
+ - "!examples/**"
63
+ - "!tests/**"
64
+ - "!verl/trainer/main_*.py"
65
+ - "!verl/trainer/fsdp_sft_trainer.py"
66
+ # Home
67
+ - "verl/experimental/one_step_off_policy"
68
+ # Entrypoints
69
+ - ".github/workflows/e2e_one_step_off_policy_ascend.yml"
70
+ - "examples/data_preprocess/gsm8k.py"
71
+ - "tests/special_e2e/run_one_step_off_policy.sh"
72
+
73
+ # Cancel jobs on the same ref if a new one is triggered
74
+ concurrency:
75
+ group: ${{ github.workflow }}-${{ github.ref }}
76
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
77
+
78
+ # Declare permissions just read content.
79
+ permissions:
80
+ contents: read
81
+
82
+ jobs:
83
+ # Test FSDP2 strategy
84
+ e2e_one_step_off_policy_fsdp2_ascend:
85
+ if: github.repository_owner == 'verl-project'
86
+ runs-on: linux-aarch64-a3-8
87
+ timeout-minutes: 60 # Increase this timeout value as needed
88
+ container:
89
+ image: swr.cn-southwest-2.myhuaweicloud.com/modelfoundry/ascend-ci/verl/verl:verl-8.5.0-a3-ubuntu22.04-py3.11-latest
90
+ options: >-
91
+ --shm-size 16g
92
+ env:
93
+ HF_ENDPOINT: "https://hf-mirror.com"
94
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
95
+ ACTOR_STRATEGY: "fsdp2"
96
+ device_name: "npu"
97
+ steps:
98
+ - name: Check npu and CANN info
99
+ run: |
100
+ cat /usr/local/Ascend/ascend-toolkit/latest/"$(uname -i)"-linux/ascend_toolkit_install.info
101
+ npu-smi info
102
+ - name: Check initial pip list from image
103
+ run: |
104
+ pip list
105
+ - name: Checkout verl-project/verl repo
106
+ uses: actions/checkout@v4
107
+ with:
108
+ fetch-depth: 0
109
+ clean: true
110
+ - name: Install the current repository
111
+ run: |
112
+ pip install --no-deps -e .
113
+ - name: Check final pip list
114
+ run: |
115
+ pip list
116
+ - name: Prepare weights
117
+ run: |
118
+ ln -s /root/.cache/models ~/models
119
+ - name: Prepare GSM8K dataset
120
+ run: |
121
+ python examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/.cache/datasets/openai/gsm8k
122
+ - name: Running the E2E test with one_step_off_policy algorithm (FSDP2)
123
+ run: |
124
+ ray stop --force
125
+ bash tests/special_e2e/run_one_step_off_policy.sh
126
+
127
+ # Test Megatron strategy
128
+ e2e_one_step_off_policy_megatron_ascend:
129
+ if: github.repository_owner == 'verl-project'
130
+ runs-on: linux-aarch64-a3-8
131
+ timeout-minutes: 60 # Increase this timeout value as needed
132
+ container:
133
+ image: swr.cn-southwest-2.myhuaweicloud.com/modelfoundry/ascend-ci/verl/verl:verl-8.5.0-a3-ubuntu22.04-py3.11-latest
134
+ options: >-
135
+ --shm-size 16g
136
+ env:
137
+ HF_ENDPOINT: "https://hf-mirror.com"
138
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
139
+ ACTOR_STRATEGY: "megatron"
140
+ device_name: "npu"
141
+ steps:
142
+ - name: Check npu and CANN info
143
+ run: |
144
+ cat /usr/local/Ascend/ascend-toolkit/latest/"$(uname -i)"-linux/ascend_toolkit_install.info
145
+ npu-smi info
146
+ - name: Check initial pip list from image
147
+ run: |
148
+ pip list
149
+ - name: Checkout verl-project/verl repo
150
+ uses: actions/checkout@v4
151
+ with:
152
+ fetch-depth: 0
153
+ clean: true
154
+ - name: Install the current repository
155
+ run: |
156
+ pip install --no-deps -e .
157
+ - name: Check final pip list
158
+ run: |
159
+ pip list
160
+ - name: Prepare weights
161
+ run: |
162
+ ln -s /root/.cache/models ~/models
163
+ - name: Prepare GSM8K dataset
164
+ run: |
165
+ python examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/.cache/datasets/openai/gsm8k
166
+ - name: Running the E2E test with one_step_off_policy algorithm (Megatron)
167
+ run: |
168
+ ray stop --force
169
+ export PYTHONPATH=$PYTHONPATH:/Megatron-LM
170
+ bash tests/special_e2e/run_one_step_off_policy.sh
verl/.github/workflows/e2e_ppo_grpo_trainer_trtllm.yml ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+ name: e2e_ppo_trainer_megatron_trtllm
33
+
34
+ on:
35
+ # Trigger the workflow on push or pull request,
36
+ # but only for the main branch.
37
+ # For push, for now only anti-patterns are specified so it is more conservative
38
+ # and achieves higher coverage.
39
+ push:
40
+ branches:
41
+ - main
42
+ - v0.*
43
+ paths:
44
+ - "**/*.py"
45
+ # Other entrypoints
46
+ - "!verl/trainer/fsdp_sft_trainer.py"
47
+ # Recipes
48
+ - "!recipe/**"
49
+ # FSDP
50
+ - "!verl/workers/**/*dp_*.py"
51
+ pull_request:
52
+ branches:
53
+ - main
54
+ - v0.*
55
+ paths:
56
+ - "**/*.py"
57
+ # Other entrypoints
58
+ - "!docker/**"
59
+ # Docs
60
+ - "!**/*.md"
61
+ - "!docs/**"
62
+ - "!examples/**"
63
+ - "!tests/**"
64
+ - "!verl/trainer/main_*.py"
65
+ - "!verl/trainer/fsdp_sft_trainer.py"
66
+ # Recipes
67
+ - "!recipe/**"
68
+ # FSDP
69
+ - "!verl/workers/**/*dp_*.py"
70
+ # Entrypoints
71
+ - "verl/workers/rollout/trtllm_rollout/**"
72
+ - "tests/workers/rollout/rollout_trtllm/**"
73
+ - ".github/workflows/e2e_ppo_grpo_trainer_trtllm.yml"
74
+ - "examples/data_preprocess/gsm8k.py"
75
+ - "examples/data_preprocess/geo3k.py"
76
+ - "examples/data_preprocess/dapo_multiturn_w_tool.py"
77
+ - "examples/data_preprocess/aime2024_multiturn_w_tool.py"
78
+ - "examples/grpo_trainer/run_qwen3_8b_megatron.sh"
79
+ - "examples/grpo_trainer/run_qwen3_30b_a3b_megatron.sh"
80
+ # add back when ppo flow is ready
81
+ # - "tests/special_e2e/run_ppo_trainer_megatron.sh"
82
+ # - "verl/trainer/main_ppo.py"
83
+ # - "verl/trainer/config/ppo_megatron_trainer.yaml"
84
+
85
+ # Cancel jobs on the same ref if a new one is triggered
86
+ concurrency:
87
+ group: ${{ github.workflow }}-${{ github.ref }}
88
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
89
+
90
+ # Declare permissions just read content.
91
+ permissions:
92
+ contents: read
93
+
94
+ env:
95
+ IMAGE: "verl-ci-cn-beijing.cr.volces.com/verlai/verl:trtllm1.3.0rc14"
96
+ DYNAMIC_RUNNER_ENDPOINT: "https://sd10g3clalm04ug7alq90.apigateway-cn-beijing.volceapi.com/runner"
97
+
98
+ jobs:
99
+ setup:
100
+ if: github.repository_owner == 'verl-project'
101
+ runs-on: ubuntu-latest
102
+ outputs:
103
+ runner-label: ${{ steps.create-runner.outputs.runner-label }}
104
+ mlp-task-id: ${{ steps.create-runner.outputs.mlp-task-id }}
105
+ steps:
106
+ - uses: actions/checkout@v4
107
+ - id: create-runner
108
+ uses: volcengine/vemlp-github-runner@v1
109
+ with:
110
+ mode: "create"
111
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
112
+ mlp-image: "${{ env.IMAGE }}"
113
+
114
+ trtllm_unit_tests:
115
+ needs: setup
116
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
117
+ timeout-minutes: 30 # Increase this timeout value as needed
118
+ env:
119
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
120
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
121
+ HF_ENDPOINT: "https://hf-mirror.com"
122
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
123
+ TORCH_CUDA_ARCH_LIST: "7.5;8.0;8.9;9.0;10.0;12.0+PTX"
124
+ steps:
125
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
126
+ with:
127
+ fetch-depth: 0
128
+ - name: Reinstall FlashInfer for runtime GPU arch
129
+ run: |
130
+ unset TORCH_CUDA_ARCH_LIST
131
+ pip3 install --force-reinstall --no-deps flashinfer-python
132
+ - name: Install the current repository
133
+ run: |
134
+ pip3 install pytest-asyncio
135
+ pip3 install -r requirements-test.txt
136
+ pip3 install --no-deps -e .
137
+ - name: Run TRTLLM unit tests
138
+ run: |
139
+ unset TORCH_CUDA_ARCH_LIST # Let TRT-LLM CUTLASS DSL auto-detect runtime GPU arch
140
+ export TRTLLM_ENABLE_PDL=0 # Disable Programmatic Dependent Launch (uses SM 9.0 griddepcontrol)
141
+ export TRTLLM_TEST_MODEL_PATH_ROOT="${HOME}/models"
142
+ ray stop --force
143
+ pytest -v -s --durations=20 \
144
+ tests/workers/rollout/rollout_trtllm/test_adapter.py \
145
+ tests/workers/rollout/rollout_trtllm/test_async_server.py \
146
+ tests/workers/rollout/rollout_trtllm/test_inter_node_rollout.py \
147
+ tests/workers/rollout/rollout_trtllm/test_trtllm_rollout_utils.py
148
+
149
+ e2e_grpo_trainer_megatron-qwen2:
150
+ needs: setup
151
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
152
+ timeout-minutes: 30 # Increase this timeout value as needed
153
+ env:
154
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
155
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
156
+ HF_ENDPOINT: "https://hf-mirror.com"
157
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
158
+ TORCH_CUDA_ARCH_LIST: "7.5;8.0;8.9;9.0;10.0;12.0+PTX"
159
+ steps:
160
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
161
+ with:
162
+ fetch-depth: 0
163
+ - name: Reinstall FlashInfer for runtime GPU arch
164
+ run: |
165
+ unset TORCH_CUDA_ARCH_LIST
166
+ pip3 install --force-reinstall --no-deps flashinfer-python
167
+ - name: Install the current repository
168
+ run: |
169
+ pip3 install -r requirements-test.txt
170
+ pip3 install --no-deps -e .
171
+ - name: Prepare GSM8K dataset
172
+ run: |
173
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k --local_save_dir ${PWD}/data/gsm8k
174
+ - name: Running GSM8K E2E training tests with 3D parallelism on 8 L20 GPUs with Megatron (Qwen)
175
+ run: |
176
+ unset TORCH_CUDA_ARCH_LIST # Let TRT-LLM CUTLASS DSL auto-detect runtime GPU arch
177
+ export TRTLLM_ENABLE_PDL=0 # Disable Programmatic Dependent Launch (uses SM 9.0 griddepcontrol)
178
+ ray stop --force
179
+ ACTOR_TP=2 \
180
+ ACTOR_PP=1 \
181
+ ROLLOUT_TP=2 \
182
+ INFER_BACKEND=trtllm \
183
+ MODEL_PATH="${HOME}/models/Qwen/Qwen2.5-0.5B-Instruct" \
184
+ bash examples/grpo_trainer/run_qwen3_8b_megatron.sh \
185
+ trainer.total_training_steps=1 \
186
+ data.train_batch_size=32 \
187
+ data.max_prompt_length=128 \
188
+ data.max_response_length=64 \
189
+ actor_rollout_ref.rollout.n=1 \
190
+ actor_rollout_ref.actor.ppo_mini_batch_size=32 \
191
+ actor_rollout_ref.rollout.max_num_seqs=32 \
192
+ actor_rollout_ref.rollout.max_num_batched_tokens=1024 \
193
+ actor_rollout_ref.rollout.max_model_len=256 \
194
+ data.train_files="['${PWD}/data/gsm8k/train.parquet']" \
195
+ data.val_files="['${PWD}/data/gsm8k/test.parquet']" \
196
+ trainer.logger='["console"]'
197
+ - name: clean up
198
+ run: |
199
+ rm -rf checkpoints
200
+ e2e_grpo_trainer_megatron-vlm:
201
+ needs: setup
202
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
203
+ timeout-minutes: 30 # Increase this timeout value as needed
204
+ env:
205
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
206
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
207
+ HF_ENDPOINT: "https://hf-mirror.com"
208
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
209
+ TORCH_CUDA_ARCH_LIST: "7.5;8.0;8.9;9.0;10.0;12.0+PTX"
210
+ steps:
211
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
212
+ with:
213
+ fetch-depth: 0
214
+ - name: Reinstall FlashInfer for runtime GPU arch
215
+ run: |
216
+ unset TORCH_CUDA_ARCH_LIST
217
+ pip3 install --force-reinstall --no-deps flashinfer-python
218
+ - name: Install the current repository
219
+ run: |
220
+ pip3 install -r requirements-test.txt
221
+ pip3 install --no-deps -e .
222
+ pip3 install qwen_vl_utils
223
+ pip3 install mathruler
224
+ - name: Prepare GEO3K dataset
225
+ run: |
226
+ python3 examples/data_preprocess/geo3k.py --local_dataset_path ${HOME}/models/hf_data/hiyouga/geometry3k --local_save_dir ${PWD}/data/geo3k
227
+ - name: Running GEO3K E2E training tests with FSDP on 8 L20 GPUs (VLM)
228
+ run: |
229
+ ray stop --force
230
+ ROLLOUT_TP=2 \
231
+ INFER_BACKEND=trtllm \
232
+ MODEL_PATH="${HOME}/models/Qwen/Qwen3-VL-2B-Instruct" \
233
+ bash examples/grpo_trainer/run_qwen2_5_vl_7b_fsdp.sh \
234
+ trainer.total_training_steps=1 \
235
+ data.train_batch_size=8 \
236
+ data.max_response_length=64 \
237
+ actor_rollout_ref.actor.ppo_mini_batch_size=8 \
238
+ actor_rollout_ref.actor.ppo_max_token_len_per_gpu=2048 \
239
+ actor_rollout_ref.rollout.n=2 \
240
+ actor_rollout_ref.rollout.gpu_memory_utilization=0.35 \
241
+ actor_rollout_ref.rollout.max_model_len=2048 \
242
+ actor_rollout_ref.rollout.max_num_seqs=16 \
243
+ actor_rollout_ref.rollout.log_prob_max_token_len_per_gpu=2048 \
244
+ data.train_files="['${PWD}/data/geo3k/train.parquet']" \
245
+ data.val_files="['${PWD}/data/geo3k/test.parquet']" \
246
+ trainer.logger='["console"]'
247
+ - name: clean up
248
+ run: |
249
+ rm -rf checkpoints
250
+ - name: Prepare GSM8K dataset (data_preprocess)
251
+ run: |
252
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k --local_save_dir ${PWD}/data/gsm8k
253
+ - name: Running GRPO E2E with FP8 TRT-LLM rollout (Qwen3-0.6B, gsm8k)
254
+ run: |
255
+ unset TORCH_CUDA_ARCH_LIST # Let TRT-LLM CUTLASS DSL auto-detect runtime GPU arch
256
+ export TRTLLM_ENABLE_PDL=0 # Disable Programmatic Dependent Launch (uses SM 9.0 griddepcontrol)
257
+ ray stop --force
258
+ export INFER_TP=2 ACTOR_TP=2 ACTOR_PP=2 ACTOR_VPP=2 ACTOR_EP=1 ACTOR_CP=2 REF_TP=2 REF_PP=2 REF_VPP=2 REF_EP=1 REF_CP=2 GEN_MOE_TP=null GEN_MOE_EP=null
259
+ export NNODES=1 GPUS_PER_NODE=8 TRTLLM_MOE_BACKEND=TRITON
260
+ export DATA_DIR=${PWD} DAPO_MATH_TRAIN=${PWD}/data/gsm8k/train.parquet AIME_VAL=${PWD}/data/gsm8k/test.parquet MODEL_PATH=${HOME}/models/Qwen/Qwen3-0.6B
261
+ INFER_BACKEND=trtllm ROLLOUT_QUANTIZATION=fp8 bash examples/grpo_trainer/run_qwen3_30b_a3b_megatron.sh \
262
+ reward_model.reward_kwargs.overlong_buffer_cfg.len=258 \
263
+ reward_model.reward_kwargs.max_resp_len=512 \
264
+ data.max_prompt_length=128 \
265
+ data.max_response_length=64 \
266
+ data.train_batch_size=32 \
267
+ actor_rollout_ref.rollout.n=1 \
268
+ actor_rollout_ref.rollout.max_num_seqs=16 \
269
+ actor_rollout_ref.rollout.max_num_batched_tokens=1024 \
270
+ actor_rollout_ref.rollout.max_model_len=1024 \
271
+ actor_rollout_ref.actor.megatron.override_transformer_config.moe_grouped_gemm=False \
272
+ actor_rollout_ref.actor.megatron.override_transformer_config.moe_permute_fusion=False \
273
+ trainer.total_training_steps=1 \
274
+ trainer.logger='["console"]'
275
+ - name: clean up
276
+ run: |
277
+ rm -rf checkpoints
278
+
279
+ cleanup:
280
+ runs-on: ubuntu-latest
281
+ needs: [setup, trtllm_unit_tests, e2e_grpo_trainer_megatron-qwen2, e2e_grpo_trainer_megatron-vlm]
282
+ if: always()
283
+ steps:
284
+ - id: destroy-runner
285
+ uses: volcengine/vemlp-github-runner@v1
286
+ with:
287
+ mode: "destroy"
288
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
289
+ mlp-task-id: "${{ needs.setup.outputs.mlp-task-id }}"
verl/.github/workflows/e2e_ppo_trainer.yml ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: e2e_ppo_trainer
2
+
3
+ on:
4
+ # Trigger the workflow on push or pull request,
5
+ # but only for the main branch
6
+ # For push, for now only anti-patterns are specified so it is more conservative
7
+ # and achieves higher coverage.
8
+ push:
9
+ branches:
10
+ - main
11
+ - v0.*
12
+ paths:
13
+ - "**/*.py"
14
+ # Other entrypoints
15
+ - "!verl/trainer/fsdp_sft_trainer.py"
16
+
17
+ # Megatron
18
+ - "!verl/workers/**/megatron_*.py"
19
+
20
+ pull_request:
21
+ branches:
22
+ - main
23
+ - v0.*
24
+ paths:
25
+ - "**/*.py"
26
+ # Other entrypoints
27
+ - "!**/*.md"
28
+ - "!docker/**"
29
+ - "!examples/**"
30
+ - "!tests/**"
31
+ - "!verl/trainer/main_*.py"
32
+ - "!verl/trainer/fsdp_sft_trainer.py"
33
+ # Docs
34
+ - "!docs/**"
35
+
36
+ # Megatron
37
+ - "!verl/workers/**/megatron_*.py"
38
+ # Entrypoints
39
+ - ".github/workflows/e2e_ppo_trainer.yml"
40
+ - "examples/data_preprocess/gsm8k.py"
41
+ - "examples/data_preprocess/geo3k.py"
42
+ - "tests/special_e2e/ppo_trainer"
43
+ - "verl/trainer/main_ppo.py"
44
+ - "verl/trainer/config/ppo_trainer.yaml"
45
+
46
+ # Cancel jobs on the same ref if a new one is triggered
47
+ concurrency:
48
+ group: ${{ github.workflow }}-${{ github.ref }}
49
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
50
+
51
+ # Declare permissions just read content.
52
+ permissions:
53
+ contents: read
54
+
55
+ jobs:
56
+ pre_commit_for_ppo:
57
+ runs-on: ubuntu-latest
58
+ strategy:
59
+ matrix:
60
+ python-version: ["3.12"]
61
+ steps:
62
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
63
+ - name: Set up Python ${{ matrix.python-version }}
64
+ uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
65
+ with:
66
+ python-version: ${{ matrix.python-version }}
67
+ - name: Install the current repository
68
+ run: |
69
+ pip install pre-commit hydra-core
70
+ pip3 install --no-deps -e .
71
+ - name: Set ruff --output-format=github
72
+ run: |
73
+ sed -i 's/--output-format=full/--output-format=github/' .pre-commit-config.yaml
74
+ git add .pre-commit-config.yaml
75
+ - uses: pre-commit/action@v3.0.1
76
+ with:
77
+ extra_args: "" # Overriding default "--all-files"
78
+
verl/.github/workflows/e2e_ppo_trainer_megatron_sglang.yml ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # Tests layout
2
+
3
+ # Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance:
4
+ # - `tests/trainer` for testing functionality related to `verl/trainer`
5
+ # - `tests/models` for testing functionality related to `verl/models`
6
+ # - ...
7
+
8
+ # There are a few folders with `special_` prefix, created for special purposes:
9
+ # - `special_distributed`: unit tests that must run with multiple GPUs
10
+ # - `special_e2e`: end-to-end tests with training/generation scripts
11
+ # - `special_npu`: tests for NPUs
12
+ # - `special_sanity`: a suite of quick sanity tests
13
+ # - `special_standalone`: a set of test that are designed to run in dedicated environments
14
+
15
+ # Accelerators for tests
16
+ # - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`.
17
+ # - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment.
18
+
19
+ # # Workflow layout
20
+
21
+ # All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs:
22
+ # 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml`
23
+ # 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml`
24
+ # 3. End-to-end tests: `e2e_*.yml`
25
+ # 4. Unit tests
26
+ # - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py`
27
+ # - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix.
28
+ # - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when
29
+ # - new workflow yaml is added to `.github/workflows`
30
+ # - new tests are added to workflow mentioned in 2.
31
+
32
+ name: e2e_ppo_trainer_megatron_sglang
33
+
34
+ on:
35
+ # Trigger the workflow on push or pull request,
36
+ # but only for the main branch.
37
+ # For push, for now only anti-patterns are specified so it is more conservative
38
+ # and achieves higher coverage.
39
+ push:
40
+ branches:
41
+ - main
42
+ - v0.*
43
+ paths:
44
+ - "**/*.py"
45
+ # Other entrypoints
46
+ - "!verl/trainer/fsdp_sft_trainer.py" # FSDP
47
+ - "!verl/workers/**/*dp_*.py"
48
+ - "!verl/utils/fsdp_utils.py"
49
+ - "!verl/utils/checkpoint/fsdp_checkpoint_manager.py"
50
+ - "!verl/model_merger/fsdp_model_merger.py"
51
+ pull_request:
52
+ branches:
53
+ - main
54
+ - v0.*
55
+ paths:
56
+ - "**/*.py"
57
+ # Other entrypoints
58
+ - "!docker/**"
59
+ # Docs
60
+ - "!**/*.md"
61
+ - "!docs/**"
62
+ - "!examples/**"
63
+ - "!tests/**"
64
+ - "!verl/trainer/main_*.py"
65
+ - "!verl/trainer/fsdp_sft_trainer.py" # FSDP
66
+ - "!verl/workers/**/*dp_*.py"
67
+ - "!verl/utils/fsdp_utils.py"
68
+ - "!verl/utils/checkpoint/fsdp_checkpoint_manager.py"
69
+ - "!verl/model_merger/fsdp_model_merger.py"
70
+ # Entrypoints
71
+ - "verl/worksers/rollout/sglang_rollout/*"
72
+ - ".github/workflows/e2e_ppo_trainer_megatron_sglang.yml"
73
+ - "examples/data_preprocess/gsm8k.py"
74
+ - "examples/data_preprocess/geo3k.py"
75
+ - "tests/special_e2e/run_ppo_trainer_megatron.sh"
76
+ - "verl/trainer/main_ppo.py"
77
+ - "verl/trainer/config/ppo_megatron_trainer.yaml"
78
+
79
+ # Cancel jobs on the same ref if a new one is triggered
80
+ concurrency:
81
+ group: ${{ github.workflow }}-${{ github.ref }}
82
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
83
+
84
+ # Declare permissions just read content.
85
+ permissions:
86
+ contents: read
87
+
88
+ env:
89
+ IMAGE: "verl-ci-cn-beijing.cr.volces.com/verlai/verl:sgl059.dev2"
90
+ DYNAMIC_RUNNER_ENDPOINT: "https://sd10g3clalm04ug7alq90.apigateway-cn-beijing.volceapi.com/runner"
91
+
92
+ jobs:
93
+ setup:
94
+ if: github.repository_owner == 'verl-project'
95
+ runs-on: ubuntu-latest
96
+ outputs:
97
+ runner-label: ${{ steps.create-runner.outputs.runner-label }}
98
+ mlp-task-id: ${{ steps.create-runner.outputs.mlp-task-id }}
99
+ steps:
100
+ - uses: actions/checkout@v4
101
+ - id: create-runner
102
+ uses: volcengine/vemlp-github-runner@v1
103
+ with:
104
+ mode: "create"
105
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
106
+ mlp-image: "${{ env.IMAGE }}"
107
+
108
+ e2e_ppo_trainer_megatron-deepseek:
109
+ needs: setup
110
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
111
+ timeout-minutes: 60 # Increase this timeout value as needed
112
+ env:
113
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
114
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
115
+ HF_ENDPOINT: "https://hf-mirror.com"
116
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
117
+ ENGINE: sglang
118
+ steps:
119
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
120
+ with:
121
+ fetch-depth: 0
122
+ - name: Install the current repository
123
+ run: |
124
+ pip3 install -r requirements-test.txt
125
+ pip3 install megatron-bridge --no-deps
126
+ pip3 install git+https://github.com/ISEEKYAN/mbridge.git@main --no-deps --no-build-isolation
127
+ pip3 install --no-deps -e .
128
+ - name: Prepare GSM8K dataset
129
+ run: |
130
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k
131
+ - name: Running GSM8K E2E training tests with 3D parallelism on 8 L20 GPUs with Megatron (DeepSeek)
132
+ run: |
133
+ ray stop --force
134
+ OPTIM_MEMORY_EFFICIENT=True ENGINE=sglang SAVE_FREQ=1 MODEL_ID=deepseek-ai/deepseek-coder-1.3b-instruct bash tests/special_e2e/run_ppo_trainer_megatron.sh
135
+ - name: Running GSM8K E2E training tests with 3D parallelism on 8 L20 GPUs with Megatron (DeepSeek)
136
+ run: |
137
+ ray stop --force
138
+ export VLLM_USE_V1=1
139
+ ray start --head
140
+ ENGINE=sglang MODE=async RESUME_MODE=auto MODEL_ID=deepseek-ai/deepseek-coder-1.3b-instruct TOTAL_TRAIN_STEPS=2 bash tests/special_e2e/run_ppo_trainer_megatron.sh
141
+ - name: Profiling GRPO GSM8K E2E training tests with 3D parallelism on 8 L20 GPUs with Megatron (Deepseek)
142
+ run: |
143
+ ray stop --force
144
+ PROFILE_ENABLE=True ENGINE=sglang ADV_ESTIMATOR=grpo USE_DYNAMIC_BSZ=False MODEL_ID=deepseek-ai/deepseek-coder-1.3b-instruct bash tests/special_e2e/run_ppo_trainer_megatron.sh
145
+ if [ -z "$( ls -A '/tmp/ray/session_latest/logs/nsight/' )" ]; then
146
+ echo "[ERROR] not found any profiling files"
147
+ exit 1
148
+ else
149
+ echo "[SUCCESS] profile success"
150
+ fi
151
+ - name: clean up
152
+ run: |
153
+ rm -rf checkpoints
154
+
155
+ # Qwen3-0.6B: dense, tie_word_embeddings=True
156
+ e2e_ppo_trainer_megatron-qwen3:
157
+ needs: setup
158
+ runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"]
159
+ timeout-minutes: 60 # Increase this timeout value as needed
160
+ env:
161
+ HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }}
162
+ NO_PROXY: "localhost,127.0.0.1,hf-mirror.com"
163
+ HF_ENDPOINT: "https://hf-mirror.com"
164
+ HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable
165
+ ENGINE: sglang
166
+ steps:
167
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
168
+ with:
169
+ fetch-depth: 0
170
+ - name: Install the current repository
171
+ run: |
172
+ pip3 install -r requirements-test.txt
173
+ pip3 install --no-deps -e .
174
+ pip3 install megatron-bridge --no-deps
175
+ - name: Prepare GSM8K dataset
176
+ run: |
177
+ python3 examples/data_preprocess/gsm8k.py --local_dataset_path ${HOME}/models/hf_data/gsm8k
178
+ - name: Running GSM8K E2E training tests with 3D parallelism on 8 L20 GPUs with Megatron (Qwen3) testing learning rate scheduler
179
+ run: |
180
+ ray stop --force
181
+ ALL_OFFLOAD=True VAL_BEFORE_TRAIN=True TEST_FREQ=1 SAVE_FREQ=1 LR_WARMUP_STEPS=1 TOTAL_TRAIN_STEPS=2 MODEL_ID=Qwen/Qwen3-0.6B bash tests/special_e2e/run_ppo_trainer_megatron.sh
182
+ - name: Running GSM8K E2E training tests with 3D parallelism on 8 L20 GPUs with FP8 rollout
183
+ run: |
184
+ ray stop --force
185
+ export VLLM_USE_V1=1
186
+ ROLLOUT_QUANTIZATION=fp8 TOTAL_TRAIN_STEPS=2 MODEL_ID=Qwen/Qwen3-0.6B bash tests/special_e2e/run_ppo_trainer_megatron.sh
187
+ - name: clean up
188
+ run: |
189
+ rm -rf checkpoints
190
+
191
+ cleanup:
192
+ runs-on: ubuntu-latest
193
+ needs:
194
+ [setup, e2e_ppo_trainer_megatron-deepseek, e2e_ppo_trainer_megatron-qwen3]
195
+ if: always()
196
+ steps:
197
+ - id: destroy-runner
198
+ uses: volcengine/vemlp-github-runner@v1
199
+ with:
200
+ mode: "destroy"
201
+ faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}"
202
+ mlp-task-id: "${{ needs.setup.outputs.mlp-task-id }}"