Skip to content

[Feat] sgl-router: Serve the remaining native gRPC inference RPCs - #42681

Merged
ShangmingCai merged 15 commits into
mainfrom
sgl-router-grpc-all
Oct 16, 2026
Merged

ShangmingCai merged 15 commits into
mainfrom
sgl-router-grpc-all

Conversation

@sherlockwu

@sherlockwu sherlockwu commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #42674 (gRPC ChatComplete). That PR is stacked on #42673 (tonic 0.14).

Complete the router's native gRPC API: each inference RPC behaves like its HTTP route, in both unified and PD fleets.

Changes

RPC Routed like
ChatComplete (#42674) /v1/chat/completions
Complete /v1/completions (new HTTP route too)
Generate, TextGenerate /generate
Embed, TextEmbed, OpenAIEmbed /v1/embeddings
Classify, OpenAIClassify /v1/classify
Rerank /v1/rerank
  • OpenAI RPCs forward the HTTP route's JSON body as is.

  • Typed RPCs are prepared from their fields with the same rules as their HTTP route, with no JSON round trip. Routing inputs are read straight from the proto, and the router's additions (rid, PD bootstrap, DP rank, sampling defaults) are set as fields on each leg.

  • Text prompts are tokenized once, as their HTTP routes forward input_ids:

  • Shared code:

    • HTTP handlers and gRPC calls share one prepare step per Endpoint, and forwarding is generic over the engine RPC.
    • HTTP /v1/completions shares chat's preparation but never forwards input_ids.
  • Not served: Tokenize, HealthCheck, model info and the admin RPCs still answer UNIMPLEMENTED. Score does too, since it has no router HTTP route.

  • Review fixes: [Feat] sgl-router: Serve native gRPC ChatComplete #42674's review fixes (abort by rid, breaker classification, streaming status, router error codes) are carried to the generic forwarding, so they cover every RPC.

  • Second review round:

    • A completions prompt list is a fan-out batch, so it is no longer pinned to one DP rank in PD, and token-id prompts are counted exactly.
    • When routing needs token counts, each text prompt in a batch is tokenized, as /generate does, instead of estimated from bytes. The body is still forwarded as sent.
    • Typed requests count the contract's default n.
    • Typed sampling values compare as the decimal sent (0.7, not the f32's 0.699999988).
    • Typed RPCs carry trace headers sent as gRPC metadata to the engine's trace_headers, except authorization.

Tests

  • Mock tests: tests/proxy/grpc_routing.rs uses mock engines, so CI needs no GPUs. It covers:
    • OpenAI RPCs sending the HTTP body and relaying chunks;
    • PD for chat and typed Generate, under both legacy and reorg routing: the same bootstrap room on both legs, and the same rid;
    • a typed unary Embed;
    • TextGenerate/TextEmbed reaching the engine as Generate/Embed with the router's ids;
    • cancellation;
    • errors.
  • Unit test: checks that the router's additions never override a caller's value.
  • Real engines (manual run, not CI), with router HTTP, router gRPC and direct engine gRPC compared at temperature 0: 27/27 checks pass.
    • Unified Qwen3-4B: chat, completions, Generate, TextGenerate, streaming and non-streaming all match HTTP. The engine's Generate with return_text returns HTTP's text, and none without it. An over-long prompt gets the same 400.
    • PD (prefill + decode over NIXL): every generation RPC matches HTTP and matches the unified outputs.
    • Qwen3-Embedding-0.6B: OpenAIEmbed, Embed, TextEmbed, Classify and OpenAIClassify all match.
    • Reorg routing (--chat-routing reorg), unified and PD: 19/19 generation checks match HTTP and the unified outputs.
    • Qwen3-Reranker-0.6B: Rerank matches. The engine's own gRPC Rerank serialization fix is in [gRPC] Move runtime.v1 bindings to tonic 0.14, report OpenAI status codes, return text on request #42673.

CI States

Latest PR Test (Base): ✅ Run #38857817139
Latest PR Test (Extra): ❌ Run #38857817136
Latest PR Test (AMD ROCm 10): ➖ No AMD PR run found for this commit.

@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file labels Oct 5, 2026
@sherlockwu
sherlockwu added this pull request to stack #42675 October 5, 2026 23:30
@sherlockwu sherlockwu added router run-ci CI: run the baseline test suite on this PR and removed dependencies Pull requests that update a dependency file labels Oct 5, 2026
@sherlockwu
sherlockwu force-pushed the sgl-router-grpc-all branch from af01f4b to 7e37fd5 Compare October 5, 2026 23:37
@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Oct 5, 2026
@sherlockwu
sherlockwu force-pushed the sgl-router-grpc-all branch from 7e37fd5 to f2f98d7 Compare October 6, 2026 00:07
@sherlockwu
sherlockwu force-pushed the sgl-router-grpc-all branch 2 times, most recently from f866fc8 to 82d1ee3 Compare October 6, 2026 00:44
Kan Wu and others added 5 commits October 13, 2026 13:48
`--grpc-port` serves the engine's own sglang.runtime.v1.SglangService, so a
gRPC client can point at the router or at an engine. ChatComplete runs the
same preparation and selection as /v1/chat/completions (rendering, input_ids
forwarding, PD bootstrap, DP rank) and calls the selected engine's native
ChatComplete, whose port comes from /server_info. HTTP is unchanged.

Dispatch is generic over the transport, so PD, guards and metrics are
shared; the SSE pump also carries gRPC chunks. Other RPCs are unimplemented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Docker builds

- Abort an unfinished engine call by the router's rid, as /abort_request
  does over HTTP: a dropped call alone aborts an id of the engine's own.
- Classify a status that ends a stream by its code, so backpressure
  (RESOURCE_EXHAUSTED) no longer opens the breaker.
- Take an engine's plain response to a streaming call (one finished chunk
  with an HTTP status) as that status, for metrics and the breaker.
- Keep the router's error code on streams it ends itself (idle timeout,
  stale expiry, prefill abort) as x-router-error-code.
- Copy proto/ and rust/Cargo.toml into both router Docker builds, keeping
  the repo layout, and run router CI when they change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A non-streaming call that exceeds the request timeout now returns
DEADLINE_EXCEEDED with x-router-error-code: upstream_timeout, like the
streaming timeouts, so clients can tell it from an engine deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sherlockwu and others added 9 commits October 13, 2026 14:16
Route Complete, Generate, TextGenerate, Embed, TextEmbed, Classify,
OpenAIEmbed, OpenAIClassify and Rerank like their HTTP routes and forward
each to the same engine RPC, in unified and PD fleets.

OpenAI RPCs reuse the HTTP body as is. Typed RPCs are prepared through the
JSON their HTTP route reads, and the router's additions (rid, PD bootstrap,
DP rank, sampling defaults) are set back on the proto. HTTP /v1/completions
is added beside Complete, sharing chat's preparation, so every routed RPC
has an HTTP twin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Typed RPCs no longer round-trip through JSON (2 ms at 32k prompt tokens,
9 ms at 128k): routing inputs come straight from the proto, and the
router's additions (rid, PD bootstrap, DP rank, sampling defaults) are set
as fields per leg.

A text prompt is tokenized once, as its HTTP route forwards input_ids:
TextGenerate is sent as Generate with return_text, TextEmbed as Embed, and
Classify with ids. They stay text RPCs when ids are not forwarded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run the gRPC PD test on the legacy and the reorg (bucket-first) selection
paths; both prepare and select through the same steps as HTTP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apply #42674's fixes to the generic engine RPC forwarding: abort an
unfinished call by the engine rid, classify a stream-ending status by its
code, take a plain response to a streaming call as its status, and keep
the router's error code on streams it ends itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… code

Carry #42674's fix to the generic forwarding, so a non-streaming call of
any RPC that exceeds the request timeout returns upstream_timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e; keep gRPC trace context

- A completions prompt list is a batch the engine fans out, so it is no
  longer pinned to one DP rank in PD; token-id prompts count exactly.
- Typed requests count the sampling contract's default n in load.
- Typed sampling values compare as the decimal sent (0.7, not f32's
  0.699999988), so exact and range contracts accept them.
- Typed RPCs carry gRPC-metadata trace headers (not authorization) to
  the engine's trace_headers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When routing needs token counts, tokenize each text prompt of a
completions batch, as /generate does, instead of estimating from bytes;
the body is still forwarded as sent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from sgl-router-grpc-chat to main October 16, 2026 05:11
Resolve conflicts with #42674 (squash-merged), #42818 and #44072:
- prepare() resolves OpenAI-route models with lookup_openai, so HTTP
  `model: "base:adapter"` keeps routing to the base model.
- Leg::new passes the pinned prefill rank so decode still receives
  disagg_prefill_dp_rank under --dp-aware.
- PreparedRequest::completion and ::typed set cache_namespace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ShangmingCai ShangmingCai changed the title [sgl-router] Serve the remaining native gRPC inference RPCs [Feat] sgl-router: Serve the remaining native gRPC inference RPCs Oct 16, 2026

@ShangmingCai ShangmingCai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good.

@ShangmingCai
ShangmingCai merged commit 3d61bdd into main Oct 16, 2026
106 of 111 checks passed
@ShangmingCai
ShangmingCai deleted the sgl-router-grpc-all branch October 16, 2026 08:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation router run-ci CI: run the baseline test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants