Repository navigation
Conversation
Implement POST /v1/responses (plus GET/cancel stubs) in the Rust frontend, mirroring the stateless non-harmony path of the Python frontend: message/function-call/reasoning input items, streaming SSE with the full output-item event sequence, reasoning items, and function tool calls. Store-dependent features follow store-off Python behavior: store=true executes without retention, background=true is rejected, previous_response_id and retrieval/cancellation return 404. Co-authored-by: Codex Signed-off-by: Canlin Guo <canlinguosdu@gmail.com>
|
@BugenZhao Hi, do we have plan to support this API? It seems that codex has deprecated /chat api and we need /responses api when using rust frontend. I'm not familiar with rust so this PR may not be production ready. |
There was a problem hiding this comment.
thanks for this PR!
i maintain the vLLM Agentic API project, which is written in Rust, and recently worked on the Responses API here. i think we should keep Responses stateless, as this implementation already does, and leave response storage out of scope. i've left a few inline comments on streaming behavior, stateless replay, and request handling.
| AssistantBlockKind::Reasoning => { | ||
| let mut events = self.open_reasoning_if_needed(); | ||
| events.push(self.reasoning_delta(delta.clone())); | ||
| events |
There was a problem hiding this comment.
include_reasoning=false still emits reasoning over SSE, even though the final response excludes it. this also leaves the streamed message at index 1 while it appears at index 0 in the final output. Python suppresses those deltas. we should filter them before opening output items.
| #[serde(default)] | ||
| pub metadata: Option<Value>, | ||
| #[serde(default)] | ||
| pub previous_response_id: Option<String>, |
There was a problem hiding this comment.
previous_input_messages is missing from the request type and silently ignored during deserialization, so clients using Python’s stateless Harmony replay format lose their supplied history. supporting that format can stay out of scope, but we should explicitly reject the field until it is supported.
|
|
||
| if let Some(truncation) = &request.truncation { | ||
| // TODO: implement context-length truncation for `truncation="auto"`. | ||
| if !matches!(truncation.as_str(), "auto" | "disabled") { |
There was a problem hiding this comment.
truncation="auto" is accepted and echoed back without being applied, so long conversations still hit the normal context-length error. we should either implement truncation or reject the option explicitly for now.
| let request_id = request | ||
| .request_id | ||
| .clone() | ||
| .unwrap_or_else(|| format!("resp_{}", Uuid::new_v4().simple())); |
There was a problem hiding this comment.
we resolve X-Request-ID in the handler, then ignore it here and use the body ID or a new UUID. this breaks request correlation and differs from the other Rust endpoints. we should use the resolved request context here.
|
@franciscojavierarceo Thank you for your review! But I have a little concern about my totally vibe PR. It's better that someone who's familiar with rust can take it. Feel free to open one new PR! |
|
Hi @gcanlin and @franciscojavierarceo, thanks so much for your work and review. We've recently started discussing the future direction of integration between I agree with what @franciscojavierarceo said in #57499 (comment) — perhaps it would be best if we could share the protocol definitions, lifecycle, and so on between both sides. That said, I think it is also fine to temporarily provide a minimal subset on the Rust frontend side that can power use cases like Codex (just like what DeepSeek provides in their official API platform): the code would only be 2–3k lines, and it would give us an opportunity to see what changes the If this direction looks good to you, I believe I can help take over this PR and bring it to a milestone! |
|
Yeah that sounds good to me. @BugenZhao. I can test wiring it up with Agentic API as well and identify any issues. |
|
I prepared a validated follow-up commit for the Rust Responses frontend: franciscojavierarceo@4f80bcad71 The branch is It closes the parity gaps identified in review:
Validated with:
This is complementary to the shared Agentic API Responses event contract in vllm-project/agentic-api#382; it deliberately does not create a second vLLM Responses endpoint PR. |
|
Update: the maintainer handoff is now captured as draft #58783, preserving @gcanlin’s original commit as the first commit and adding the validated parity follow-up as the second. Please continue review there; this original draft can remain as historical context or be closed once the takeover is accepted. |
|
This pull request has merge conflicts that must be resolved before it can be |
Note: This PR is totally vibe coded.
Summary
Add the OpenAI Responses API to the Rust frontend, tracking the feature-parity roadmap in #44280:
POST /v1/responses— string or item-list input (messages, function calls, function call outputs, reasoning), streaming and non-streaming, with reasoning items and function tool calls, mapped onto the sharedvllm-chatpipeline.response.created→response.in_progress→ per-itemresponse.output_item.*/ part deltas →response.completed), with bothevent:anddata:lines and a globally assignedsequence_number. Item IDs stay consistent between streamed events and the terminal payload.store=trueexecutes without retention,background=trueis rejected,previous_response_idandGET/cancelreturn 404,item_referenceinputs are rejected. Onlyfunctiontools are supported; builtin tool types return 400.reasoning.effort(injectsenable_thinkingtemplate kwarg like the Python frontend) andinclude_reasoning.Out of scope (TODOs left in code): harmony channel analysis, builtin-tool execution, response store, logprobs on output items,
truncation="auto",allowed_toolschoices.Searched open PRs for Responses-API/Rust-frontend work; none covers this endpoint (only tracked as pending in #44280).
Tests
cd rust && cargo nextest run -p vllm-server→ 359 passed (12 new Responses API integration tests inroutes/tests.rscovering non-streaming, streaming event order, reasoning include/exclude, function-call roundtrip across turns, and store/tool error paths), clippy/fmt clean.--tool-call-parser deepseek_v4 --reasoning-parser deepseek_v4): verified end-to-end via curl — non-streaming response with reasoning+message items and usage, streaming event sequence, a realget_weatherfunction call, function-call-output multi-turn replay, and all rejection paths. No output/accuracy-affecting change to existing endpoints.AI assistance
This change was implemented with AI (Codex CLI) assistance; all changes were reviewed and tested by the human submitter.