[Feature]Routing replay (R3) for vLLM rollout (vLLM v0.21.0) - #34
[Feature]Routing replay (R3) for vLLM rollout (vLLM v0.21.0)#34CalvinXKY wants to merge 2 commits into
Conversation
Wire prompt_routed_experts and choices[].routed_experts in the disagg generate API to match /v1/completions, and apply the patch at Docker build time.
There was a problem hiding this comment.
Code Review
This pull request adds support for exposing and processing MoE routing information (both prompt and generation routed experts) from vLLM 0.21.0. It includes a Dockerfile patch step, updates to the vLLM server launch arguments, and logic to merge prompt and generation routing arrays. The review feedback highlights two key issues: first, the patch utility might not be pre-installed in the Docker base image, which could cause build failures; second, empty routing lists could lead to dimension mismatches or index errors during array concatenation, which can be resolved with safer list-length checks.
| if pre is not None: | ||
| parts.append(_vllm_routed_experts_payload_to_array(pre)) | ||
| if gen is not None: | ||
| parts.append(_vllm_routed_experts_payload_to_array(gen)) | ||
| arr = np.concatenate(parts, axis=0) if len(parts) > 1 else parts[0] |
There was a problem hiding this comment.
If prompt_routed_experts (pre) or routed_experts (gen) is returned as an empty list [] (which can happen if routing is empty or disabled for a segment), _vllm_routed_experts_payload_to_array will return a 1D empty array of shape (0,). Concatenating a 1D array with a 3D array of shape (gen_len, num_layers, top_k) will raise a ValueError due to dimension mismatch. Additionally, if both are empty, parts will be empty, causing an IndexError when accessing parts[0]. Checking len(...) > 0 before appending ensures we only concatenate non-empty 3D arrays.
| if pre is not None: | |
| parts.append(_vllm_routed_experts_payload_to_array(pre)) | |
| if gen is not None: | |
| parts.append(_vllm_routed_experts_payload_to_array(gen)) | |
| arr = np.concatenate(parts, axis=0) if len(parts) > 1 else parts[0] | |
| if pre is not None and len(pre) > 0: | |
| parts.append(_vllm_routed_experts_payload_to_array(pre)) | |
| if gen is not None and len(gen) > 0: | |
| parts.append(_vllm_routed_experts_payload_to_array(gen)) | |
| if not parts: | |
| return | |
| arr = np.concatenate(parts, axis=0) if len(parts) > 1 else parts[0] |
| subprocess.run( | ||
| ["patch", "-p1", "--forward", "-i", str(patch_file)], | ||
| cwd=pkg_parent, | ||
| check=True, | ||
| ) |
There was a problem hiding this comment.
The patch utility is invoked here to apply vllm.patch. However, patch is not explicitly installed in the apt-get install command on line 20, and minimal base images (such as vllm/vllm-openai) typically do not have it pre-installed. This could cause the Docker build to fail with a FileNotFoundError when trying to run the patch command. Please ensure patch is added to the list of packages installed via apt-get on line 20.
andakai
left a comment
There was a problem hiding this comment.
Hi, overall it looks good to me. I left a few comments for your reference.
| if getattr(args, "vllm_enable_expert_parallel", False): | ||
| cmd += ["--enable-expert-parallel"] | ||
| if not _user_overrode("vllm_expert_placement_strategy"): | ||
| cmd += ["--expert-placement-strategy", "linear"] |
There was a problem hiding this comment.
This part is a bit redundant with the generic vLLM arg init. _forward_vllm_cli_args(args, cmd) will deal with vllm_enable_expert_parallel. expert-placement-strategy is linear by default.
| vLLM 0.21.0 with ``--enable-return-routed-experts`` returns prompt routing via | ||
| ``output["prompt_routed_experts"]`` and generation routing via | ||
| ``choice["routed_experts"]`` (nested int lists, or legacy base64 ``.npy``). | ||
| The concatenated tensor must match SGLang's contract: | ||
| ``(len(sample.tokens) - 1, num_layers, moe_router_topk)``. |
There was a problem hiding this comment.
Mentioning “SGLang's contract” here is a bit confusing.
Maybe "The concatenated tensor must match the rollout_routed_experts contract consumed by Megatron routing replay:"?
| if getattr(args, "use_rollout_routing_replay", False): | ||
| cmd += ["--enable-return-routed-experts"] | ||
| if not _user_overrode("vllm_async_scheduling"): | ||
| cmd += ["--no-async-scheduling"] |
There was a problem hiding this comment.
Sounds it's very struggle so that we need to add these guard? Actually the pr vllm-project/vllm#39568 that I mentioned earlier has already enabled async scheduling and prefix caching, we can just remove these guard as 0.22.0 is expected to release today or tomorrow.
There was a problem hiding this comment.
Agreed for vLLM ≥ 0.22 (#39568). vime Docker still pins 0.21.0; we keep --no-async-scheduling and --no-enable-prefix-caching as defaults under use_rollout_routing_replay until the base image bumps.


Purpose
Implements Phase 1 of RFC #32: MoE routing replay (
--use-rollout-routing-replay) on the vLLM rollout path, pinned to vLLM 0.21.0.SGLang already fills
sample.rollout_routed_expertswith shape(len(tokens) - 1, num_layers, moe_router_topk). vLLM 0.21.0 records routing internally but/inference/v1/generatedid not expose it, andvllm_rolloutonly handled base64 npy onchoices[].routed_experts.Follow-up (not this PR): colocate E2E aligned with
tests/test_qwen3_30B_A3B_r3.py; upgrade to latest vLLM (RFC Phase 2).What's included
docker/patch/latest/vllm.patch— addprompt_routed_experts+choices[].routed_expertson/inference/v1/generate; bake indocker/Dockerfile; docs indocker/README.md.slime/rollout/vllm_rollout.py— merge prompt + gen routing (nested list or base64 npy); assign Megatron-shapedrollout_routed_experts.slime/backends/vllm_utils/vllm_engine.py— when R3 enabled:--enable-return-routed-experts,--no-async-scheduling(unless overridden);--enable-expert-parallel+linearplacement only if--vllm-enable-expert-parallel.tests/unit/rollout/test_vllm_rollout.py; shape checks intests/test_vllm_generate_endpoint.py(R3 case).Test plan
Model: Qwen3-30B-A3B (
num_layers=48,moe_router_topk=8)Unit
Integration (GPU + model)
Manual — decoupled (Gate B, per RFC #32)
docker build -f docker/Dockerfile ...or runtime patch perdocker/README.md).--use-rollout-routing-replay --vllm-enable-expert-parallel(MoE multi-GPU), no--colocate./inference/v1/generatereturnsprompt_routed_expertsandchoices[].routed_experts.rollout_routed_experts, shape(len(tokens)-1, 48, 8).Manual — colocate (Gate C, follow-up)
Same layout as
tests/test_qwen3_30B_A3B_r3.pywith vLLM rollout args; not required to merge this PR.Closes #32 (Phase 1)