KV Cache SDK#
The LMCache SDK lets you retrieve a request’s KV cache from a LMCache server, transform it on the CPU, and store it back. This can be used for KV cache transformations, such as token dropping. In the example: we prefill a batch of long prompts, drop half of each request’s KV chunks, and show the decode-throughput gain. The SDK API is meant for offline setup. The full runnable notebook lives at examples/token_dropping.
Why KV Cache SDK#
Improving Decode Throughput when shrinking KV cache using token dropping. Token dropping reduces the KV cache size, allowing more requests to fit in a batch, improving decode throughput. Doing so will affect the accuracy minimally, as we demonstrated with the SnapKV example in the examples/token_dropping/snapkv_token_dropping.ipynb.
The SDK gives you the hooks to retrieve a request’s KV and other intermediate tensors (currently only query tensors), supply your function to edit the KV, and store the edited KV back. The SDK also provides a batched-stream API to prefill, modify, and store the cache back before decoding continues.
Since many token dropping algorithms rely on the intermediate tensors, we also provided a flag to transfer the intermediate tensors from vLLM to LMCache. Currently, the SDK only supports transferring query intermediate tensors.
How it works#
A request flows through three phases on the batched-stream API:
prefill — run each prompt through vLLM once (
max_tokens=1); vLLM computes the KV cache and stores it in LMCache.modify — the SDK retrieves the cached KV to CPU, hands it to your edit function, and stores the result back.
decode — continue generation against the smaller, edited cache.
The SDK runs on CPU and hands you KV tensors in HND order with shape
[2, L, T, D] (K/V, layers, chunk-aligned tokens, num_kv_heads * head_dim).
Configuration#
The SDK runs on CPU and talks to the server over the engine-driven
transfer path, which the server does not load by default – start the
server with --supported-transfer-mode auto so both the vLLM
(lmcache-driven) and SDK (engine-driven) paths are available.
To enable shared-memory transfer, pass --shm-name and disable lazy
L1 allocation with --no-l1-use-lazy. If shared memory is
unavailable and these flags are not specified, the SDK falls back to
pickle.
To transfer query tensors, add --enable transfer_query flag.
lmcache server \
--l1-size-gb 150 \
--eviction-policy LRU \
--chunk-size 256 \
--port 6555 \
--http-port 8080 \
--supported-transfer-mode auto \
--shm-name lmcache_kvcache_sdk \
--no-l1-use-lazy \
--enable transfer_query
Then start vLLM with the LMCache MP connector.
vllm serve Qwen/Qwen3-8B \
--port 8000 \
--enforce-eager \
--gpu-memory-utilization 0.65 \
--kv-transfer-config '{
"kv_connector":"LMCacheMPConnector",
"kv_role":"kv_both",
"kv_connector_extra_config":{"lmcache.mp.port":6555}
}' \
--trust-remote-code \
--return-tokens-as-token-ids
To also send intermediate tensors, add
"lmcache.mp.transfer_intermediate_tensors": true to
kv_connector_extra_config.
By default, the QRingBuffer, a temporary staging buffer for containing
query tensor, has the capacity to hold the query tensor of 2 forward
passes. However, it can also be configured via
"lmcache.mp.q.ring_depth":2.
Example:
vllm serve Qwen/Qwen3-8B \
--port 8000 \
--enforce-eager \
--gpu-memory-utilization 0.65 \
--kv-transfer-config '{
"kv_connector":"LMCacheMPConnector",
"kv_role":"kv_both",
"kv_connector_extra_config":{
"lmcache.mp.transfer_intermediate_tensors": true,
"lmcache.mp.port":6555,
"lmcache.mp.q.ring_depth":2
}
}' \
--trust-remote-code \
--return-tokens-as-token-ids
The SDK keys the KV cache by token ids: create_request takes the prompt as
token ids, and every post_completion must report a token_id for each
generated token. The example gets these ids straight from vLLM by passing
--return-tokens-as-token-ids. Otherwise, if vLLM returns only text, the
post_completion must tokenize each generated token back into a token id.
Here’s an example of creating a context and connecting to the LMCache server. Each type of tensor (KV, query intermediate) has its own context.
import lmcache.sdk as lmc_sdk
kv_ctx = lmc_sdk.kvcache.connect(
url="tcp://localhost:6555", # must match --port
http_url="http://localhost:8080", # must match --http-port
model_name="Qwen/Qwen3-8B",
timeout=60,
)
q_ctx = lmc_sdk.qcache.connect(
url="tcp://localhost:6555", # must match --port
http_url="http://localhost:8080", # must match --http-port
model_name="Qwen/Qwen3-8B",
timeout=60,
)
...
kv_ctx.close()
q_ctx.close()
Writing a custom edit function#
An edit function takes the Mapping[kind, retrieved tensor] and its token ids and returns the
edited (kv, tokens). batch.modify(fn) applies it for every requests. The
function should be implemented as if it’s only for single request, and the SDK
will call it in parallel for every requests in the batch.
modify operates only on the chunk-aligned prefix. A trailing partial
chunk is tracked by the SDK and re-sent on the next decode, so
tokens arrives already truncated to the cached length.
API reference#
The SDK lives under lmcache.sdk. The examples above alias
import lmcache.sdk as lmc_sdk (and lmcache.sdk.request / .batch as
lmc_request / lmc_batch).
Modules#
Module |
Purpose |
|---|---|
|
Package entry point; |
|
|
|
|
|
The server-connection context plus the shared cache-kind enum and error type. |
|
Per-request streaming: |
|
Orchestrates many request streams together via |
|
|
Classes#
Class |
Purpose |
|---|---|
|
Enum selecting the cache: |
|
A connection to one LMCache server for one model + kind; returned by
|
|
One request’s lifecycle (prefill / decode / retrieve / modify). |
|
Runs a set of |
|
Throughput / latency report for a single |
|
One generated-token event passed back through the request. |
|
Protocol you implement: a callable that submits a request to your engine. |
|
Type alias for the edit function you pass to |
|
Aggregated report returned by |
|
Error types raised by the SDK, streams, and batches respectively. |
Functions and methods#
Function / method |
Description |
|---|---|
|
Connect and register caches for |
|
Connect for the KV cache directly. Alternatively, use
|
|
Connect for the query cache directly (model name is |
|
Fetch the server-registered layout for this model + kind (called by
|
|
Pull the cached tensor for a token sequence ( |
|
Push a tensor of shape |
|
Release the context’s resources when done. |
|
Create one request stream bound to one or more contexts (e.g. KV and Q). |
|
Create an empty batch. |
|
Register a request stream to the batch. |
|
Fetch a registered request stream by its |
|
Prefill every request stream once ( |
|
Apply the edit function |
|
Decode every request stream. Returns |
|
Print the |
|
Return the |
Metrics reports input_tokens / input_tput for prefill,
duration for modify, and output_tokens / output_tput for decode.
Attributes#
Attribute |
Description |
|---|---|
|
|
|
|