Repository navigation
[RL][Frontend] Return token-level weight-version spans on final outputs - #59582
Conversation
871425d to
b15dcc0
Compare
|
This pull request has merge conflicts that must be resolved before it can be |
b15dcc0 to
34c86ac
Compare
|
This pull request has merge conflicts that must be resolved before it can be |
34c86ac to
fd3feaa
Compare
|
Documentation preview: https://vllm--59582.org.readthedocs.build/en/59582/ |
|
This pull request has merge conflicts that must be resolved before it can be |
…puts
EngineCore stamps its committed weight-version label on every scheduled
step, and the step's EngineCoreOutputs carries it. OutputProcessor records
a span start whenever a request's tokens arrive under a new label, and the
final output of every sequence returns
`weight_versions: [{version, start, end}]` on CompletionOutput and on
/inference/v1/generate choices.
Signed-off-by: Shuolei Wang <shuoleiwang123@gmail.com>
aacca46 to
c942ce5
Compare
- EngineCore stamps each step's outputs; the scheduler is unchanged. - Span tracking moves into two small RequestState helpers. - Document weight_versions and use it in the async RL example. Signed-off-by: Shuolei Wang <shuoleiwang123@gmail.com> Co-authored-by: Codex <noreply@openai.com> Signed-off-by: Shuolei Wang <948904026@qq.com>
c942ce5 to
006ebac
Compare
|
✅ @ShuoleiWang, CI is now available for this PR.
|
|
/ci run |
|
❌ This PR is 7 commits behind upstream |
|
/ci run |
|
✅ Triggered Buildkite CI #94038 for commit |
Purpose
Draft for early testing. This is a first version for others to try; it will change.
RL rollouts often cross a weight update. With
pause(mode="keep"), a running request is frozen, the weights and the weight-version label change, and the request resumes, so one output is sampled by two weight versions. vLLM only reports the engine's current label (/weight_info), so the caller cannot tell which tokens came from which weights.This PR returns, on the final output of every sequence, the label of the weights that sampled each range of output tokens:
Behavior:
[].CompletionOutput.weight_versionsand on/inference/v1/generatechoices in both output modes (non-stream and the final stream chunk). OpenAI-compatible endpoints are unchanged in this PR.Implementation:
EngineCorestamps its current label on each step'sEngineCoreOutputs, next to the existing iteration details. The scheduler and the weight-update path are unchanged.OutputProcessorrecords a span start whenever a request's tokens arrive under a different label than its previous ones, and builds the spans on the final output.RequestOutput.addtakes the spans from the last merged output, likefinish_reason. Merged DELTA outputs therefore keep them, and output merged past the end of a streaming-input chunk does not carry stale ones.EngineCoreOutputsfield, because it rejects unknown trailing fields of that struct. It does not expose the field yet.weight_versionsinstead of pausing, sleeping five seconds and counting streamed tokens. CI runs that example, so its check against a fresh instance of the new model now also covers the span boundary.Not a duplicate:
routed_expertsorsampling_maskneed these offsets.This change only adds metadata. It does not change scheduling, sampling or the generated tokens, so no model evaluation is included.
Test Plan
Unit tests (CPU):
pytest tests/v1/engine/test_output_processor.py -k weight_version cargo test -p vllm-engine-core-clientEnd-to-end on H20, on this revision: the precompiled wheel of the base commit with this PR's Python files on top.
/inference/v1/generate. Two servers with Qwen3-1.7B,VLLM_SERVER_DEV_MODE=1and--enable-scale-out, one with async scheduling and one without.lm_headthrough/collective_rpc, and/update_weight_versionsets a new label. After that, greedy decoding emits only token 0, so each span boundary must sit at the first 0 token.tokensandtext):pause(mode="keep")withclear_cache=falseand withclear_cache=true, using three streams and one non-streaming request, plus a request admitted during the pause;pause(mode="abort")with streams, a non-streaming request and a long-prompt request sent just before the abort;/weight_info.examples/rl/rlhf_async_new_apis.pyon two GPUs, with only the model names pointed at local copies of the same checkpoints:Test Result
vllm-engine-core-clientpasses 125/125./inference/v1/generatetests): they give the same results as on the base commit, including the same 59 errors on both, which come from the CPU test environment (e.g. model weights not available offline)./inference/v1/generate: 135/135 checks pass with async scheduling on, and 135/135 with it off.[("A", 0, k), ("B", k, 300)]with k between 17 and 21. Every token from k on was 0, and the token before k was not. Requests admitted during the pause got[("B", 0, 64)].finish_reason: "abort"and one span over its partial output, including final chunks with no new tokens. The long-prompt request was aborted before its first token once (async scheduling on,tokensmode) and got[]. In the other three runs it had already produced one token and got one span./weight_inforeturned the last label.weight_versionswere 10 tokens for 12 prompts and 12 tokens for one.Coverage notes:
OutputProcessor.EngineCorestamping the label and the/inference/v1/generatechanges are covered by the end-to-end runs and the example.RequestOutput.addis not tested.On current main,
/inference/v1/generateis only registered with--enable-scale-out(or--tokens-only).AI assistance (Claude Code) was used to write the code, the tests and this description.