lifeboatcache 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lifeboatcache-0.1.0/.gitignore +15 -0
- lifeboatcache-0.1.0/PKG-INFO +121 -0
- lifeboatcache-0.1.0/README.md +92 -0
- lifeboatcache-0.1.0/presets/base.yaml +16 -0
- lifeboatcache-0.1.0/presets/dev_cpu.yaml +22 -0
- lifeboatcache-0.1.0/presets/fleet_50k.yaml +53 -0
- lifeboatcache-0.1.0/presets/max_accuracy.yaml +15 -0
- lifeboatcache-0.1.0/presets/max_concurrency.yaml +15 -0
- lifeboatcache-0.1.0/presets/single_node_gpu.yaml +18 -0
- lifeboatcache-0.1.0/presets/storage_efficient.yaml +13 -0
- lifeboatcache-0.1.0/pyproject.toml +78 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/__init__.py +7 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/compress/__init__.py +57 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/compress/accuracy.py +53 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/compress/base.py +229 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/compress/calibration.py +81 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/compress/demote.py +16 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/compress/fp8.py +160 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/compress/kvtc.py +140 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/compress/live.py +284 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/compress/oscar2.py +166 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/compress/turboquant.py +131 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/config/__init__.py +36 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/config/cli.py +276 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/config/loader.py +278 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/config/paths.py +22 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/config/schema.py +450 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/control/__init__.py +54 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/control/config_push.py +243 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/control/controld.py +187 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/control/membership.py +216 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/control/placement.py +81 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/control/router_export.py +87 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/control/telemetry_agg.py +101 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/core/__init__.py +59 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/core/coalesce.py +100 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/core/hashing.py +96 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/core/header.py +158 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/core/keys.py +57 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/core/ring.py +52 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/dashboard/__init__.py +334 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/dashboard/static/index.html +582 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/device/__init__.py +48 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/device/base.py +75 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/device/cpu.py +15 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/device/cuda.py +51 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/errors.py +31 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/index/__init__.py +67 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/index/backends.py +207 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/index/entry.py +35 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/index/hashring.py +104 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/index/local.py +136 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/index/sharded.py +101 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/__init__.py +1 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/host.py +104 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/k8s/__init__.py +76 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/lifeboat_store.py +386 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/lmcache_adapter.py +395 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/lmcache_defaults.py +54 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/policies.py +39 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/registration.py +60 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/serde.py +109 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/sglang_backend.py +187 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/sglang_plugin.py +110 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/vllm_connector.py +50 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/vllm_cpu_patches.py +116 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/integrations/vllm_plugin.py +80 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/policy/__init__.py +98 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/policy/accuracy_guard.py +105 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/policy/admission.py +60 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/policy/base.py +68 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/policy/classic.py +205 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/policy/episodic.py +289 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/policy/predictive.py +89 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/policy/reuse_score.py +100 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/registry.py +114 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/session/__init__.py +142 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/sim/__init__.py +110 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/sim/fleet.py +148 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/sim/trace.py +78 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/__init__.py +198 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/backpressure.py +89 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/circuit_breaker.py +89 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/coalesced.py +63 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/doca_memos.py +82 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/filesystem.py +177 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/manager.py +238 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/memory.py +42 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/ontap.py +385 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/rdma_nixl.py +100 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/s3.py +181 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/storage/tier.py +75 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/telemetry/__init__.py +89 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/telemetry/metrics.py +86 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/tenancy/__init__.py +98 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/tenancy/qos.py +76 -0
- lifeboatcache-0.1.0/src/lifeboat_cache/tenancy/quota.py +73 -0
- lifeboatcache-0.1.0/upstream_patches/README.md +124 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lifeboatcache
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lifeboat Cache — tiered, compressed, persistent KV cache for vLLM, SGLang and Lifeboat, with a built-in dashboard
|
|
5
|
+
Author-email: "Iterate.ai" <brian@iterate.ai>
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: numpy>=1.24
|
|
9
|
+
Requires-Dist: pydantic<3,>=2.7
|
|
10
|
+
Requires-Dist: pyyaml<7,>=6
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: cython; extra == 'dev'
|
|
13
|
+
Requires-Dist: maturin<2,>=1.7; extra == 'dev'
|
|
14
|
+
Requires-Dist: moto<6,>=5; extra == 'dev'
|
|
15
|
+
Requires-Dist: openai; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest<9,>=8; extra == 'dev'
|
|
18
|
+
Provides-Extra: engines
|
|
19
|
+
Requires-Dist: lmcache<0.6,>=0.5.6; extra == 'engines'
|
|
20
|
+
Requires-Dist: nixl<2,>=1.5.0; extra == 'engines'
|
|
21
|
+
Requires-Dist: sglang<0.6,>=0.5.19; extra == 'engines'
|
|
22
|
+
Requires-Dist: vllm<0.33,>=0.32.0; extra == 'engines'
|
|
23
|
+
Provides-Extra: s3
|
|
24
|
+
Requires-Dist: boto3>=1.28; extra == 's3'
|
|
25
|
+
Provides-Extra: sglang
|
|
26
|
+
Provides-Extra: vllm
|
|
27
|
+
Requires-Dist: lmcache<0.6,>=0.5.6; extra == 'vllm'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# Lifeboat Cache
|
|
31
|
+
|
|
32
|
+
Tiered, compressed, persistent KV cache for **vLLM**, **SGLang**, and **Lifeboat** (which
|
|
33
|
+
embeds SGLang), with a built-in dashboard to configure, monitor and observe it.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install "lifeboatcache[vllm]" # into the environment that runs vLLM
|
|
37
|
+
pip install lifeboatcache # into the environment that runs SGLang / Lifeboat
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Installing registers engine plugins; nothing else is needed to start. With no config file,
|
|
41
|
+
Lifeboat writes KV to local disk under `~/.lifeboat_cache/kv`, compresses it with fp8, and
|
|
42
|
+
serves the dashboard on <http://127.0.0.1:9400>.
|
|
43
|
+
|
|
44
|
+
## Turn it on
|
|
45
|
+
|
|
46
|
+
| Engine | How |
|
|
47
|
+
|---|---|
|
|
48
|
+
| vLLM | `LIFEBOAT_CACHE=1 vllm serve <model>` — or explicitly `--kv-transfer-config '{"kv_connector":"LifeboatConnector","kv_role":"kv_both"}'` |
|
|
49
|
+
| SGLang | `LIFEBOAT_CACHE=1 python -m sglang.launch_server --model-path <model>` |
|
|
50
|
+
| Lifeboat | set `LIFEBOAT_CACHE=1` in the Lifeboat container's environment, with `lifeboatcache` installed in its Python env |
|
|
51
|
+
|
|
52
|
+
What happens underneath:
|
|
53
|
+
|
|
54
|
+
- **vLLM** — `LifeboatConnector` is LMCache's multiprocess connector with Lifeboat defaults: it
|
|
55
|
+
auto-starts a local LMCache server (port 6555) whose L2 storage is the Lifeboat adapter.
|
|
56
|
+
GPU ↔ host movement is LMCache's; everything below host RAM is Lifeboat.
|
|
57
|
+
- **SGLang / Lifeboat** — Lifeboat is a HiCache storage backend (`--hicache-storage-backend
|
|
58
|
+
dynamic`). With `LIFEBOAT_CACHE=1` the plugin enables HiCache and selects the backend unless
|
|
59
|
+
the launch already chose a storage backend. Explicit flags work too:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
python -m sglang.launch_server --model-path <model> --enable-hierarchical-cache \
|
|
63
|
+
--hicache-storage-backend dynamic \
|
|
64
|
+
--hicache-storage-backend-extra-config '{"backend_name":"lifeboat","module_path":"lifeboat_cache.integrations.sglang_backend","class_name":"LifeboatHiCacheStorage"}'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Storage targets and the local fallback
|
|
68
|
+
|
|
69
|
+
Targets are tried in order; the first usable one takes new writes. Each is write-probed at
|
|
70
|
+
startup. One that can't be reached (ONTAP/NFS export not mounted, S3 without credentials) is
|
|
71
|
+
skipped with the reason shown on the dashboard, and if nothing persistent is left, KV goes to
|
|
72
|
+
`storage.fallback_local` on local disk.
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
storage:
|
|
76
|
+
tiers:
|
|
77
|
+
- {name: ontap, type: ontap, enabled: true, mount: /mnt/kvcache, protocol: pnfs_rdma}
|
|
78
|
+
- {name: nfs, type: nfs, enabled: true, mount: /mnt/nfs, path: /mnt/nfs/lifeboat/kv}
|
|
79
|
+
- {name: s3, type: s3, enabled: true, bucket: kv-archive, region: us-east-1}
|
|
80
|
+
fallback_local: {enabled: true, path: ~/.lifeboat_cache/kv, capacity_gb: 50}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Persisted KV survives restarts: on startup the index is rebuilt from disk in the background.
|
|
84
|
+
|
|
85
|
+
## Dashboard
|
|
86
|
+
|
|
87
|
+
`http://127.0.0.1:9400` on the node (use `ssh -L 9400:localhost:9400 <node>` from your
|
|
88
|
+
laptop). It shows the cache hit rate, KV served, bytes saved by compression and the codec's
|
|
89
|
+
sampled reconstruction error, per-target status (circuit breaker, usage, queue), evictions,
|
|
90
|
+
and startup recovery. The **Configure** tab edits storage targets, the local fallback, the
|
|
91
|
+
compression codec per rung, the eviction policy and the write mode; changes are validated,
|
|
92
|
+
applied live, and saved to the config file (startup-bound sections are flagged "restart
|
|
93
|
+
required"). `/metrics` serves Prometheus text (also on port 9402).
|
|
94
|
+
|
|
95
|
+
To expose it beyond localhost, set `dashboard.host: 0.0.0.0` **and** `dashboard.auth_token`;
|
|
96
|
+
open the page once with `?token=<token>`.
|
|
97
|
+
|
|
98
|
+
CLI: `lifeboat-cache status` prints the same numbers; `lifeboat-cache init` writes a starter
|
|
99
|
+
config; `lifeboat-cache server` runs a standalone Lifeboat-backed LMCache server for several
|
|
100
|
+
vLLM instances to share.
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
One YAML file, resolved as: `--config` / `$LIFEBOAT_CACHE_CONFIG` →
|
|
105
|
+
`~/.lifeboat_cache/lifeboat.yaml` → built-in defaults (`$LIFEBOAT_CACHE_HOME` moves the home
|
|
106
|
+
directory). `lifeboat-cache config show` prints the resolved config. Presets ship with the
|
|
107
|
+
package: `extends: single_node_gpu`.
|
|
108
|
+
|
|
109
|
+
## Compression — what the numbers say
|
|
110
|
+
|
|
111
|
+
Measured on real Qwen3 KV (2,048-token prefix, see `docs/`):
|
|
112
|
+
|
|
113
|
+
| codec | bits/value | Qwen3-4B perplexity (base 2.297) |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| fp8 (default) | 8.0 | 2.293 |
|
|
116
|
+
| turboquant4, keep first/last 2 layers | 6.2 | 2.300 |
|
|
117
|
+
| turboquant3 | 4.0 | 824 (breaks the model) |
|
|
118
|
+
| oscar2 on 2,048-token chunks | 5.0 | 72 (breaks the model) |
|
|
119
|
+
|
|
120
|
+
fp8 is the default for every stored rung. The dashboard shows the sampled reconstruction error
|
|
121
|
+
of whatever codec is live.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Lifeboat Cache
|
|
2
|
+
|
|
3
|
+
Tiered, compressed, persistent KV cache for **vLLM**, **SGLang**, and **Lifeboat** (which
|
|
4
|
+
embeds SGLang), with a built-in dashboard to configure, monitor and observe it.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install "lifeboatcache[vllm]" # into the environment that runs vLLM
|
|
8
|
+
pip install lifeboatcache # into the environment that runs SGLang / Lifeboat
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Installing registers engine plugins; nothing else is needed to start. With no config file,
|
|
12
|
+
Lifeboat writes KV to local disk under `~/.lifeboat_cache/kv`, compresses it with fp8, and
|
|
13
|
+
serves the dashboard on <http://127.0.0.1:9400>.
|
|
14
|
+
|
|
15
|
+
## Turn it on
|
|
16
|
+
|
|
17
|
+
| Engine | How |
|
|
18
|
+
|---|---|
|
|
19
|
+
| vLLM | `LIFEBOAT_CACHE=1 vllm serve <model>` — or explicitly `--kv-transfer-config '{"kv_connector":"LifeboatConnector","kv_role":"kv_both"}'` |
|
|
20
|
+
| SGLang | `LIFEBOAT_CACHE=1 python -m sglang.launch_server --model-path <model>` |
|
|
21
|
+
| Lifeboat | set `LIFEBOAT_CACHE=1` in the Lifeboat container's environment, with `lifeboatcache` installed in its Python env |
|
|
22
|
+
|
|
23
|
+
What happens underneath:
|
|
24
|
+
|
|
25
|
+
- **vLLM** — `LifeboatConnector` is LMCache's multiprocess connector with Lifeboat defaults: it
|
|
26
|
+
auto-starts a local LMCache server (port 6555) whose L2 storage is the Lifeboat adapter.
|
|
27
|
+
GPU ↔ host movement is LMCache's; everything below host RAM is Lifeboat.
|
|
28
|
+
- **SGLang / Lifeboat** — Lifeboat is a HiCache storage backend (`--hicache-storage-backend
|
|
29
|
+
dynamic`). With `LIFEBOAT_CACHE=1` the plugin enables HiCache and selects the backend unless
|
|
30
|
+
the launch already chose a storage backend. Explicit flags work too:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
python -m sglang.launch_server --model-path <model> --enable-hierarchical-cache \
|
|
34
|
+
--hicache-storage-backend dynamic \
|
|
35
|
+
--hicache-storage-backend-extra-config '{"backend_name":"lifeboat","module_path":"lifeboat_cache.integrations.sglang_backend","class_name":"LifeboatHiCacheStorage"}'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Storage targets and the local fallback
|
|
39
|
+
|
|
40
|
+
Targets are tried in order; the first usable one takes new writes. Each is write-probed at
|
|
41
|
+
startup. One that can't be reached (ONTAP/NFS export not mounted, S3 without credentials) is
|
|
42
|
+
skipped with the reason shown on the dashboard, and if nothing persistent is left, KV goes to
|
|
43
|
+
`storage.fallback_local` on local disk.
|
|
44
|
+
|
|
45
|
+
```yaml
|
|
46
|
+
storage:
|
|
47
|
+
tiers:
|
|
48
|
+
- {name: ontap, type: ontap, enabled: true, mount: /mnt/kvcache, protocol: pnfs_rdma}
|
|
49
|
+
- {name: nfs, type: nfs, enabled: true, mount: /mnt/nfs, path: /mnt/nfs/lifeboat/kv}
|
|
50
|
+
- {name: s3, type: s3, enabled: true, bucket: kv-archive, region: us-east-1}
|
|
51
|
+
fallback_local: {enabled: true, path: ~/.lifeboat_cache/kv, capacity_gb: 50}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Persisted KV survives restarts: on startup the index is rebuilt from disk in the background.
|
|
55
|
+
|
|
56
|
+
## Dashboard
|
|
57
|
+
|
|
58
|
+
`http://127.0.0.1:9400` on the node (use `ssh -L 9400:localhost:9400 <node>` from your
|
|
59
|
+
laptop). It shows the cache hit rate, KV served, bytes saved by compression and the codec's
|
|
60
|
+
sampled reconstruction error, per-target status (circuit breaker, usage, queue), evictions,
|
|
61
|
+
and startup recovery. The **Configure** tab edits storage targets, the local fallback, the
|
|
62
|
+
compression codec per rung, the eviction policy and the write mode; changes are validated,
|
|
63
|
+
applied live, and saved to the config file (startup-bound sections are flagged "restart
|
|
64
|
+
required"). `/metrics` serves Prometheus text (also on port 9402).
|
|
65
|
+
|
|
66
|
+
To expose it beyond localhost, set `dashboard.host: 0.0.0.0` **and** `dashboard.auth_token`;
|
|
67
|
+
open the page once with `?token=<token>`.
|
|
68
|
+
|
|
69
|
+
CLI: `lifeboat-cache status` prints the same numbers; `lifeboat-cache init` writes a starter
|
|
70
|
+
config; `lifeboat-cache server` runs a standalone Lifeboat-backed LMCache server for several
|
|
71
|
+
vLLM instances to share.
|
|
72
|
+
|
|
73
|
+
## Configuration
|
|
74
|
+
|
|
75
|
+
One YAML file, resolved as: `--config` / `$LIFEBOAT_CACHE_CONFIG` →
|
|
76
|
+
`~/.lifeboat_cache/lifeboat.yaml` → built-in defaults (`$LIFEBOAT_CACHE_HOME` moves the home
|
|
77
|
+
directory). `lifeboat-cache config show` prints the resolved config. Presets ship with the
|
|
78
|
+
package: `extends: single_node_gpu`.
|
|
79
|
+
|
|
80
|
+
## Compression — what the numbers say
|
|
81
|
+
|
|
82
|
+
Measured on real Qwen3 KV (2,048-token prefix, see `docs/`):
|
|
83
|
+
|
|
84
|
+
| codec | bits/value | Qwen3-4B perplexity (base 2.297) |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| fp8 (default) | 8.0 | 2.293 |
|
|
87
|
+
| turboquant4, keep first/last 2 layers | 6.2 | 2.300 |
|
|
88
|
+
| turboquant3 | 4.0 | 824 (breaks the model) |
|
|
89
|
+
| oscar2 on 2,048-token chunks | 5.0 | 72 (breaks the model) |
|
|
90
|
+
|
|
91
|
+
fp8 is the default for every stored rung. The dashboard shows the sampled reconstruction error
|
|
92
|
+
of whatever codec is live.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Base preset: codec definitions + fleet-neutral defaults. Other presets extend this.
|
|
2
|
+
# (Defining codecs here means any preset keeping the default ladder validates.)
|
|
3
|
+
compression:
|
|
4
|
+
recompress_on_demote: true
|
|
5
|
+
codecs:
|
|
6
|
+
turboquant4: {type: turboquant, bits: 4, keep_bf16_layers: [0, 1, -2, -1], group_size: 64}
|
|
7
|
+
turboquant3: {type: turboquant, bits: 3}
|
|
8
|
+
oscar2: {type: oscar, bits: 2, rotation: calibrated, sink_tokens: 64, recent_tokens: 256}
|
|
9
|
+
kvtc: {type: kvtc, pca_rank: auto, entropy_coder: ans}
|
|
10
|
+
fp8: {type: fp8}
|
|
11
|
+
accuracy_guard:
|
|
12
|
+
enabled: true
|
|
13
|
+
sample_rate: 0.01
|
|
14
|
+
metric: logit_kl
|
|
15
|
+
threshold: 0.05
|
|
16
|
+
action: raise_precision
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# dev_cpu: single-process CPU dev on a Mac ("fleet of size one"). No control plane.
|
|
2
|
+
extends: base.yaml
|
|
3
|
+
device: cpu
|
|
4
|
+
compression:
|
|
5
|
+
ladder: {gpu: turboquant3, cpu: turboquant3, local_nvme: kvtc, shared: kvtc, archive: kvtc}
|
|
6
|
+
control_plane:
|
|
7
|
+
enabled: false
|
|
8
|
+
membership: static
|
|
9
|
+
metadata_store: memory
|
|
10
|
+
offline_mode: local_only
|
|
11
|
+
eviction:
|
|
12
|
+
l1_policy: lru
|
|
13
|
+
episodic: {enabled: false}
|
|
14
|
+
storage:
|
|
15
|
+
enabled: true
|
|
16
|
+
write_mode: low_latency
|
|
17
|
+
tiers:
|
|
18
|
+
- {name: local, type: filesystem, enabled: true, path: ./.lifeboat_kv, scope: node, capacity_gb: 2}
|
|
19
|
+
tenancy:
|
|
20
|
+
enabled: false
|
|
21
|
+
telemetry:
|
|
22
|
+
prometheus: {enabled: false}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# fleet_50k: 50,000+ GPU fleet. Control plane on, gossip membership, etcd metadata,
|
|
2
|
+
# heavy sharding + replication, topology-aware placement, full telemetry.
|
|
3
|
+
extends: base.yaml
|
|
4
|
+
device: auto
|
|
5
|
+
topology:
|
|
6
|
+
discovery: k8s
|
|
7
|
+
numa_aware: true
|
|
8
|
+
control_plane:
|
|
9
|
+
enabled: true
|
|
10
|
+
membership: gossip
|
|
11
|
+
metadata_store: etcd
|
|
12
|
+
index_shards: 4096
|
|
13
|
+
replication_factor: 3
|
|
14
|
+
heartbeat_ms: 1000
|
|
15
|
+
offline_mode: cached_placement
|
|
16
|
+
config_push: {enabled: true, canary_percent: 5, auto_rollback_on_regression: true}
|
|
17
|
+
compression:
|
|
18
|
+
ladder: {gpu: turboquant4, cpu: turboquant3, local_nvme: kvtc, shared: kvtc, archive: kvtc}
|
|
19
|
+
eviction:
|
|
20
|
+
l1_policy: episodic
|
|
21
|
+
episodic: {enabled: true, persist_evicted_episodes: true, prefetch_on_topic_return: true}
|
|
22
|
+
reuse:
|
|
23
|
+
cross_node_p2p: {enabled: true, transport: nixl, max_hops: 1, prefer_nvlink_domain: true}
|
|
24
|
+
storage:
|
|
25
|
+
enabled: true
|
|
26
|
+
write_mode: auto
|
|
27
|
+
placement:
|
|
28
|
+
write_local_first: true
|
|
29
|
+
replicate_to_shared_async: true
|
|
30
|
+
read_nearest: true
|
|
31
|
+
hot_object_replicas: 3
|
|
32
|
+
hotspot_threshold_rps: 500
|
|
33
|
+
tiers:
|
|
34
|
+
- {name: nvme, type: nvme, enabled: true, path: /mnt/nvme/kv, io: io_uring,
|
|
35
|
+
direct_io: true, capacity_gb: 800, scope: node}
|
|
36
|
+
- {name: ontap, type: ontap, enabled: true, mount: /mnt/kvcache, protocol: pnfs_rdma,
|
|
37
|
+
prefer_nearest_flexcache: true, fabricpool_cold_after_h: 24, scope: cluster}
|
|
38
|
+
- {name: s3, type: s3, enabled: true, bucket: kv-archive, multipart_parallelism: 8,
|
|
39
|
+
byte_range_reads: true, scope: region}
|
|
40
|
+
- {name: rdma, type: nixl, enabled: true, scope: pod}
|
|
41
|
+
backpressure:
|
|
42
|
+
queue_depth_per_tier: 16384
|
|
43
|
+
shed_below_reuse_score: 0.1
|
|
44
|
+
circuit_breaker: {error_rate: 0.2, open_s: 30}
|
|
45
|
+
tenancy:
|
|
46
|
+
enabled: true
|
|
47
|
+
default_qos: throughput
|
|
48
|
+
fair_share: true
|
|
49
|
+
telemetry:
|
|
50
|
+
prometheus: {enabled: true, port: 9402, max_label_cardinality: 10000}
|
|
51
|
+
otel: {enabled: true, endpoint: "otel-collector:4317", trace_sample_rate: 0.001}
|
|
52
|
+
chargeback_per_tenant: true
|
|
53
|
+
export_locality_to_router: {enabled: true, target: dynamo}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# max_accuracy: preserve quality. Higher-precision codecs, strict accuracy guard.
|
|
2
|
+
extends: base.yaml
|
|
3
|
+
device: auto
|
|
4
|
+
compression:
|
|
5
|
+
ladder: {gpu: fp8, cpu: turboquant4, local_nvme: turboquant4, shared: turboquant4, archive: kvtc}
|
|
6
|
+
recompress_on_demote: true
|
|
7
|
+
accuracy_guard:
|
|
8
|
+
enabled: true
|
|
9
|
+
sample_rate: 0.05
|
|
10
|
+
metric: logit_kl
|
|
11
|
+
threshold: 0.01
|
|
12
|
+
action: raise_precision
|
|
13
|
+
eviction:
|
|
14
|
+
l1_policy: episodic
|
|
15
|
+
episodic: {enabled: true, per_layer_budget: sensitivity}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# max_concurrency: throughput-first. Aggressive coalescing, write-behind, low-latency codecs.
|
|
2
|
+
extends: base.yaml
|
|
3
|
+
device: auto
|
|
4
|
+
compression:
|
|
5
|
+
ladder: {gpu: turboquant4, cpu: turboquant4, local_nvme: turboquant4, shared: kvtc, archive: kvtc}
|
|
6
|
+
eviction:
|
|
7
|
+
l1_policy: arc
|
|
8
|
+
admission: {enabled: true, min_reuse_score: 0.1, write_behind: always}
|
|
9
|
+
storage:
|
|
10
|
+
write_mode: low_latency
|
|
11
|
+
coalesce: {target_tokens: 4096, min_tokens: 512, max_tokens: 8192, adaptive: true, flush_interval_ms: 20}
|
|
12
|
+
backpressure: {queue_depth_per_tier: 16384, shed_below_reuse_score: 0.2}
|
|
13
|
+
tenancy:
|
|
14
|
+
default_qos: throughput
|
|
15
|
+
fair_share: true
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# single_node_gpu: one GPU node, local NVMe tier, no control plane.
|
|
2
|
+
extends: base.yaml
|
|
3
|
+
device: cuda
|
|
4
|
+
control_plane:
|
|
5
|
+
enabled: false
|
|
6
|
+
membership: static
|
|
7
|
+
metadata_store: memory
|
|
8
|
+
offline_mode: local_only
|
|
9
|
+
eviction:
|
|
10
|
+
l1_policy: episodic
|
|
11
|
+
storage:
|
|
12
|
+
enabled: true
|
|
13
|
+
write_mode: auto
|
|
14
|
+
tiers:
|
|
15
|
+
- {name: nvme, type: nvme, enabled: true, path: /mnt/nvme/kv, io: io_uring,
|
|
16
|
+
direct_io: true, capacity_gb: 800, scope: node}
|
|
17
|
+
telemetry:
|
|
18
|
+
prometheus: {enabled: true, port: 9402}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# storage_efficient: minimize bytes. Aggressive low-bit codecs, recompress on demote, dedup.
|
|
2
|
+
extends: base.yaml
|
|
3
|
+
device: auto
|
|
4
|
+
compression:
|
|
5
|
+
ladder: {gpu: turboquant4, cpu: turboquant3, local_nvme: oscar2, shared: kvtc, archive: kvtc}
|
|
6
|
+
recompress_on_demote: true
|
|
7
|
+
eviction:
|
|
8
|
+
l1_policy: episodic
|
|
9
|
+
admission: {enabled: true, min_reuse_score: 0.35, write_behind: idle}
|
|
10
|
+
storage:
|
|
11
|
+
write_mode: efficient
|
|
12
|
+
coalesce: {target_tokens: 8192, min_tokens: 1024, max_tokens: 8192, adaptive: true, flush_interval_ms: 100}
|
|
13
|
+
index: {content_addressed: true, dedup: true, dedup_scope: global, tenant_salted_hashing: true}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
# Distribution name is `lifeboatcache` (pip install lifeboatcache); the import package is
|
|
3
|
+
# `lifeboat_cache`.
|
|
4
|
+
name = "lifeboatcache"
|
|
5
|
+
version = "0.1.0"
|
|
6
|
+
description = "Lifeboat Cache — tiered, compressed, persistent KV cache for vLLM, SGLang and Lifeboat, with a built-in dashboard"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
# 3.10+: SGLang (and the Lifeboat product that embeds it) supports 3.10.
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
license = { text = "Proprietary" }
|
|
11
|
+
authors = [{ name = "Iterate.ai", email = "brian@iterate.ai" }]
|
|
12
|
+
|
|
13
|
+
# Supported upstream ranges. Vendored at: vllm v0.32.0, lmcache v0.5.6,
|
|
14
|
+
# sglang v0.5.22, nixl v1.5.0 (see docs/VERSIONS.md). These are installed
|
|
15
|
+
# editable from vendor/ during dev; ranges here gate the version-compat test.
|
|
16
|
+
dependencies = [
|
|
17
|
+
"pydantic>=2.7,<3",
|
|
18
|
+
"pyyaml>=6,<7",
|
|
19
|
+
"numpy>=1.24",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
# vLLM path: Lifeboat sits under LMCache's MP server. Install into the env that runs vLLM.
|
|
24
|
+
vllm = ["lmcache>=0.5.6,<0.6"]
|
|
25
|
+
# SGLang / Lifeboat path: nothing extra beyond SGLang itself (already in that env).
|
|
26
|
+
sglang = []
|
|
27
|
+
s3 = ["boto3>=1.28"]
|
|
28
|
+
# Upstream engines — pinned supported ranges (installed editable from vendor/ in dev).
|
|
29
|
+
# vllm 0.32.0: builds on Apple clang (0.31.0 does NOT — OpenMP/structured-binding
|
|
30
|
+
# error), but 0.32.0 has a stale CPU slot-mapping fallback. We repair it at runtime
|
|
31
|
+
# via lifeboat_cache.integrations.vllm_cpu_patches. See upstream_patches/README.md §2.
|
|
32
|
+
engines = [
|
|
33
|
+
"vllm>=0.32.0,<0.33",
|
|
34
|
+
"lmcache>=0.5.6,<0.6",
|
|
35
|
+
"sglang>=0.5.19,<0.6",
|
|
36
|
+
"nixl>=1.5.0,<2",
|
|
37
|
+
]
|
|
38
|
+
dev = [
|
|
39
|
+
"maturin>=1.7,<2",
|
|
40
|
+
"pytest>=8,<9",
|
|
41
|
+
"pytest-cov>=5",
|
|
42
|
+
"moto>=5,<6",
|
|
43
|
+
"Cython",
|
|
44
|
+
"openai",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[project.scripts]
|
|
48
|
+
lifeboat-cache = "lifeboat_cache.config.cli:main"
|
|
49
|
+
lifeboat-controld = "lifeboat_cache.control.controld:main"
|
|
50
|
+
|
|
51
|
+
# Loaded automatically by the engines in every process once the package is installed.
|
|
52
|
+
[project.entry-points."vllm.general_plugins"]
|
|
53
|
+
lifeboat = "lifeboat_cache.integrations.vllm_plugin:register"
|
|
54
|
+
|
|
55
|
+
[project.entry-points."sglang.srt.plugins"]
|
|
56
|
+
lifeboat = "lifeboat_cache.integrations.sglang_plugin:register"
|
|
57
|
+
|
|
58
|
+
[build-system]
|
|
59
|
+
requires = ["hatchling"]
|
|
60
|
+
build-backend = "hatchling.build"
|
|
61
|
+
|
|
62
|
+
[tool.hatch.build.targets.wheel]
|
|
63
|
+
packages = ["src/lifeboat_cache"]
|
|
64
|
+
|
|
65
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
66
|
+
"presets" = "lifeboat_cache/presets"
|
|
67
|
+
|
|
68
|
+
[tool.hatch.build.targets.sdist]
|
|
69
|
+
include = ["src/lifeboat_cache", "presets", "README.md", "pyproject.toml"]
|
|
70
|
+
|
|
71
|
+
[tool.pytest.ini_options]
|
|
72
|
+
testpaths = ["tests"]
|
|
73
|
+
pythonpath = ["src"]
|
|
74
|
+
markers = [
|
|
75
|
+
"requires_gpu: needs a CUDA/ROCm GPU (skipped on Mac)",
|
|
76
|
+
"requires_docker: needs docker/docker-compose",
|
|
77
|
+
"requires_cluster: needs a multi-node cluster",
|
|
78
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""CPU reference compression layer for Lifeboat Cache.
|
|
2
|
+
|
|
3
|
+
A set of KV-cache codecs behind one :class:`Codec` interface, driven by the
|
|
4
|
+
``compression`` config section. Every codec is pure-numpy (torch optional) and produces a
|
|
5
|
+
**self-describing blob** so ``decode`` needs no external arguments.
|
|
6
|
+
|
|
7
|
+
Codecs (each approximates a published method; see the module docstrings for exactly what is
|
|
8
|
+
reproduced vs. approximated):
|
|
9
|
+
|
|
10
|
+
* ``turboquant`` — group-wise affine int3/int4 with float16-kept layers.
|
|
11
|
+
* ``fp8`` — e4m3 / e5m2 emulation via an exact FP8 value grid.
|
|
12
|
+
* ``oscar`` — 2-bit middle + full-precision sink/recent tokens + optional rotation.
|
|
13
|
+
* ``kvtc`` — PCA + quantized coefficients/residual + zlib (ANS stand-in).
|
|
14
|
+
|
|
15
|
+
Importing this package registers all four codec factories, so
|
|
16
|
+
:func:`build_codecs` can instantiate any ladder from config.
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from .accuracy import cosine, logit_kl, mse, tensor_relative_error
|
|
21
|
+
from .base import (
|
|
22
|
+
Codec,
|
|
23
|
+
build_codecs,
|
|
24
|
+
create_codec,
|
|
25
|
+
pack_arrays,
|
|
26
|
+
pack_blob,
|
|
27
|
+
register_codec,
|
|
28
|
+
unpack_arrays,
|
|
29
|
+
unpack_blob,
|
|
30
|
+
)
|
|
31
|
+
from .calibration import CalibrationProfile
|
|
32
|
+
from .demote import recompress
|
|
33
|
+
from .fp8 import FP8Codec
|
|
34
|
+
from .kvtc import KVTCCodec
|
|
35
|
+
from .oscar2 import Oscar2Codec
|
|
36
|
+
from .turboquant import TurboQuantCodec
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"Codec",
|
|
40
|
+
"register_codec",
|
|
41
|
+
"create_codec",
|
|
42
|
+
"build_codecs",
|
|
43
|
+
"pack_blob",
|
|
44
|
+
"unpack_blob",
|
|
45
|
+
"pack_arrays",
|
|
46
|
+
"unpack_arrays",
|
|
47
|
+
"TurboQuantCodec",
|
|
48
|
+
"FP8Codec",
|
|
49
|
+
"Oscar2Codec",
|
|
50
|
+
"KVTCCodec",
|
|
51
|
+
"CalibrationProfile",
|
|
52
|
+
"recompress",
|
|
53
|
+
"logit_kl",
|
|
54
|
+
"cosine",
|
|
55
|
+
"mse",
|
|
56
|
+
"tensor_relative_error",
|
|
57
|
+
]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Accuracy metrics for the compression accuracy-guard.
|
|
2
|
+
|
|
3
|
+
The guard samples a fraction of writes, decodes them, and compares against the original to
|
|
4
|
+
decide whether a codec is degrading quality (config ``compression.accuracy_guard.metric``).
|
|
5
|
+
The headline metric is softmax KL on logits — the distributional distance that actually
|
|
6
|
+
matters for generation quality — plus cheaper tensor-space helpers.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
_EPS = 1e-12
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _log_softmax(z: np.ndarray) -> np.ndarray:
|
|
16
|
+
z = z - z.max(axis=-1, keepdims=True)
|
|
17
|
+
return z - np.log(np.exp(z).sum(axis=-1, keepdims=True) + _EPS)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def logit_kl(p_logits: np.ndarray, q_logits: np.ndarray) -> float:
|
|
21
|
+
"""Mean KL(softmax(p) || softmax(q)) over the last axis.
|
|
22
|
+
|
|
23
|
+
Zero iff the two logit tensors induce the same distribution (identical logits, or logits
|
|
24
|
+
differing only by a per-row additive constant), strictly positive otherwise. Computed in
|
|
25
|
+
log-space for numerical stability.
|
|
26
|
+
"""
|
|
27
|
+
lp = _log_softmax(np.asarray(p_logits, dtype=np.float64))
|
|
28
|
+
lq = _log_softmax(np.asarray(q_logits, dtype=np.float64))
|
|
29
|
+
p = np.exp(lp)
|
|
30
|
+
kl = (p * (lp - lq)).sum(axis=-1)
|
|
31
|
+
return float(np.maximum(kl, 0.0).mean())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def cosine(a: np.ndarray, b: np.ndarray) -> float:
|
|
35
|
+
"""Mean cosine similarity in [-1, 1] over flattened vectors (1.0 == identical)."""
|
|
36
|
+
a = np.asarray(a, dtype=np.float64).reshape(-1)
|
|
37
|
+
b = np.asarray(b, dtype=np.float64).reshape(-1)
|
|
38
|
+
denom = (np.linalg.norm(a) * np.linalg.norm(b)) + _EPS
|
|
39
|
+
return float(np.dot(a, b) / denom)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def mse(a: np.ndarray, b: np.ndarray) -> float:
|
|
43
|
+
"""Mean squared error between two arrays."""
|
|
44
|
+
a = np.asarray(a, dtype=np.float64)
|
|
45
|
+
b = np.asarray(b, dtype=np.float64)
|
|
46
|
+
return float(np.mean((a - b) ** 2))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def tensor_relative_error(a: np.ndarray, b: np.ndarray) -> float:
|
|
50
|
+
"""Relative reconstruction error ``||a - b|| / (||a|| + eps)`` (Frobenius norm)."""
|
|
51
|
+
a = np.asarray(a, dtype=np.float64)
|
|
52
|
+
b = np.asarray(b, dtype=np.float64)
|
|
53
|
+
return float(np.linalg.norm(a - b) / (np.linalg.norm(a) + _EPS))
|