Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b8856c1
feat: add native static router frontends
cquil11 Aug 10, 2026
a888bea
fix: run setup scripts inside router containers
cquil11 Aug 10, 2026
fedc844
fix: advertise vLLM NIXL bootstrap ports
cquil11 Aug 10, 2026
c328cbc
validate SGLang tensor and data parallelism
cquil11 Aug 10, 2026
8605639
fix: label static router health checks accurately
cquil11 Aug 10, 2026
1630a9a
fix: align vLLM Router startup timeout
cquil11 Aug 10, 2026
8affae4
fix: skip local SGLang P/D warmup behind router
cquil11 Aug 10, 2026
0afc6c2
docs: cover stable vllm device binding
cquil11 Aug 10, 2026
5d13899
make router frontend names semantic
cquil11 Aug 10, 2026
d573ba6
Revert "make router frontend names semantic"
cquil11 Aug 10, 2026
be02143
preserve native frontend compatibility
cquil11 Aug 10, 2026
ab98030
test router log capture path
cquil11 Aug 10, 2026
cebe610
support router-native vLLM data parallelism
cquil11 Aug 10, 2026
f8b8e36
feat(runtime): add AMD accelerator device masking
cquil11 Aug 10, 2026
c99c0a4
feat(slurm): support configurable GPU directives
cquil11 Aug 10, 2026
0d61ad7
feat(vllm): add vendor-neutral device binding
cquil11 Aug 10, 2026
1a63cb9
fix(ci): format GPU directive fallback
cquil11 Aug 10, 2026
750dc88
Support node-local runtime config transport
cquil11 Aug 10, 2026
4e2355a
fix: translate node-local log mounts in containers
cquil11 Aug 10, 2026
96f10cb
fix: support immutable worker containers
cquil11 Aug 10, 2026
98a7448
feat: separate submit and runtime source paths
cquil11 Aug 10, 2026
29b9ddd
fix: support immutable AMD control plane
cquil11 Aug 10, 2026
f2e95e4
fix: prefer private fabric addresses
cquil11 Aug 10, 2026
1d1ce61
feat: orchestrate MoRI-IO vLLM workers
cquil11 Aug 10, 2026
10d2bc1
fix: embed cluster profile for node-local jobs
cquil11 Aug 10, 2026
6e3e9b9
fix(router): probe dynamic MoRI readiness end to end
cquil11 Aug 10, 2026
b0c60c3
fix(runtime): preserve default and mock compatibility
cquil11 Aug 10, 2026
cfa04c3
style: format transport fallback
cquil11 Aug 10, 2026
24de885
fix(setup): skip unused infra for native routers
cquil11 Aug 10, 2026
0e18f98
build: add compute-only setup target
cquil11 Aug 10, 2026
9d44a0e
fix(identity): detect native router and AMD MoRI
cquil11 Aug 10, 2026
e1ac409
fix(hf): propagate cache environment during prefetch
cquil11 Aug 10, 2026
3b6b13c
fix(hf): resolve the worker hub cache exactly
cquil11 Aug 10, 2026
ab0ba14
fix(router): propagate runtime srun options
cquil11 Aug 10, 2026
f94026b
test(router): cover runtime srun options
cquil11 Aug 10, 2026
dd0109d
fix(router): skip Dynamo infra for SGLang Router
cquil11 Aug 11, 2026
31e72da
fix(router): use the native SGLang frontend key
cquil11 Aug 11, 2026
8bd8aef
fix(logs): tolerate non-UTF-8 backend output
cquil11 Aug 11, 2026
b4bf6eb
Gate benchmarks on advertised vLLM workers
cquil11 Aug 11, 2026
aca0e48
Merge srt-slurm PR #2 for ATOM integration
cquil11 Aug 11, 2026
633e71f
Merge srt-slurm PR #1 for ATOM integration
cquil11 Aug 11, 2026
9bded70
feat: add ATOM backend and Infera frontend
cquil11 Aug 11, 2026
d93b481
fix(atom): pin Mooncake to advertised worker IP
cquil11 Aug 11, 2026
297da66
fix(infera): honor worker registry health endpoint
cquil11 Aug 11, 2026
f71cbb1
fix(atom): pool Mooncake TCP fallback connections
cquil11 Aug 11, 2026
83ef50b
feat(atom): make Mooncake transport explicit
cquil11 Aug 11, 2026
141f035
fix(fingerprint): detect ATOM and Infera versions
cquil11 Aug 11, 2026
5ecfb13
fix: force Mooncake TCP transport for ATOM
cquil11 Aug 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: lint test test-cov ci check setup cleanup gb200-fp8 gb200-fp4
.PHONY: lint test test-cov ci check setup setup-compute cleanup gb200-fp8 gb200-fp4

NATS_VERSION ?= v2.10.28
ETCD_VERSION ?= v3.5.21
Expand Down Expand Up @@ -40,6 +40,24 @@ gb200-fp4:
srtctl apply -f recipes/gb200-fp4/8k1k/max-tpt.yaml
srtctl apply -f recipes/gb200-fp4/8k1k/mid-curve.yaml

setup-compute:
@case "$(ARCH)" in \
x86_64) ARCH_FILE_PATTERN="x86-64" ;; \
aarch64) ARCH_FILE_PATTERN="aarch64" ;; \
*) echo "❌ Unsupported architecture: $(ARCH)"; exit 1 ;; \
esac; \
echo "--- uv (compute node arch: $(ARCH)) ---"; \
if [ -f bin/uv ] && file bin/uv | grep -q "$$ARCH_FILE_PATTERN"; then \
echo "✅ uv already installed at bin/uv ($(ARCH))"; \
else \
echo "⬇️ Downloading uv for $(ARCH)..."; \
mkdir -p bin; \
UV_URL="https://github.com/astral-sh/uv/releases/latest/download/uv-$(ARCH)-unknown-linux-gnu.tar.gz"; \
curl -LsSf "$$UV_URL" | tar -xz --strip-components=1 -C bin; \
chmod +x bin/uv bin/uvx 2>/dev/null; \
echo "✅ uv installed to bin/uv ($$(file bin/uv | grep -o 'ARM aarch64\|x86-64'))"; \
fi

setup:
@echo "📦 Setting up configs and logs directories..."
@mkdir -p logs
Expand Down Expand Up @@ -90,17 +108,7 @@ setup:
echo "✅ ETCD installed to configs/etcd"; \
fi; \
echo ""; \
echo "--- uv (compute node arch: $(ARCH)) ---"; \
if [ -f bin/uv ] && file bin/uv | grep -q "$$ARCH_FILE_PATTERN"; then \
echo "✅ uv already installed at bin/uv ($(ARCH))"; \
else \
echo "⬇️ Downloading uv for $(ARCH)..."; \
mkdir -p bin; \
UV_URL="https://github.com/astral-sh/uv/releases/latest/download/uv-$(ARCH)-unknown-linux-gnu.tar.gz"; \
curl -LsSf "$$UV_URL" | tar -xz --strip-components=1 -C bin; \
chmod +x bin/uv bin/uvx 2>/dev/null; \
echo "✅ uv installed to bin/uv ($$(file bin/uv | grep -o 'ARM aarch64\|x86-64'))"; \
fi; \
$(MAKE) --no-print-directory setup-compute ARCH=$(ARCH); \
echo ""; \
echo "--- srtslurm.yaml ---"; \
if [ -f srtslurm.yaml ]; then \
Expand Down Expand Up @@ -175,4 +183,4 @@ cleanup:
echo "✅ Cleanup complete!"; \
else \
echo "❌ Cleanup cancelled."; \
fi
fi
94 changes: 89 additions & 5 deletions docs/config-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ The `srtslurm.yaml` file can contain the following fields:
| `default_time_limit` | string | Default job time limit |
| `gpus_per_node` | int | Default GPUs per node |
| `network_interface` | string | Network interface for NCCL |
| `accelerator_vendor` | string | Accelerator runtime: `nvidia` (default) or `amd` |
| `gpu_sbatch_directive` | string | GPU allocation directive: `gpus-per-node`, `gres`, or `none` |
| `runtime_config_transport` | string | Runtime config transport: `shared-filesystem` (default) or `embedded` |
| `srtctl_root` | string | Root directory for srtctl |
| `output_dir` | string | Custom output directory (overrides srtctl_root/outputs) |
| `model_paths` | dict | Model path aliases |
Expand All @@ -121,6 +124,8 @@ The `srtslurm.yaml` file can contain the following fields:

**output_dir**: When set, job logs are written to `output_dir/{job_id}/logs` instead of `srtctl_root/outputs/{job_id}/logs`. Useful for CI/CD and ephemeral environments.

**runtime_config_transport**: Leave this as `shared-filesystem` when the submitter and compute nodes see the same output directory. Use `embedded` when the output path is node-local: srtctl safely embeds the exact resolved recipe and active `srtslurm.yaml` in the Slurm script, materializes both with owner-only permissions on the allocated head node, points compute-side cluster lookups at that embedded profile, and bootstraps the Slurm log from the existing output base into the normal per-job log directory. This preserves container aliases, mounts, accelerator settings, and network selection even when the login and compute nodes do not share the original cluster-profile path. Embedded payloads are data-safe but not a secrets store: users who can inspect Slurm batch scripts can decode them.

**default_bash_preamble**: A shell snippet (e.g. `"ulimit -n 1048576 -s unlimited -u 1048576"`) prepended to every container srun launched by srtctl — workers, frontends, telemetry, benchmark, postprocess. Runs before per-call `bash_preamble` and the main command, so cluster-wide ulimits apply to everything downstream. Silently dropped for distroless containers (e.g. `prom/node-exporter`) that bypass the bash wrapper; a WARNING log is emitted in that case.

**nginx_raise_ulimit**: When set to `true` or `false`, this value is applied to jobs that omit `frontend.nginx_raise_ulimit` in the recipe. Use `true` on clusters where raising the nginx container’s open-file limit is allowed; leave unset if each job should rely on the frontend default (`false`). A recipe that sets `frontend.nginx_raise_ulimit` always wins.
Expand Down Expand Up @@ -270,7 +275,8 @@ Frontend/router configuration.

```yaml
frontend:
# Frontend type: "dynamo" (default), "sglang", or "trtllm_serve"
# Frontend type: "dynamo" (default), "sglang", "vllm-router",
# "trtllm_serve", or direct "vllm".
type: dynamo

# Scaling
Expand All @@ -290,20 +296,56 @@ frontend:
# Environment variables for frontend processes
env:
MY_VAR: "value"

# Optional router-specific image; defaults to model.container
container_image: "router-image"
```

| Field | Type | Default | Description |
| --------------------------- | ---- | ------------- | ----------------------------------- |
| `type` | str | dynamo | Frontend type: "dynamo", "sglang", or "trtllm_serve" |
| `type` | str | dynamo | Frontend type: `dynamo`, `sglang`, `vllm-router`, `trtllm_serve`, or direct `vllm` |
| `enable_multiple_frontends` | bool | true | Scale with nginx + multiple routers |
| `num_additional_frontends` | int | 9 | Additional routers beyond master |
| `nginx_container` | str | nginx:1.27.4 | Custom nginx container image |
| `nginx_raise_ulimit` | bool | false | When true with nginx in use, run `ulimit -n 1048576` before nginx and emit `worker_rlimit_nofile 1048576` in generated `nginx.conf`. Off by default so restrictive clusters do not fail. Cluster `srtslurm.yaml` may set `nginx_raise_ulimit` for jobs that omit this field. |
| `args` | dict | null | CLI args for the frontend |
| `env` | dict | null | Env vars for frontend processes |
| `container_image` | str | null | Router process image; defaults to `model.container` |

For `vllm-router`, srtctl sets Router's `--worker-startup-timeout-secs` to the
total `health_check` window so large-model compilation cannot outlive the router.
Set `frontend.args.worker-startup-timeout-secs` to override it explicitly.

See [SGLang Router](sglang-router.md) for detailed architecture.

### vllm-router frontend

`type: vllm-router` pairs with `backend.type: vllm` and launches the official
`vllm-router` process against direct private `vllm serve` endpoints. Aggregate
layouts use `--worker-urls`; disaggregated layouts use
`--vllm-pd-disaggregation` with the allocated prefill and decode URLs. For
data-parallel endpoints, srtctl derives Router's
`--intra-node-data-parallel-size`. Router expands each node-local backend URL
into DP-aware targets and injects `X-Data-Parallel-Rank`; vLLM continues to own
the engine processes behind that HTTP server. Multi-node DP endpoints use one
hybrid-LB `vllm serve` process per node and require
`backend.dp_launch_mode: per_node`. Direct `frontend.type: vllm` retains its
existing single-server behavior. No NATS or etcd infrastructure is started for
this frontend.

For ROCm P/D deployments, `backend.connector: moriio` switches the same
frontend to vLLM Router's ZMQ discovery mode. srtctl supplies
`--kv-connector moriio`, owns the discovery port, and generates each direct
`vllm serve` worker's role-aware `MoRIIOConnector` JSON from the realized Slurm
node address and HTTP port. This mode requires one router on the head node, so
set `frontend.enable_multiple_frontends: false`.

Router's `/workers` response currently covers its static worker registry, not
the MoRI ZMQ discovery registry. For dynamic MoRI discovery, srtctl therefore
waits on a one-token `/v1/completions` probe instead. This validates that both
roles have registered and that the complete Router-to-prefill-to-decode path is
usable before the configured benchmark begins.

### trtllm_serve frontend

`type: trtllm_serve` runs the `trtllm-serve disaggregated` orchestrator as the
Expand Down Expand Up @@ -422,6 +464,22 @@ Each worker leader gets a globally unique port starting at 5550:
| decode_0 | 5552 |
| decode_1 | 5553 |

### vLLM device binding

Recent vLLM builds accept `--device-ids`. For builds that do not, set
`backend.set_visible_devices: true` and srtslurm will bind each worker with the
accelerator vendor's native environment variable: `CUDA_VISIBLE_DEVICES` for
NVIDIA and `ROCR_VISIBLE_DEVICES` for AMD.

```yaml
backend:
type: vllm
set_visible_devices: true
```

The legacy `set_cuda_visible_devices` field remains supported for existing
recipes, but new cross-platform recipes should use `set_visible_devices`.

### vLLM DP launch mode

vLLM data-parallel endpoints use one process per GPU by default. Set
Expand All @@ -444,6 +502,14 @@ backend:
| `per_gpu` | One process per DP rank/GPU (default) |
| `per_node` | One process manages all DP ranks allocated per node |

For `frontend.type: vllm-router`, Router-native DP expansion keeps one backend
URL per node and sends `X-Data-Parallel-Rank` to select a node-local engine. A
single-node endpoint needs no special launch mode. A multi-node DP endpoint must
use `per_node`; srtctl then derives the global/local DP topology and advertises
each node-local HTTP server to Router. All routed backends must have the same
node-local DP size because Router exposes one global
`--intra-node-data-parallel-size` setting.

`per_gpu` remains the compatibility default for now, but srtslurm will switch
the default to `per_node` in a future release. Existing vLLM DP configurations
should set `backend.dp_launch_mode: per_node` now; srtslurm emits a
Expand All @@ -452,14 +518,32 @@ configuration-time migration warning while they still use `per_gpu`.
In `per_node` mode, srtslurm derives `--data-parallel-size-local` and
`--data-parallel-start-rank` from the allocated topology. Do not set those
two flags manually. srtslurm also always enables `--data-parallel-hybrid-lb`
so every node-local process registers with the Dynamo frontend. This is the
recommended vLLM topology for Dynamo and ensures the frontend can route to
each node-local DP engine. Do not set `data-parallel-hybrid-lb` manually;
so every node-local process registers with the Dynamo frontend or exposes its
local ranks to vLLM Router. This is the recommended multi-node vLLM topology.
Do not set `data-parallel-hybrid-lb` manually;
srtslurm enables it automatically, warns when it is configured, and ignores
the configured value. `headless` is incompatible with `per_node` DP because a
headless process does not register with Dynamo, so srtslurm rejects that
combination during configuration loading.

### vLLM device binding compatibility

Direct `vllm` and `vllm-router` frontends use vLLM's `--device-ids` option by
default. For stable vLLM releases from before
[vllm-project/vllm#45026](https://github.com/vllm-project/vllm/pull/45026),
select the existing CUDA namespace binding instead:

```yaml
backend:
type: vllm
set_cuda_visible_devices: true
```

srtslurm then omits `--device-ids` and scopes sub-node workers with
`CUDA_VISIBLE_DEVICES`. A worker that owns every GPU on its node needs no
explicit CUDA mask. This setting changes only device binding; worker topology,
private router ports, and P/D KV-transfer arguments remain unchanged.

### TRTLLM Backend

When using `type: trtllm`, the backend uses TRTLLM with MPI-style launching:
Expand Down
16 changes: 15 additions & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ If you are trying to deploy onto Grace (GH200, GB200, etc.), you need to use the
make setup ARCH=aarch64 # or ARCH=x86_64
```

Native SGLang Router, vLLM Router, and direct-backend deployments do not run
the Dynamo control plane. CI or cluster launchers for those paths can install
only the compute-architecture `uv` binary:

```bash
make setup-compute ARCH=aarch64 # or ARCH=x86_64
```

This target does not download NATS or etcd and does not create
`srtslurm.yaml`; provide the cluster profile separately.

The setup will:

1. Download NATS/ETCD binaries for your architecture
Expand Down Expand Up @@ -108,7 +119,10 @@ default_time_limit: "4:00:00"
gpus_per_node: 4

# SLURM directive compatibility
use_gpus_per_node_directive: true # Set false if cluster doesn't support --gpus-per-node
gpu_sbatch_directive: gpus-per-node # Use gres for --gres=gpu:N clusters, or none
# Legacy compatibility: use_gpus_per_node_directive: true
accelerator_vendor: nvidia # Use amd for ROCm clusters
runtime_config_transport: shared-filesystem # Use embedded for node-local output paths
use_segment_sbatch_directive: true # Set false if cluster doesn't support --segment
use_exclusive_sbatch_directive: false # Set true if cluster requires --exclusive

Expand Down
7 changes: 5 additions & 2 deletions docs/sglang-router.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SGLang Router Mode

This page explains the sglang router mode for prefill-decode (PD) disaggregation, an alternative to the default Dynamo frontend architecture.
This page explains the first-class SGLang Model Gateway router mode for aggregate
and prefill-decode (PD) topologies, an alternative to the default Dynamo frontend.

## Table of Contents

Expand Down Expand Up @@ -42,7 +43,9 @@ frontend:
type: sglang
```

That's it. The workers will launch with `sglang.launch_server` instead of `dynamo.sglang`, and the router will handle request distribution.
Workers launch with `sglang.launch_server` instead of `dynamo.sglang`, and the
router receives only logical worker-leader URLs from srtctl's allocated
topology.

### Router Arguments

Expand Down
4 changes: 4 additions & 0 deletions src/srtctl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
# Logging utilities (should be first)
# Backend configs
from .backends import (
AtomProtocol,
AtomServerConfig,
BackendConfig,
BackendProtocol,
BackendType,
Expand All @@ -54,6 +56,8 @@
from .logging_utils import setup_logging

__all__ = [
"AtomProtocol",
"AtomServerConfig",
"BackendConfig",
# Backends
"BackendProtocol",
Expand Down
5 changes: 4 additions & 1 deletion src/srtctl/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,19 @@
- TRTLLM: TensorRT-LLM backend with prefill/decode disaggregation
"""

from .atom import AtomProtocol, AtomServerConfig
from .base import BackendProtocol, BackendType, SrunConfig
from .mocker import MockerProtocol, MockerServerConfig
from .sglang import SGLangProtocol, SGLangServerConfig
from .trtllm import TRTLLMProtocol, TRTLLMServerConfig
from .vllm import VLLMProtocol, VLLMServerConfig

# Union type for all backend configs
BackendConfig = SGLangProtocol | TRTLLMProtocol | VLLMProtocol | MockerProtocol
BackendConfig = AtomProtocol | SGLangProtocol | TRTLLMProtocol | VLLMProtocol | MockerProtocol

__all__ = [
"AtomProtocol",
"AtomServerConfig",
"BackendConfig",
# Base types
"BackendProtocol",
Expand Down
Loading
Loading