Skip to content

[Rust Frontend] Let the server run over any Llm - #83

Closed
BugenZhao wants to merge 28 commits into
bz/engine-client-traitfrom
bz/engine-client-server
Closed

BugenZhao wants to merge 28 commits into
bz/engine-client-traitfrom
bz/engine-client-server

Conversation

@BugenZhao

@BugenZhao BugenZhao commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

Upstream: vllm-project#64729 (stacked on vllm-project#64695).

Motivation

With the Llm trait (vllm-project#64695), a frontend can build TextLlm and ChatLlm over an Llm that is not backed by an engine-core client, but it cannot run the server over one:

  • serve() always connects to engine-core and builds an EngineCoreLlm. A router frontend embedding the server has to re-assemble AppState, the router, listeners, TLS, gRPC, and graceful shutdown itself; a frontend-only vllm-rs mode (such as the HTTP generation client planned in [Rust Frontend] engine client interface vllm-project/vllm#63887) has no place to plug its Llm in.
  • Admin, LoRA lifecycle, checkpoint, /kv_event_sources routes and the gRPC Control service call the engine-core client directly.
  • A request that cannot be taken right now surfaces as a 500, so callers cannot tell that a retry may succeed.

Changes

  1. Split engine-core settings out of the server config (pure refactor). The transport and coordinator modes, stream interval, stats logging, request ID randomization, engine lifecycle, static LoRA adapters, and profiler move to EngineCoreConfig; the rest is renamed ServerConfig. Starting the server goes through a Server builder instead of serve and serve_with_router_extension, with LlmSource naming where the server's Llm comes from:

    Server::new(config, LlmSource::EngineCore(engine_core))      // vllm-cmd
    Server::new(config, LlmSource::Provided(Box::new(routed)))   // a router frontend
        .router_extension(f)
        .serve(shutdown)
        .await

    vllm-cmd builds both halves as a FrontendConfig; /server_info still reports one flat rust_frontend_config.

  2. Serve over a caller-built Llm.

    • LlmSource::Provided(llm) builds the state around the given Llm instead of connecting to engine-core; model backends, listeners, TLS, gRPC, and graceful shutdown are shared. TextLlm::from_boxed keeps the box as is, so the server still recognizes an EngineCoreLlm by downcasting.
    • A provided Llm is opaque. Routes and services that drive the engine-core client are only served over an EngineCoreLlm: dev-mode admin routes, runtime LoRA loading, checkpoints, /kv_event_sources, and gRPC Control. gRPC health follows the Llm and covers only the served services.
    • An EngineCoreLlm passed as Provided is rejected at startup. It would otherwise get the engine-core routes but none of the settings in EngineCoreConfig (server info, profiler, static LoRA, engine shutdown), and its request histograms would already be built with the default buckets, since the server applies --custom-histogram-buckets while building its state.
    • Unavailable maps to HTTP 503 and gRPC UNAVAILABLE.

AppState and build_router stay private, in line with vllm-project#43774.

Stack

  1. [Rust Frontend] Generate through an Llm trait vllm-project/vllm#64695 (upstream draft): Llm trait and error contract
  2. [Rust Frontend] Let the server run over any Llm #83: this PR
  3. [Rust Frontend] Remote Llm over an internal MessagePack gRPC service #84: internal MessagePack gRPC remote Llm (not yet restacked onto this version)

Validation

  • cargo clippy --workspace --all-targets --all-features, cargo fmt --check: clean.
  • cargo nextest run -p vllm-llm -p vllm-text -p vllm-chat -p vllm-server -p vllm-cmd -p vllm-bench: 1505 passed. The vllm-cmd config snapshots change only by nesting fields under server (ServerConfig) and engine_core.
  • New llm_without_engine_core_serves_generation_routes_only: over a fake Llm with no engine-core client and dev mode plus runtime LoRA enabled, engine-core routes return 404, /health follows the Llm, and an unavailable Llm gives 503. It fails without the route gating (the handler panics on the missing engine-core client) and without the mapping (500).
  • New provided_engine_core_llm_is_rejected: an EngineCoreLlm is rejected as a provided Llm and other Llms pass; it fails when the check is removed.
  • Fit check with a downstream router frontend, on a local restack of [Rust Frontend] Remote Llm over an internal MessagePack gRPC service #84: its custom AppState + build_router + axum::serve + drain loop became Server::new(config, LlmSource::Provided(..)).router_extension(..).serve(shutdown) (checked with the earlier Server::llm spelling of the same entry), and it no longer loads model backends itself. Against two vllm-rs serve workers over mock engines (DP=2 each): chat, stop strings, streaming usage, 40 concurrent requests, /kv_event_sources → 404 on the router, and SIGTERM draining an in-flight request to 200 all work.

Open questions

  • Building a ServerConfig by hand needs all 30 fields; the router fit spelled out ~30 lines of defaults. A Default or a small constructor would help embedders, at the cost of a second set of defaults next to the CLI's.
  • gRPC over a caller-built Llm (Inference and health only) is not exercised end to end yet.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CJckCBxv8Gtnq23avKQwau

@BugenZhao
BugenZhao added this pull request to stack #85 October 29, 2026 12:30
@BugenZhao
BugenZhao force-pushed the bz/engine-client-server branch from 9151a59 to a7849cd Compare October 29, 2026 13:41
@BugenZhao BugenZhao changed the title [Rust Frontend] Serve the HTTP API over a non-local EngineClient [Rust Frontend] Serve the HTTP API over any Llm Oct 29, 2026
@BugenZhao
BugenZhao force-pushed the bz/engine-client-server branch from a7849cd to de1b88e Compare October 29, 2026 14:01
@BugenZhao
BugenZhao force-pushed the bz/engine-client-server branch from de1b88e to 97a1cb6 Compare October 29, 2026 14:08
@BugenZhao
BugenZhao force-pushed the bz/engine-client-server branch 2 times, most recently from a28e675 to 374096e Compare October 30, 2026 09:15
@BugenZhao BugenZhao changed the title [Rust Frontend] Serve the HTTP API over any Llm [Rust Frontend] Let the server run over any Llm Oct 30, 2026
@BugenZhao
BugenZhao force-pushed the bz/engine-client-server branch 2 times, most recently from 560ee38 to cd5d4ee Compare October 30, 2026 09:51
@BugenZhao
BugenZhao force-pushed the bz/engine-client-server branch 2 times, most recently from 6486976 to 9740d6c Compare November 3, 2026 06:31
@BugenZhao
BugenZhao force-pushed the bz/engine-client-server branch from 9740d6c to 6f0f4f9 Compare November 4, 2026 08:36
AndreasKaratzas and others added 8 commits November 4, 2026 22:02
…rnel (vllm-project#62725)

Signed-off-by: Andreas Karatzas <akaratza@amd.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
Signed-off-by: Juntian Liu <Juntianl777@gmail.com>
Signed-off-by: Nick Hill <nickhill123@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Nick Hill <nickhill123@gmail.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…lm-project#63711)

Signed-off-by: Ganesh R <ganesh.r@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Douglas Lehr <91553416+dllehr-amd@users.noreply.github.com>
…t#60824)

Signed-off-by: Chinmay Kulkarni <Chinmay.Kulkarni@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Douglas Lehr <91553416+dllehr-amd@users.noreply.github.com>
…llm-project#65293)

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t the role (vllm-project#65294)

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…a on_new_request (vllm-project#61292)

Signed-off-by: Dao Le <Dao007forever@gmail.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Sy0307 <24841857+Sy0307@users.noreply.github.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Sage Ahrac <80211083+sagearc@users.noreply.github.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
JimmyToluene and others added 20 commits November 5, 2026 04:56
…project#65692)

Signed-off-by: jimmyjia <jimmytoluene@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…roject#65810)

Signed-off-by: khluu <khluu000@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…llm-project#65412)

Signed-off-by: artemspector <artems@il.ibm.com>
Co-authored-by: artemspector <artems@il.ibm.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…long prefills (vllm-project#64898)

Signed-off-by: Gin-Sin <ginxian@foxmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
Co-authored-by: Thien Tran <gau.nernst@yahoo.com.sg>
Signed-off-by: Yan Ma <yan.ma@intel.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
…ayer (vllm-project#65806)

Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
… top-k writes (vllm-project#63888)

Signed-off-by: Oxana Korzh <okorzh@amd.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…h --monokernel (vllm-project#63891)

Signed-off-by: Oxana Korzh <okorzh@amd.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…weight writes (vllm-project#64886)

Signed-off-by: Oxana Korzh <okorzh@amd.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: zexplorerhj <zhjoneson@163.com>
vllm-project#63117)

Signed-off-by: BaoYunkai <269084791+BaoYunkai@users.noreply.github.com>
Co-authored-by: BaoYunkai <269084791+BaoYunkai@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
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>
Signed-off-by: jackyYang6 <jacky6.yang@gmail.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
Signed-off-by: Allan Lin <allin@redhat.com>
Co-authored-by: Shengqi Chen <harry-chen@outlook.com>
…roject#64880)

Signed-off-by: Hollow Man <hollowman@opensuse.org>
Co-authored-by: vllm-agent <claw@inferact.ai>
Co-authored-by: linitra24 <renshuang.zhou@daocloud.io>
…#65812)

Signed-off-by: lHrHenry233 <2381623149@qq.com>
Signed-off-by: Thien Tran <gau.nernst@yahoo.com.sg>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Co-authored-by: Thien Tran <gau.nernst@yahoo.com.sg>
Co-authored-by: Kimi <noreply@moonshot.cn>
Co-authored-by: vllm-agent <claw@inferact.ai>
…e MXFP4 linear/MOE quant methods (vllm-project#51808)

Signed-off-by: Felix Marty <Felix.Marty@amd.com>
Co-authored-by: vllm-agent <claw@inferact.ai>
The server `Config` mixed the server's own settings with settings that only
apply when the server connects to engine-core and drives it through an
`EngineCoreLlm`: the transport and coordinator modes, stream interval, stats
logging, request ID randomization, engine lifecycle, static LoRA adapters,
and the profiler. Move those into `EngineCoreConfig`, which carries its own
validation and coordinator helpers, and rename the rest `ServerConfig`.

Starting the server goes through a `Server` builder instead of `serve` and
`serve_with_router_extension`:
`Server::new(config, LlmSource::EngineCore(engine_core))`, optionally
`.router_extension(...)`, then `.serve(shutdown)`. `LlmSource` names where
the server's `Llm` comes from. `vllm-cmd` builds both halves as a
`FrontendConfig`, and `/server_info` still reports them as one flat
`rust_frontend_config` object.

No behavior changes.

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CJckCBxv8Gtnq23avKQwau
A downstream binary, or `vllm-rs` itself with another generation backend,
can build an `Llm` that is not backed by an engine-core client, such as a
router over several engines or a client for a remote engine. Let the server
run over one:

- `LlmSource::Provided(llm)` builds the state around the given `Llm`
  instead of connecting to engine-core; model backends, listeners, TLS,
  gRPC, and graceful shutdown stay the same. `TextLlm::from_boxed` takes the
  boxed `Llm` without wrapping it again, so the server can still recognize
  an `EngineCoreLlm`.
- The server treats a provided `Llm` as opaque. Routes and services that
  drive the engine-core client directly are only served over an
  `EngineCoreLlm`: the dev-mode admin routes, runtime LoRA loading,
  checkpoints, `/kv_event_sources`, and the gRPC Control service. An
  `EngineCoreLlm` passed as `Provided` is rejected: it would miss its
  engine-core settings, and its request histograms would already use the
  default buckets.
- gRPC health publishes `vllm.Control` only when it is served.
- `Unavailable` errors map to HTTP 503 and gRPC `UNAVAILABLE` instead of an
  internal error, so callers know a retry may succeed.

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CJckCBxv8Gtnq23avKQwau
@BugenZhao
BugenZhao force-pushed the bz/engine-client-server branch from 6f0f4f9 to 21d7d92 Compare November 5, 2026 07:42
@BugenZhao BugenZhao closed this Nov 5, 2026
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.