Skip to content

[Rust Frontend][2/3] Add stateless non-streaming Responses API - #2

Open
franciscojavierarceo wants to merge 118 commits into
codex/responses-rendererfrom
codex/responses-nonstreaming
Open

franciscojavierarceo wants to merge 118 commits into
codex/responses-rendererfrom
codex/responses-nonstreaming

Conversation

@franciscojavierarceo

@franciscojavierarceo franciscojavierarceo commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Overview

active upstream review: vllm-project/vllm#63902. this fork PR keeps the isolated native-stack diff and previous comments. all layers were rebased onto 3d81a510f9; the fork's pinned base was advanced to that commit.

add stateless, non-streaming POST /v1/responses through the existing vllm-chat pipeline, with input replay, function tools, reasoning, and usage.

upstream review order: 1/3: renderer vllm-project/vllm#63901 → 2/3: non-streaming vllm-project/vllm#63902 → 3/3: streaming vllm-project/vllm#63903. all three upstream review PRs target main; later diffs include earlier layers.

this PR includes and depends on vllm-project#63901. the fork layer shows its isolated diff.

Claims

  • lower Responses input and function-call/result replay through the shared chat path, reusing compatible chat/audio/tool primitives.
  • validate request controls and preserve model-resolved sampling defaults.
  • use the current frontend's typed engine failures and numeric HTTP errors, with real route coverage for 400/503 failures and unknown-response 404s.
  • reject stream: true with HTTP 400 on param: stream until layer 3.

Validation

Latest upstream synchronization — October 28

current head: d5b7df30aa; rebased onto upstream f4fd1ab68e. the fork keeps its native stacked bases; upstream PRs still target main.

  • resolved the shared error-handler overlap with upstream's missing-LoRA 404 change. both NotFound and ResponseNotFound retain their existing wire envelopes; the LoRA/gRPC and Responses HTTP regressions pass.
  • cargo test --locked -p vllm-server -p vllm-text --lib -- --test-threads=1: 579 server + 121 text passed, one existing text test ignored.
  • strict Clippy for server/chat/text/CLI, applicable pre-commit hooks, and git diff --check: passed. cargo nextest remains unavailable.
  • range-diff shows only the error-handler conflict adaptation; all other feature/fix patches are unchanged. all branch tips passed merge-tree checks against f4fd1ab68e.
  • the old umbrella [Rust Frontend] Add OpenAI Responses API endpoint vllm-project/vllm#58783 was synchronized without rewriting its original history. its source tree equals the tested complete stack and it stays draft.

the model-backed HTTP/Codex evidence below predates this synchronization (complete-stack head 3db4c9abf0); it was not repeated after this rebase. today's evidence covers compilation and deterministic wire/route/renderer regressions, not new model qualification. CI reruns on these new heads are separate from the earlier successful workflows. agentic-api was not changed or pushed.

Earlier review and runtime evidence

reviewed and revised on October 27; non-streaming head: 91a782aa08.

  • a regression first reproduced the bug: token-limited responses had overall incomplete status but a final message item marked completed.
  • this layer's cargo test --locked -p vllm-server -p vllm-text --lib -- --test-threads=1: 552 server + 118 text passed, one existing text test ignored.
  • added a route regression for reasoning followed by a truncated function call: earlier output stays completed; only the final message/function item becomes incomplete.
  • strict Clippy, applicable pre-commit hooks, formatting, and git diff --check: passed.
  • all three tips merge cleanly with upstream f8f7634cb4; the fork's isolated stack bases are preserved.

commands ran from rust/ with OPENSSL_DIR pointing to the local OpenSSL build. cargo nextest was unavailable. full-stack tests ran serially because of the previously reproduced shared chat audio-metrics collector collision.

fresh model-backed HTTP and real Codex checks passed on the revised complete stack, as recorded in vllm-project#63903. this intermediate branch was tested separately with the in-process HTTP route harness; it does not contain streaming.

Details

Responses owns its wire envelopes and conversions while reusing the chat generation path and matching shared audio/tool primitives. the rebase preserves upstream's other route registrations and error serialization rather than restoring the older envelope.

  • omitted or null stream selects non-streaming. omitted/null watermarking and explicit overrides follow current Chat Completions semantics.
  • metadata is a string map limited to 16 pairs, 64-character keys, and 512-character values.
  • omitted temperature/top-p use model defaults; explicit overrides are preserved and checked against the engine request.
  • unknown service tiers, invalid summary/verbosity values, unsupported include outputs, and nonzero top_logprobs are rejected.
  • unknown previous_response_id uses the current shared NotFoundError envelope: HTTP 404, param: response_id, numeric code: 404.

the frontend remains stateless: clients replay history, and agentic-api owns persistence and tool execution. the shared native adapter tracked by vllm-project/agentic-api#359 and vllm-project/agentic-api#382 is still unfinished. accepted reasoning-summary, text-verbosity, and encrypted-reasoning inputs are documented compatibility no-ops; valid service tiers are echoed without scheduling guarantees. persistence, retrieval/cancellation, built-in/custom/namespace tool execution, Harmony rendering, output logprobs, and allowed_tools are out of scope. the rendered Responses documentation describes the supported subset.

these are the existing vllm-project#58783 layers opened upstream at @BugenZhao's request, rather than competing implementations. the original authored history and discussion remain in vllm-project#58783. @gcanlin's vllm-project#53380 implementation is credited in the endpoint and streaming commits. vllm-project#56124 covers render endpoints and vllm-project#51535 covers chat allowed_tools; neither duplicates this endpoint.

this layer is 13 files, +2987 / -1 relative to vllm-project#63901. tests stay under the test-only route module. agentic-api compatibility follow-ups remain local and unpushed. Codex assistance was used; human review of every changed line and independent validation are required before merge.


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.

BEFORE SUBMITTING, PLEASE READ https://docs.vllm.ai/en/latest/contributing (anything written below this line will be removed by GitHub Actions)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Valid controls are silently ignored and some response metadata misrepresents actual behavior.

Review effort: Balanced
Findings: 7 Medium severity

Open (7)
What changed in this PR

Adds a stateless, non-streaming OpenAI-compatible Responses API to the Rust frontend.

Changes:

  • Adds request validation, chat lowering, response assembly, and tool/reasoning replay.
  • Registers create, retrieve, and cancel routes with runtime offloading.
  • Adds unit and HTTP regression coverage.
File Description
rust/​src/​server/​src/​routes/​tests/​responses.rs Adds Responses HTTP tests.
rust/​src/​server/​src/​routes/​tests.rs Registers the test module.
rust/​src/​server/​src/​routes/​openai/​responses/​validate.rs Validates supported request features.
rust/​src/​server/​src/​routes/​openai/​responses/​types.rs Defines request and response wire types.
rust/​src/​server/​src/​routes/​openai/​responses/​convert.rs Converts Responses requests and outputs.
rust/​src/​server/​src/​routes/​openai/​responses.rs Implements endpoint handlers.
rust/​src/​server/​src/​routes/​openai/​mod.rs Exports Responses handlers.
rust/​src/​server/​src/​routes.rs Registers HTTP routes.
rust/​src/​server/​src/​middleware/​offload.rs Offloads response generation work.
rust/​src/​server/​src/​error.rs Adds unknown-response errors.
rust/​src/​parser/​src/​tool/​mod.rs Allows omitted function schemas.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread rust/src/server/src/error.rs Resolved
Comment on lines +119 to +123
top_logprobs,
text,
truncation,
user,
include: _,
Comment on lines +113 to +115
/// Output presentation controls such as `include`, `reasoning.summary`, and
/// `text.verbosity` are accepted for compatibility. They do not add output
/// fields that this frontend cannot produce.
#[serde(default = "default_true")]
pub include_reasoning: bool,
#[serde(default)]
pub service_tier: Option<String>,
@franciscojavierarceo
franciscojavierarceo force-pushed the codex/responses-nonstreaming branch from 2049693 to ee46722 Compare October 27, 2026 03:41
@franciscojavierarceo
franciscojavierarceo marked this pull request as ready for review October 27, 2026 11:39
@franciscojavierarceo
franciscojavierarceo force-pushed the codex/responses-nonstreaming branch 2 times, most recently from 5bc7b88 to d5b7df3 Compare October 28, 2026 10:32
erezzarum and others added 16 commits November 2, 2026 02:59
…llm-project#64585)

Signed-off-by: Erez Zarum <erezz@amazon.com>
Co-authored-by: Artem Perevedentsev <aperevedents@nvidia.com>
…ncelled (vllm-project#64265)

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…ithout the developer role (vllm-project#63901)

Signed-off-by: Francisco Javier Arceo <4163062+franciscojavierarceo@users.noreply.github.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…es (vllm-project#63974)

Signed-off-by: Luyi Xiao <81631911+luyixiao95@users.noreply.github.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Co-authored-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…tion prefix (vllm-project#58618)

Signed-off-by: Yuting Wu (DLAlgo) <yutwu@nvidia.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…untime (vllm-project#64294)

Signed-off-by: reidliu41 <reid201711@gmail.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…llm-project#63137)

Signed-off-by: world-executed <yolohalo@163.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: world-executed <yolohalo@163.com>
Co-authored-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
)

Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…0+ (vllm-project#65059)

Signed-off-by: Seungryong Kim <137975233+srkim-business@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…dule's hook (vllm-project#57678)

Signed-off-by: aoshen02 <aoshen@inferact.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: nicole-lihui <44200120+nicole-lihui@users.noreply.github.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…64838)

Signed-off-by: zjy0516 <riverclouds.zhu@qq.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…m-project#63870)

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Julien Darve <jdarve@NVIDIA.com>
Co-authored-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
vllm-project#65166)

Signed-off-by: davidteather <34144122+davidteather@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
xu797 and others added 22 commits November 3, 2026 14:35
Signed-off-by: xujixian <xjxian02@gmail.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…65153)

Signed-off-by: akii96 <aakif.nawaz@amd.com>
Co-authored-by: Simon Danielsson <70206058+simondanielsson@users.noreply.github.com>
…llm-project#64312)

Signed-off-by: Stefano Castagnetta <scastagnetta@nvidia.com>
Signed-off-by: lesj0610 <lesj0610@gmail.com>
Signed-off-by: Isotr0py <Isotr0py@outlook.com>
Co-authored-by: Stefano Castagnetta <scastagnetta@nvidia.com>
Co-authored-by: Isotr0py <Isotr0py@outlook.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…project#63641)

Signed-off-by: Chaojun Zhang <chaojun.zhang@intel.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…ase in v1/shutdown (vllm-project#65097)

Signed-off-by: zengxian <xiangdong.zeng@intel.com>
…ce copies on integrated GPUs (vllm-project#58868)

Signed-off-by: Willian <willian@willian.email>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
Signed-off-by: wang.yuqi <yuqi.wang@daocloud.io>
Co-authored-by: vllm-agent <claw@inferact.ai>
…62802)

Signed-off-by: Tianyu Guo <guoty@inferact.ai>
Co-authored-by: vllm-agent <claw@inferact.ai>
Signed-off-by: Canlin <canlinguosdu@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Jee Jee Li <jeejeelee@inferact.ai>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…t#56102)

Signed-off-by: kbenkhaled <kbenkhaled@nvidia.com>
Co-authored-by: Codex <noreply@openai.com>
…eshold (vllm-project#64558)

Signed-off-by: Sohom Chakraborty <16609933+sohom-cs@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Or Ozeri <oro@il.ibm.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…llm-project#64993)

Signed-off-by: waizuichougou <2082431897@qq.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
Add `POST /v1/responses` on top of `ChatLlm::chat`: requests lower into
a chat request, and `OutputItemStreamer` turns the chat event stream into
Responses output items and SSE lifecycle events. Store-dependent features
are rejected or return 404. Non-streaming requests are rejected for now.

This squashes the streaming-first part of the original stack:

- 9e714a4 [Rust Frontend] Add stateless non-streaming Responses API
- f785541 fix: validate Responses controls and report model sampling defaults
- 881a178 fix: adapt Responses to current Rust frontend contracts
- ef5a082 fix: mark truncated Responses output items incomplete
- 5bc7b88 fix: adapt Responses decoding and cache usage to upstream
- 6698c5b [Rust Frontend] Add Responses streaming lifecycle
- 062c680 test: cover Responses partial output after stream decode failure
- cf47141 fix: adapt Responses streaming to current chat events
- a2adabf fix: preserve final output item status in Responses streams

Its non-streaming path returns in a later commit. Route tests that do not
depend on the response mode now run through streaming.

Co-authored-by: Canlin Guo <canlinguosdu@gmail.com>
Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Francisco Javier Arceo <4163062+franciscojavierarceo@users.noreply.github.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Replace raw JSON probing in the Responses route with typed DTOs that
reuse the chat-completions definitions:

- Tools are `ResponsesTool::Function(Function)`, the chat-completions
  `Function` flattened next to `type`, lowered through a shared
  `Function -> ChatTool` conversion.
- `tool_choice` reuses `ToolChoiceValue`, `FunctionChoice`, and
  `ToolReference`.
- Input items deserialize into typed items; a message item may omit
  `type`, mirroring Python's `input_item_parsing`. Errors keep the item
  index.
- `truncation`, `service_tier`, `reasoning.summary`, and
  `text.verbosity` are serde enums instead of string allow-lists.

Stream events become a typed enum serialized directly into SSE data,
with `type` and `sequence_number` added by a wrapper. Lifecycle events
share the response through `Arc` instead of converting it to `Value`.
`function_call_arguments` events no longer carry a `content_index`,
matching the SDK event shape.

Unknown tool, tool-choice, and input-item types now fail
deserialization, so errors name the JSON path instead of a `param`.
The unread `store` field is dropped; `store=true` still executes.

Add an async-openai typed-client test for streaming Responses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Remove logic the Responses route duplicated from lower layers:

- Echo `temperature`, `top_p`, `max_output_tokens`, and the
  presence/frequency penalties from the sampling values sent to the
  engine, as Python does. `ChatEventStream::sampling()` exposes them,
  read from the prepared generate request in `ChatLlm::chat`, instead
  of the handler pre-filling model defaults and repeating vllm-text's
  precedence. The echoed output limit is the context-clamped one, and
  an omitted limit or penalty no longer disappears from the response.
  The `TextRequestProcessor::sampling_hints()` accessor goes away.
- Drop the `enable_thinking` template kwarg injection. The HF renderer
  already derives it from `reasoning_effort`, and native renderers
  resolve reasoning controls through `ReasoningControl`.
- Revert `#[serde(default)]` on `vllm_parser::Tool.parameters`. Wire
  tools now deserialize into the server `Function` DTO, which owns that
  default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
The terminal streaming response rebuilt its output from the final chat
message with fresh IDs and patched the streamed IDs back by item kind and
order, while failed streams used the streamer's completed items instead.

Make the streamer's completed items the only source of `output`, and
drop the rebuild and the ID reconciliation. This also drops an empty
reasoning block from the final output, which streaming never emitted as
an item.

Derive the next output index and the deferred `output_item.done` from
the completed items instead of keeping copies of them, and drop a
function-call field that was written but never read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Serve requests without `stream: true` by feeding chat events through
the same `OutputItemStreamer` and discarding its SSE events, like
`ChatEventStream::collect_message` does for Chat Completions. The
streamer's completed items stay the only source of `output`, so both
modes return the same items and IDs. Unlike streaming, engine errors
map to typed HTTP errors (400 for validation, 503 for retryable
failures) because no response has been sent yet.

Both modes now share the terminal assembly: the handler builds the
in-progress response once after submission, and the completed and
failed responses derive from it. The terminal streaming event follows
from the final response status.

Restore the non-streaming route tests from the original stack and add
an async-openai typed-client test for non-streaming Responses.

Co-authored-by: Francisco Javier Arceo <4163062+franciscojavierarceo@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CJckCDvqfKYQRRwCYtsCmk
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Attach `metrics` to the final Responses object when
`--enable-per-request-metrics` is set, as Python Responses does, for both
the non-streaming body and the terminal streaming event. Python
Responses reports timing only, so speculative-decoding statistics stay
on Chat Completions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CJckCDvqfKYQRRwCYtsCmk
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
When generation hits the output limit, the last output item is the
truncated one. Message and function-call items were marked
`incomplete`, but a reasoning item kept `completed`, so a request that
ran out of tokens while reasoning returned an incomplete response whose
only item claimed to be complete. Python passes the same `incomplete`
status to reasoning items.

Extend the length-limit test to two tool calls so it also checks that
only the last one is marked, and cover reasoning-only truncation in
both modes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CJckCDvqfKYQRRwCYtsCmk
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
The Responses request type lacked several vLLM controls that Python
declares, so serde dropped them and requests ran with the defaults:
`min_p` was ignored, and `spaces_between_special_tokens: false`,
`media_io_kwargs`, `mm_processor_kwargs`, and
`enable_response_messages: true` were accepted without effect.

Forward `min_p` to the shared sampling parameters, and reject the
others with 400 as Chat Completions does. Prompt-cache hints and
`safety_identifier` stay ignored, which Python documents as no-ops.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CJckCDvqfKYQRRwCYtsCmk
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
The response object omitted every unset optional field, so the initial
lifecycle objects had no `usage`, completed ones had no
`incomplete_details`, and unset echoes such as `instructions` or
`reasoning` disappeared. Python and the OpenAI schema keep these
fields present with `null`. Serialize them as `null`, and keep omitting
`metrics` when it is disabled, as Python does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CJckCDvqfKYQRRwCYtsCmk
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
@BugenZhao
BugenZhao force-pushed the codex/responses-nonstreaming branch 2 times, most recently from 7af1e25 to a1d8bd8 Compare November 3, 2026 08:42
kliuae and others added 6 commits November 3, 2026 16:46
…t chunks (vllm-project#64330)

Signed-off-by: kliuae <kuanfu.liu@embeddedllm.com>
Co-authored-by: Claude Opus <noreply@anthropic.com>
…connect (vllm-project#64299)

Signed-off-by: jiuyueQ <631905507@qq.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…e module's parameters (vllm-project#65296)

Signed-off-by: aoshen02 <aoshen@inferact.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…d weight now starts (vllm-project#65281)

Signed-off-by: aoshen02 <aoshen@inferact.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Add `usage.input_tokens_details.cache_write_tokens`, the prompt tokens
newly written to the prefix cache, as the Python frontend has since
vllm-project#60973. The OpenAI SDK `Response` type requires the
field, so strict validation of Rust responses failed without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CJckCDvqfKYQRRwCYtsCmk
Signed-off-by: Bugen Zhao <i@bugenzhao.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.