The LLM inference router: the L7 switchboard we called a LoadBalancer in the canary post
Contents
This post is the natural continuation of Canary, blue-green and shadow for LLM models. There the promotion mechanics dumped all the traffic-splitting complexity into a box we called “LoadBalancer”. The description was operational, good enough to understand the choreography, but structurally vague: what actually does that splitting is an L7 inference router with LLM awareness, a piece of the stack in its own right (layer 1 of the seven layers) that deserves its own post.
TL;DR
In the previous post on canary we called LoadBalancer the piece that splits traffic between the stable v1 and candidate v2 pools. The description was enough to understand the flow, but technically it was blurry: neither an L4 LoadBalancer (kube-proxy, MetalLB, IPVS) nor a generic L7 HTTP LoadBalancer (NGINX or HAProxy with no extension) knows what a model is, what a version is, how many tokens a request costs, what prefix the prompt has or which replica has which KV cache hot. The right piece is an LLM inference router: an L7 proxy with explicit knowledge of the domain. It combines four functions: model catalogue (resolving model=llama-70b@v2 → service.namespace:port), traffic splitting (applying the canary weight with a deterministic hash or deliberate stickiness for A/B), cross-cutting policy (OIDC auth, per-tenant rate limit and quota, pre-prompt PII redaction, lightweight inline guardrails, gen_ai.* tracing propagation) and failover/degradation (if v2 falls over, redirect to v1; if the whole cluster is saturated, return 503 with Retry-After instead of queueing forever). The non-obvious piece that justifies its technical existence beyond the operational one is prefix-aware routing: the router decides which fleet replica each request goes to based on the prompt prefix, so that a RAG system with the same system prompt plus the same block of retrieved documents systematically hits the prefix cache (RadixAttention in SGLang, PrefixCaching in vLLM, KV reuse in TensorRT-LLM) of the same replica, multiplying the hit rate from 5–15 % (blind round-robin) to 60–85 % (prefix affinity). The concrete pieces in May 2026 are LiteLLM Proxy (the simplest option, OpenAI-compatible, declarative YAML catalogue), vLLM Production Stack router (specific to vLLM fleets, aware of the KV cache and the prefix), Envoy AI Gateway (LLM-aware Envoy filters, integrable with Istio), Kong AI Gateway (enterprise alternative with a plugin ecosystem), KGateway (CNCF, still incubating) and the NVIDIA Dynamo router (production-grade, aware of disaggregated prefill/decode serving). In the seven-layer stack it lives in layer 1 (gateway); in the five maturity levels it appears from level 3 onwards; in the seven-phase deployment cycle it is the last piece that F6 closes. This post includes a minimal manifest applicable to a generic 4×H100 SXM cluster.
You are here: DEPLOY (layer 1 of the stack)
The background: what the canary post called a “LoadBalancer”
In Canary, blue-green and shadow for LLM models we described the flow like this: “the LoadBalancer progressively splits traffic following a schedule: 1 % → 5 % → 25 % → 100 %”. It was a correct operational description, and the reader understood the choreography without needing more. But technically it left unnamed a piece that deserves explicit treatment, because neither of the two usual senses of “LoadBalancer” does what that paragraph assumed:
- An L4 LoadBalancer (kube-proxy with iptables/IPVS, MetalLB, F5 BIG-IP in TCP mode) splits IP packets without looking inside the payload. It does not know which model is being requested, nor which version, nor how many tokens it carries, nor whether the client has quota. It cannot apply the canary weight “for model X version 2”: to it, every packet heading for the
vllm-llama70bVIP is indistinguishable. - A generic L7 HTTP LoadBalancer (NGINX or HAProxy in HTTP mode with no extension, a
ClusterIPService with multiple backends) does split by URL and can route by header, but it does not understand the OpenAI-compatible body of the request. It does not know that{"model": "llama-70b", "messages": [...]}carries the routing key in themodelfield; it does not count tokens; it does not apply policies over LLM structures; it does not do prefix-aware routing because that requires parsingmessagesand hashing the common prefix.
The piece the canary post assumed was doing this work is an L7 inference router with LLM awareness. A layer in its own right, with its own configuration, its own CI/CD, its own metrics and its own pitfalls. This post names it and takes it apart.
The analogy: the switchboard and triage of a hospital with multiple specialities
A large hospital receives patients who arrive at A&E through different doors and who need different specialities: trauma, cardiology, paediatrics, oncology. There are three possible models of “front door”.
A single door with no triage. All patients wait in the same room and are sent in arrival order to the first free doctor, whatever their speciality. It works in a village surgery with a single general practitioner. When there are 200 patients a day and 12 specialities, it falls into dysfunction fast: the cardiologist treats sprains, the paediatrician treats heart attacks, specialised resources are wasted. This is the equivalent of the L4 LoadBalancer, which distributes bodies without understanding what they bring.
A door with a receptionist who asks about the symptom. Someone at the front desk asks “what’s wrong?” and directs the patient to the right corridor. The cardiologist sees only cardiology, the paediatrician only children. Better, but the receptionist is slow, does not gauge urgency and does not know the state of the rooms: they may send you to the cardiologist in corridor A when the one in B is free. This is the equivalent of a generic L7 HTTP proxy with path-based routing, which distributes by category but with no information about internal state.
Professional triage with full awareness. A trained triage nurse who knows the catalogue of specialities, knows which bay is busy and which is free, remembers the recurring patient whose file is already open in the system (and sends them to the same doctor for continuity), applies cross-cutting policy (checks insurance cover, records allergens, redirects to paediatric A&E if the patient is a minor) and, if the cardiology room goes down because the electrocardiograph has broken, redirects to the hospital on the other side of the city. This is the piece a large hospital needs. In LLM terms it is called an inference router.
The analogy holds down to the last detail, including the “file already open” one: the patient who returns to the same doctor is exactly the client whose prompt shares a prefix with the one from five minutes ago. If the router sends it to the same replica, that replica still has the KV cache hot and the request hits the prefix cache. If it sends it to a different replica because that one was “next in round-robin”, the KV cache has to be rebuilt from scratch and TTFT doubles. The triage nurse knows this. The blind LoadBalancer does not.
The four functions of the inference router
Function 1 — Model catalogue
The router keeps a declarative catalogue mapping model identity to a concrete deployment:
models:
- name: "llama-70b" # stable alias
version: "v2" # canary version
weight: 5 # 5% of the traffic
endpoint: "vllm-llama70b-v2.inference.svc.cluster.local:8000"
capabilities: [chat, tool_use]
lifecycle: canary
- name: "llama-70b"
version: "v1"
weight: 95
endpoint: "vllm-llama70b-v1.inference.svc.cluster.local:8000"
capabilities: [chat, tool_use]
lifecycle: stable
- name: "embedding-multilingual"
version: "v1"
weight: 100
endpoint: "tei-bge-m3.inference.svc.cluster.local:8080"
capabilities: [embeddings]
lifecycle: stable
The client sends {"model": "llama-70b", "messages": [...]} without knowing there are two replica pools behind it. The router resolves. If tomorrow you migrate from vLLM to SGLang for a specific version, the client never notices; you change the endpoint in the catalogue and that is it.
What this decoupling buys you is the freedom to move topology without breaking clients. What it costs is keeping the naming convention disciplined (llama-70b is always the stable alias; llama-70b@v2 is the specific version for canary). Without that discipline, the aliases get dirty with llama-70b-prod-fixed-real-final-v3 and the catalogue stops being navigable within a few weeks.
Function 2 — Traffic splitting
The splits from the canary post (1 % → 5 % → 25 % → 100 %) materialise here, not in the inference engine. The router computes a deterministic hash of the request_id (or of the user_id, if you want stickiness) and maps it to the weight range in the catalogue. For a weight of [v1: 95, v2: 5], 5 % of the hash space falls in v2 and 95 % in v1.
Three design decisions that matter:
- Hash by random
request_id= independent sampling. Each request is an independent observation of the v1 vs v2 distribution. It is the right setting for statistically comparable canaries. - Hash by
user_id= per-user stickiness. The same client always sees the same pool. Useful for A/B testing with persisted conversational memory, but it breaks the statistical comparability of the canary because user populations are not symmetric, a pitfall explained in the previous post. - Hash by
tenant_id= hard partitioning. Tenant A goes to v1, tenant B to v2. This is the pattern for clients with different SLAs, or for validating v2 in an internal tenant before exposing it to external clients.
Function 3 — Cross-cutting policy
Once, across all models, the router applies:
- Auth: OIDC with JWT tokens validated against Keycloak / Authentik.
Authorization: Bearer ...headers translated intotenant_idandroles. - Rate limit: token bucket per tenant (
X req/min) or per model (Y req/minfor llama-70b because it is expensive). - Quota: monthly quota of tokens consumed per tenant. The router counts
gen_ai.usage.input_tokens+gen_ai.usage.output_tokensand rejects with429 Quota exceededwhen it runs out. - Pre-prompt PII redaction: Presidio or Llama Guard inline before the prompt touches the model. What the model does not see is not trained on, not logged, not leaked.
- Lightweight inline guardrails: PromptGuard 2, Llama Guard 4, Granite Guardian, the ones covered in Guardrails and safety in LLMs, run in the router because their latency (30–150 ms) fits within the TTFT budget.
gen_ai.*tracing propagation: the router starts the parent span, propagatestraceparentto the engine and emits thegen_ai.system,gen_ai.request.modelandgen_ai.request.versionattributes that OTel GenAI tracing consumes.- Semantic cache: for prompts that repeat exactly or with high semantic similarity (embedding cosine > 0.97 against the previous cache), it returns the cached answer without touching the engine. Typical saving in RAG with frequently asked questions: 20–40 % of requests.
Function 4 — Failover and degradation
The router knows the health state of each endpoint (active health probes every 5–15 s, recent TTFT latency) and decides:
- If v2 returns persistent 5xx or does not respond, the circuit breaker opens: the router redirects the traffic that was going to v2 towards v1 until the probes go green again. This is automatic canary rollback in its simplest form.
- If the whole cluster is saturated (all replicas reporting
num_requests_waiting > Nfor T seconds), the router returns503 Service UnavailablewithRetry-After: 30instead of queueing forever. It is better to tell the client “come back in 30 seconds” than to keep them waiting four minutes and then time out. - If there is multi-region or multi-cluster, cross-cluster failover via DNS or L7: the primary region goes down and the secondary router takes over.
The non-obvious piece: prefix-aware routing
This is the function a conventional LoadBalancer cannot do and the one that justifies an LLM-specific router beyond the four generic ones.
The KV cache in vLLM, SGLang and TensorRT-LLM can reuse common prefixes across requests, see KV cache. Specifically:
- vLLM with
--enable-prefix-caching: it detects that the current request shares a prefix (of a length that is a multiple of the block size, 16 tokens by default) with a previous request whose pages are still in HBM, and reuses those pages instead of reprocessing them. - SGLang with RadixAttention: it structures the cache as a radix tree indexed by tokens; each request hits the common path of the tree and only computes the tail.
- TensorRT-LLM: a similar feature, called KV cache reuse.
The prefix cache hit rate is the key metric: every token that hits is a token that is not processed in prefill, reducing TTFT in direct proportion. For a typical RAG system, with a 400-token system prompt plus 2,000 tokens of retrieved documents plus a 50-token user question, the common prefix (system_prompt + docs) is 2,400 of the 2,450 total tokens. If the cache hits, prefill only processes 50 tokens instead of 2,450: TTFT falls to roughly a twentieth.
But the cache lives per replica, not globally. If two requests with the same 2,400-token prefix land on different replicas, both do the full prefill: the first one’s cache is no use to the second. The second pays the full cost.
With blind round-robin (any conventional LB), requests are spread uniformly across N replicas. For a cluster of 4 replicas and 1,000 requests with the same system_prompt + docs, each replica receives ~250 requests, but all 4 do their own “first prefill” and the following 249 benefit within their replica. The global hit rate is decent but not optimal. For traffic with many different system prompts and little intra-prefix repetition, the hit rate hovers around 5–15 %.
With prefix-aware routing, the router computes a hash of the prompt prefix (the first N tokens, or the system_prompt declared in messages[0]) and keeps an affinity table of hash → replica. All requests with the same prefix land on the same replica. The first pays the full prefill; the following 999 hit the cache. Global hit rate: 60–85 %.
The cost of implementing it: the router has to parse the request body (not just the HTTP header), apply a lightweight tokeniser or a byte-based hash, and maintain an LRU/consistent-hash affinity table that rebalances when a replica comes in or out. That is server work, not generic proxy work. vLLM Production Stack router implements it natively. NVIDIA Dynamo does too. LiteLLM has a beta in its enterprise version. Envoy AI Gateway is adding it as an experimental filter.
Correction (September 2026). The previous sentence about LiteLLM is false: prefix-aware routing does not exist in any version, open or paid, verified against the code of 1.102.0. The detail, what LiteLLM does have, and the real state of the field are in Prefix routing: what LiteLLM does not do.
The operational difference for a production RAG system: with prefix-aware routing, the same cluster serves 2–4× more requests without adding GPUs, simply because prefill disappears in most cases.
Token-aware load balancing
The second non-obvious piece. Classic round-robin splits by number of requests; but a 50-token prompt and an 8,000-token one cost radically different amounts (a factor of ~160× in prefill). Splitting equally by count severely unbalances the real load.
Token-aware load balancing adds up the expected prefill tokens (prompt length) and expected decode tokens (the client’s max_tokens) per active replica, and sends the new request to the replica with the lowest accumulated load. It is what both vLLM Production Stack and NVIDIA Dynamo implement as the default strategy when it is enabled.
The metric feeding the calculation is, once again, vllm:num_requests_running and vllm:gpu_cache_usage_perc, see GPU observability for LLM inference, ideally complemented with an estimator of the incoming prompt’s tokens. Mature routers use tiktoken or the model’s real tokeniser to count prompt tokens before choosing a replica.
Comparison of concrete pieces (May 2026)
| Piece | LLM awareness | Prefix-aware | Token-aware LB | Multi-model | Semantic cache | Plug & play |
|---|---|---|---|---|---|---|
| LiteLLM Proxy | High | Beta (enterprise) | Yes | Excellent | Yes (Redis) | Very high |
| vLLM Production Stack router | vLLM-specific | Yes, native | Yes | vLLM only | No (external) | Medium |
| NVIDIA Dynamo router | High + disagg-aware | Yes | Yes | vLLM/SGLang/TRT-LLM | No (external) | Low |
| Envoy AI Gateway | Medium (filters) | Experimental | Yes | Yes | Via filter | Medium |
| Kong AI Gateway | Medium (plugins) | No | Yes | Yes | Yes (plugin) | Medium |
| KGateway | Medium | Roadmap | Yes | Yes | Roadmap | Low (CNCF incubating) |
| NGINX + custom Lua | Manual | No | Manual | Manual | No | Low (build it yourself) |
LiteLLM Proxy is the default option to start with. OpenAI-compatible, simple YAML, supports the commercial providers plus any self-hosted OpenAI-compatible backend. The OSS version covers the four basic functions and semantic cache; prefix-aware routing and the enterprise version add advanced multi-tenancy.
vLLM Production Stack router is the right option if the fleet is 100 % vLLM. Aware of the KV cache, the prefix and the LoRA loaded per replica. It integrates better with native vLLM metrics.
NVIDIA Dynamo router is the most complete production-grade option, especially if you operate disaggregated serving (separate prefill workers and decode workers). It requires an NVIDIA-aligned stack.
Envoy AI Gateway and Kong AI Gateway are the options if the organisation already has Envoy or Kong as its corporate gateway and wants to extend it with LLM awareness without introducing another new piece.
Minimal manifest: LiteLLM Proxy on a generic cluster
apiVersion: v1
kind: ConfigMap
metadata: { name: litellm-config, namespace: inference }
data:
config.yaml: |
model_list:
- model_name: llama-70b
litellm_params:
model: openai/llama-70b
api_base: http://vllm-llama70b-v1.inference.svc:8000/v1
weight: 95
model_info:
version: v1
lifecycle: stable
- model_name: llama-70b
litellm_params:
model: openai/llama-70b
api_base: http://vllm-llama70b-v2.inference.svc:8000/v1
weight: 5
model_info:
version: v2
lifecycle: canary
- model_name: embedding-multilingual
litellm_params:
model: openai/bge-m3
api_base: http://tei-bge-m3.inference.svc:8080
router_settings:
routing_strategy: least-busy # basic token-aware
num_retries: 1
timeout: 60
general_settings:
master_key: "os.environ/LITELLM_MASTER_KEY"
database_url: "os.environ/DATABASE_URL"
litellm_settings:
cache: true
cache_params:
type: redis
host: redis.inference.svc
port: 6379
similarity_threshold: 0.97
success_callback: ["langfuse"]
failure_callback: ["langfuse"]
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: litellm-router, namespace: inference }
spec:
replicas: 3
selector: { matchLabels: { app: litellm } }
template:
metadata: { labels: { app: litellm } }
spec:
containers:
- name: litellm
image: ghcr.io/berriai/litellm:v1.55.0
args: ["--config=/config/config.yaml", "--port=4000", "--num_workers=4"]
ports: [{ containerPort: 4000, name: http }, { containerPort: 4000, name: metrics }]
env:
- { name: LITELLM_MASTER_KEY, valueFrom: { secretKeyRef: { name: litellm-secret, key: master_key } } }
- { name: DATABASE_URL, valueFrom: { secretKeyRef: { name: litellm-secret, key: db_url } } }
- { name: LANGFUSE_PUBLIC_KEY, valueFrom: { secretKeyRef: { name: langfuse-keys, key: public } } }
- { name: LANGFUSE_SECRET_KEY, valueFrom: { secretKeyRef: { name: langfuse-keys, key: secret } } }
volumeMounts: [{ name: config, mountPath: /config }]
readinessProbe: { httpGet: { path: /health, port: 4000 } }
volumes: [{ name: config, configMap: { name: litellm-config } }]
---
apiVersion: v1
kind: Service
metadata: { name: litellm-router, namespace: inference }
spec:
selector: { app: litellm }
ports: [{ name: http, port: 80, targetPort: 4000 }]
---
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata: { name: litellm-metrics, namespace: inference }
spec:
selector: { matchLabels: { app: litellm } }
podMetricsEndpoints:
- port: metrics
path: /metrics
interval: 15s
The end client points at litellm-router.inference.svc:80/v1/chat/completions, sets model=llama-70b, and the router decides on each request whether it goes to v1 (95 %) or v2 (5 %), applies the rate limit, looks in the semantic cache, propagates tracing to Langfuse, and translates from OpenAI-compatible to the OpenAI-compatible API of the destination vLLM. Three router replicas for HA and so the gateway itself can scale horizontally with KEDA if needed, see LLM autoscaling on Kubernetes.
Four operational pitfalls
Pitfall 1 — the router becomes a SPoF if it is not replicated. Three or more replicas of the router itself, behind a LoadBalancer Service (this one genuinely L4) with healthchecks. A single router replica means every configuration deploy closes the whole service for a few seconds.
Pitfall 2 — the router’s latency adds to the model’s. Each function adds milliseconds: body parsing (5–10 ms), JWT auth (2–5 ms), rate limit (1–2 ms), PII redaction with Presidio (20–80 ms), inline guardrails with Llama Guard (50–150 ms), prefix hash (5–10 ms), token counting with a tokeniser (10–30 ms). In total, 100–300 ms of overhead before touching the engine. If the model’s TTFT is 400 ms and the router’s is 200 ms, the client sees 600 ms, so it is worth measuring what each function costs and disabling the non-critical ones in the low-latency path.
Pitfall 3 — the catalogue drifts from the real state of the cluster. The router believes vllm-llama70b-v2 exists because it is in its YAML, but the deployment was withdrawn three days ago and nobody updated the config. The router returns 502 on 5 % of the traffic. Fix: validate the catalogue against kubectl get svc in CI; no endpoint in the catalogue may point at a non-existent Service. Or better: the router discovers the available endpoints dynamically via a label selector (app=vllm,model=llama-70b) and applies the catalogue weights over the ones that are alive.
Pitfall 4 — semantic cache with an outdated embedding. The semantic cache compares the new prompt’s embedding against the embeddings of cached prompts. If you update the embedding model (see RAG corpus curation), the distances are computed in a different space and the cache stops working correctly (false hits or false misses). Policy: the cache is invalidated when the embedding model changes; generations are never mixed.
Fit within the stack and maturity
In the seven-layer stack, the router is layer 1: the front door that precedes the inference engine (layer 2), the KV cache + PagedAttention (layer 3) and the rest. It is the only piece that sees all the traffic from the outside; any policy not applied here gets duplicated N times in the engines.
In the five maturity levels, the router appears from level 3 (MANAGED) onwards: without OIDC + RBAC + cert-manager + default-deny NetworkPolicy, the router has nobody to authenticate and nobody to apply quotas to; before level 3, what you should build is a minimal proxy with no pretension of a catalogue. Platforms that try to have a polished router at level 1 end up with a large YAML nobody maintains.
In the seven deployment phases, the router is what closes F6: the last atomic step that puts the cluster into production. Without a router, F6 does not finish, because the catalogue, the quotas, the canaries and the failovers are a necessary condition for opening production traffic.
Applied to typical on-premise hardware
For a generic cluster of 4 nodes × 4×H100 SXM 80 GB, the inference router consumes modest resources: 3 replicas of the router pod (2 CPU cores, 4 GiB of memory each) are enough to sustain thousands of RPS because its work is light (parsing, hashing, routing, no inference). The router lives on non-GPU nodes of the cluster (control plane nodes or general workload nodes), and never consumes nvidia.com/gpu.
Traffic volume that a LiteLLM with 3 replicas and 4 workers each will sustain: 2,000–5,000 RPS routing to a vLLM backend, with 80–150 ms of overhead on the full path (auth + rate limit + cache check + propagation). If more is needed, scaling the router with KEDA on litellm:requests_per_second is trivial.
For larger clusters (16+ GPU nodes), consider the vLLM Production Stack router or the NVIDIA Dynamo router, which are more complex but squeeze out the prefix-aware routing and token-aware LB that LiteLLM OSS does not cover. For multi-region clusters, Envoy AI Gateway with Istio Service Mesh is the standard choice.
What we have not covered (upcoming articles)
- Deep comparison of LiteLLM vs vLLM PStack vs Dynamo, with prefix-aware benchmarks on a real on-premise cluster.
- Semantic cache with Redis Stack + RedisVL: hit rate, false positives, TTL policy.
- Multi-region routing: how the router decides between the DC1 and DC2 clusters based on latency, health and load.
- AI Gateway specific features: token-bucket cost-based rate limiting (penalising long prompts), guardrails policy engine in the router.
- Migration path: how to introduce a router into a cluster that already has clients pointing straight at the vLLM service, with no downtime.
See also
FinOps and multi-tenancy of the GPU cluster: who pays for what — the gateway’s virtual keys as the basis for per-team chargeback.
Canary, blue-green and shadow for LLM models — the previous post where we called this piece a “LoadBalancer”; this one names it and takes it apart.
Seven layers of the on-premise LLM inference stack — the router is layer 1 of the stack.
Five maturity levels — the router appears from level 3 onwards.
Seven deployment phases — the router is what closes F6.
LLM autoscaling on Kubernetes — the router can scale with KEDA on its own metrics; it coexists with engine autoscaling.
GPU observability for LLM inference — the token-aware LB consumes
vllm:num_requests_runningandvllm:gpu_cache_usage_percto choose a replica.KV cache — what prefix-aware routing caches and why it multiplies the hit rate.
Disaggregated prefill/decode serving — production-grade routers (Dynamo) are aware of disaggregation and route prefill and decode to different pools.
LLM tracing with OpenTelemetry GenAI — the router emits the
gen_ai.*parent spans and propagatestraceparentto the engines.Guardrails and safety in LLMs — lightweight inline guardrails typically run in the router.
Mixed NVIDIA + Intel environments for LLM inference — capability-based routing makes full sense when there are heterogeneous backends (NVIDIA for the large LLM, Intel for embeddings/reranker, NUC for edge); the catalogue is extended with
backendandregion.Choosing the switchboard: which OSS gateway to put in front — the decision companion: this post explains what a router is; that one picks which with verified licences (LiteLLM, Envoy AI Gateway + Inference Extension, Higress, APISIX, Kong) and a recommendation for a K8s-native RKE2 + vLLM stack.
LiteLLM and Langfuse: the operational pair — day 2 of the router: cost per token on self-hosted models, trace correlation down to vLLM, content redaction and the measured overhead of the proxy.
LiteLLM on day 2: high availability — operating the router once it is up, with the latency each routing strategy adds.
References
- LiteLLM project —
litellm.ai(documentación de Proxy, routing strategies, semantic cache). - vLLM Production Stack —
github.com/vllm-project/production-stack(router con prefix-aware nativo). - NVIDIA Dynamo —
developer.nvidia.com/blog/nvidia-dynamo-1-production-ready/(router production-grade con disaggregated-aware). - Envoy AI Gateway —
gateway.envoyproxy.io/docs/tasks/ai-gateway/(proyecto en gestación dentro de Envoy). - Kong AI Gateway —
konghq.com/products/kong-ai-gateway(proxy enterprise con plugin LLM). - KGateway —
kgateway.dev(alternativa CNCF en gestación). - Zheng et al. — SGLang: Efficient Execution of Structured Language Model Programs (NeurIPS 2024) — RadixAttention y prefix caching.
- vLLM project — Automatic Prefix Caching (
docs.vllm.ai/en/latest/features/automatic_prefix_caching.html). - Patel et al. — SplitWise: Efficient Generative LLM Inference Using Phase Splitting (ISCA 2024) — base teórica del routing prefill/decode aware.