Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
ce9faba
feat(speco): add separate multi-GPU draft model training
755651978 Jul 9, 2026
00fc5a8
fix(speco): compose standalone draft training config
755651978 Jul 9, 2026
33eb98e
fix(speco): break trainer worker import cycle
755651978 Jul 9, 2026
2940acd
fix(speco): flush collected features by training step
755651978 Jul 9, 2026
4f6ebeb
fix(speco): separate primary Hydra configs
755651978 Jul 9, 2026
1b550ce
fix(speco): normalize offline draft training features
755651978 Jul 10, 2026
841e32e
fix(speco): stabilize standalone draft training
755651978 Jul 10, 2026
659949b
fix(speco): restore offline draft feature alignment
755651978 Jul 13, 2026
85fc79e
fix(speco): point verl contract test at shared base config
755651978 Jul 13, 2026
05552bd
fix(speco): address standalone draft review feedback
755651978 Jul 14, 2026
b2ba8f1
update readme
755651978 Jul 14, 2026
782de13
docs: add vLLM and SGLang GPU Dockerfiles
755651978 Jul 14, 2026
5499154
fix: export standalone drafter runtime config
755651978 Jul 15, 2026
a1089fc
Add trusted NPU DSpark example CI
755651978 Jul 16, 2026
d12c334
Merge upstream main
755651978 Jul 16, 2026
6f783ad
Address DSpark NPU CI review feedback
755651978 Jul 16, 2026
b664a91
Align NPU examples with 8-card CI runners
755651978 Jul 16, 2026
0cadd36
Merge remote-tracking branch 'upstream/main'
755651978 Jul 20, 2026
eef7dff
Fix standalone draft checkpoint lm_head export
755651978 Jul 20, 2026
d43a1a3
Merge branch 'verl-project:main' into main
755651978 Jul 21, 2026
5b76854
Fix: Support sharded standalone lm_head export
755651978 Jul 21, 2026
1a0aaf8
Merge branch 'main' of https://github.com/755651978/verl-SpeCo
755651978 Jul 21, 2026
13f79c6
Add standalone draft training workflow and checkpoint fixes
755651978 Jul 24, 2026
b387234
Merge remote-tracking branch 'upstream/main'
755651978 Jul 24, 2026
09b395e
tq适配
zjuKirito Jul 27, 2026
4eece75
Merge remote-tracking branch 'upstream/main'
755651978 Jul 28, 2026
a83276e
Add token replay for standalone draft training
755651978 Jul 28, 2026
0b7422e
Add vLLM hidden-state replay and linear LR decay
755651978 Jul 29, 2026
e224d57
Fix vLLM hidden-state lock handling for token replay
755651978 Jul 31, 2026
68a46f9
actor hidden states tq 实现
zjuKirito Jul 31, 2026
99d0e40
Support JSONL token replay feature stores
755651978 Jul 31, 2026
3d12c4c
TQ 分支加了 _densify_tq_tensor,把 TQ 取回的 NestedTensor 还原成生产端存进去时的 [rows, h…
zjuKirito Jul 31, 2026
adb8616
修复tq 多次从cpu取hidden的bug,改成取一次,剩余从cache取
zjuKirito Aug 3, 2026
351a952
merge upstream/main: resolve formatting conflict in speco_worker
zjuKirito Aug 3, 2026
08f411d
Merge branch 'verl-project:main' into main
755651978 Aug 3, 2026
9d878c5
Support conversation JSONL token replay for standalone draft training
755651978 Aug 4, 2026
d581744
Merge branch 'main' of https://github.com/755651978/verl-SpeCo
755651978 Aug 4, 2026
c929576
Improve standalone draft token replay training
755651978 Aug 4, 2026
213d915
Merge remote-tracking branch 'upstream/main'
755651978 Aug 5, 2026
e2055ed
Gated Head
755651978 Aug 10, 2026
7dc8450
feat(draft-training): add producer pipeline and Mooncake transfer
755651978 Aug 13, 2026
884df06
Merge branch 'pr-48'
xxyyrr598 Aug 19, 2026
73fe304
Merge remote-tracking branch 'origin/main'
xxyyrr598 Aug 19, 2026
75ac479
Add standalone TransferQueue foundation
xxyyrr598 Aug 19, 2026
c61fa39
Remove internal TQ design documents
xxyyrr598 Aug 19, 2026
7a71151
Add standalone TQ consumer implementation
xxyyrr598 Aug 20, 2026
2e0de5f
Add standalone TQ producer implementation
vx120 Aug 21, 2026
ab15739
feat(tq): remove hardcoded DSPARK algorithm restriction from TQ proto…
xxyyrr598 Aug 21, 2026
35fed68
Merge remote-tracking branch 'origin/main'
vx120 Aug 21, 2026
1351632
feat(tq): complete standalone producer and training workflow
vx120 Aug 21, 2026
4887e8b
feat(tq): support drafter hidden state vllm training and enhance stan…
xxyyrr598 Aug 25, 2026
d72d589
feat(tq): bump drafter sample protocol to schema v2 and add ready tag…
xxyyrr598 Aug 26, 2026
9b1c7ad
Fix standalone EAGLE3 training and checkpoint export compatibility fo…
755651978 Aug 26, 2026
2258be2
feat: improve EAGLE3 standalone training workflow
vx120 Aug 26, 2026
c99ad69
change the version of the tq
vx120 Aug 27, 2026
b6dcd47
refactor(tq): reorganize standalone TQ scripts and fix lint/type checks
xxyyrr598 Aug 27, 2026
a5a76ca
Merge remote-tracking branch 'upstream/main'
755651978 Aug 27, 2026
6f62918
chore: stop tracking dev-only docs to align with upstream
xxyyrr598 Aug 27, 2026
960d672
refactor(tq): drop mooncake transfer path and consolidate dspark sepa…
xxyyrr598 Aug 27, 2026
4295f37
Merge remote-tracking branch 'upstream/main'
xxyyrr598 Aug 28, 2026
b238e36
feat: add standalone resume and TQ producer/consumer updates
xxyyrr598 Sep 1, 2026
92d9432
remove local personal planning docs from tracking
xxyyrr598 Sep 1, 2026
c3faa46
style: apply ruff format to standalone_resume.py
xxyyrr598 Sep 1, 2026
9fa82c0
Merge remote-tracking branch 'upstream/main'
xxyyrr598 Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,19 +300,23 @@ actor_rollout_ref.rollout.drafter.speculative_algorithm=EAGLE3

## Separate Draft Model Training

verl-SpeCo also supports a separate draft model training workflow. In this
mode, rollout workers collect drafter training features into a feature store,
and the draft model can be trained separately after feature collection.
verl-SpeCo also supports standalone DSpark draft-model training from a finite
verl-style prompt Parquet or prompt/response JSONL/Parquet file. For prompt-only
rows, a producer asks the target vLLM service to generate the response while
extracting prompt/output hidden states. It transfers each global batch through
TransferQueue, and a consumer trains the drafter independently of PPO.

Quickstart:

```bash
bash examples/run_qwen3-8b_drafter_separate_training.sh
bash examples/run_qwen3-8b_drafter_dspark_separate_training.sh
```

Replace the model, drafter, dataset, feature-store, and checkpoint paths in
the script before running it. The script uses `collect_only` mode for rollout
feature collection and `offline` mode for standalone drafter training.
Set the same model, dataset, drafter, checkpoint, GPU, and optimization values
used by ordinary standalone training near the top of the script. Transport
identity, Ray/TQ connection settings, and the Producer/Consumer lifecycle are
derived and managed internally. The target hidden-state vLLM service uses the
local port 8000 convention.

The main mode values are:

Expand Down
165 changes: 165 additions & 0 deletions examples/run_qwen3-8b_drafter_dspark_separate_training.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#!/usr/bin/env bash
# Copyright 2026 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -euo pipefail
set -x

script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
repo_root=$(cd -- "${script_dir}/.." && pwd)
cd "${repo_root}"

# Standalone DSpark draft-model training using an already-running hidden-state
# vLLM. Start tools/run_qwen3-8b_drafter_hidden_state_vllm.sh in another terminal
# first. This process owns Ray/TQ, Producer and Consumer, but it must not own
# the target vLLM so inference and training can use different accelerators.

project_name=${PROJECT_NAME:-verl_dspark_drafter}
exp_name=${EXP_NAME:-qwen3_8b_dspark_separate_training}

draft_train_gpus_per_node=${TRAIN_GPUS:-2}

MODEL_PATH=${MODEL_PATH:-/path/to/Qwen3-8B}
# Ordinary verl prompt Parquet is supported; target vLLM generates responses.
TRAIN_FILE=${TRAIN_FILE:-/path/to/train_file.parquet}
# Optional. Leave empty to initialize DSpark from the target-model/config
# fallback; set it only when loading or resuming an existing drafter.
DRAFTER_PATH=${DRAFTER_PATH:-}
DRAFT_CKPTS_DIR=${DRAFT_CKPTS_DIR:-/path/to/dspark_draft_checkpoints}

PYTHON_BIN=${PYTHON_BIN:-python3}
DEVICE_ENV=${DEVICE_ENV:-ASCEND_RT_VISIBLE_DEVICES}
TRAIN_DEVICES=${TRAIN_DEVICES:-2,3}
SPECO_VLLM_ENDPOINTS=${SPECO_VLLM_ENDPOINTS:-'[http://127.0.0.1:8000/v1,http://127.0.0.1:8001/v1]'}
VLLM_READY_TIMEOUT_SECONDS=${VLLM_READY_TIMEOUT_SECONDS:-120}

# Producer -> vLLM concurrency and bounded queues. MAX_INFLIGHT_REQUESTS is the
# process-wide request limit; PER_ENDPOINT_CONCURRENCY applies independently to
# every URL in SPECO_VLLM_ENDPOINTS.
VLLM_REQUEST_TIMEOUT=${VLLM_REQUEST_TIMEOUT:-120}
VLLM_MAX_INFLIGHT_REQUESTS=${VLLM_MAX_INFLIGHT_REQUESTS:-16}
VLLM_PER_ENDPOINT_CONCURRENCY=${VLLM_PER_ENDPOINT_CONCURRENCY:-4}
PRODUCER_INPUT_QUEUE_SIZE=${PRODUCER_INPUT_QUEUE_SIZE:-32}
PRODUCER_PUBLISH_QUEUE_SIZE=${PRODUCER_PUBLISH_QUEUE_SIZE:-16}
PRODUCER_MAX_PENDING_SAMPLES=${PRODUCER_MAX_PENDING_SAMPLES:-1024}
PRODUCER_PENDING_POLL_INTERVAL=${PRODUCER_PENDING_POLL_INTERVAL:-0.5}
PRODUCER_MAX_SEQUENCE_LENGTH=${PRODUCER_MAX_SEQUENCE_LENGTH:-8192}
PRODUCER_MAX_FEATURE_LENGTH=${PRODUCER_MAX_FEATURE_LENGTH:-512}
PRODUCER_GENERATION_MAX_TOKENS=${PRODUCER_GENERATION_MAX_TOKENS:-512}

# Standalone trainer.
MAX_STEPS=${MAX_STEPS:-10}
SAVE_INTERVAL_STEPS=${SAVE_INTERVAL_STEPS:-5}
SAVE_FINAL_CHECKPOINT=${SAVE_FINAL_CHECKPOINT:-true}
BATCH_SIZE_PER_GPU=${BATCH_SIZE_PER_GPU:-2}
LEARNING_RATE=${LEARNING_RATE:-1e-6}
LR_WARMUP_STEPS=${LR_WARMUP_STEPS:-0}
LR_SCHEDULER_TYPE=${LR_SCHEDULER_TYPE:-constant}
LR_DECAY_STEPS=${LR_DECAY_STEPS:-100}
MIN_LR_RATIO=${MIN_LR_RATIO:-0.1}
PARAM_OFFLOAD=${PARAM_OFFLOAD:-true}
OPTIMIZER_OFFLOAD=${OPTIMIZER_OFFLOAD:-true}

# DSpark architecture, sampling and losses. TARGET_LAYER_IDS must match the
# auxiliary layers exposed by both hidden-state vLLM services.
DSPARK_BLOCK_SIZE=${DSPARK_BLOCK_SIZE:-7}
DSPARK_NUM_ANCHORS=${DSPARK_NUM_ANCHORS:-32}
DSPARK_MAX_WINDOW=${DSPARK_MAX_WINDOW:-512}
DSPARK_LOSS_MODE=${DSPARK_LOSS_MODE:-full_vocab}
DSPARK_SAMPLED_CE_NEGATIVES=${DSPARK_SAMPLED_CE_NEGATIVES:-0}
DSPARK_LOSS_DECAY_GAMMA=${DSPARK_LOSS_DECAY_GAMMA:-7}
DSPARK_NUM_TARGET_LAYERS=${DSPARK_NUM_TARGET_LAYERS:-5}
DSPARK_NUM_HIDDEN_LAYERS=${DSPARK_NUM_HIDDEN_LAYERS:-5}
DSPARK_TARGET_LAYER_IDS=${DSPARK_TARGET_LAYER_IDS:-'[1,9,17,25,33]'}
DSPARK_MARKOV_RANK=${DSPARK_MARKOV_RANK:-256}
DSPARK_MARKOV_HEAD_TYPE=${DSPARK_MARKOV_HEAD_TYPE:-vanilla}
DSPARK_CE_LOSS_ALPHA=${DSPARK_CE_LOSS_ALPHA:-0.1}
DSPARK_L1_LOSS_ALPHA=${DSPARK_L1_LOSS_ALPHA:-0.45}
DSPARK_L1_CHUNK_SIZE=${DSPARK_L1_CHUNK_SIZE:-0}
# The current DSpark trainer rejects nonzero confidence loss because target
# acceptance labels are not part of the standalone feature protocol yet.
DSPARK_CONFIDENCE_LOSS_ALPHA=${DSPARK_CONFIDENCE_LOSS_ALPHA:-0.0}
DSPARK_DEBUG_LOG=${DSPARK_DEBUG_LOG:-false}
DSPARK_DEBUG_LOG_FIRST_N=${DSPARK_DEBUG_LOG_FIRST_N:-2}
DSPARK_DEBUG_LOG_INTERVAL=${DSPARK_DEBUG_LOG_INTERVAL:-100}

export "${DEVICE_ENV}=${TRAIN_DEVICES}"
export SPECO_VLLM_ENDPOINTS

# Fail before entering the unified launcher when the separately managed vLLM
# is absent. Otherwise a localhost endpoint would make the launcher start its
# fallback vLLM inside the training process and on the training devices.
if ! "${PYTHON_BIN}" tools/wait_for_vllm_endpoints.py \
--endpoints "${SPECO_VLLM_ENDPOINTS}" \
--timeout-seconds "${VLLM_READY_TIMEOUT_SECONDS}"; then
echo "Start tools/run_qwen3-8b_drafter_hidden_state_vllm.sh first" >&2
exit 1
fi

PYTHONUNBUFFERED=1 "${PYTHON_BIN}" -m verl_speco.standalone_tq_training_launcher \
speco.draft_training.num_gpus_per_node=${draft_train_gpus_per_node} \
speco.draft_training.nnodes=1 \
speco.draft_training.standalone=True \
data.train_files=${TRAIN_FILE} \
actor_rollout_ref.model.path=${MODEL_PATH} \
actor_rollout_ref.actor.strategy=fsdp2 \
actor_rollout_ref.actor.fsdp_config.param_offload=${PARAM_OFFLOAD} \
actor_rollout_ref.actor.fsdp_config.optimizer_offload=${OPTIMIZER_OFFLOAD} \
actor_rollout_ref.rollout.tensor_model_parallel_size=1 \
actor_rollout_ref.rollout.drafter.enable=True \
actor_rollout_ref.rollout.drafter.enable_drafter_training=True \
actor_rollout_ref.rollout.drafter.model_path=${DRAFTER_PATH} \
actor_rollout_ref.rollout.drafter.checkpoint_path=${DRAFT_CKPTS_DIR} \
actor_rollout_ref.rollout.drafter.speculative_algorithm=DSPARK \
actor_rollout_ref.rollout.drafter.training.mode=offline \
actor_rollout_ref.rollout.drafter.training.max_steps=${MAX_STEPS} \
actor_rollout_ref.rollout.drafter.training.save_interval_steps=${SAVE_INTERVAL_STEPS} \
actor_rollout_ref.rollout.drafter.training.save_final_checkpoint=${SAVE_FINAL_CHECKPOINT} \
actor_rollout_ref.rollout.drafter.training.batch_size_per_gpu=${BATCH_SIZE_PER_GPU} \
actor_rollout_ref.rollout.drafter.training.lr=${LEARNING_RATE} \
actor_rollout_ref.rollout.drafter.training.lr_warmup_steps=${LR_WARMUP_STEPS} \
actor_rollout_ref.rollout.drafter.training.lr_scheduler_type=${LR_SCHEDULER_TYPE} \
actor_rollout_ref.rollout.drafter.training.lr_decay_steps=${LR_DECAY_STEPS} \
actor_rollout_ref.rollout.drafter.training.min_lr_ratio=${MIN_LR_RATIO} \
actor_rollout_ref.rollout.drafter.training.use_logits=False \
actor_rollout_ref.rollout.drafter.training.dspark_block_size=${DSPARK_BLOCK_SIZE} \
actor_rollout_ref.rollout.drafter.training.dspark_num_anchors=${DSPARK_NUM_ANCHORS} \
actor_rollout_ref.rollout.drafter.training.dspark_max_window=${DSPARK_MAX_WINDOW} \
actor_rollout_ref.rollout.drafter.training.dspark_loss_mode=${DSPARK_LOSS_MODE} \
actor_rollout_ref.rollout.drafter.training.dspark_sampled_ce_negatives=${DSPARK_SAMPLED_CE_NEGATIVES} \
actor_rollout_ref.rollout.drafter.training.dspark_loss_decay_gamma=${DSPARK_LOSS_DECAY_GAMMA} \
actor_rollout_ref.rollout.drafter.training.dspark_num_target_layers=${DSPARK_NUM_TARGET_LAYERS} \
actor_rollout_ref.rollout.drafter.training.dspark_num_hidden_layers=${DSPARK_NUM_HIDDEN_LAYERS} \
actor_rollout_ref.rollout.drafter.training.dspark_target_layer_ids=${DSPARK_TARGET_LAYER_IDS} \
actor_rollout_ref.rollout.drafter.training.dspark_markov_rank=${DSPARK_MARKOV_RANK} \
actor_rollout_ref.rollout.drafter.training.dspark_markov_head_type=${DSPARK_MARKOV_HEAD_TYPE} \
actor_rollout_ref.rollout.drafter.training.dspark_ce_loss_alpha=${DSPARK_CE_LOSS_ALPHA} \
actor_rollout_ref.rollout.drafter.training.dspark_l1_loss_alpha=${DSPARK_L1_LOSS_ALPHA} \
actor_rollout_ref.rollout.drafter.training.dspark_l1_chunk_size=${DSPARK_L1_CHUNK_SIZE} \
actor_rollout_ref.rollout.drafter.training.dspark_confidence_loss_alpha=${DSPARK_CONFIDENCE_LOSS_ALPHA} \
actor_rollout_ref.rollout.drafter.training.dspark_debug_log=${DSPARK_DEBUG_LOG} \
actor_rollout_ref.rollout.drafter.training.dspark_debug_log_first_n=${DSPARK_DEBUG_LOG_FIRST_N} \
actor_rollout_ref.rollout.drafter.training.dspark_debug_log_interval=${DSPARK_DEBUG_LOG_INTERVAL} \
speco.standalone_tq_producer.request_timeout=${VLLM_REQUEST_TIMEOUT} \
speco.standalone_tq_producer.max_inflight_requests=${VLLM_MAX_INFLIGHT_REQUESTS} \
speco.standalone_tq_producer.per_endpoint_concurrency=${VLLM_PER_ENDPOINT_CONCURRENCY} \
speco.standalone_tq_producer.input_queue_size=${PRODUCER_INPUT_QUEUE_SIZE} \
speco.standalone_tq_producer.publish_queue_size=${PRODUCER_PUBLISH_QUEUE_SIZE} \
speco.standalone_tq_producer.max_pending_samples=${PRODUCER_MAX_PENDING_SAMPLES} \
speco.standalone_tq_producer.pending_poll_interval_seconds=${PRODUCER_PENDING_POLL_INTERVAL} \
speco.standalone_tq_producer.max_sequence_length=${PRODUCER_MAX_SEQUENCE_LENGTH} \
speco.standalone_tq_producer.max_feature_length=${PRODUCER_MAX_FEATURE_LENGTH} \
speco.standalone_tq_producer.generation_max_tokens=${PRODUCER_GENERATION_MAX_TOKENS} \
trainer.project_name=${project_name} \
trainer.experiment_name=${exp_name} \
"$@"
164 changes: 164 additions & 0 deletions examples/run_qwen3-8b_drafter_eagle3_separate_training.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#!/usr/bin/env bash
# Copyright 2026 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -euo pipefail
set -x

# Standalone EAGLE3 drafter training. Start
# examples/run_qwen3-8b_drafter_hidden_state_vllm.sh in another terminal first.
# The target-model vLLM and the drafter trainer can therefore use disjoint GPUs.
#
# EAGLE3 can initialize its drafter structure from the target model config, so
# no pre-initialized drafter directory is required. Set model_path only when
# loading an existing drafter checkpoint/config is desired.
#
# The vLLM hidden-state layer IDs must be EAGLE3_TARGET_LAYER_IDS followed by
# the target model's final layer. For Qwen3-8B the default is:
# [1,9,17,25,33,36]
# The EAGLE3 drafter config must have the same number (five) of aux states.

project_name=${PROJECT_NAME:-verl_eagle3_drafter}
exp_name=${EXP_NAME:-qwen3_8b_eagle3_separate_training}

draft_train_gpus_per_node=${TRAIN_GPUS:-2}
MODEL_PATH=${MODEL_PATH:-/path/to/Qwen3-4B}
TRAIN_FILE=${TRAIN_FILE:-/path/to/data}
DRAFT_CKPTS_DIR=${DRAFT_CKPTS_DIR:-/path/to/ckpt}

PYTHON_BIN=${PYTHON_BIN:-python3}
DEVICE_ENV=${DEVICE_ENV:-CUDA_VISIBLE_DEVICES}
TRAIN_DEVICES=${TRAIN_DEVICES:-6,7}
SPECO_VLLM_ENDPOINTS=${SPECO_VLLM_ENDPOINTS:-'[http://127.0.0.1:8000/v1]'}
VLLM_READY_TIMEOUT_SECONDS=${VLLM_READY_TIMEOUT_SECONDS:-120}

# These IDs must equal the auxiliary prefix of VLLM_HIDDEN_STATE_LAYER_IDS in
# run_qwen3-8b_drafter_hidden_state_vllm.sh. Do not include the final layer.
EAGLE3_TARGET_LAYER_IDS=${EAGLE3_TARGET_LAYER_IDS:-'[1,9,17,25,33]'}

# Producer throughput and bounded queues.
VLLM_REQUEST_TIMEOUT=${VLLM_REQUEST_TIMEOUT:-120}
VLLM_MAX_INFLIGHT_REQUESTS=${VLLM_MAX_INFLIGHT_REQUESTS:-16}
VLLM_PER_ENDPOINT_CONCURRENCY=${VLLM_PER_ENDPOINT_CONCURRENCY:-4}
PRODUCER_INPUT_QUEUE_SIZE=${PRODUCER_INPUT_QUEUE_SIZE:-32}
PRODUCER_PUBLISH_QUEUE_SIZE=${PRODUCER_PUBLISH_QUEUE_SIZE:-16}
PRODUCER_MAX_PENDING_SAMPLES=${PRODUCER_MAX_PENDING_SAMPLES:-1024}
PRODUCER_PENDING_POLL_INTERVAL=${PRODUCER_PENDING_POLL_INTERVAL:-0.5}
PRODUCER_MAX_SEQUENCE_LENGTH=${PRODUCER_MAX_SEQUENCE_LENGTH:-8192}
PRODUCER_MAX_FEATURE_LENGTH=${PRODUCER_MAX_FEATURE_LENGTH:-512}
PRODUCER_GENERATION_MAX_TOKENS=${PRODUCER_GENERATION_MAX_TOKENS:-511}

# Standalone EAGLE3 trainer settings.
MAX_STEPS=${MAX_STEPS:-1000}
SAVE_INTERVAL_STEPS=${SAVE_INTERVAL_STEPS:-100}
SAVE_FINAL_CHECKPOINT=${SAVE_FINAL_CHECKPOINT:-true}
BATCH_SIZE_PER_GPU=${BATCH_SIZE_PER_GPU:-2}
LEARNING_RATE=${LEARNING_RATE:-1e-5}
LR_WARMUP_STEPS=${LR_WARMUP_STEPS:-0}
LR_SCHEDULER_TYPE=${LR_SCHEDULER_TYPE:-constant}
LR_DECAY_STEPS=${LR_DECAY_STEPS:-1000}
MIN_LR_RATIO=${MIN_LR_RATIO:-0.1}
PARAM_OFFLOAD=${PARAM_OFFLOAD:-true}
OPTIMIZER_OFFLOAD=${OPTIMIZER_OFFLOAD:-true}

if [[ "${MODEL_PATH}" == /path/to/* || "${TRAIN_FILE}" == /path/to/* ]]; then
echo "Set MODEL_PATH and TRAIN_FILE before starting training." >&2
exit 2
fi

export "${DEVICE_ENV}=${TRAIN_DEVICES}"
export SPECO_VLLM_ENDPOINTS

# Avoid the launcher's localhost fallback vLLM: this job must consume the
# separately managed hidden-state services, which keep target inference off the
# training devices.
"${PYTHON_BIN}" - "${SPECO_VLLM_ENDPOINTS}" "${VLLM_READY_TIMEOUT_SECONDS}" <<'PY'
import sys
import time
from urllib.error import URLError
from urllib.request import urlopen

raw_endpoints = sys.argv[1].strip()
if not (raw_endpoints.startswith("[") and raw_endpoints.endswith("]")):
raise SystemExit("SPECO_VLLM_ENDPOINTS must use [url0,url1] syntax")
endpoints = [
item.strip().strip("'\"").rstrip("/")
for item in raw_endpoints[1:-1].split(",")
if item.strip()
]
if not endpoints:
raise SystemExit("SPECO_VLLM_ENDPOINTS must contain at least one URL")
deadline = time.monotonic() + float(sys.argv[2])
pending = set(endpoints)
while pending:
for endpoint in list(pending):
try:
with urlopen(f"{endpoint}/models", timeout=2) as response:
if 200 <= response.status < 300:
print(f"EXTERNAL_VLLM_READY endpoint={endpoint}", flush=True)
pending.remove(endpoint)
except (OSError, URLError):
pass
if pending and time.monotonic() >= deadline:
raise SystemExit(
"external hidden-state vLLM is not ready at: "
+ ", ".join(sorted(pending))
+ "; start examples/run_qwen3-8b_drafter_hidden_state_vllm.sh first"
)
if pending:
time.sleep(1)
PY

PYTHONUNBUFFERED=1 "${PYTHON_BIN}" -m verl_speco.standalone_tq_training_launcher \
speco.draft_training.num_gpus_per_node=${draft_train_gpus_per_node} \
speco.draft_training.nnodes=1 \
speco.draft_training.standalone=True \
data.train_files=${TRAIN_FILE} \
actor_rollout_ref.model.path=${MODEL_PATH} \
actor_rollout_ref.actor.strategy=fsdp2 \
actor_rollout_ref.actor.fsdp_config.param_offload=${PARAM_OFFLOAD} \
actor_rollout_ref.actor.fsdp_config.optimizer_offload=${OPTIMIZER_OFFLOAD} \
actor_rollout_ref.rollout.tensor_model_parallel_size=1 \
actor_rollout_ref.rollout.drafter.enable=true \
actor_rollout_ref.rollout.drafter.enable_drafter_training=true \
actor_rollout_ref.rollout.drafter.checkpoint_path=${DRAFT_CKPTS_DIR} \
actor_rollout_ref.rollout.drafter.speculative_algorithm=EAGLE3 \
actor_rollout_ref.rollout.drafter.rollout.spec_steps=3 \
actor_rollout_ref.rollout.drafter.rollout.spec_topk=1 \
actor_rollout_ref.rollout.drafter.rollout.spec_verify_tokens=4 \
actor_rollout_ref.rollout.drafter.training.mode=offline \
actor_rollout_ref.rollout.drafter.training.max_steps=${MAX_STEPS} \
actor_rollout_ref.rollout.drafter.training.save_interval_steps=${SAVE_INTERVAL_STEPS} \
actor_rollout_ref.rollout.drafter.training.save_final_checkpoint=${SAVE_FINAL_CHECKPOINT} \
actor_rollout_ref.rollout.drafter.training.batch_size_per_gpu=${BATCH_SIZE_PER_GPU} \
actor_rollout_ref.rollout.drafter.training.lr=${LEARNING_RATE} \
actor_rollout_ref.rollout.drafter.training.lr_warmup_steps=${LR_WARMUP_STEPS} \
actor_rollout_ref.rollout.drafter.training.lr_scheduler_type=${LR_SCHEDULER_TYPE} \
actor_rollout_ref.rollout.drafter.training.lr_decay_steps=${LR_DECAY_STEPS} \
actor_rollout_ref.rollout.drafter.training.min_lr_ratio=${MIN_LR_RATIO} \
actor_rollout_ref.rollout.drafter.training.use_logits=false \
actor_rollout_ref.rollout.drafter.training.eagle3_target_layer_ids=${EAGLE3_TARGET_LAYER_IDS} \
speco.standalone_tq_producer.target_layer_ids=${EAGLE3_TARGET_LAYER_IDS} \
speco.standalone_tq_producer.request_timeout=${VLLM_REQUEST_TIMEOUT} \
speco.standalone_tq_producer.max_inflight_requests=${VLLM_MAX_INFLIGHT_REQUESTS} \
speco.standalone_tq_producer.per_endpoint_concurrency=${VLLM_PER_ENDPOINT_CONCURRENCY} \
speco.standalone_tq_producer.input_queue_size=${PRODUCER_INPUT_QUEUE_SIZE} \
speco.standalone_tq_producer.publish_queue_size=${PRODUCER_PUBLISH_QUEUE_SIZE} \
speco.standalone_tq_producer.max_pending_samples=${PRODUCER_MAX_PENDING_SAMPLES} \
speco.standalone_tq_producer.pending_poll_interval_seconds=${PRODUCER_PENDING_POLL_INTERVAL} \
speco.standalone_tq_producer.max_sequence_length=${PRODUCER_MAX_SEQUENCE_LENGTH} \
speco.standalone_tq_producer.max_feature_length=${PRODUCER_MAX_FEATURE_LENGTH} \
speco.standalone_tq_producer.generation_max_tokens=${PRODUCER_GENERATION_MAX_TOKENS} \
trainer.project_name=${project_name} \
trainer.experiment_name=${exp_name} \
"$@"
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ dependencies = [
"packaging>=24",
]

[project.optional-dependencies]
transfer-queue = ["TransferQueue==0.1.10"]

[project.urls]
Repository = "https://github.com/verl-project/verl-SpeCo"

[project.scripts]
verl-speco = "verl_speco.main:main"
verl-speco-draft-train = "verl_speco.draft_train_launcher:main"
verl-speco-inspect-features = "verl_speco.inspect_feature_store:main"
verl-speco-tq-owner = "verl_speco.tq_owner:main"
verl-speco-tq-producer = "verl_speco.standalone_tq_producer:main"

[tool.setuptools.dynamic]
version = { attr = "verl_speco.__version__" }
Expand Down
Loading
Loading