Hidden states#
The HiddenStateStore caches per-token hidden-state tensors
(intermediate activations from an inference forward pass) alongside
the KV cache, keyed by the same chunk keys. It exists to support any
system where a downstream stage needs an upstream stage’s
intermediate activations for the cached prefix and cannot
reconstruct them from KV alone — for example a vLLM-Omni
thinker -> talker pipeline, where the talker consumes the
thinker’s hidden states for tokens whose KV the engine restored
without re-running prefill.
Without this store, restoring KV alone causes downstream stages to
see truncated or wrong activations for the cached prefix. With it,
the talker (or any equivalent consumer) gets a contiguous
[num_cached_prefix_tokens, hidden_dim] tensor matching the KV
prefix LMCache restored.
Enabling it#
Set three keys in the LMCache YAML (LMCACHE_CONFIG_FILE):
chunk_size: 256
enable_hidden_state_cache: true
max_hidden_state_cpu_size: 4 # GiB, pinned CPU pool size
# hidden_state_layers: [0, 1] # optional allowlist (see below)
enable_hidden_state_cache— master switch. Whenfalse(the default),engine.hidden_state_storeisNoneand integrators must skip all HS APIs on that worker.max_hidden_state_cpu_size— pinned-CPU pool size in GiB, dedicated to hidden states. Must be> 0when the store is enabled. The pool is independent of the KV pool; HS allocator pressure never evicts KV.hidden_state_layers— optional allowlist oflayer_idxvalues accepted onstore_hidden_states. Leave unset to accept all layer indices. Use this only when you know exactly which hook indices the worker writes (e.g. matching a fork’s hybrid multimodal hook list).
Using it from a worker#
The store is exposed on the engine (not as engine-level methods):
hs = engine.hidden_state_store
if hs is None:
return # HS caching disabled; nothing to do
# Store: hidden_states corresponds to token_ids[token_offset:]
hs.store_hidden_states(
token_ids, # full prefix (same as used for KV)
hidden_states, # [len(token_ids) - token_offset, hidden_dim]
layer_idx=0,
token_offset=num_computed, # 0 for non-incremental callers
)
# Retrieve: contiguous prefix tensor or None on full miss
restored = hs.retrieve_hidden_states(token_ids, layer_idx=0)
Notes:
token_idsis always the full prefix so chunk keys align with KV exactly.token_offsetlets incremental callers (such as vLLM-Omni) pass only the newly computed rows inhidden_stateswithout zero-padding the already-cached prefix.layer_idxis the storage layer index. Callers that need several intermediate tensors per request (e.g. multimodal hook outputs plus the main text hidden state) callstore_hidden_statesonce per distinctlayer_idxand retrieve per layer on restore.retrieve_hidden_statesis prefix-strict: it returns the longest contiguous CPUfloat32prefix where every chunk has both KV and HS for the requestedlayer_idx, and stops at the first chunk where either is missing.
Eviction model#
The store implements a coupled-but-asymmetric eviction rule:
KV evicted ⇒ HS evicted. On every
retrieve_hidden_statesthe store asks the KVStorageManagerwhether KV is still present for each chunk key. If KV is gone, the orphan HS entry is dropped and the returned prefix ends there.HS evicted ⇏ KV evicted. Pressure on the HS pinned pool only evicts HS entries (its own LRU), never KV.
Restore stops at first missing HS or KV chunk for the requested layer.
Design notes (user-visible)#
Separate pinned pool from KV. KV tensors and hidden-state tensors have heterogeneous shapes and dtypes, so they get independent allocators.
max_hidden_state_cpu_sizesizes the HS pool only.Same chunk keys as KV. The store reuses the engine’s
TokenDatabaseso HS chunks share the exactCacheEngineKeyas the matching KV chunks. This is what enables the lazy coupled eviction check.Lazy coupled-eviction check. No callbacks or shared indices are required between KV and HS — the store asks
storage_manager.contains(key)per chunk on retrieve. Works uniformly for any backend that implementscontains().
The full internal design (class layering, code paths, follow-up
work) lives at docs/design/v1/hidden_state_store.md in the
source tree.