Files
2026-07-13 12:24:33 +08:00

255 lines
8.0 KiB
Markdown

# `lmcache kvcache` CLI Command Design
**Status:** Proposal | **Date:** 2026-03-19
## Why
Users need a way to manage KV cache state for **specific requests** from the
command line — pin a request's cache to prevent eviction, compress it, or clear
it. Part of **Phase 1** of the
[CLI design](../commands.md).
---
## Design Principles
`lmcache kvcache` is a **per-request management tool**. Every sub-command
operates on a specific request's KV cache, identified by request ID or token
sequence.
### All management goes through HTTP
ZMQ is reserved for **performance-critical data-path** communication between the
inference engine and LMCache (store, retrieve, prefetch). Every `lmcache kvcache`
CLI operation goes through the **MP HTTP server**
(`lmcache/v1/multiprocess/http_server.py`).
Today some operations (e.g. `pin`) only have ZMQ implementations. These
need new HTTP endpoints on the MP HTTP server before the CLI can use them.
### Indexing KV caches
Per-request sub-commands (`pin`, `compress`, `info`) take a **positional
request ID** and optional range flags:
- **`<request_id>`** (positional, required) — identifies the request whose
KV cache to operate on.
- **`--start <st> --end <ed>`** (optional) — narrow the operation to a token
range `[st, ed)` within the request. Defaults to the full sequence.
`clear` is a **bulk operation** — it clears all L1 cache and does not take a
request ID.
### Pipe- and script-friendly output
- **Exit codes:** `0` = success, `1` = error, `2` = rejected (e.g. pin rejected
due to memory pressure). Scripts branch on `$?` without parsing output.
- **`--format json`:** Structured output for piping into `jq` (already exists).
- **`--format terminal`:** Human-readable ASCII table (default, already exists).
- **`--quiet` / `-q`:** Suppress all stdout. Exit code only.
- **Stdout vs stderr:** Metrics to stdout (pipeable). Errors to stderr.
---
## Command Overview
```
lmcache kvcache
├── info # Per-request cache state (locations, pinned status)
├── clear # Clear all cached KV data in L1 (CPU)
├── pin # Pin a request's KV cache to L1/CPU (may be rejected)
└── compress # Compress a request's KV cache in-place
```
| Sub-command | Target | Description |
|------------|--------|-------------|
| `info` | instance | Show per-request cache state: which chunks, where stored, pinned status |
| `clear` | instance | Clear all cached KV data in L1 (CPU) |
| `pin` | instance | Pin a request's KV cache to L1/CPU; may be rejected if memory pressure is too high |
| `compress` | instance | Compress a request's KV cache to reduce memory footprint |
```bash
$ lmcache kvcache -h
usage: lmcache kvcache [-h] {clear} ...
Manage KV cache state.
subcommands:
clear Clear all cached KV data in L1 (CPU)
# info, pin, compress will appear here once implemented
```
---
## Commands in Detail
### `info`
> **Status: needs further design — will not be implemented yet.**
> The output format, filtering options, and server-side endpoint are TBD.
> The sketch below is a placeholder to illustrate intent.
Show the cache state for a specific request: which chunks exist, which storage
backend holds each one, and whether they are pinned.
```bash
# By request ID
$ lmcache kvcache info --url http://localhost:8000 req-abc-123
===== KV Cache Info (req-abc-123) =====
Total chunks: 32
Pinned: 8
---------------- Chunks ---------------
[0:256]: L1, pinned
[256:512]: L1, pinned
[512:768]: L1, L2
[768:1024]: L2
...
========================================
# Narrowed to a token range
$ lmcache kvcache info --url http://localhost:8000 \
req-abc-123 --start 0 --end 512
# JSON for scripting
$ lmcache kvcache info --url http://localhost:8000 \
req-abc-123 --format json
{
"title": "KV Cache Info (req-abc-123)",
"metrics": {
"total_chunks": 32,
"pinned": 8,
"chunks": {
"0:256": "L1, pinned",
"256:512": "L1, pinned",
"512:768": "L1, L2",
"768:1024": "L2"
}
}
}
# Find chunks on L2
$ lmcache kvcache info --url http://localhost:8000 \
req-abc-123 --format json \
| jq '.metrics.chunks | to_entries[] | select(.value | contains("L2"))'
```
### `clear`
Clear all cached KV data in **L1 (CPU memory)** on the target instance.
```bash
$ lmcache kvcache clear --url http://localhost:8000
========== KV Cache Clear ==================
Status: OK
=============================================
```
### `pin`
Pin a request's KV cache chunks to L1 (CPU memory) to prevent eviction. The
server **may reject** the request if CPU memory pressure is too high.
Exit codes: `0` = pinned, `2` = rejected, `1` = error.
```bash
$ lmcache kvcache pin --url http://localhost:8000 req-abc-123
======== KV Cache Pin (req-abc-123) ========
Status: OK
Chunks pinned: 32
=============================================
$ echo $?
0
# Quiet mode for scripts
if lmcache kvcache pin -q --url http://localhost:8000 req-abc-123; then
echo "pinned"
else
echo "rejected or error"
fi
# Narrowed to a token range
$ lmcache kvcache pin --url http://localhost:8000 \
req-abc-123 --start 0 --end 512
# Rejected case (exit code 2)
$ lmcache kvcache pin --url http://localhost:8000 req-xyz
======== KV Cache Pin (req-xyz) =============
Status: REJECTED
Reason: L1 memory pressure (91%)
=============================================
$ echo $?
2
```
### `compress`
Compress a request's KV cache chunks in-place to reduce memory footprint.
```bash
$ lmcache kvcache compress --url http://localhost:8000 \
req-abc-123 --method zstd
===== KV Cache Compress (req-abc-123) ======
Status: OK
Method: zstd
Chunks compressed: 32
=============================================
$ lmcache kvcache compress --url http://localhost:8000 \
req-abc-123 --start 0 --end 512 --method zstd
```
| Flag | Required | Description |
|------|----------|-------------|
| `<request_id>` | yes (positional) | Target request |
| `--method` | yes | Compression method (e.g. `zstd`) |
| `--start`, `--end` | no | Narrow to token range `[st, ed)` |
---
## Existing API Surface & Gaps
All CLI operations target the **MP HTTP server**
(`lmcache/v1/multiprocess/http_server.py`).
### Usable today (no new endpoints needed)
| CLI sub-command | Existing MP HTTP endpoint | Notes |
|----------------|--------------------------|-------|
| `clear` | `POST /cache/clear` | Clears all L1 cache. Works as-is. |
### Needs new MP HTTP endpoints
| CLI sub-command | What exists today | New endpoint needed on MP HTTP server |
|----------------|------------------|---------------------------------------|
| `info` | No per-request HTTP endpoint | `GET /kvcache-info?request_id=...` returning chunk ranges, locations, pinned status |
| `pin` | ZMQ only (no HTTP) | `POST /pin` accepting request-id, returning OK or REJECTED with reason |
| `compress` | ZMQ only (no HTTP) | `POST /compress` accepting request-id + method |
---
## Implementation
- **Single `KVCacheCommand`** (`BaseCommand` subclass) with second-level
argparse subparsers. File: `lmcache/cli/commands/kvcache.py`.
- **MP HTTP only:** `_http_request()` wraps `urllib.request` (no new deps).
All requests target the MP HTTP server.
- **Indexing args** for future per-request sub-commands: positional
`request_id` + optional `--start`/`--end`, added via a shared helper.
- **Output:** `self.create_metrics()` — use `--format json | jq` for scripting.
- **New `--quiet` / `-q` flag** on `BaseCommand`: skips `StreamHandler`.
- **Exit codes:** `0` success, `1` error, `2` rejected. Errors to stderr.
## Phasing
| Phase | Work |
|-------|------|
| **1a** | `clear` (HTTP exists) |
| **1b** | `pin` (needs per-instance endpoint), `compress` (needs per-instance endpoint) |
| **future** | `info` (needs further design — deferred) |