Repository navigation
Recipes Integration
The sikkatu/recipes production launchers carry an opt-in switch that attaches
the persistent cache without changing the recipe's plain behaviour.
The launchers (scripts/run-tp4-uva.sh, scripts/run-tp4-hotsplit.sh of
recipes/rtx-pro-6000/mimo-v2.6-pro-hotsplit) contain a KV-PERSIST block:
KV_PERSIST="${KV_PERSIST:-0}"
if [ "$KV_PERSIST" = "1" ]; then
... derives the cache-identity namespace ...
MODEL_ARGS=(--model "/models/$KV_PERSIST_NAMESPACE")
MODEL_ARGS+=(--kv-transfer-config "$KV_TRANSFER_CONFIG")
MODEL_ARGS+=(--enable-prompt-tokens-details)
MODEL_ARGS+=(--block-size "${BLOCK_SIZE:-16}")
fi-
OFF by default — with
KV_PERSISTunset or0the recipe behaves byte-identically to before: no connector, no namespace remount, no new arguments. The plain run needs noMODEL_DIRand no kv-persistence install. -
ON (
KV_PERSIST=1) the block does four things (review fix, round 19):-
Cache-identity namespace —
MODEL_DIRis mounted at/models/<namespace>(NOT/model). LMCache keys every L2 object bymodel_config.model(the--modelvalue), so a fixed/modelpath made EVERY deployment share one namespace: different revisions, KV dtypes or geometries could read each other's KV blocks. With the namespace path, each identity gets its own namespace and an incompatible deployment gets a SAFE cache miss — provided the namespace is derived from the engine's ACTUAL arguments (review fix: realKV_CACHE_DTYPE,MAX_MODEL_LEN, block size and TP size are passed to the CLI asKVP_*overrides, andautois resolved to the model's effective dtype first). The namespace is derived frompython3 -m kvpersist.cli model-name(the verified identity hash) when a kv-persistence checkout is found (KVP_REPO_ROOTor~/kv-persistence), else from a deterministic hash of the KV-compatibility fields (config.json, weight indexes + shard sizes, tokenizer files, chat template, kv dtype, max-model-len, block size, TP size).MODEL_DIRis therefore required forKV_PERSIST=1. If you setKV_PERSIST_NAMESPACEmanually, YOU own the isolation guarantee. -
--kv-transfer-config— the LMCache connector (same JSON as kv-persistence's own launcher). -
--enable-prompt-tokens-details— without itusage.prompt_tokens_detailsis never reported,cached_tokens(the only machine-readable reuse counter) is unavailable, and kv-persistence's acceptance run (scripts/test-recovery.sh) can never PASS. -
--block-sizepinned (16 by default) — the kv-persistence identity gate (verify-engine-identity.sh) needs a verifiable engine argument.
-
Cache-identity namespace —
Knobs are documented in tp4.env.example / .ru / .zh (identical names,
values and order in all three languages): KV_PERSIST, KV_PERSIST_HOST,
KV_PERSIST_PORT, KV_PERSIST_NAMESPACE, BLOCK_SIZE.
- Install and start the cache service per Quick Start steps 1–6.
- In the recipe's
tp4.envadd:KV_PERSIST=1
- Launch the recipe exactly as usual:
bash scripts/run-tp4-uva.sh
The launcher prints two confirmation lines when the block is active:
KV_PERSIST=1: attaching LMCache connector … and
KV_PERSIST=1: cache-identity namespace -> /models/<hash> ….
The recipe's block cannot run kv-persistence's verify-engine-identity.sh
automatically (the recipe has no kv-persistence checkout path wired in), so run
it manually BEFORE trusting any cache reuse:
bash ~/kv-persistence/scripts/verify-engine-identity.sh <container-name>Exit 0 = identity verified (cache reuse allowed within that scope). Exit 4 =
MISMATCH (do NOT reuse the cache — reconcile
configs/mimo-v2.6-pro/identity.env.local with the engine args and re-run).
Exit 5 = INCONCLUSIVE (fix the value sources first). The gate compares the
container's startup args and the served model's config.json; with
KV_PERSIST=1 the launcher pins --block-size and the namespace mount, so all
fields are checkable. The gate only reads engine config — it does not alter
production behaviour.
Three different variables name the same endpoint (review note):
| Side | Host | Port |
|---|---|---|
cache service (kv-persistence configs/generic/lmcache-server.env) |
LMCACHE_HOST |
LMCACHE_PORT |
cache service (kv-persistence configs/generic/cache.env docs alias) |
KVP_LMCACHE_HOST |
KVP_LMCACHE_PORT |
recipe launcher (tp4.env) |
KV_PERSIST_HOST |
KV_PERSIST_PORT |
The recipe side is what vLLM connects WITH; the cache side is what the service
listens ON. If you change the port on the cache side (LMCACHE_PORT), you MUST
set the same value in KV_PERSIST_PORT on the recipe side — otherwise vLLM
cannot reach the cache and the connector silently retries a dead endpoint. Same
for the host. Defaults match (127.0.0.1:5555).
kv-persistence/scripts/run-vllm-with-persist.sh (the specialized launcher) and
the recipe's KV_PERSIST=1 branch now agree on everything that determines cache
compatibility:
| Parameter | kv-persistence launcher | recipe KV_PERSIST=1
|
plain recipe run |
|---|---|---|---|
--model |
/models/<namespace> |
/models/<namespace> |
/model or MODEL_ID
|
--kv-transfer-config |
yes (LMCacheMPConnector) | yes (same JSON) | no |
--enable-prompt-tokens-details |
yes | yes | no |
--block-size |
16 (pinned) | 16 (pinned via KV_PERSIST_BLOCK_SIZE) |
not passed (engine default) |
MAX_NUM_SEQS |
2 | 2 | 2 (env; user-verified: no speed loss, more convenient default) |
KV_CACHE_DTYPE |
auto (resolves to model dtype) |
auto (resolves to model dtype) |
auto (env) |
MAX_MODEL_LEN |
1048576 | 1048576 | 1048576 (env) |
| identity gate | automatic (refuses on mismatch) | manual reminder | none |
| The specialized launcher additionally runs the identity gate automatically and | |||
| REFUSES to attach the cache on mismatch; the recipe branch reminds you to run it | |||
| manually. **A value difference in any identity field (dtype string included) is | |||
| a SAFE cache miss** — the namespace hash changes, so the two paths simply stop | |||
| sharing cached blocks. They never read wrong KV; they just stop reusing. If you | |||
| want reuse between both paths, keep the values identical. |
Note on KV_PERSIST_NAMESPACE override: if you set KV_PERSIST_NAMESPACE
explicitly (skipping the derived namespace), YOU own the isolation guarantee —
the launcher cannot verify that the namespace matches the engine's actual
configuration. Use the override only when you understand the identity fields.
Remove the KV_PERSIST=1 line (or set 0) and relaunch. That is the whole
rollback: the recipe is untouched by design, and cached data on NVMe is simply
ignored while disabled.
The cache service must be running BEFORE the launcher attaches. Start order:
start-cache.sh first, recipe launcher second. If the service is down when vLLM
starts, remove KV_PERSIST=1 and use the plain recipe (or start the cache and
relaunch) — never leave vLLM retrying a dead connector in production.
| Launcher | When |
|---|---|
run-tp4-uva.sh |
UVA baseline (verified working recipe path) |
run-tp4-hotsplit.sh |
hot/cold expert split (dynamic/static modes) |
kv-persistence/scripts/run-vllm-with-persist.sh |
when you want the identity gate enforced automatically and the full kv-persistence tooling (preflight, benchmark, test-recovery) |
All three accept the same KV-PERSIST knobs (the specialized launcher reads them from kv-persistence's own config instead). Use whichever you run today.
Contract tests (string checks) plus BEHAVIOURAL tests in
sikkatu/recipes/tests/test_kv_persist_behaviour.py: both launchers are
actually EXECUTED against stub docker/nvidia-smi/curl/python3 binaries,
and the stub python3 records the KVP_* overrides it received from the
block. Covered: the namespace follows the engine's real arguments (fp8 vs
bfloat16 yield different namespaces), auto resolves to the model's
effective dtype, a CLI failure aborts the launch with the real error (no
silent fallback), KV_PERSIST_NAMESPACE override is honoured, the plain
(KV_PERSIST=0) run derives nothing, KV_PERSIST_BLOCK_SIZE reaches both the
CLI and --block-size, and the model-name prefix is stable across launchers
(mimo-pro/<hash> on both paths). kv-persistence additionally has
tests/unit/test_launcher_namespace_coherence.py, which executes the real
default-expression line of run-vllm-with-persist.sh — a regression to
basename $MODEL_DIR fails the test.
The cache server itself, capacity policy, identity pins and administrative tools
live in kv-persistence; its wiki is
the reference for setup, persistence verification and rollback. Note that
recovery acceptance (scripts/test-recovery.sh) still requires a GPU-host run —
see kv-persistence docs/final-report.md for the honest status. The recipe's
contract tests verify the launcher carries the machinery; they do not prove
real NVMe recovery by themselves.