Skip to content

feat(grpc): serve /v1/responses in PD mode - #1956

Merged
slin1237 merged 1 commit into
mainfrom
feat/grpc-pd-responses
Jul 23, 2026
Merged

feat(grpc): serve /v1/responses in PD mode#1956
slin1237 merged 1 commit into
mainfrom
feat/grpc-pd-responses

Conversation

@slin1237

@slin1237 slin1237 commented Jul 23, 2026

Copy link
Copy Markdown
Member

Description

Problem

/v1/responses returns 501 Not Implemented on the gRPC PD router (#1946). Since #1923 the gap is purely the router gate: PD shares the Mode-parameterized pipeline that already does prefill/decode pair selection, bootstrap injection, and dual dispatch for chat, and the responses stack touches workers only through that pipeline — but the responses contexts were built only for Mode::Regular.

Solution

Build the responses contexts in every mode except EPD. With the contexts present, the existing endpoint guards pass and the full responses surface — regular + Harmony stacks, MCP tool loop, streaming, conversation history, storage-backed retrieval/cancel — serves over PD unchanged. PD router construction now requires the MCP orchestrator, which production always initializes (AppContext::from_config).

EPD keeps its 501: the responses stack requires the Harmony pipeline, which EPD legitimately doesn't build.

Behavior note: each MCP tool-loop iteration under PD performs a fresh pair selection and bootstrap room, identical to a client-driven chat loop.

Closes #1946.

Changes

  • grpc/router.rs — flip the responses-context gate from mode == Regular to mode == EPD ? (None, None) : build both; mode-aware harmony-pipeline construction error; correct stale comments (some predated this PR: the struct doc claimed harmony_pipeline is Regular-only while PD has built and served it for Harmony chat since refactor(grpc): unify regular/PD/EPD into one Mode-parameterized router + EncodeStage #1923).
  • grpc/router.rs tests — pd_retry_testspd_tests: the fixture now initializes a real McpOrchestrator (multi-thread runtime for the Harmony encoding block_in_place load, matching factory.rs); new tests pin PD behavior (404 for unknown model / missing response id, previously 501) and EPD's kept 501s.
  • factory.rs — stale test-helper comment.
  • e2e_test/router/test_pd_responses.py — new TestPDResponsesGrpc over pd_grpc (sglang + vllm, gpu(2), Llama-3.1-8B, --history-backend memory, openai + smg clients): basic create, streaming events, previous_response_id chaining, store=false. Placed under router/ so e2e-2gpu-pd runs it on all three legs (sglang, vllm-nixl, vllm-mooncake). First e2e coverage of responses over PD.

Test Plan

  • cargo test -p smg: lib 1225 passed; all integration binaries green (api_tests 106, routing_tests 93, spec_test 96, security_tests 50, reliability_tests 26, mcp_test 23, wasm_test 17; 0 failures).
  • New unit tests: pd_router_serves_responses_and_cancel, epd_router_501s_responses_and_cancel.
  • cargo clippy --all-targets -- -D warnings, cargo +nightly fmt --check, ruff check / ruff format --check on the e2e file.
  • e2e: pytest e2e_test/router/test_pd_responses.py (2 GPUs; sglang or E2E_RUNTIME=vllm).
Checklist
  • cargo +nightly fmt passes
  • cargo clippy --all-targets --all-features -- -D warnings passes
  • (Optional) Documentation updated
  • (Optional) Please join us on Slack #sig-smg to discuss, review, and merge PRs

Summary by CodeRabbit

  • New Features

    • Enabled the OpenAI-compatible Responses API for PD routing mode, including response creation, streaming, and multi-step context chaining.
  • Bug Fixes

    • Corrected PD-mode handling so /v1/responses no longer returns “not implemented”.
    • Improved mode-specific error behavior (including missing-model requests and cancellation handling).
    • Ensured store=false responses aren’t retrievable after creation.
  • Tests

    • Added end-to-end coverage for PD Responses API behavior across engines and client implementations, including chaining and streaming semantics.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions github-actions Bot added grpc gRPC client and router changes tests Test changes model-gateway Model gateway crate changes labels Jul 23, 2026
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 77bb080b-78a7-47b8-b323-91f215abeff6

📥 Commits

Reviewing files that changed from the base of the PR and between 57e714c and 2006b80.

📒 Files selected for processing (3)
  • e2e_test/router/test_pd_responses.py
  • model_gateway/src/routers/factory.rs
  • model_gateway/src/routers/grpc/router.rs

📝 Walkthrough

Walkthrough

Changes

PD Responses API support

Layer / File(s) Summary
Mode-aware Responses routing
model_gateway/src/routers/grpc/router.rs
Responses and Harmony contexts are initialized for Regular and PD modes, while EPD retains unsupported behavior.
PD and EPD router validation
model_gateway/src/routers/factory.rs, model_gateway/src/routers/grpc/router.rs
Router helpers initialize the MCP orchestrator and test PD 404 versus EPD 501 responses and cancellation.
End-to-end Responses scenarios
e2e_test/router/test_pd_responses.py
PD gRPC tests cover creation, streaming, response chaining, and store=False retrieval behavior for both clients.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant APIClient
  participant GrpcRouter
  participant MCPOrchestrator
  APIClient->>GrpcRouter: create Responses request
  GrpcRouter->>MCPOrchestrator: process response context
  MCPOrchestrator-->>GrpcRouter: response or stream events
  GrpcRouter-->>APIClient: response or events
Loading

Possibly related issues

Possibly related PRs

  • lightseekorg/smg#1923 — Also updates mode-aware gRPC Responses context initialization for PD and EPD.

Suggested labels: openai

Suggested reviewers: key4ng, catherinesue

Poem

A bunny hops through PD’s new stream,
Responses bloom like a carrot dream.
Chained names echo, events appear,
Stored-away answers vanish clear.
EPD waits while PD takes flight—
Thump, thump, shipped right! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: enabling /v1/responses in PD gRPC mode.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/grpc-pd-responses

Comment @coderabbitai help to get the list of available commands.

@slin1237
slin1237 force-pushed the feat/grpc-pd-responses branch from e48154c to 57e714c Compare July 23, 2026 03:27

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e_test/router/test_pd_responses.py`:
- Around line 59-63: Update the assertions in the response event test to require
a terminal response.completed event rather than accepting response.in_progress,
and additionally assert that the streamed events contain at least one
output-text delta. Keep the existing response.created validation unchanged.

In `@model_gateway/src/routers/grpc/router.rs`:
- Around line 378-379: Make GrpcRouter::new runtime-neutral by changing the
Harmony initialization through RequestPipeline::build so its encoding load does
not rely on block_in_place. Use async-compatible offloading for the blocking
work, preserving the existing unsupported (endpoint, mode) handling while
ensuring initialization works on current-thread Tokio runtimes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e373df7b-bd3f-47a8-b05a-872a75cc6cf1

📥 Commits

Reviewing files that changed from the base of the PR and between 6e31345 and 57e714c.

📒 Files selected for processing (3)
  • e2e_test/router/test_pd_responses.py
  • model_gateway/src/routers/factory.rs
  • model_gateway/src/routers/grpc/router.rs

Comment thread e2e_test/router/test_pd_responses.py Outdated
Comment on lines +378 to 379
// `None` when the (endpoint, mode) combo is unsupported; those endpoints 501.
let harmony_pipeline = RequestPipeline::build(Endpoint::Harmony, mode, &configured_deps);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C2 'tokio::main|new_current_thread|flavor\s*=\s*"current_thread"|GrpcRouter::new|RouterFactory::create_router' model_gateway

Repository: lightseekorg/smg

Length of output: 16936


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the PD router construction and any block_in_place usage around the cited area.
sed -n '340,430p' model_gateway/src/routers/grpc/router.rs

echo '---'
rg -n -C3 'block_in_place|Harmony|PrefillDecode|RequestPipeline::build' model_gateway/src/routers/grpc/router.rs model_gateway/src/routers/factory.rs model_gateway/src/health.rs model_gateway/tests -g '!**/target/**'

Repository: lightseekorg/smg

Length of output: 30219


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for any block_in_place usage along the router construction path.
rg -n -C3 'block_in_place|spawn_blocking|RequestPipeline::build|Endpoint::Harmony|GrpcRouter::new' model_gateway/src/routers model_gateway/tests

Repository: lightseekorg/smg

Length of output: 16548


model_gateway/src/routers/grpc/router.rs:379 — Make PD router init runtime-neutral. GrpcRouter::new eagerly builds Harmony here, and Harmony’s encoding load still uses block_in_place, which will panic on a current-thread Tokio runtime. Use async offloading instead of relying on multi-thread-only tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/router.rs` around lines 378 - 379, Make
GrpcRouter::new runtime-neutral by changing the Harmony initialization through
RequestPipeline::build so its encoding load does not rely on block_in_place. Use
async-compatible offloading for the blocking work, preserving the existing
unsupported (endpoint, mode) handling while ensuring initialization works on
current-thread Tokio runtimes.

Build the responses contexts for every mode except EPD, so PD serves the
full responses surface (regular + Harmony, MCP tool loop, streaming,
storage) through the existing PD pipeline. Add gRPC PD responses e2e
coverage.

Closes #1946

Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com>
@slin1237
slin1237 force-pushed the feat/grpc-pd-responses branch from 57e714c to 2006b80 Compare July 23, 2026 03:35

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2006b80e9b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +385 to +387
let (responses_context, harmony_responses_context) = if mode == Mode::EncodePrefillDecode {
(None, None)
} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve per-request storage context for PD responses

When --storage-context-headers or a storage hook is configured, PD /v1/responses now enters this branch during router construction, so current_request_context() below is evaluated at startup rather than under the request middleware's task-local. The resulting responses_context is then reused by the non-Harmony responses path, so persisted PD responses lose tenant/user header context and hooks cannot apply per-request scoping. Please build the ResponsesContext (or refresh its request context) inside route_responses_impl for each request, similar to the Harmony branch.

Useful? React with 👍 / 👎.

@slin1237
slin1237 merged commit 8ee05db into main Jul 23, 2026
48 of 49 checks passed
@slin1237
slin1237 deleted the feat/grpc-pd-responses branch July 23, 2026 04:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

grpc gRPC client and router changes model-gateway Model gateway crate changes tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: /v1/responses api support for grpc_pd

1 participant