Skip to content

examples: add Qwen3-8B streaming DSpark launcher example + walkthrough - #2164

Open
h-guo18 wants to merge 2 commits into
mainfrom
haoguo/qwen3-8b-dspark-streaming-example
Open

examples: add Qwen3-8B streaming DSpark launcher example + walkthrough#2164
h-guo18 wants to merge 2 commits into
mainfrom
haoguo/qwen3-8b-dspark-streaming-example

Conversation

@h-guo18

@h-guo18 h-guo18 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Type of change: new example

Adds the missing Qwen3-8B streaming DSpark launcher example, plus an end-to-end walkthrough doc covering data synthesis → streaming training → evaluation.

DSpark examples so far only covered large MoE targets (Kimi-K2.6, MiniMax-M3 — both multi-node, 8 GPU/node). There was no small-scale entry point, so anyone new to the pipeline had to start from a config tuned for a very large warm-start run. This one runs on 2 nodes × 1 GPU, which makes it practical to exercise the whole flow before scaling up.

Two files, both additive — no existing behavior changes:

  • tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml — combines the MiniMax-M3 DSpark streaming settings with the Qwen3-8B DFlash streaming topology. Notably it does not copy M3's per_device_train_batch_size=4 / learning_rate=1e-4 / warmup_steps=2000: those assume 8 GPUs per node and a warm-started backbone, and at training_seq_len=4096 on one GPU a batch of 4 will OOM. Batch/LR stay at the dspark.yaml defaults, matching the sibling DFlash example.

  • examples/speculative_decoding/QWEN3_8B_DSPARK_WALKTHROUGH.md — the walkthrough. Covers why synthesis matters for acceptance length, the fields that actually matter, a Qwen3-8B-vs-large-MoE scaling table, and the failure modes that are silent rather than loud (capture-id off-by-one, non-inherited draft dims, synthesis yield loss, prompt-only-corpus hang).

Usage

cd tools/launcher

# Step 1 — data synthesis (regenerate assistant turns with the target model)
uv run launch.py --yaml examples/Qwen/Qwen3-8B/hf_synth.yaml --yes

# Step 2 — streaming DSpark training (+ export + vLLM smoke test)
uv run launch.py --yaml examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml --yes

Testing

  • YAML parses and resolves; task topology, arg list and env block verified against common/eagle3/train_eagle_streaming.sh (every env knob used here is one the script actually reads).
  • Pre-commit hooks applicable to these files pass: yamlfmt, check-launcher-yaml (validates the recipe/template references resolve), markdownlint-cli2, insert-license.
  • All doc cross-links verified to resolve.

Not yet run as a live Slurm job — worth a --dryrun on a login node before merge. Flagging that rather than implying end-to-end validation.

Before your PR is "Ready for review"

  • Is this change backward compatible?: ✅ (additive only — two new files, nothing existing modified)
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: N/A
  • Did you write any new necessary tests?: N/A (launcher example YAML + docs; covered by the existing check-launcher-yaml hook)
  • Did you update Changelog?: N/A
  • Did you get Claude approval on this PR?: ❌ (not yet run)

Note on trust_remote_code (re: Security Best Practices): EXPORT_EXTRA_ARGS: "--trust_remote_code" is set in task_1. This is required because DSpark exports a custom modeling file that the exporter must load — it is not applied to the base model, which is stock Qwen3. This matches the existing DFlash/DSpark examples already on main.

Additional Information

The synthesis step uses tools/launcher/common/query.py, already in-tree — no external tooling needed.

Summary by CodeRabbit

  • New Features
    • Added an end-to-end Qwen3-8B speculative decoding training pipeline.
    • Added streaming target-model data transfer and checkpoint export workflows.
    • Added a smoke test for validating exported checkpoints with vLLM.
  • Documentation
    • Added setup instructions covering prerequisites, configuration, scaling, evaluation, networking, and troubleshooting.
    • Documented launcher commands, dataset settings, capture configuration, and runtime requirements.

Adds the missing Qwen3-8B streaming DSpark pipeline. DSpark examples so far
covered only large MoE targets (Kimi-K2.6, MiniMax-M3, both multi-node); this
is the small-scale entry point that runs on 2 nodes x 1 GPU, so the pipeline
can be exercised end-to-end before scaling up.

- tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml
  Combines the MiniMax-M3 DSpark streaming settings with the Qwen3-8B DFlash
  streaming topology. Keeps batch size and LR at the dspark.yaml defaults
  rather than the M3 warm-start values, which assume 8 GPUs/node.

- examples/speculative_decoding/QWEN3_8B_DSPARK_WALKTHROUGH.md
  End-to-end handoff doc: data synthesis (hf_synth.yaml) -> streaming
  training -> evaluation, plus the failure modes that are silent (capture-id
  off-by-one, non-inherited draft dims, synthesis yield loss).

Pre-commit hooks applicable to these two files were run and pass
(yamlfmt, check-launcher-yaml, markdownlint-cli2, insert-license).

Signed-off-by: h-guo18 <67671475+h-guo18@users.noreply.github.com>
@h-guo18
h-guo18 requested review from a team as code owners August 12, 2026 11:40
@h-guo18
h-guo18 requested a review from kevalmorabia97 August 12, 2026 11:40
@copy-pr-bot

copy-pr-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: dcf91b7b-7aad-4739-a1c4-909876fd5f30

📥 Commits

Reviewing files that changed from the base of the PR and between cd510e0 and 6e52baf.

📒 Files selected for processing (2)
  • examples/speculative_decoding/QWEN3_8B_DSPARK_WALKTHROUGH.md
  • tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml
🚧 Files skipped from review as they are similar to previous changes (2)
  • tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml
  • examples/speculative_decoding/QWEN3_8B_DSPARK_WALKTHROUGH.md

📝 Walkthrough

Walkthrough

The PR adds a Qwen3-8B DSpark walkthrough and launcher pipeline. It covers target-data synthesis, streaming hidden-state training, configuration, scaling, exported-drafter evaluation, smoke testing, and troubleshooting.

Changes

Qwen3-8B DSpark workflow

Layer / File(s) Summary
Pipeline prerequisites and data synthesis
examples/speculative_decoding/QWEN3_8B_DSPARK_WALKTHROUGH.md, tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml
The walkthrough documents prerequisites and target-data synthesis. The launcher configuration adds shared metadata and a one-node dataset-generation task.
Streaming DSpark training
examples/speculative_decoding/QWEN3_8B_DSPARK_WALKTHROUGH.md, tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml
The pipeline adds two-node streaming training with DSpark loss, capture-layer, draft-model, export, and optional RDMA settings. The walkthrough documents overrides, scaling, and runtime constraints.
Export evaluation and troubleshooting
examples/speculative_decoding/QWEN3_8B_DSPARK_WALKTHROUGH.md, tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml
The configuration adds a vLLM DSpark smoke test. The walkthrough describes acceptance-length evaluation, fallback benchmarking, and common runtime failures.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: chenhanyu, yeyu-nvidia

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main changes: a Qwen3-8B streaming DSpark launcher example and walkthrough.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Anti-Patterns ✅ Passed The PR adds only Markdown and YAML; no Python or dependency changes, and its export flag uses an existing caller-configurable --trust_remote_code option that defaults to false.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch haoguo/qwen3-8b-dspark-streaming-example

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml`:
- Around line 134-136: In hf_streaming_dspark.yaml, replace the mutable image
tags for both vLLM containers at lines 134-136 and 142-156 with immutable image
digests. Preserve a digest that includes the auxiliary-capture fix from vLLM PR
`#46788` for task_1 and a digest providing DSpark support for task_2; update both
container declarations accordingly.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f3865e82-c88d-4575-91d7-a736fab26b97

📥 Commits

Reviewing files that changed from the base of the PR and between a21173a and cd510e0.

📒 Files selected for processing (2)
  • examples/speculative_decoding/QWEN3_8B_DSPARK_WALKTHROUGH.md
  • tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml

Comment on lines +134 to +136
# Needs the aux-capture fix (vllm#46788), in-tree in recent nightlies;
# without it the final capture id is off by one and caps acceptance length.
container: vllm/vllm-openai:latest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Record the merge commit required by the training task.
curl -fsSL https://api.github.com/repos/vllm-project/vllm/pulls/46788 |
  jq '{state, merged_at, merge_commit_sha: .merge_commit_sha.sha}'

# Record the current mutable-tag digests before selecting immutable replacements.
token="$(
  curl -fsSL \
    'https://auth.docker.io/token?service=registry.docker.io&scope=repository:vllm/vllm-openai:pull' |
    jq -r '.token'
)"

for tag in latest nightly; do
  echo "vllm/vllm-openai:${tag}"
  curl -fsSI \
    -H "Authorization: Bearer ${token}" \
    -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
    "https://registry-1.docker.io/v2/vllm/vllm-openai/manifests/${tag}" |
    tr -d '\r' |
    awk 'tolower($1) == "docker-content-digest:" { print $2 }'
done

Repository: NVIDIA/Model-Optimizer

Length of output: 211


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml"
sed -n '120,165p' "$file"

printf '\nAll vLLM image references in the file:\n'
rg -n 'container:|vllm|speculative|dspark|46788' "$file"

Repository: NVIDIA/Model-Optimizer

Length of output: 3486


🌐 Web query:

vLLM pull request 46788 auxiliary capture DSpark speculative decoding vllm-openai Docker image tags

💡 Result:

The information regarding the items in your query is synthesized below based on recent vLLM developments: Pull Request #46788 Pull request #46788 in the vllm-project/vllm repository is titled "benchmark refactor" [1]. It is a development-related PR focused on infrastructure improvements rather than the DSpark speculative decoding feature [1]. DSpark Speculative Decoding and Auxiliary Capture DSpark is a state-of-the-art parallel drafting algorithm recently integrated into vLLM [2]. Key details include: - Functionality: DSpark builds upon a DFlash backbone by adding a lightweight autoregressive correction head and a confidence estimator to selectively forward tokens, improving verification efficiency [2]. - Implementation: It utilizes non-causal sliding-window attention by repurposing SparseMLA backends [3]. - Auxiliary Capture: DSpark requires capturing auxiliary hidden states from the target model's layers to inform the drafter [4][5]. While initial implementations were restricted to specific configurations (e.g., pipeline parallel size 1) [4], recent efforts have focused on enabling "aux-state forwarding" to support speculative decoding under pipeline parallelism [5]. vLLM-OpenAI Docker Image Tags vLLM does not maintain a specific "vllm-openai" Docker image tag. Official vLLM Docker images are typically hosted on platforms like Docker Hub (under vllm/vllm). The OpenAI-compatible API server is a core component of the standard vLLM image, enabled by default when running the vLLM entrypoint (e.g., vllm.entrypoints.openai.api_server) [2]. Users should refer to the official vLLM documentation or the vLLM Docker Hub repository for the latest versioned tags (e.g., latest, 0.x.x). For tracking specific features across releases, you can use the official vLLM PR release lookup tool at https://vllm.ai/pr-lookup [6].

Citations:


🌐 Web query:

site:docs.vllm.ai DSpark speculative decoding vLLM Docker image nightly

💡 Result:

DSpark is a speculative decoding algorithm supported by the vLLM ecosystem [1][2]. It is part of the "Speculators" project—a dedicated component within the vLLM ecosystem for speculative decoding algorithms—rather than a feature directly embedded in the main vLLM core engine's base configuration in the same manner as methods like MTP or EAGLE [3][1][2]. There is no specific "DSpark nightly" Docker image. Instead, vLLM provides official nightly Docker images, such as vllm/vllm-openai:nightly, which contain the latest development branch of the vLLM codebase [4]. Because DSpark is managed within the broader vLLM speculative decoding infrastructure (specifically the speculators project), users testing DSpark typically use the latest vLLM nightly builds to ensure they have the most recent implementation, features, and fixes [4][5]. To use DSpark or other speculative decoding methods, you should: 1. Pull the standard vLLM nightly Docker image: docker pull vllm/vllm-openai:nightly [4]. 2. Refer to the vLLM speculative decoding documentation to configure your serving parameters [6][7]. 3. Ensure your environment meets the requirements for the specific algorithm, as DSpark and other speculative methods are actively evolving [1][2]. If you are developing or testing specific experimental features from the speculators project, you may need to build the Docker image from source using the vLLM Dockerfile to include custom dependencies or specific commits if they have not yet been merged into the main nightly release [4][5][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for pr in 46788 46965 46995 50514; do
  url="https://github.com/vllm-project/vllm/pull/$pr"
  printf '\nPR %s:\n' "$pr"
  curl -LfsS -A 'Mozilla/5.0' "$url" |
    tr '\n' ' ' |
    sed -n 's/.*<title>\([^<]*\)<\/title>.*/\1/p'
done

printf '\nDocker Hub repository lookup:\n'
curl -LfsS -A 'Mozilla/5.0' \
  'https://hub.docker.com/v2/repositories/vllm/vllm-openai/tags?page_size=5' |
  jq -r '.name // .message // empty' 2>/dev/null || true

Repository: NVIDIA/Model-Optimizer

Length of output: 711


Pin both vLLM containers to immutable image digests.

task_1 requires the auxiliary-capture fix from vLLM PR #46788. task_2 requires DSpark support. The mutable latest and nightly tags can change and cause incorrect capture behavior or startup rejection.

📍 Affects 1 file
  • tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml#L134-L136 (this comment)
  • tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml#L142-L156
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/launcher/examples/Qwen/Qwen3-8B/hf_streaming_dspark.yaml` around lines
134 - 136, In hf_streaming_dspark.yaml, replace the mutable image tags for both
vLLM containers at lines 134-136 and 142-156 with immutable image digests.
Preserve a digest that includes the auxiliary-capture fix from vLLM PR `#46788`
for task_1 and a digest providing DSpark support for task_2; update both
container declarations accordingly.

@kevalmorabia97
kevalmorabia97 requested review from ChenhanYu and yeyu-nvidia and removed request for kevalmorabia97 August 12, 2026 11:48
@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.54%. Comparing base (e4fe1e5) to head (6e52baf).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2164      +/-   ##
==========================================
- Coverage   78.73%   78.54%   -0.20%     
==========================================
  Files         522      522              
  Lines       60357    60692     +335     
==========================================
+ Hits        47523    47670     +147     
- Misses      12834    13022     +188     
Flag Coverage Δ
unit 55.30% <ø> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Validated the walkthrough end-to-end on a Slurm cluster (build -> streaming
train -> export). Training and export work as described; the corrections below
are things that only surface when you actually follow the doc.

Blocking setup gaps:
- HF_TOKEN was unlisted, but the prompt corpus is gated; without it Step 1 dies
  as DatasetNotFoundError, which reads like a wrong dataset name.
- SLURM_HOST=localhost does not work: the launcher stages artifacts over SSH
  even when submitting locally, so sbatch reads from a path nothing populated.
- SLURM_JOB_DIR must pre-exist, and launch.py needs 'uv pip install -e .'
  first; both fail with errors that name neither cause.
- Note the identity=<ssh_key> argument, and the nvcr.io pull failure mode.

Content corrections:
- Synthesis writes shard_{id}.jsonl + a .done sentinel; resume needs both and
  is whole-shard only, so an interrupted shard restarts from zero.
- Added a runtime estimate (hours per shard) - the most useful thing to know
  before launching Step 1.
- The shipped --max-model-len leaves no headroom for this corpus, the exact
  failure the doc warns about; say so and give the grep to size it.
- Capture ids are tied to dflash_architecture_config.num_hidden_layers, not a
  fixed count of 5.
- Dropped dflash_loss_decay_factor: dflash_loss_objective defaults to 'dpace',
  which ignores it outright (the trainer logs a warning saying so).
- data.mode= is inert; streaming is selected by data.streaming_server_url,
  injected at runtime by the launcher script.
- On an older vLLM, task_2 fails as AttributeError on 'hc_mult' from the
  DeepSeek-V4 DSpark loader, not as a clean method-name rejection.
- Corrected the stale prompt-only-messages hang entry: hf_streaming_dataset
  now prefers 'conversations' and skips bad entries rather than stalling.
- Point at /scratchspace/vllm_serve.<n>.log, where serve errors actually land.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: h-guo18 <67671475+h-guo18@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant