Skip to content

[Frontend] Add derender output mode to generate - #60082

Merged
vllm-bot merged 12 commits into
vllm-project:mainfrom
hickeyma:add-batch-derender-level
Oct 21, 2026
Merged

vllm-bot merged 12 commits into
vllm-project:mainfrom
hickeyma:add-batch-derender-level

Conversation

@hickeyma

@hickeyma hickeyma commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Overview

Adds output_mode="derender" to /inference/v1/generate, so the generate server returns the parsed assistant message itself. This is Phase 2 of #56851 and builds on #60073.

Claims

  • output_mode="derender" returns message (content, reasoning, tool calls) on every choice without a separate /derender call
  • message, finish_reason and logprobs match coupled /v1/chat/completions even for parsers whose adjust_request changes decoding. /derender can't match those
  • /v1/chat/completions/render and /v1/messages/render now return parse_context. Other output modes ignore it, including fields added by a newer render, so prefill legs don't change
  • A bad parse_context gets a 400 before generation. When a parser hides the reasoning, token_ids are dropped along with logprobs, like coupled chat
  • Batch /derender now supports parallel_tool_calls=false, like coupled chat

Validation

pytest tests/entrypoints/cohere/test_api_router.py -v
pytest tests/entrypoints/scale_out/token_in_token_out/test_generate_stream.py -v 
pytest tests/entrypoints/scale_out/token_in_token_out/test_protocol.py -v
pytest tests/entrypoints/scale_out/token_in_token_out/test_serving_tokens.py -v
pytest tests/entrypoints/scale_out/derender/test_derender_stream.py -v 
pytest tests/entrypoints/scale_out/derender/test_derender_parity.py -v 

The parity tests run R1-Distill with hermes and deepseek_r1 against coupled chat. They cover plain output, reasoning shown and hidden, tool_choice auto, required and named, parallel_tool_calls=false, a stop string, n=2 and logprobs with top_logprobs=3 and logprob_token_ids. Every case checks finish_reason and the tool call ID shape.

Details

The derender level parses the engine's own output.text. That text already has the stop string cut and was decoded with the flags adjust_request set which is where /derender drifts from coupled chat (#42729).

  • Chat context. /render puts a ParseContext on the GenerateRequest with the tools, tool_choice, parallel_tool_calls, include_reasoning, resolved chat_template_kwargs and the history tool call count. It's only read when output_mode="derender". I went with always sending it, rather than only on request, so a rendered request works as is (decision 2 on [RFC]: Request level text and derender output on /inference/v1/generate #56851). parse_context is validated in serve_tokens before the request reaches the engine. Unknown fields fail the request only when output_mode="derender" because that's the only level that reads them. At the other levels they're logged once and ignored, so an older generate node keeps serving a newer render. chat_template_kwargs is typed as JSON values to make sure whatever goes in it has to survive the wire. The trade-off is that every rendered request carries the tools list, prefill legs included.
  • Parsing. The generate server builds a slim ChatCompletionRequest from parse_context and reuses _parse_and_assemble from [Frontend] Extract parse and assembly out of batch chat derender #60073. /render resolves the template kwargs once and uses them for both reasoning_parser_kwargs and parse_context. A handler can pass its _engine_chat_template_kwargs to keep API server only state out of parse_context. Cohere uses this for _position_to_source and friends. Tool call IDs keep the parser's tc.id ([Bugfix][Frontend] Keep the parser's tool call IDs in batch derender #61852).
  • finish_reason goes through resolve_finish_reason ([Frontend] Make finish_reason and Responses item status truthful #59324), the same as coupled chat and batch /derender.
  • Logprobs are chat shaped and cut with chat_top_logprobs_limit ([Frontend] Apply each endpoint's top_logprobs cut in derender #59518). They're on whenever num_logprobs is set, so logprob_token_ids works. When a parser hides the reasoning, logprobs and token_ids are both dropped. The tokens and text levels don't change.

Limitations:


Pull Request Checklist
  • I used vLLM's /pr-checklist skill. (Mandatory for agents, optional for humans).

  • AI assistance was used during the creation of this PR.

  • Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.

  • Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.

  • Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.

  • Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.

@mergify

mergify Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Documentation preview: https://vllm--60082.org.readthedocs.build/en/60082/

@mergify

mergify Bot commented Oct 5, 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, @hickeyma.

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 Oct 5, 2026
@hickeyma
hickeyma force-pushed the add-batch-derender-level branch from 9927ac6 to 07f3ccd Compare October 6, 2026 09:41
@mergify mergify Bot removed the needs-rebase label Oct 6, 2026
@mergify

mergify Bot commented Oct 6, 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, @hickeyma.

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

@hickeyma

Copy link
Copy Markdown
Contributor Author

I am looking at the ruff error now which was caused by rebase.

@mergify

mergify Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Hi @hickeyma, the pre-commit checks have failed. Please run:

uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Signed-off-by: Martin Hickey <martin.hickey@ie.ibm.com>
@hickeyma

Copy link
Copy Markdown
Contributor Author

Ruff error is fixed now.

@DarkLight1337

Copy link
Copy Markdown
Member

/ci run

@DarkLight1337
DarkLight1337 enabled auto-merge (squash) October 21, 2026 09:58
@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #96393 for commit 7769f8284161.

ℹ️ This PR is 1 commit behind upstream main. Merged upstream main into this branch as 7769f8284161 before starting CI. Pull the branch before pushing more changes.

With include_reasoning=False, both the coupled and inline paths hide
token_ids which means the determinism check was comparing None to None. The two
greedy runs can still produce different text (seen on ROCm) and the
content check then failed.

When token ids are hidden, only check that token_ids and reasoning are
hidden on both sides and skip the text comparison. The reasoning case
still covers content parity for the same prompt.

Signed-off-by: Martin Hickey <martin.hickey@ie.ibm.com>
auto-merge was automatically disabled October 21, 2026 12:07

Head branch was pushed to by a user without write access

@hickeyma
hickeyma force-pushed the add-batch-derender-level branch from 7769f82 to caba434 Compare October 21, 2026 12:07
@hickeyma

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #96409 for commit 7b605335ea60.

@arpera arpera removed this from Tool Calling Oct 21, 2026
@vllm-bot
vllm-bot merged commit 817fa35 into vllm-project:main Oct 21, 2026
116 of 118 checks passed
@hickeyma
hickeyma deleted the add-batch-derender-level branch October 21, 2026 16:03
hao-aaron added a commit to hao-aaron/vllm that referenced this pull request Oct 23, 2026
Adapt to the derender output mode (vllm-project#60082): build each choice's fields in
_build_choice_fields, shared by the tokens/text builder (which may still
run off the event loop) and an async derender builder that keeps main's
logic. Usage is recorded by main's _build_response_fields.

Signed-off-by: hao-aaron <aaronhao@inferact.ai>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bantianyue pushed a commit to bantianyue/vllm that referenced this pull request Oct 28, 2026
Signed-off-by: Martin Hickey <martin.hickey@ie.ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cohere Related to Cohere models documentation Improvements or additions to documentation frontend ready ONLY add when PR is ready to merge/full CI is needed tool-calling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants