A best-in-class tutorial · zero prior knowledge required

Master the Shepherd Model Gateway

SMG is the open-source traffic controller for large-scale LLM inference — a Rust gateway that sits between your applications and your GPU fleet. This tutorial takes you from "never heard of it" to "can read a production deployment config" in about an hour. It has stories, live demos, and an exam at the end.

RustOpenAI-compatiblegRPC + HTTP vLLM · SGLang · TRT-LLM · TokenSpeed · MLXApache-2.0
<1 msrouting decision
~70%lower time-to-first-token with cache-aware routing
10load-balancing policies
90+Prometheus metrics
SourcesBuilt from the official repo github.com/smg-project/smg, its docs at lightseek.org/smg, and a full walk of the source code (model_gateway/, crates/). Chapter 11 adds a real production case study you won't find anywhere else.
Chapter 0 · The Why

The problem nobody warns you about

Congratulations — your team got GPUs. You fire up an open model on vLLM or SGLang, point your app at it, and everything works… on your laptop. Then reality shows up:

😰 "One GPU isn't enough"

You now run 8, 50, 300 replicas of the model. Which one should each request go to? Your app can't know. Round-robin DNS it is… and performance is mysteriously bad.

🔥 "The GPUs are busy but slow"

Every request re-processes the same 4,000-token system prompt from scratch. The engines literally remember having seen it (their KV cache) — but requests land on random machines, so the memory is wasted.

🧩 "Every model speaks a dialect"

DeepSeek "thinks" in <think> tags, Llama emits tool calls with <|python_tag|>, Qwen does XML. Your app now contains if/else spaghetti for every model family.

🚪 "We also use OpenAI for some things"

Half your traffic goes to self-hosted models, half to cloud APIs. Two SDKs, two auth schemes, two retry logics, zero shared observability.

💥 "A worker died at 3am"

Requests kept getting routed to the corpse. You invented circuit breakers at 3:15am. They were buggy.

🏢 "Legal says history stays in-house"

Chat history, rate limits per customer, audit trails — suddenly "just call the model" became an infrastructure company.

Every one of these is the same missing layer: something should sit between apps and engines, own routing + translation + reliability, and do it at line rate. That something is a model gateway. SMG is the best open-source one — written in Rust, born from the SGLang ecosystem, and battle-tested in front of fleets of hundreds of GPU replicas.

TakeawaySMG exists because "call the model" stops being a function call and becomes a distributed systems problem the moment you have more than one engine — or more than one model.
Chapter 1 · The Mental Model

Think of it as a pizza chain

Forget computers for two minutes. SMG's whole design fits in one analogy — a pizza delivery business:

📞 You, the customeryour app — speaks plain English ("one large pepperoni")
🏪 The cashier = SMGtakes your messy order → translates to kitchen language → wraps the result nicely
🔥 The oven = the engine (vLLM / SGLang / TRT-LLM)speaks only "dough & heat" (token IDs in, token IDs out)

The cashier (SMG) does everything except cooking: applies the chat template, tokenizes your text, picks which oven to use, then detokenizes the output and wraps it as a clean OpenAI-style response. The oven (engine) does only the heavy math on GPUs.

And a real business has many shops. SMG knows every shop's menu, how busy each oven is, and — crucially — what dough each oven already has prepped. If your order looks like yesterday's, SMG sends you back to the same oven so it reuses the prep instead of starting from scratch. That's "cache-aware routing", and it's SMG's superpower (Chapter 6).

One-linerSMG is a cashier that fronts a fleet of ovens: apps talk to SMG like it's OpenAI; SMG talks to engines like a kitchen; engines never talk to apps at all.
Chapter 2 · Vocabulary

The 8 words you actually need

Everything in SMG's docs and configs reduces to these terms. Learn them now, skim the full glossary later.

1 · Token

The atom of LLM I/O. Models don't read text — they read integer IDs. "Hello world"[9906, 1917]. Converting text↔tokens is tokenization/detokenization. SMG does this at the gateway, with a two-level cache, so engines don't have to.

2 · Worker

One running engine instance SMG can route to (a URL + metadata + health state). A deployment = a fleet of workers behind one gateway. Workers register in SMG's Worker Registry, get health-checked, and get picked by policies.

3 · Engine / Backend

The software doing inference on the worker: SGLang, vLLM, TensorRT-LLM, TokenSpeed, MLX, or any OpenAI-compatible server. SMG is engine-agnostic — it speaks HTTP to all of them, and high-performance gRPC to the majors.

4 · KV cache

The engine's short-term memory: processed tokens leave computed state ("KV") in GPU memory, so continuing a familiar prompt is far cheaper than starting over. Prefix reuse = massive speedup. SMG tracks which worker holds which prefixes.

5 · TTFT

Time To First Token — how long until the user sees the first word. Dominated by re-processing the prompt ("prefill"). Reusing KV cache slashes TTFT (~70% in SMG's benchmarks). The metric every inference team lives and dies by.

6 · Routing policy

The algorithm that picks a worker per request: cache_aware, round_robin, power_of_two, least_load, … 10 of them, pluggable per model (Chapter 6).

7 · Chat template

Every model family wants messages formatted differently (Llama vs Qwen vs DeepSeek). SMG applies the model's Jinja2 template at the gateway — your app just sends OpenAI-style messages.

8 · PD disaggregation

Splitting inference into Prefill (process the prompt) and Decode (generate tokens) on different workers, shipping the KV cache between them. An advanced scaling trick SMG supports natively on both HTTP and gRPC.

Chapter 3 · The Big Picture

One request, end to end — with a live walkthrough

Here is the entire system. Every request your app ever sends walks this exact path. Press Next step and watch it happen — 8 steps, ~40 seconds.

Client appcurl · OpenAI SDK · your service — speaks OpenAI API
SMG begins
Axum HTTP serverserver.rs — port 30000, OpenAI/Anthropic/Responses endpoints
Middleware gauntletauth → rate limit → WASM plugins → tenant → metrics
RouterManager — the brainrouter_manager.rs — picks ONE of three paths
gRPC path
gateway = full server
HTTP path
gateway = smart proxy
External path
gateway = cloud router
Chosen workerSGLang / vLLM / TRT-LLM / TokenSpeed / MLX — or a cloud API
Response processingdetokenize → extract reasoning → parse tool calls → stream SSE
SMG ends
Client appreceives tokens as they generate — OpenAI-compatible SSE
Press Start. A request is about to arrive…

Notice the shape: one shared front half (server → middleware → brain), one of three paths through the middle, one shared back half (response processing → stream). Once you see this shape, every SMG config file and every source module becomes obvious.

🔍 Deep dive — where each box lives in the source code
  • HTTP server: model_gateway/src/server.rsstartup() builds the Axum app, routes, and AppState.
  • Middleware: model_gateway/src/middleware/ — auth.rs, token_bucket.rs, wasm.rs, tenant_resolution.rs, concurrency.rs, metrics.rs.
  • RouterManager: routers/router_manager.rs + routers/factory.rs — 7 router implementations behind one RouterTrait.
  • Paths: routers/grpc/, routers/http/, routers/openai/ + anthropic/, gemini/.
  • Response processing: routers/grpc/regular/processor.rs (non-stream) and streaming.rs (SSE).
Chapter 4 · The Three Paths

Gateway = full server, smart proxy, or cloud router

The RouterManager's choice depends on what kind of worker serves the requested model. There are exactly three possibilities:

gRPC path — "full server"

When: worker speaks SMG's gRPC protocol (SGLang, vLLM, TRT-LLM, TokenSpeed, MLX with the shim).

SMG does: chat template, tokenization (+cache), worker pick, detokenization, reasoning & tool parsing, MCP loop. The worker does raw inference only — tokens in, tokens out.

Why it's the flagship: token-level streaming, token-aware routing, and all model quirks handled in one place instead of in every app.

HTTP path — "smart proxy"

When: worker runs its own full OpenAI-compatible HTTP server.

SMG does: load balancing, retries, circuit breakers, failover — the engine handles its own templating/tokenization.

Party trick: PD mode — send prefill to one worker, decode to another, transfer the KV cache between them via routing headers.

External path — "cloud router"

When: the "worker" is OpenAI, Anthropic, Gemini, xAI, OCI, Bedrock, or any OpenAI-compatible provider.

SMG does: provider abstraction — one endpoint, one auth scheme, model discovery via /v1/models, dialect translation (e.g. Anthropic Messages vs OpenAI Chat).

Result: mix self-hosted and cloud behind a single API.

ResponsibilitygRPC modeHTTP mode
Chat templateGatewayWorker
TokenizationGateway (L0/L1 cached)Worker
Load balancingToken-awareRequest-count aware
Reasoning / tool parsingGatewayWorker
MCP tool executionGatewayn/a
How the brain choosesIn single-router mode the path comes from your launch config. In multi-model gateway mode (--enable-igw), SMG inspects which workers serve the requested model and picks: external provider → gRPC-PD → HTTP-PD → gRPC → HTTP.
Chapter 5 · The gRPC Pipeline

Seven stages, one conveyor belt

On the gRPC path, every request is assembled by a RequestPipeline — an ordered list of stages, each with one job. This is the heart of SMG; if you understand these 7 stages, you understand the codebase.

#StageWhat happensSource
1PreparationValidate tools, apply the Jinja2 chat template, tokenize (with L0/L1 cache), expand multimodal inputs (images/video)regular/stages/preparation.rs
2Worker selectionRouting policy picks one worker (or a prefill+decode pair) from the Worker Registrycommon/stages/worker_selection.rs
3Client acquisitionGrab the tonic gRPC client for that workercommon/stages/client_acquisition.rs
4Request buildingBuild the engine's proto request (GenerateReqInput), attach LoRA adapters & grammar constraintsregular/stages/request_building.rs
5Dispatch metadataAttach request IDs & routing metadata for tracingcommon/stages/dispatch_metadata.rs
6ExecutionFire the gRPC call — unary (full response) or streaming (token-by-token)common/stages/request_execution.rs
7Response processingDetokenize, extract reasoning spans, parse tool calls, assemble SSE chunks or final JSONregular/processor.rs · regular/streaming.rs
Why this mattersThe same stage pattern is reused for chat, completions, embeddings, Anthropic Messages, Responses API, Harmony models, and PD variants — only the prep/build/response stages swap out. Learn one pipeline, you've learned all seven pipelines.
Chapter 6 · Load Balancing

Cache-aware routing — the superpower, live

SMG ships 10 policies. Nine of them you already half-understand (round_robin, random, least_load, power_of_two = sample two workers pick the lighter, consistent_hashing = sticky by a key, prefix_hash, bucket, manual, passthrough). The one that makes SMG famous is cache_aware:

  1. Tokenize the incoming request's prefix.
  2. Search a radix tree per worker for the longest matching cached prefix.
  3. If the match covers enough of the prompt → route to that worker (its KV cache will be reused → tiny TTFT).
  4. Otherwise → route to the worker with the most free capacity / least load.
  5. If the fleet gets imbalanced → fall back to least-loaded.

Don't read about it — play with it. Below are three workers with their current KV caches. Fire requests and watch the policy choose. Flip between cache_aware and round_robin and watch the "tokens recomputed" counter explode:

POLICY:

Fire a request:

Choose a request above. The gateway will tokenize it, match prefixes against each worker's radix tree, and route it.
0tokens reused (KV hit)
0tokens recomputed (miss)
0requests routed
Production noteReal SMG matches on token IDs in radix trees (crates/kv_index), considers queued token work and KV pressure for the fallback, and can stream live cache events from the engines via a KV event monitor. The toy above uses word chunks, but the decision logic is the same shape.

Which policy when?

PolicyHow it picksUse it when
cache_awareRadix-tree prefix match + loadDefault for production chat — shared system prompts, multi-turn chats, agents
bucketRequest-length bucketsPD disaggregation deployments
power_of_twoSample two, pick lighterHeterogeneous load, no prefix reuse
least_loadLeast busy workerSimple load-aware setups
consistent_hashingHash ring + virtual nodesSession affinity that survives scaling events
prefix_hashHash of prefix tokensLightweight cache locality without radix trees
manualExplicit routing-key mapStateful chat pinned by your own key
round_robin / randomCycling / uniformEven distribution; testing
passthroughAs-is, no balancing logicSingle-worker or externally-managed setups
Chapter 7 · Parsers

Teaching one gateway to speak 20 model dialects

Modern models emit more than prose. Two extras matter most — and every model family formats them differently:

🧠 Reasoning ("thinking")

DeepSeek-R1, Qwen3, Kimi, GLM-4.5… produce chain-of-thought before the answer, wrapped in family-specific markers. SMG's 16 reasoning parsers split the stream into reasoning_content vs content while streaming — auto-detected from the model name, overridable with --reasoning-parser.

🔧 Tool calls

Llama uses <|python_tag|>, DeepSeek uses XML, Qwen3-Coder uses XML with parameter tags, others emit raw JSON. SMG's 21 tool-call parsers extract and validate calls against your tool schemas — and can feed them straight into the MCP loop (next chapter).

Before SMG, every application carried per-model if/else spaghetti for this. After SMG, the gateway absorbs dialects and your app sees one clean OpenAI-style shape regardless of which engine or model answered.

# what your app receives (separate_reasoning: true) — regardless of model family
{
  "choices": [{
    "message": {
      "role": "assistant",
      "reasoning_content": "Let me think step by step…",
      "content": "The answer is 42.",
      "tool_calls": [ … ]
    }
  }]
}
Chapter 8 · The Toolbox

MCP, chat history, WASM plugins, multi-tenancy

🔌 MCP — tools that actually run

Connect Model Context Protocol servers (stdio/SSE/HTTP). When a model emits a tool call, SMG can execute it via MCP, inject the result, and continue generation — the full agentic loop inside the gateway, with approval policies and audit logging. Exposed via the Responses API.

💾 Chat history — in your infra

Pluggable storage backends (PostgreSQL, Oracle, Redis, in-memory) with schema migrations. Conversations and Responses state live behind your privacy boundary — never at a third party. The data_connector crate owns this.

🧩 WASM plugins

Custom request/response logic in WebAssembly (wasmtime component model): auth enrichments, PII redaction, logging, header mutation — loaded as middleware, hot-manageable via admin APIs, no gateway recompiles.

🏢 Multi-tenant control

API-key auth, OIDC/JWT validation, per-tenant rate limits (token bucket), concurrency caps with queuing, and priority admission scheduling with preemption for the data plane.

🖼️ Multimodal

Image (and optional video) fetch + preprocessing for vision models, with a separate encode disaggregation stage — heavy vision encoding can run on dedicated workers.

🎯 Everything OpenAI-shaped

Chat Completions, Completions, Embeddings, Rerank, Classify, Responses & Conversations APIs, Anthropic Messages, Gemini Interactions, Realtime (WebSocket/WebRTC), audio transcription, tokenize/detokenize utilities.

Chapter 9 · Production Traits

Staying alive: HA mesh, resilience, observability

🕸️ Mesh HA

Run many SMG nodes as one logical gateway. SWIM gossip handles membership + failure detection (1s heartbeats); CRDT stores replicate worker registry, rate-limit counters, and cache-aware radix trees — so any node routes as well as any other. Rolling updates with request draining = zero downtime. Kubernetes pod discovery via label selectors.

🛡️ Resilience

Per-worker circuit breakers (stop routing to the sick), retries with exponential backoff + jitter, background health probes, graceful shutdown with drain windows, and crash record & replay to reproduce failures offline.

📊 Observability

90+ Prometheus metrics (per-pipeline-stage timings, mesh convergence, token counts, policy decisions), OpenTelemetry traces with W3C context propagated into the engines over HTTP and gRPC, and structured JSON logs with request correlation.

# a 3-node HA cluster is this boring to start (boring = good)
smg launch --enable-mesh --mesh-advertise-host 10.0.0.11 --worker-urls http://w1:8000
smg launch --enable-mesh --mesh-advertise-host 10.0.0.12 --mesh-peer-urls 10.0.0.11:39527 --worker-urls http://w2:8000
smg launch --enable-mesh --mesh-advertise-host 10.0.0.13 --mesh-peer-urls 10.0.0.11:39527 --worker-urls http://w3:8000
# then: curl http://any-node:30000/ha/status
Chapter 10 · Hands-on Lab

Install it, run it, break it

1 · Install (pick one)

# Docker
docker pull lightseekorg/smg:latest
# Kubernetes
helm install smg oci://ghcr.io/smg-project/charts/smg
# Python (embeds the Rust binary)
pip install smg
# Rust (needs protoc)
cargo install smg

2 · Point it at workers

# one worker
smg launch --worker-urls http://localhost:8000
# a fleet, with the superpower on
smg launch --worker-urls http://gpu1:8000 http://gpu2:8000 --policy cache_aware

3 · Call it like it's OpenAI

curl http://localhost:30000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"llama3","messages":[{"role":"user","content":"Hello!"}]}'

4 · The gRPC flagship (what production actually runs)

# engine side: expose SGLang/vLLM over gRPC using the bundled Python shim (grpc_servicer)
vllm serve meta-llama/Llama-3.1-8B --grpc
# or: sglang serve --model-path … --grpc-mode

# gateway side: full-server mode with parsers + tokenizer cache
smg launch --worker-urls grpc://worker:50051 \
  --reasoning-parser deepseek_r1 \
  --tool-call-parser llama \
  --tokenizer-cache-enable-l0 --tokenizer-cache-enable-l1 \
  --policy cache_aware
Lab exerciseRun two workers, fire the same long system prompt twice with --policy round_robin, then twice with cache_aware. Compare smg_router_stage_duration_seconds and TTFT in the metrics. You just reproduced Chapter 6 for real.
Chapter 11 · Bonus Case Study

SMG in production Kubernetes — the two-tier pattern

Large serving platforms typically run SMG (or a fork of it) inside every model pod, with a separate cluster-level router tier in front. Here is the deployment shape you'll meet in the wild — and the part that confuses everyone:

Clientthe platform's public API endpoint
Edge networkTLS termination, auth, and data-center selection
Cluster router — the city dispatchercluster-level tier · picks WHICH pod (session/prefix-sticky policies keep KV caches warm)
SMG — the shop cashier (inside every pod, HTTP port)full server: chat template → tokenize → parse → metrics
Engine — the oven (same pod, gRPC port)raw inference: SGLang / vLLM / TRT-LLM, often tensor-parallel across the pod's GPUs

Cluster router ≠ SMG (often same ancestors, different jobs)

The cluster tier is frequently built from the same gateway codebase — same boot/config/metrics skeleton. But it stays HTTP-only: it never tokenizes; it load-balances across pods and carries platform concerns (auth hot path, metering, usage logging). SMG lives inside each pod as the actual server, with the full gRPC pipeline. Dispatcher vs cashier.

How to read any deployment config in 4 lines

Pod command runs smg → the pod's front door is SMG.
--backend sglang (or vllm/trtllm) → which engine is inside.
--connection-mode grpc → full-server mode (SMG tokenizes).
A router tier named in the ingress path → who dispatches across pods.

Response pathResponses travel back up the same chain: engine → in-pod SMG → cluster router → client. The router tier stays in the path for streaming too — it forwards SSE tokens as they arrive, while keeping its inflight/session books. That bookkeeping is what makes session-sticky cache reuse possible at hundreds-of-pods scale.
Chapter 12 · Final Exam

Prove you own this

Eight questions. Instant feedback. 6+ correct = you understand SMG better than most people running it.

Mastery checklist

  • I can explain why a gateway exists (Chapter 0) in one sentence.
  • I can draw the request lifecycle: server → middleware → RouterManager → 3 paths → response processing.
  • I know when the gateway is a "full server" vs a "smart proxy" — and who tokenizes in each.
  • I can name the 7 gRPC pipeline stages in order.
  • I can explain cache-aware routing with the radix tree and why it cuts TTFT.
  • I know what reasoning/tool parsers do and why apps no longer need per-model code.
  • I can start SMG against workers and hit it with curl.
  • I can read a production deployment config: which process fronts the pod, which engine is inside, HTTP vs gRPC mode, and which tier dispatches across pods.
Reference

Glossary

SMG

Shepherd Model Gateway — the Rust LLM gateway this tutorial covers. OSS: smg-project/smg.

Worker

A registered engine instance SMG routes to (URL + health + metadata).

Engine / backend

Inference software on a worker: SGLang, vLLM, TRT-LLM, TokenSpeed, MLX, Ollama…

Token / tokenizer

Integer unit of model I/O; the component converting text↔tokens. SMG caches it two levels deep (L0 exact, L1 prefix).

KV cache

Engine-side memory of processed prefixes; reuse = skipping prefill compute.

TTFT

Time to first token — the latency users feel most; cache-aware routing's main victim.

Prefill / decode

The two phases of inference: digesting the prompt vs generating tokens.

PD disaggregation

Running prefill and decode on different workers with KV transfer between them.

RouterManager

SMG's dispatch brain; chooses gRPC/HTTP/external router per request.

Routing policy

Worker-selection algorithm (cache_aware, power_of_two, …). Per-model, pluggable.

Radix tree

Prefix tree structure (kv_index crate) mapping token prefixes → workers for cache-aware routing.

gRPC pipeline

SMG's flagship 7-stage request assembly line for token-level engine streaming.

grpc_servicer

Python shim inside vLLM/SGLang that exposes the engine over SMG's gRPC protocol.

Chat template

Jinja2 formatting that turns OpenAI-style messages into a model's exact prompt format.

Reasoning parser

Extracts "thinking" spans per model family (16 supported, auto-detected).

Tool-call parser

Extracts function calls per model family (21 supported) and validates arguments.

MCP

Model Context Protocol — standard for external tool servers; SMG can execute tools mid-generation.

Responses API

OpenAI's agentic API (tool loops, conversations) — hosted by SMG with MCP.

Mesh

SMG's multi-node HA mode: SWIM gossip + CRDT state replication.

CRDT

Conflict-free replicated data type — merges state across nodes without locks.

Circuit breaker

Per-worker switch that stops routing to a failing worker until it recovers.

WASM plugin

WebAssembly middleware module for custom request/response logic.

Cluster router tier

A separate router in front of many SMG pods (often the same gateway codebase); picks pods, never tokenizes.

IGW mode

--enable-igw: multi-model gateway mode — several routers, per-model policies.

Reference

Cheat sheet

# ── run ──
smg launch --worker-urls http://w1:8000 http://w2:8000 --policy cache_aware
smg launch --worker-urls grpc://w1:50051 --reasoning-parser deepseek_r1 --tool-call-parser llama
smg launch --enable-mesh --mesh-advertise-host 10.0.0.1 --mesh-peer-urls 10.0.0.2:39527

# ── call ──
curl localhost:30000/v1/chat/completions -d '{"model":"m","messages":[…]}'
curl localhost:30000/v1/models            # discovery
curl localhost:30000/ha/status            # mesh cluster state

# ── the four-line production-config reader ──
# pod command runs smg   → pod's front door is SMG
# --backend sglang       → engine = SGLang   |   --connection-mode grpc → full-server mode
# ingress/router tier    → who dispatches requests across pods

# ── the one-sentence summary, memorize it ──
# Axum HTTP → middleware → RouterManager picks gRPC/HTTP/external →
# gRPC path: template → tokenize(+cache) → policy pick → build → execute → detokenize+parse