Skip to content

[Rust Frontend] /derender: streaming derender + two-process e2e test (phase 3/3) - #53419

Draft
ezhoureal wants to merge 4 commits into
vllm-project:mainfrom
ezhoureal:rust-derender-3-streaming
Draft

ezhoureal wants to merge 4 commits into
vllm-project:mainfrom
ezhoureal:rust-derender-3-streaming

Conversation

@ezhoureal

Copy link
Copy Markdown
Contributor

Purpose

Stacked on #53418 (phase 2/3) — review only the top commit(s). Split per @sagearc's review on #53223, mirroring the Python derender phasing in #42729 (#43606 detok → #45919 parsing → #48617 streaming).

Phase 3: streaming derender for both /v1/chat/completions/derender and /v1/completions/derender, mirroring Python #48617 — plus a two-process GPU end-to-end test.

  • Implements the client-carried DerenderStreamState protocol: bounded incremental detok window (prev_tokens/prefix_offset/read_offset, capped at 1024) porting detokenize_incrementally, role emitted once, usage forwarding. Chunked derendering produces byte-identical text to one-shot decode, including multibyte characters split across chunk boundaries.
  • Chat streaming fails closed when a reasoning/tool parser is configured — same as Python's NotImplementedError path.
  • Adds tests/entrypoints/scale_out/derender/test_derender_rust_e2e.py: boots a real GPU engine (vllm serve) and the Rust render server (vllm-rs render) as two processes and verifies render → /inference/v1/generate → derender roundtrips, Python↔Rust derender parity on identical greedy token IDs, and streaming chunked == one-shot over real tokens. Adds RemoteRustRenderServer to tests/utils.py (skips cleanly when no vllm-rs binary is built).

Duplicate-work check: no open PR implements streaming derender in rust/; #50550 extends the Python streaming derender only. Related: #42729, #47161.

AI assistance: implemented with AI assistance (Kimi Code CLI). I have reviewed the full diff and run the tests below myself.

Test Plan

cargo nextest run -p vllm-server -p vllm-chat -p vllm-text -p vllm-tokenizer
cargo clippy -p vllm-server -p vllm-chat -p vllm-text -p vllm-tokenizer --all-targets
cargo fmt --all --check
pytest -v -s tests/entrypoints/scale_out/derender/test_derender_rust_e2e.py  # requires GPU + vllm-rs binary

Test Result

  • cargo nextest run: 847 passed, 1 skipped (includes all 21 streaming derender tests).
  • cargo clippy --all-targets: clean. cargo fmt --check: clean. pre-commit run on the Python files: all hooks pass.
  • GPU e2e on a single RTX 3070 (8 GB, WSL2, deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B with hermes/deepseek_r1 parsers): 4 passed, 1 skipped in ~95 s — the skip is test_derender_parity_tool_call gated on the model emitting a tool call (same gating as test_derender_parity.py).

Model evals: N/A — adds endpoints to the Rust frontend without changing existing serving behavior or model outputs.

Notes for reviewers

  • The e2e surfaced a pre-existing (main-branch) wire incompatibility: the Rust render server serializes unset sampling params as explicit nulls, which the Python /inference/v1/generate msgspec validation rejects. The test strips them before the generate hop with a comment; a render-side fix can be a separate PR.
  • Local WSL2 bring-up needed VLLM_WSL2_ENABLE_PIN_MEMORY=1 and VLLM_USE_FLASHINFER_SAMPLER=0 (no CUDA toolkit); not test-code concerns.

@mergify

mergify Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @ezhoureal.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Aug 29, 2026
…ase 1/3)

Phase 1 of 3 splitting the Rust derender endpoints per review on vllm-project#53223,
mirroring the Python phasing in vllm-project#43606 (detokenization first; parsing and
streaming follow). Related issues: vllm-project#42729, vllm-project#47161.

Adds /v1/chat/completions/derender and /v1/completions/derender to both the
render-only and engine-backed servers: shared incremental detokenization
foundation, DerenderStreamState, logprob placeholder resolution, and the
plain non-streaming derender paths with bounds validation. Reasoning/
tool-call parsing (phase 2) and streaming (phase 3) are intentionally
absent; a request body with stream=true fails deserialization with 400.

This change was developed with AI assistance (Kimi Code CLI).

Co-authored-by: Kimi Code CLI
Signed-off-by: zireael <zireael@users.noreply.github.com>
Signed-off-by: Tianer Zhou <ezhoureal@gmail.com>
Phase 2 of the derender split per review on vllm-project#53223, mirroring the Python
implementation from vllm-project#45919. Stacked on phase 1 (detokenization + state).

Restores non-streaming chat reasoning/tool-call parsing: when a parser is
configured and `chat_request` is supplied, generated tokens are replayed
through the production chat output pipeline so the configured parser splits
them into reasoning, content and tool calls; otherwise the endpoint falls
back to plain detokenization. Hidden reasoning also suppresses per-token
logprobs, matching the normal chat path.

Streaming endpoints remain phase 3: `stream: true` bodies still fail
deserialization with a 400.

Related: vllm-project#42729

Co-authored-by: Kimi Code CLI
Signed-off-by: zireael <zireael@users.noreply.github.com>
Signed-off-by: Tianer Zhou <ezhoureal@gmail.com>
…(phase 3/3)

Phase 3 of the 3-phase split per review on vllm-project#53223, stacked on phase 2
(rust-derender-2-parsing). Restores the remaining streaming functionality
of the Rust /derender endpoints — streaming wire types and union variants,
derender_chat_stream / derender_completion_stream handlers with
validate_stream_bounds / stream_usage / tool_parsing_would_engage,
stream dispatch in derender/mod.rs, and removal of the phase-1/2
#[allow(dead_code)] gates — and adds the two-process Python e2e test
(RemoteRustRenderServer + test_derender_rust_e2e.py). Implements the
client-carried DerenderStreamState protocol, mirroring the Python
implementation from vllm-project#48617.

Related: vllm-project#42729, vllm-project#47161.

This change was made with AI assistance (Kimi Code CLI).

Co-authored-by: Kimi Code CLI
Signed-off-by: zireael <zireael@users.noreply.github.com>
Signed-off-by: Tianer Zhou <ezhoureal@gmail.com>
…ping, reasoning assertion

- _resolve_vllm_rs_binary: prefer rust/target builds over the wheel-packaged
  vllm-rs, which may be built from an older main snapshot without derender.
- _generate: strip explicit null sampling params emitted by the Rust render
  server, which the Python /inference/v1/generate msgspec validation rejects
  (pre-existing render-side wire incompatibility, unrelated to derender).
- test_e2e_chat_roundtrip: accept output in reasoning when a reasoning
  parser is configured.

Verified on RTX 3070 (8GB, WSL2): 4 passed, 1 skipped (tool-call parity
skip: model did not emit a tool call, gated by design).

Co-authored-by: Kimi Code CLI
Signed-off-by: zireael <zireael@users.noreply.github.com>
Signed-off-by: Tianer Zhou <ezhoureal@gmail.com>
@mergify

mergify Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @ezhoureal.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant