Repository navigation
Conversation
This was referenced Oct 29, 2026
BugenZhao
force-pushed
the
bz/engine-rpc
branch
from
October 29, 2026 13:41
6cc1ce5 to
114e3fb
Compare
EngineClient over an internal MessagePack gRPC serviceLlm over an internal MessagePack gRPC service
BugenZhao
force-pushed
the
bz/engine-rpc
branch
from
October 29, 2026 14:01
114e3fb to
96ef870
Compare
BugenZhao
force-pushed
the
bz/engine-rpc
branch
from
October 29, 2026 14:08
96ef870 to
01e4c2f
Compare
BugenZhao
force-pushed
the
bz/engine-rpc
branch
from
October 30, 2026 07:54
01e4c2f to
3b0eda0
Compare
BugenZhao
force-pushed
the
bz/engine-rpc
branch
from
November 4, 2026 08:36
3b0eda0 to
7ac3655
Compare
BugenZhao
force-pushed
the
bz/engine-rpc
branch
4 times, most recently
from
November 5, 2026 09:01
e6e6279 to
d42966f
Compare
Add `vllm-remote-llm` so one Rust frontend can use another frontend's `Llm`, for example a router frontend that keeps rendering, tokenization, detokenization, and parsing local while workers run `vllm-rs serve`. The wire carries the `vllm-llm` types as they are: a lowered `GenerateRequest` in, a stream of `GenerateOutput`s out. Mirroring them in `vllm-proto` would turn engine-core internals (`_all_stop_token_ids`, `_backend`, ...) into a published schema that needs a release for every change. Instead: - `vllm.internal.Llm` is defined with `tonic_build::manual`, with no `.proto`, and a MessagePack codec that encodes fields by name, like the engine-core ZMQ wire. `Generate` is server-streaming; `Info` returns the server `Llm`'s `LlmInfo`; `Abort` takes external request IDs. - `LlmInfo`, `GenerateRequest`, `GenerateOutput`, and the types they carry derive serde; `None` fields are skipped. - The protocol is internal and unversioned. Adding an `Option` or `#[serde(default)]` field to a struct with named fields stays compatible between adjacent revisions; adding a field to an engine-core type encoded as a tuple, such as `LoraRequest` or the tensor types, does not, nor do renames, type changes, and new enum variants. `vllm-server` serves `vllm.internal.Llm` on the gRPC port next to Inference and Control; its health status follows readiness, like `vllm.Inference`. `RemoteLlm` uses only that service and health, so the server may run any `Llm`, not only an engine-core one: it adopts the server's `LlmInfo`, including the model dtype, so multimodal inputs are prepared on the calling side; it aborts through `Abort`, which rejects an empty list, since aborting every request of a shared server is not one caller's to do; and it reports the server's `vllm.internal.Llm` status as its readiness. Its health stays `true`: a failing server makes it unready, not failed, since restarting this process would not fix it. A request with `n > 1` is rejected as unsupported for now; carrying `n` to the server, so its `Llm` fans out the samples, is left as a follow-up. Round-trip tests run the same requests through an `EngineCoreLlm` and through a `RemoteLlm` against a served `EngineCoreLlm`, and compare what the engine receives and what the caller gets back, including stop strings and logprobs, against a server that serves no Control. Another test checks that `RemoteLlm` adopts the server's `LlmInfo` and aborts by external ID, and another pauses the server and checks that the `RemoteLlm` turns unready and back while staying healthy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJckCBxv8Gtnq23avKQwau Signed-off-by: Bugen Zhao <i@bugenzhao.com>
BugenZhao
force-pushed
the
bz/engine-rpc
branch
from
November 5, 2026 09:13
d42966f to
aa9b199
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
A router frontend that keeps rendering, tokenization, detokenization, and parsing local needs to reach
vllm-rs serveworkers at theLlmboundary, without losing anything on the way. The existing public interfaces do not fit:Inference.Generateand/inference/v1/generatetake user-facing sampling parameters, so a request the frontend has already lowered would have to be mapped back, losing fields such as the structured-output backend, and the worker would apply its own defaults again.GenerateRequest/GenerateOutputinvllm-protowould turn engine-core internals (_all_stop_token_ids,_backend, ...) into a published schema that needs a release for every change.Changes
vllm-remote-llmwithRemoteLlm: Llm.vllm.internal.Llm: a gRPC service defined withtonic_build::manual, without a.proto, with a MessagePack codec that encodes fields by name, like the engine-core ZMQ wire.Generateis server-streaming;Inforeturns the serverLlm'sLlmInfo;Aborttakes external request IDs.LlmInfo,GenerateRequest,GenerateOutput, and the types they carry derive serde, skippingNonefields.vllm-serverservesvllm.internal.Llmon the gRPC port next to Inference and Control; its health status follows readiness, likevllm.Inference.RemoteLlmuses onlyvllm.internal.Llmand health, so the server may run anyLlm, not only an engine-core one (Control is served only with an engine core). It adopts the server'sLlmInfo, including the model dtype, so multimodal inputs are prepared on the calling side; it aborts throughAbort, which rejects an empty list, since aborting every request of a shared server is not one caller's to do; and it reports the server'svllm.internal.Llmstatus as its readiness. Its health staystrue: a failing server makes it unready, not failed, since restarting the caller would not fix it.The protocol is internal and unversioned. Adding an
Optionor#[serde(default)]field to a struct with named fields stays compatible between adjacent revisions. Engine-core types encoded as tuples, such asLoraRequestand the tensor types, are positional, so adding a field there does not, nor do renames, type changes, and new enum variants. The crate docs state this rule.Stack
Llmtrait vllm-project/vllm#64695 ([Rust Frontend] Generate through anLlmtrait #82):Llmtrait and error contract, mergedLlmvllm-project/vllm#64729 ([Rust Frontend] Let the server run over anyLlm#83): the server over anyLlmLlmover an internal MessagePack gRPC service #84: internal MessagePack gRPC remoteLlm(this PR)Validation
cargo clippy --workspace --all-targets --all-features,cargo fmt --check: clean.cargo nextest runon llm, text, chat, server, remote-llm, proto, engine-core-client, cmd, bench: 1802 passed, on main9901ffcb4a. The other 4 (vllm-chatroundtrip and grammar replay) download real Hugging Face models and fail locally only because the shared model cache is not readable on this machine.EngineCoreLlmand through aRemoteLlmagainst a servedEngineCoreLlm, and compare the request the engine receives and the outputs the caller gets, covering stop strings, logprobs, and raw outputs, against a server that serves no Control. Another test checks thatRemoteLlmadopts the server'sLlmInfoand aborts by external ID; it fails if the server'sInfodrops the dtype or itsAbortdoes nothing. On the original branch, droppingfrequency_penaltyorkv_transfer_paramson the server side made them fail. Another test pauses the server and checks that theRemoteLlmturns unready and back while staying healthy; it fails if the server stops publishingvllm.internal.Llmreadiness or ifRemoteLlmreports readiness as health.vllm-rs serveworkers (DP=2 each, mock engines) behind a downstream router frontend built onRemoteLlm. On this revision: the router adopted the workers'LlmInfowith the model dtype, and chat, 40 concurrent chats, and a router-side stop string all succeeded. On the previous revision (after the rebase onto9901ffcb4a): after one worker was killed the router's/healthstayed 200 and 20 concurrent requests all succeeded on the other worker, and a chat request withn: 2got a 400 from the router, while the worker serves it directly.Open questions
vllm-rs serveor an extension hook plus a custom worker binary.connecthas a TODO.RemoteLlmrejectsn > 1as unsupported. Carryingnwould need the client to split one call into a stream per sample; a version that does is onbz/remote-llm-n, kept until something needs it.🤖 Generated with Claude Code
https://claude.ai/code/session_01CJckCBxv8Gtnq23avKQwau