Skip to content

feat(frontends): add the llm-d router - #45

Open
cquil11 wants to merge 25 commits into
mainfrom
feat/llm-d-frontend
Open

cquil11 wants to merge 25 commits into
mainfrom
feat/llm-d-frontend

Conversation

@cquil11

@cquil11 cquil11 commented Oct 5, 2026 •

Copy link
Copy Markdown
  • Add frontend.type: llm-d: Envoy and the Endpoint Picker (EPP) for aggregated and prefill/decode vLLM jobs.
  • Generate worker discovery and router configs after workers pass health checks; gate readiness on Envoy and EPP.
  • Run a pd-sidecar per decode worker, with allocated proxy ports and NIXL KV transfer.
  • Add frontend.epp_config, docs/llm-d.md, and aggregated/disaggregated/DP-rank examples.
  • Route to vLLM DP ranks: data-parallel-external-lb: true launches every rank as its own vllm serve --data-parallel-rank on its own port, one EPP endpoint (and decode sidecar) per rank, on one or several nodes.
  • Route by KV-cache events: roles.<role>.kv_events connects every worker's (or rank's) vLLM publisher to the EPP's precise-prefix-cache-producer on one socket srtctl owns, with topics naming each endpoint, and points the token-producer at a worker's render endpoint.
  • Without frontend.epp_config, run the scorers of llm-d's no-Kubernetes guide (prefill and decode profiles for P/D); the EPP's own default never applied, since srtctl's file always carries discovery.
  • Map a decode MultiConnector (NIXL next to OffloadingConnector CPU offloading) to the nixlv2 sidecar protocol.
  • Scope: vLLM only, one router replica, static discovery (a failed worker or sidecar ends the job). KV events use the EPP's global socket: v0.11 file discovery has no per-endpoint rankIndex, so per-pod subscription and vLLM's multi-port DP supervisor are not used.
  • Reuse the SMG frontend's worker readiness gate. Follow llm-d-router v0.11.0's file-discovery API.
flowchart TD
    Client --> Envoy
    Envoy <-->|Endpoint selection<br/>over gRPC| EPP
    Envoy -->|Aggregated request| Agg["vLLM aggregate worker"]
    Envoy -->|Disaggregated request| Sidecar["Decode P/D sidecar"]
    Sidecar -->|Run prefill| Prefill["vLLM prefill worker"]
    Sidecar -->|Decode with<br/>transfer parameters| Decode["vLLM decode worker"]
    Prefill -.->|KV cache via NIXL| Decode
Loading

Validation

AgentX through InferenceX #3741, with image quay.io/rh-ee-imarkov/llm-d-nokube-vllm:dspark-0814-nightly (EPP and sidecar v0.10.0). MiniMax-M3 NVFP4 at concurrency 8 unless noted. The first four rows ran cd129c2e; the rest ran 0c209710, the same commits before the rebase onto upstream main.

Scenario Hardware Run
DeepSeek-V4-Pro 1P1D, DEP8 prefill / DEP8 decode, NIXL, c64 GB200 37396765122
DeepSeek-V4-Pro aggregated, DEP8, c16 GB200 37396830944
MiniMax-M3 1P1D, TP4 / TP4, NIXL, c8 GB300 37396770618
MiniMax-M3 aggregated, 2x TP4, c8 GB300 37396773335
Precise prefix, aggregated 2x TP4 GB300 37553005166
Precise prefix, 1P1D TP4 / TP4 GB300 37553008202
DP ranks, one node, DP2 x TP2, precise prefix GB300 37553011355
DP ranks, two nodes, DP4 x TP2, precise prefix GB300 37553014497
DP ranks, 1P1D DP2 x TP2 each, precise prefix GB300 37553017562
Precise prefix over CPU offload, 1P1D TP4, NIXL + OffloadingConnector in a MultiConnector GB300 37553020113
No epp_config (guide scorers), aggregated 2x TP4 GB300 37553023865
Baseline rerun, aggregated 2x TP4 GB300 37553026821

@cursor cursor 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.

Stale Bugbot comment from a previous run.

Comment thread src/srtctl/frontends/llm_d.py
Comment thread src/srtctl/frontends/llm_d.py
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

src/srtctl diff: +1066 -370

cquil11 added a commit to SemiAnalysisAI/InferenceX that referenced this pull request Oct 6, 2026
Carry SemiAnalysisAI/srt-slurm#45 at 96fae460 in the llm-d frontend patch and add DNM keys for precise prefix-cache routing (aggregated and prefill/decode) and for routing to external-LB DP ranks (one node, two nodes, and prefill/decode) with MiniMax-M3 on GB300.

Signed-off-by: Cam Quilici <cjquilici@gmail.com>

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 96fae46. Configure here.

Comment thread src/srtctl/backends/vllm.py
cquil11 added a commit to SemiAnalysisAI/InferenceX that referenced this pull request Oct 6, 2026
Carry SemiAnalysisAI/srt-slurm#45 at 33ccb769, add a DNM key for precise prefix-cache routing over CPU KV offloading, and run the two-node DP-rank key with the MNNVL all-reduce backend.

Signed-off-by: Cam Quilici <cjquilici@gmail.com>
cquil11 added a commit to SemiAnalysisAI/InferenceX that referenced this pull request Oct 7, 2026
Carry SemiAnalysisAI/srt-slurm#45 at 0c209710 and add a DNM key that runs the llm-d frontend without an epp_config.

Signed-off-by: Cam Quilici <cjquilici@gmail.com>
@cquil11
cquil11 force-pushed the feat/llm-d-frontend branch from 0c20971 to 82a76fe Compare October 7, 2026 22:18
@cquil11
cquil11 changed the base branch from feat/smg-frontend to main October 7, 2026 22:18
@cquil11
cquil11 force-pushed the feat/llm-d-frontend branch from 82a76fe to e473e3c Compare October 7, 2026 22:20
)
return dp_size, model_parallel_size

def _get_int_flag(self, mode: WorkerMode, name: str, default: int = 1) -> int:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

FYI:
This diff is just general cleanup as this is mainly duplicated logic
For instance _get_int_flag functionality is achieved via _get_parallel_size

cquil11 added 17 commits October 8, 2026 17:08
frontend.type: llm-d runs llm-d's Endpoint Picker (EPP) behind Envoy in
front of direct vLLM workers. llm-d discovers workers through Kubernetes
or a file, so srtctl is the registrar: once every worker answers /health
it writes the EPP's file-discovery endpoints, the EPP configuration (the
recipe's new frontend.epp_config plus the discovery plugin), and the
Envoy configuration into the log directory, then starts the EPP and
Envoy on the router node. The job is ready when Envoy's admin /ready
answers and the EPP's llm_d_epp_ready_endpoints gauge counts every
endpoint.

For prefill/decode the frontend implies llm-d's P/D sidecar as a
per-worker service (type llm-d-sidecar) on every routable decode worker,
started before the workers and gated on its /health. The EPP addresses
the sidecar, which runs the prefill and hands the KV transfer
parameters to its vLLM. Its listener is a new allocator kind,
PROXY_PORTS, carried on Process.proxy_port: a frontend names the modes
it reaches through a proxy (Frontend.proxied_worker_modes) and the job
topology allocates the port for their routable workers; a service kind
attaches its per-worker instances only where it applies
(ServiceKind.attaches_to). Per-worker services also get
{worker_http_port} and {worker_proxy_port}. The sidecar's --kv-connector
protocol comes from the decode workers' vLLM connector class
(VLLMBackend.kv_connector_class); NixlConnector maps to nixlv2.

Signed-off-by: Cam Quilici <cjquilici@gmail.com>
A client that scrapes the public endpoint's /metrics (AIPerf does) would get whichever worker the EPP picked, a different one on every scrape. Envoy answers it with 404, as llm-d's own Slurm submission does; the workers and the EPP serve their metrics on their own listeners.

Signed-off-by: Cam Quilici <cjquilici@gmail.com>
Signed-off-by: Cam Quilici <cjquilici@gmail.com>
data-parallel-external-lb launches every vLLM DP rank as its own vllm serve that the EPP addresses. roles.<role>.kv_events connects the workers' KV-cache event publishers to the EPP's precise prefix-cache producer, with srtctl wiring the event socket, topics, and the token producer's render URL.

Signed-off-by: Cam Quilici <cjquilici@gmail.com>
The EPP falls back to its own default only for a configuration file without plugins, and srtctl's always carries file discovery, so a recipe without frontend.epp_config ran no scorers. srtctl now writes the scorers of llm-d's no-Kubernetes guide, split into prefill and decode profiles for a prefill/decode job.

Signed-off-by: Cam Quilici <cjquilici@gmail.com>
A decode worker that runs NIXL next to vLLM's OffloadingConnector in a MultiConnector (llm-d's tiered wide-EP layout) now gets the nixlv2 sidecar protocol instead of being rejected.

Signed-off-by: Cam Quilici <cjquilici@gmail.com>
Signed-off-by: Cam Quilici <cjquilici@gmail.com>
…am sync

Signed-off-by: Cam Quilici <cjquilici@gmail.com>
Signed-off-by: Cam Quilici <cjquilici@gmail.com>
Signed-off-by: Cam Quilici <cjquilici@gmail.com>
@cquil11
cquil11 force-pushed the feat/llm-d-frontend branch from 389d2dd to 01947da Compare October 8, 2026 22:09
Signed-off-by: Cam Quilici <cjquilici@gmail.com>
Signed-off-by: Cam Quilici <cjquilici@gmail.com>
The validated image ships the v0.10.0 EPP and P/D sidecar. Every flag,
plugin type, metric and endpoint srtctl relies on exists at v0.10.0.

Signed-off-by: Cam Quilici <cjquilici@gmail.com>
Signed-off-by: Cam Quilici <cjquilici@gmail.com>
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.

1 participant