TT Lab
Blog

한국어 English 日本語 中文

Get started
Blog

Blog

vLLM production-stack · LMCache · llm-d: Code map of the engine frontend and KV cache tier

Status: Draft (2026-10-17), partially measured. Reference versions: production-stack vllm-stack-0.1.13 (commit e8cb4959ebfa), vLLM v0.31.0 (commit db9527a46873), LMCache v0.5.6 (commit 3e11e10812b4), llm-d v0.10.0 (commit f22bbaf3173a), llm-d-router v0.11.0 (commit a5cbe600ebad). Since a small cluster does not keep vLLM running constantly, no new pods were spawned today; measurements cite raw data collected as a disposable pod on a single GPU node (8GB laptop GPU) on 2026-10-16, recounted on 2026-10-17. Writing rules: Measured data is the default; unmeasured items are marked as "Unmeasured", and unverified items are marked as "Unverified".

Table of Contents

1. One-line Summary: One Kitchen, Interchangeable Floor Manager and Refrigerator

vLLM is a kitchenthat runs models fast on a single GPU (or a few). When operating multiple kitchens, you need a floor managerto decide which kitchen receives an order, a refrigeratorto share prep work (KV cache) across isolated kitchens, and an order board (metrics) to report how busy the kitchens are. These three projects keep the kitchen (vLLM) intact while filling its surrounding environment in different ways.

Component Role Where it runs
production-stack router (vllm_router) Receives OpenAI-compatible requests, selects a single engine pod, and forwards them directly Single Deployment, Python/FastAPI process
production-stack Helm chart Installs engine Deployment, router, ServiceMonitor, and Grafana dashboard all at once helm install once
vLLM KV connector (KVConnectorBase_V1) the hook where the engine scheduler/worker calls an external KV storage Inside the engine process
LMCache Connector implementation. Splits KV into 256-token chunks to store and retrieve from CPU, disk, and remote Inside the engine process or a separate server (MP mode)
llm-d Router (Proxy + EPP) The proxy only handles connections, while EPP selects the pod and returns the address Envoy-based gateway + EPP Deployment (Go)
vLLM /metrics Exposes queue length, KV utilization, prefix cache hit rate, etc. in Prometheus format Engine API server

Why KV cache is needed and what LMCache saves are covered in the Anatomy of LLM KV Cache series. Instead of principles, this installment focuses on which function in which file does the work.

2. Architecture Diagram

요청 경로: 앞단 → vLLM 엔진 ↔ LMCache 계층 앞단은 둘 중 하나 클라이언트 production-stack 라우터 Python · FastAPI 한 프로세스 roundrobin session prefixaware kvaware k8s 파드 감시 + /metrics 수집 llm-d Router 프록시(Envoy 등) + EPP(Go) 필터 점수 고르기 ext-proc 로 파드 주소만 돌려주고 전달은 프록시가 KV 이벤트(ZMQ) 구독 vLLM 엔진 파드 스케줄러 워커(GPU) KV 커넥터: LMCache 구현 GPU 안 KV 블록(16토큰) vLLM 엔진 파드 스케줄러 워커(GPU) KV 커넥터: LMCache 구현 GPU 안 KV 블록(16토큰) LMCache: 256토큰 청크 단위로 저장·조회 LocalCPU LocalDisk 원격 백엔드 P2P·PD 앞 계층부터 찾고, 원격에서 찾으면 CPU 에 올려 둔다 MP 모드는 이 계층을 엔진 밖 별도 서버(ZMQ)로 뺀다
Figure: Request path: The client passes through the production-stack router or llm-d Router to reach the vLLM engine pod, and the engine calls the LMCache tiers (LocalCPU, LocalDisk, Remote, P2P) via the KV connector.

Here is how to read the diagram.

3. Code Map

First, let's organize "where the code resides in repositories." The llm-d/llm-d repository contains no service code. Among 1,315 files in the v0.10.0 tag, 834 are YAML, 249 are Markdown, 0 are Go files, and Python files consist of only 33 CI, tool, and example scripts. It is a collection of deployment guides, Helm values, and design documents, while the actual components reside in separate repositories pointed to by the Roster table in COMPONENTS.md . EPP is llm-d-router . Its former name llm-d-inference-scheduler address now redirects to llm-d-router (HTTP 301 verified on 2026-10-17). Since the guides/env.sh of llm-d v0.10.0 pins the EPP image v0.11.0 and the Gateway API Inference Extension (GAIE) v1.5.0 CRD, this post reads llm-d-router v0.11.0 fetched separately. API types such as InferencePool are imported from the GAIE module.

Path What File to read first
production-stack src/vllm_router/ Router core. Request handling, service discovery, routing, metrics app.py 's initialize_all
production-stack helm/ chart. Engine Deployment, router, ServiceMonitor, dashboard ConfigMap templates/deployment-vllm-multi.yaml
production-stack helm/dashboards/ Three sets of Grafana dashboards (vLLM, per-model, LMCache) vllm-dashboard.json
vLLM vllm/v1/metrics/ Prometheus metric definitions and recordings loggers.py 's PrometheusStatLogger
vLLM vllm/distributed/kv_transfer/kv_connector/v1/ KV connector interface and implementations (LMCache, NIXL, Mooncake, etc.) base.py
LMCache lmcache/integration/vllm/ Actual implementation of the vLLM connector vllm_v1_adapter.py
LMCache lmcache/v1/ Cache engine, token chunks, storage backend, MP server cache_engine.py, storage_backend/__init__.py
llm-d guides/, docs/architecture/ Deployment recipes and design docs. No code docs/architecture/core/router/README.md
llm-d-router pkg/epp/ EPP: ext-proc server, scheduler, filter/scorer/picker plugins scheduling/scheduler.go
llm-d-router pkg/kvevents/, pkg/kvcache/, pkg/sidecar/ KV event subscription/block index, P/D routing sidecar kvevents/README.md

4. Following the Core Path

4.1 production-stack: From Request to Engine

  1. Upon startup, the router sequentially instantiates service discovery, engine statistics collector, request statistics, and routing logic as singletons in initialize_all . This order is necessary because the routing logic reads the tables generated by the preceding three components.
  2. The engine list is created by K8sPodIPServiceDiscovery._watch_engines . It uses a label selector to watch pods, and queries _get_model_names for /v1/models on each ready pod to record which IP serves which model. It connects directly to pod IPs rather than a Service because the router must select a specific pod, whereas a Service delegates that choice to kube-proxy.
  3. Requests pass through route_chat_completion and enter route_general_request . Here, candidate engines are filtered using model from the text, a URL is obtained by calling route_request in the routing logic, and the response is streamed via process_request . A retry loop that removes the failed URL and selects another is also contained within this function.
  4. The routing logic consists of eight types in the RoutingLogic enum. The primary axes are RoundRobinRouter, which places session keys on a hash ring, SessionRouter, which hashes prompt strings into 128-character chunks and inserts them into a trie, and PrefixAwareRouter(HashTrie, which queries the LMCache controller for the instance with the longest matching token prefix KvawareRouter .

The difference between the two prefix routers highlights the project's design considerations. PrefixAwareRouter infers state solely based on records of what it has sent. As the class comment explicitly states, "assuming no prefix cache eviction," the router remains unaware even if the engine deletes blocks. However, it has no external dependencies. KvawareRouter queries the LMCache controller for the actual stored location, making it accurate, but it requires the LMCache package to be included in the router image. The default router image embeds pyproject.toml 's optional dependency lmcache==0.3.11 , which differs significantly from the engine side, and whether the controller messages between the two match remains unverified (unconfirmed).

The Helm chart exposes these router arguments directly. deployment-router.yaml passes routerSpec.routingLogic to --routing-logic , and deployment-vllm-multi.yaml assembles the modelSpec command for each vllm serve . When LMCache is enabled, --kv-transfer-config receives LMCacheConnectorV1 , and the size is provided via the LMCACHE_* environment variable. Because engine arguments are assembled as strings, if vLLM deprecates a flag, the chart and tutorial quietly become outdated and break at runtime. Section 5 addresses contributions in this area.

4.2 vLLM ↔ LMCache: The Connector Interface

vLLM v1 abstracts external KV storage into a single KVConnectorBase_V1 . The key point is that the same object is instantiated for two roles.

Why are they split? The scheduler schedules batches without touching GPU tensors, whereas the worker directly handles GPU tensors, and the two may even run in separate processes. Because "how much can be loaded" must be known before scheduling batches, while actual copying must overlap with forward passes, the architecture connects decision-making and execution through a single block of metadata. The per-layer function aims to overlap copy time by loading the KV of the next layer while computing the current one.

The LMCacheConnectorV1 inside the vLLM tree is a thin shell.

use_native = vllm_config.kv_transfer_config.get_from_extra_config(
    "use_native", False
)
if use_native:
    from vllm.distributed.kv_transfer.kv_connector.v1 import lmcache_integration
    _adapter = lmcache_integration.vllm_v1_adapter
    cls = _adapter.LMCacheConnectorV1Impl
else:
    from lmcache.integration.vllm.vllm_v1_adapter import (
        LMCacheConnectorV1Impl as LMCacheConnectorLatestImpl,
    )
    cls = LMCacheConnectorLatestImpl

self._lmcache_engine = cls(vllm_config, role, self)

Source: vllm v0.31.0 vllm/distributed/kv_transfer/kv_connector/v1/lmcache_connector.py(lines 88–108, log lines omitted), Apache-2.0

The default value is the implementation in the installed LMCache packageIf the implementation were locked inside vLLM, LMCache would have to wait for a vLLM release every time a fix was made. Therefore, vLLM provides the interface while LMCache owns the implementation. The MP connector also lmcache_mp_connector.py looks for the LMCache implementation first, and uses the copy in the tree only when it is not found.

The actual implementation on the LMCache side is LMCacheConnectorV1Impl . Lookups start from get_num_new_matched_tokens , go through the lookup client, and tokens are ChunkedTokenDatabase by _chunk_tokens and split by _prefix_hash , chaining the previous chunk hash to generate the key. The default chunk size is config.py tokens, which is 16 times larger than a vLLM GPU block (measured at 16 tokens). This is because larger chunks reduce the number of objects and round trips in the remote storage, at the cost that overlaps shorter than 256 tokens will not be cached in LMCache.

The storage tier is where CreateStorageBackends puts OrderedDict into The orderis the lookup order (among PD, LocalCPU, P2P, NIXL, LocalDisk, GDS, Maru, and Remote, whichever are enabled). StorageManager.batched_get It searches from the front, and anything found remotely is written back to LocalCPU. The remote side storage_backend/connector/ branches out into seventeen adapters (Redis, Valkey, S3, file system, Mooncake, etc.).

MP mode takes this entire layer outside the engine. MPCacheServer assembles multiple engine modules, and MPServerConfig uses ZMQ (port 5555) for its default transport, 256 for chunks, and blake3 for hashing. The goal is to allow the cache to persist even if the engine restarts and to be shared across multiple engines.

4.3 llm-d: How EPP chooses pods

The llm-d Router is divided into a proxy and an EPP. When the proxy intercepts a request and asks the EPP via an ext-proc stream, the StreamingServer.Process of the EPP receives it, runs the scheduler, and the generateHeaders puts the chosen pod address in the x-gateway-destination-endpoint header and sends it back. It is a decoupled architecture that leaves body transmission, TLS, and retries to a proven proxy while isolating LLM-specific decisions, which is the exact opposite of a production-stack router that handles both in a single Python process.

The scheduler Scheduler.Schedule is a loop of "running the profile chosen by the profile handler, checking the results, and choosing again." A single profile goes through SchedulerProfile.Run inside: filter → weighted scorer (runScorerPlugins) → picker. The reason profiles can be run multiple times is P/D separation. The disagg handler's Pick first selects a D pod using the decode profile, and if necessary, selects a P pod using the prefill profile. The actual P→D flow is handled by the routing sidecar (llm-d-router pkg/sidecar/proxy/) next to the decode pod, which sends the request to P max_tokens=1 first, attaches the received KV transfer information, and passes it to D. The principles of P/D separation itself are covered in the NVIDIA Dynamo post.

The numbers read by the scorer come in through two channels. One is the datalayer source that scrapes the engine /metrics , where parseMetrics parses the Prometheus text and determines which engine-specific label maps to which attribute.

var defaultEngineConfigs = []engineConfigParams{
	{
		Name:                "vllm",
		QueuedRequestsSpec:  "vllm:num_requests_waiting",
		RunningRequestsSpec: "vllm:num_requests_running",
		KVUsageSpec:         "vllm:kv_cache_usage_perc",
		LoRASpec:            "vllm:lora_requests_info",
		CacheInfoSpec:       "vllm:cache_config_info",
	},

Source: llm-d-router v0.11.0 pkg/epp/framework/plugins/datalayer/extractor/metrics/factories.go(lines 91–99), Apache-2.0

The same table lists SGLang, TensorRT-LLM, and Triton side by side, and selects the table using the pod label llm-d.ai/engine-type . This design ensures that even if an engine changes its name, only this single table needs to be modified. Taking these values, KVCacheUtilizationScorer.Score gives 1 − KV 사용률 as a score.

The other is KV events. When vLLM stores or evicts blocks, it publishes events like BlockStored to ZmqEventPublisher , and EPP's precise-prefix-cache plugin opens a subscription for each pod via ensureSubscriber . The received messages are unpacked by VLLMAdapter.ParseMessage to add or remove from the block index. This replaces the "guessing from sent history" in PrefixAwareRouter with facts reported by the engine, and in return, --kv-events-config and open the ZMQ port.

4.4 Metric Path: A Single Line of Definition Becomes Queries in Three Places

메트릭 경로: 정의 한 줄이 세 곳의 쿼리가 된다 vLLM 엔진 · vllm/v1/metrics/loggers.py Counter(name="vllm:prefix_cache_hits") GET /metrics 에서 _total · _created 가 붙어 나간다 ① PS 라우터 engine_stats.py 30초마다 직접 긁음 이름 정확 일치 ② Prometheus ServiceMonitor service-port 포트 PromQL 로 질의 ③ llm-d EPP datalayer 소스 factories.go 이름표 파드마다 폴링 라우터 /metrics 로 같은 접두사 재노출 Grafana 대시보드 JSON 3벌 KV 사용률 점수 = 1 − 사용률 이름이 바뀌면 오류 없이 0 이나 No data 가 된다 옛 이름 v0.31.0 의 이름 gpu_cache_usage_perc → kv_cache_usage_perc gpu_prefix_cache_hits/queries → prefix_cache_hits/queries time_per_output_token_seconds → inter_token_latency_seconds 실측 /metrics(v0.31.0)에 옛 이름은 하나도 없었다
Figure: Metric Path: The definition in vLLM loggers.py is exposed to /metrics and read as-is by the production-stack router parser, Prometheus/Grafana dashboards, and llm-d EPP name tags, where changing the name results in empty values without errors.

PS in the diagram stands for production-stack. The starting point is the PrometheusStatLogger constructor, where vllm: metrics are defined line by line. The KV cache usage is vllm:kv_cache_usage_perc, the prefix cache is vllm:prefix_cache_queries·vllm:prefix_cache_hits, and the inter-token latency is vllm:inter_token_latency_seconds . The source counter names do not have _total , but when prometheus_client exports them, _total and _created is attached, so the querying side must read it as vllm:prefix_cache_hits_total . This difference is one of the causes of name confusion. This registry attaches attach_router to the /metrics path.

vLLM exposes prefix caching as "two lookup and hit counters" instead of a "hit rate gauge." This is so users can calculate the ratio for the desired interval like rate(hits[5m]) / rate(queries[5m]) (docs/design/metrics.md). When removing a metric, it follows the rule of X.Y deprecation notice, X.Y+1 hiding (restored as--show-hidden-metrics-for-version ), and X.Y+2 deletion (docs/usage/metrics.md). The engine-side rule is clear, but the problem is that the side reading this name is in a different repository.

First, the consumer, the production-stack router collector EngineStatsScraper , polls every 30 seconds by default from each engine /metrics is GETted and from_vllm_scrape extracted by matching the name precisely.

for family in text_string_to_metric_families(vllm_scrape):
    for sample in family.samples:
        if sample.name == "vllm:num_requests_running":
            num_running_reqs = sample.value
        elif sample.name == "vllm:num_requests_waiting":
            num_queuing_reqs = sample.value
        elif sample.name == "vllm:gpu_prefix_cache_hit_rate":
            gpu_prefix_cache_hit_rate = sample.value
        elif sample.name == "vllm:gpu_prefix_cache_hits_total":
            gpu_prefix_cache_hits_total = sample.value
        elif sample.name == "vllm:gpu_prefix_cache_queries_total":
            gpu_prefix_cache_queries_total = sample.value
        elif sample.name == "vllm:gpu_cache_usage_perc":
            gpu_cache_usage_perc = sample.value

Source: production-stack vllm-stack-0.1.13 src/vllm_router/stats/engine_stats.py(lines 63–76), Apache-2.0

If there is no matching name, 0 remains without any exception or log. The router exposes this value back as a gauge ( metrics_router.py of /metrics with a prefix like vllm: prefixattached (server label). Therefore, the same name gets mixed from both the engine and router sources with different labels, and the default dashboard's prefix cache panel filters by endpoint="service-port" . service-port is the name the chart assigns to the engine Service port, and the router Service port name is router-sport , so it keeps only the time series coming from the engine.

The second consumer is Prometheus and Grafana. The chart's ServiceMonitor scrapes the engine and router, and the PromQL in the dashboard JSON references these names as strings. The third consumer is the EPP label from Section 4.3. If the names are wrong in any of the three, it will silently result in 0 or "No data" rather than failing.

5. Connection to User Contributions

The author's four PRs all share the same pattern: "another repository still calls a name that vLLM deleted" (statuses as of 2026-10-17). In this post, we reopened only the v0.31.0 tag to verify "it is gone now, and this is here instead."

PR Fixed Area Corresponding vLLM v0.31.0 Line Status
vllm-project/production-stack#1147 helm/dashboards/vllm-dashboard.json:826 of the "GPU KV Cache Hit Rate" panel vllm:gpu_prefix_cache_* loggers.py:600 vllm:prefix_cache_queries, loggers.py:611 vllm:prefix_cache_hits Pending review (automatic review: "No issues found")
vllm-project/production-stack#1148 tutorials/23-whisper-api-transcription.md:24 of --task transcription --task is gone and its place is taken by arg_utils.py:930 of --runner·--convert is taken over Pending review
LMCache/LMCache#5814 In 12 documentation, benchmark, and example files (20 locations in the v0.5.6 tag), --disable-log-requests arg_utils.py:3141 has only --enable-log-requests , and since it is BooleanOptionalAction , the opposite is --no-enable-log-requests Pending review (base dev)
llm-d/llm-d#2835 document's vllm:time_per_output_token_seconds, vllm:gpu_prefix_cache_hit_rate loggers.py:832 vllm:inter_token_latency_seconds, hit rate is the ratio of the two counters on lines 600 and 611 Pending review

#1147 only fixed the dashboard, and the same issue in the router parser (engine_stats.py:69–76) is handled separately by #1140 and #1146 as mentioned in the PR body. Since llm-d#2835 is based on main, in the v0.10.0 tag, the same sentence is located near docs/architecture/advanced/autoscaling/wva.md:144·210줄 and the DigitalOcean dashboard 1544줄 , and the third fixed guide (turn-priority-fairness) is not yet in v0.10.0. The reason for the different paths was not verified (unverified).

6. Measurement

6.1 What and How We Measured

Measured on 2026-10-16 by running a disposable pod on a single GPU node (8GB-class laptop GPU) in a small cluster. The measurement script (pod.sh) fixes the node and uses only existing images (imagePullPolicy: Never), blocks Hugging Face access, and sets a memory limit of 3.5Gi and a 90-minute timeout. The model is a randomly weighted Qwen2 architecture(2 layers, hidden 256, --load-format dummy). The image is vllm/vllm-openai v0.31.0 (PyTorch 2.13, CUDA 13.0). After sending 5 requests with the same first 160 tokens, we received /metrics . Since the router, LMCache, and EPP were not launched, Section 6.3 is a static comparisonmatching the names extracted from each tag source with this output.

6.2 /metrics Output Count

Item Value
Total lines 587
# TYPE family 88 (vllm 68, http 10, process 6, python 4)
vllm: of the family _created auxiliary gauge 31
vllm: actual metric 37 (15 counters, 16 histograms, 6 gauges)
vllm:prefix_cache_queries_total / _hits_total 805 / 640 → hit rate 0.795
vllm:inter_token_latency_seconds_count 75
vllm:cache_config_info block_size="16", num_gpu_blocks="144272", gpu_memory_utilization="0.3"
Old name (gpu_prefix_cache*, gpu_cache_usage_perc, time_per_output_token_seconds) 0

The numbers match up. 5 requests × 161 tokens = 805 lookups; the first request misses all, and the remaining four hit 160 tokens (10 blocks) each, totaling 640. The token interval for 5 requests created with 16 tokens each is 15 × 5 = 75. 144,272 and 272 blocks × 16 = 2,308,352 is also the same as in the kv_cache_size_tokens line (the model is very small, so there are many blocks).

6.3 Are Consumer-Facing Names Present in the Output?

Consumer (Tag Source) Looking for vllm: Name Present in output Absence and reason
production-stack router parser engine_stats.py 6 2 4: gpu_prefix_cache_hit_rate·_hits_total·_queries_total·gpu_cache_usage_perc, not defined in v0.31.0 source
production-stack vllm-dashboard.json 13 9 4: current_qps·healthy_pods_total is the router's own metric (normal), gpu_prefix_cache_* two are removed names (#1147)
production-stack vllm-model-metrics-dashboard.json 21 21 0
llm-d-router EPP default vLLM label 5 4 1: lora_requests_info, conditional metric that appears when LoRA is enabled (loggers.py:993)
Entire llm-d v0.10.0 documentation and guides 71 40 31: 27 are conditional metrics that appear when offload and NIXL connectors are enabled, 4 are not defined in v0.31.0 source

The four "not defined" items in llm-d documentation are time_per_output_token_seconds three of the family (fixed by #2835) and vllm:kv_offload_tiering_block_hits_total one. The last one was left as a contribution candidate in Section 8.

6.4 Missing flags

Command Result
vllm serve <작은 모델> --disable-log-requests unrecognized arguments, exit code 2
vllm serve <작은 모델> --swap-space 0 unrecognized arguments, exit code 2
vllm serve List of flags extracted from the general help (about 400) --task·--disable-log-requests·--swap-space None, --enable-log-requests·--no-enable-log-requests·--runner·--kv-transfer-config·--kv-events-config·--show-hidden-metrics-for-version Present
--task with a random-weight Whisper structure without vllm serve log Supported tasks: ['transcription'], /v1/audio/transcriptions HTTP 200

The chart default engine image read on the same day (lmcache/vllm-openai:latest) was vLLM 0.29.0 and LMCache 0.5.5, and even inside it, loggers.py there was no gpu_prefix_cache . Even with the chart defaults as they are the router parser and prefix cache panels will show 0 or No data. Grafana was not actually installed and viewed with the chart (unmeasured).

7. Where to read first

  1. vllm/v1/metrics/loggers.py 's PrometheusStatLogger constructor is scanned from top to bottom. What the engine counts is the input for everything else.
  2. vllm_router/app.py 's initialize_all starting from request.py 's route_general_request to follow a single request all the way through.
  3. routing_logic.py Compare RoutingInterface and a couple of implementations. Looking at the tests (src/tests/test_*_router.py) together helps quickly grasp the expected behavior.
  4. kv_connector/v1/base.py After reading the top comments (scheduler/worker function list), jump to the LMCache vllm_v1_adapter.py function of the same name.
  5. llm-d standardizes terminology with docs/architecture/core/router/README.md , and for code, look at llm-d-router scheduler.go and factories.go .

8. Good first contribution opportunities

These are things I found while reading the source code. I haven't checked if there are already open PRs or issues, so you should search before submitting.

  1. production-stack per-cloud dashboard copies. The Helm chart dashboards are being fixed in #989 and #1147, but tutorials/terraform/{coreweave,eks,nebius}/config/ 's vllm-dashboard*.json four sets still have vllm:gpu_cache_usage_perc and vllm:gpu_prefix_cache_* remaining in 10 places each (e.g., eks/config/vllm-dashboard.json:732).
  2. in the production-stack benchmark tutorial, --swap-space 0. 07-benchmark-multi-round-qa-single-gpu.md:51 and three other tutorial sets in 07 and 08 give extraArgs: ["--swap-space", 0] . v0.31.0 exits with code 2 on this flag (Section 6.4).
  3. Metric names in the llm-d P2P guide. guides/p2p-kv-cache-sharing/benchmarking/README.md:129 uses vllm:kv_offload_tiering_block_hits_total , but v0.31.0 only defines vllm:kv_offload_tiering_chunk_hits . We first need to check whether the name was changed in the vLLM version the guide relies on (after vllm#48798).
  4. KvawareRouter chat request in route_request . request_json.get("prompt", "") only tokenized and left TODO: Handle chat completions behind. /v1/chat/completions requests seem to be queried with an empty prompt, falling back to session and QPS routing. You can refer to the handling of PrefixAwareRouter in the same file, messages .
  5. Mixed time series in the default dashboard (unverified). The "Number of Running Requests" panel plots vllm:num_requests_running without a label. Since the router also emits the same name (Section 4.4), the two time series may be plotted together. You need to check the actual screen first.

Project Guidelines: production-stack requires a proposal in proposals/ for major changes, and a DCO Signed-off-by for commits. LMCache has devas its default branch, requires DCO, has a collection of beginner-friendly issues (LMCache#627), and includes an "AI-generated code signals" review item in its AGENTS.md and coding standards. llm-d accepts AI-assisted contributions under similar standards, provided the submitter can personally explain the content. All of the author's PRs disclosed AI assistance at the end of the body.

9. Limitations

TT's closing thought. Even if the kitchen stays the same and only the menu item names change, a floor manager who doesn't adapt will serve an empty plate instead of saying "We don't have that dish." That's why a PR fixing a single name can patch a surprisingly big hole!

References

All access dates are 2026-10-17.

Cited code: vLLM, production-stack, and llm-d-router are all licensed under Apache License 2.0.

Sign in to like

Comments

No comments yet.

Sign in to leave a comment