Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 26 additions & 1 deletion lib/sidecar/vllm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ It is a standalone Rust executable.
- Sampling, stop conditions, structured output, logprobs, cache options, and priority
- Opaque `kv_transfer_params` handoff
- Data-parallel rank routing and KV-event source discovery
- Capability-gated RL pause/resume, sleep/wake, weight-transfer, and weight-version controls through native gRPC
- Image URL and data-URI inputs, including media UUIDs

The protocol does not support LoRA, encode workers, beam search, `n > 1`,
Expand Down Expand Up @@ -56,7 +57,29 @@ dynamo-vllm-sidecar \
Use `VLLM_GRPC_ENDPOINT` instead of `--vllm-endpoint` when the endpoint is
provided through the environment.

The sidecar discovers `model_id`, the served name, context length, KV capacity, scheduler limits, data-parallel topology, and KV-event sources through `vllm.Control`. `model_id` must be readable locally or fetchable by Dynamo for tokenization and chat templates.
### RL workflows

Start vLLM with the capabilities required by the workflow, then opt the sidecar into RL discovery:

```bash
vllm-rs serve Qwen/Qwen3-0.6B \
--host 127.0.0.1 \
--grpc-port 50051 \
--enable-sleep-mode \
--weight-transfer-config '{"backend":"nccl"}'

DYN_SYSTEM_PORT=8081 dynamo-vllm-sidecar \
--vllm-endpoint 127.0.0.1:50051 \
--enable-rl
```

`--enable-rl` (or `DYN_ENABLE_RL=true`) requires the Dynamo system server (`DYN_SYSTEM_PORT=0` or a positive port) and registers `dyn://<namespace>.<component>.rl`, which lets the Dynamo frontend discover this worker and its `/engine/control/*` and `/engine/update/*` routes through `/v1/rl/workers`. The sidecar advertises pause/resume, sleep-status, and weight-version controls when the vLLM server reports the RL gRPC API; mutating sleep/wake routes require `--enable-sleep-mode`, weight-transfer routes require `--weight-transfer-config`, and draft updates require speculative decoding support.

The update request bodies match vLLM's RL HTTP schemas: `init_weight_transfer_engine` requires `{"init_info": {...}}`, `update_weights` requires `{"update_info": {...}}`, `finish_weight_update` accepts `{"weight_version": "..."}`, and `update_weight_version` requires `{"new_version": "..."}`. Weight tensors remain on the configured NCCL, IPC, or sparse-NCCL transport; only backend metadata crosses gRPC.

The RL endpoint and engine routes are unauthenticated administrative surfaces that can pause serving, release GPU memory, and replace model weights. Enable them only on trusted request and system networks.

The sidecar discovers `model_id`, the served name, context length, KV capacity, scheduler limits, data-parallel topology, and KV-event sources through `vllm.Control`. `model_id` must be readable locally or fetchable by Dynamo for tokenization and chat templates. Parser defaults are not advertised because the current inference protocol cannot preserve all parser-related request semantics.

The sidecar currently supports one vLLM frontend hosting the complete data-parallel group starting at rank 0. Control reports the global size; Dynamo forwards the selected rank as `x-data-parallel-rank` gRPC metadata on each generation request. Partial and hybrid rank ownership are unsupported because the protocol does not report the locally hosted rank count, and a nonzero starting rank is rejected. When KV routing is enabled, Control must return one unique ZMQ event source for every rank in the group.

Expand Down Expand Up @@ -88,6 +111,8 @@ cargo run -p dynamo-vllm-sidecar --bin dynamo-vllm-sidecar -- \
--vllm-endpoint 127.0.0.1:50051
```

The mocker does not advertise RL capabilities; use a compatible vLLM server for RL route testing.

See [`../../mocker/servers/vllm/README.md`](../../mocker/servers/vllm/README.md)
for aggregated and prefill/decode examples, supported Mocker configuration,
and fidelity limits.
Expand Down
12 changes: 8 additions & 4 deletions lib/sidecar/vllm/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use dynamo_sidecar_common::{
};
use tokio::time::{Instant, sleep_until, timeout_at};
use tonic::metadata::MetadataValue;
use tonic::transport::Channel;
use tonic_health::pb::health_check_response::ServingStatus;
use tonic_health::pb::{HealthCheckRequest, health_client::HealthClient};

Expand Down Expand Up @@ -47,6 +48,12 @@ impl VllmClient {
self.pool.len()
}

pub(crate) fn control_client(&self) -> pb::control_client::ControlClient<Channel> {
pb::control_client::ControlClient::new(self.pool.next_channel())
.max_encoding_message_size(DEFAULT_MAX_GRPC_MESSAGE_SIZE)
.max_decoding_message_size(DEFAULT_MAX_GRPC_MESSAGE_SIZE)
}

pub(crate) async fn wait_for_services(
&self,
services: &[&str],
Expand Down Expand Up @@ -115,10 +122,7 @@ impl VllmClient {
&self,
startup_deadline: Instant,
) -> Result<(pb::ModelInfo, pb::ServerInfo), DynamoError> {
let channel = self.pool.next_channel();
let mut client = pb::control_client::ControlClient::new(channel)
.max_encoding_message_size(DEFAULT_MAX_GRPC_MESSAGE_SIZE)
.max_decoding_message_size(DEFAULT_MAX_GRPC_MESSAGE_SIZE);
let mut client = self.control_client();
let model = timeout_at(
startup_deadline,
client.get_model_info(pb::GetModelInfoRequest {}),
Expand Down
Loading
Loading