mlx-bun 0.5.0 → 1.0.0

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.
Files changed (640) hide show
  1. package/README.md +1449 -109
  2. package/THIRD_PARTY_NOTICES.md +882 -0
  3. package/bin/mlx-bun.convert.mjs +4 -0
  4. package/bin/mlx-bun.fuse.mjs +4 -0
  5. package/bin/mlx-bun.generate.mjs +4 -0
  6. package/bin/mlx-bun.lora.mjs +4 -0
  7. package/bin/mlx-bun.mjs +13 -21
  8. package/bin/mlx-bun.server.mjs +4 -0
  9. package/bin/mlx-bun.upload.mjs +4 -0
  10. package/dist/web/app.js +9584 -0
  11. package/package.json +50 -53
  12. package/src/cli/ane-install.ts +76 -0
  13. package/src/cli/args.ts +179 -0
  14. package/src/cli/engine-entry.ts +68 -0
  15. package/src/cli/inference.ts +214 -0
  16. package/src/cli/job-entry.ts +97 -0
  17. package/src/cli/library-host.ts +117 -0
  18. package/src/cli/main.ts +57 -0
  19. package/src/cli/memory-engine.ts +163 -0
  20. package/src/cli/memory.ts +36 -0
  21. package/src/cli/mlx-lm-aliases.ts +309 -0
  22. package/src/cli/model-choice.ts +33 -0
  23. package/src/cli/model-selection.ts +84 -0
  24. package/src/cli/module-host.ts +11 -0
  25. package/src/cli/module-verbs.ts +50 -0
  26. package/src/cli/numerical-policy.ts +12 -0
  27. package/src/cli/perplexity.ts +89 -0
  28. package/src/cli/serve-host.ts +423 -0
  29. package/src/cli/serve-isolated.ts +217 -0
  30. package/src/cli/serve-options.ts +106 -0
  31. package/src/cli/serve-state.ts +205 -0
  32. package/src/cli/serve.ts +500 -0
  33. package/src/cli/served-model-host.ts +102 -0
  34. package/src/cli/server-entry.ts +148 -0
  35. package/src/cli/serving-unit.ts +177 -0
  36. package/src/{tui.ts → cli/terminal.ts} +13 -8
  37. package/src/cli/worker-adapters.ts +34 -0
  38. package/src/cli/worker-entry.ts +272 -0
  39. package/src/cli/worker-unit.ts +179 -0
  40. package/src/engine/adapter-operation.ts +26 -0
  41. package/src/engine/cache-services.ts +114 -0
  42. package/src/engine/completion.ts +39 -0
  43. package/src/engine/generation-gateway.ts +358 -0
  44. package/src/engine/index.ts +59 -0
  45. package/src/engine/kv-budget.ts +120 -0
  46. package/src/engine/media-preparation.ts +232 -0
  47. package/src/engine/model-binding.ts +24 -0
  48. package/src/engine/model-host.ts +513 -0
  49. package/src/engine/model-serving.ts +89 -0
  50. package/src/{serve → engine}/preparation.ts +7 -5
  51. package/src/{serve → engine}/session-completion-engine.ts +6 -6
  52. package/src/engine/telemetry.ts +109 -0
  53. package/src/hub/downloads.ts +116 -0
  54. package/src/jobs/db.ts +16 -7
  55. package/src/jobs/events.ts +29 -0
  56. package/src/jobs/executable.ts +5 -0
  57. package/src/jobs/host.ts +51 -0
  58. package/src/jobs/in-process.ts +55 -0
  59. package/src/{contracts/jobs.ts → jobs/protocol.ts} +7 -9
  60. package/src/jobs/runner.ts +150 -231
  61. package/src/jobs/service.ts +75 -0
  62. package/src/jobs/sse.ts +59 -23
  63. package/src/jobs/worker-memory.ts +46 -0
  64. package/src/jobs/worker-process.ts +163 -0
  65. package/src/jobs/worker-supervisor.ts +258 -0
  66. package/src/modules.ts +58 -0
  67. package/src/publishing/catalog-hub.ts +18 -0
  68. package/src/publishing/credentials.ts +31 -0
  69. package/src/publishing/upload.ts +37 -0
  70. package/src/residency/leased-adapters.ts +21 -0
  71. package/src/residency/local-records.ts +16 -0
  72. package/src/residency/model-residency.ts +405 -0
  73. package/src/residency/record-index.ts +28 -0
  74. package/src/residency/resident-estimate.ts +50 -0
  75. package/src/{anthropic.ts → server/anthropic.ts} +3 -81
  76. package/src/server/cache-routes.ts +36 -0
  77. package/src/{serve → server}/chat-request.ts +10 -10
  78. package/src/{serve → server}/chat-stage.ts +14 -14
  79. package/src/{client.ts → server/client.ts} +28 -2
  80. package/src/{serve → server}/completion-executor.ts +33 -41
  81. package/src/server/decision-body.ts +55 -0
  82. package/src/server/decision-routes.ts +134 -0
  83. package/src/server/decision-wire.ts +228 -0
  84. package/src/{serve → server}/discovery-routes.ts +25 -82
  85. package/src/{serve → server}/http.ts +25 -13
  86. package/src/{serve → server}/inference-request.ts +5 -5
  87. package/src/server/job-routes.ts +20 -0
  88. package/src/server/media-prompt.ts +63 -0
  89. package/src/server/memory-completion-client.ts +46 -0
  90. package/src/server/model-routes.ts +192 -0
  91. package/src/{serve → server}/openai-wire.ts +3 -2
  92. package/src/{serve → server}/pipeline.ts +1 -1
  93. package/src/server/prompt-builder.ts +13 -0
  94. package/src/server/prompt-contracts.ts +17 -0
  95. package/src/server/proxy-routes.ts +187 -0
  96. package/src/server/publishing-routes.ts +41 -0
  97. package/src/{serve → server}/request-plan.ts +6 -8
  98. package/src/{serve → server}/request-prep.ts +47 -50
  99. package/src/{serve → server}/responses-client.ts +30 -13
  100. package/src/{responses.ts → server/responses.ts} +12 -93
  101. package/src/server/routes.ts +205 -0
  102. package/src/server/start.ts +72 -0
  103. package/src/server/status-routes.ts +108 -0
  104. package/src/{serve → server}/text-completion-stage.ts +25 -4
  105. package/src/server/text-prompt.ts +12 -0
  106. package/src/{serve → server}/token-streams.ts +36 -38
  107. package/src/server/worker-routes.ts +311 -0
  108. package/src/storage/paths.ts +61 -0
  109. package/src/web/assets.ts +37 -0
  110. package/src/web/{src → browser}/api.ts +5 -23
  111. package/src/web/browser/chat-host.ts +17 -0
  112. package/src/web/browser/installed-panels.ts +6 -0
  113. package/src/web/browser/main.ts +55 -0
  114. package/src/web/browser/markdown.ts +279 -0
  115. package/src/web/browser/memory-panel.ts +9 -0
  116. package/src/web/browser/palette.ts +64 -0
  117. package/src/web/browser/protocol.ts +13 -0
  118. package/src/web/browser/shell.ts +467 -0
  119. package/src/web/{src → browser}/status.ts +32 -36
  120. package/src/web/build.ts +51 -0
  121. package/src/web/public/app.html +1054 -0
  122. package/src/web/{sw.js → public/sw.js} +4 -3
  123. package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
  124. package/src/web/{vendor → public/vendor}/hljs.js +2 -2
  125. package/THIRD_PARTY_LICENSES.md +0 -51
  126. package/docs/README.md +0 -107
  127. package/docs/archive/hlg-sampling.md +0 -350
  128. package/docs/archive/investigations/audio-input-plan.md +0 -356
  129. package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +0 -3617
  130. package/docs/archive/investigations/chunk-finetune-experiment.md +0 -84
  131. package/docs/archive/investigations/colibri-glm52-port.md +0 -1486
  132. package/docs/archive/investigations/colibri-oracle-pin.md +0 -1284
  133. package/docs/archive/investigations/curve-bisector-routing.md +0 -123
  134. package/docs/archive/investigations/decode-roofline-lookagain.md +0 -348
  135. package/docs/archive/investigations/diffusion-gemma-port.md +0 -602
  136. package/docs/archive/investigations/dspark-handoff.md +0 -224
  137. package/docs/archive/investigations/expert-offload-single-user-moe.md +0 -292
  138. package/docs/archive/investigations/faithful-l1-consolidation.md +0 -310
  139. package/docs/archive/investigations/hlg-sampling-investigation.md +0 -970
  140. package/docs/archive/investigations/indexshare-performance-spike.md +0 -357
  141. package/docs/archive/investigations/kernel-perf-review-2026-07.md +0 -171
  142. package/docs/archive/investigations/lab-build-journal.md +0 -157
  143. package/docs/archive/investigations/minicpm5-decode-megakernel.md +0 -489
  144. package/docs/archive/investigations/openjarvis-fit.md +0 -198
  145. package/docs/archive/investigations/openwiki-evaluation.md +0 -249
  146. package/docs/archive/investigations/optimization_plan.md +0 -346
  147. package/docs/archive/investigations/orpo-base-uf-experiment-and-directions.md +0 -120
  148. package/docs/archive/investigations/orpo-flash-cce-pin-leak.md +0 -167
  149. package/docs/archive/investigations/orpo-uf-testing-handoff.md +0 -126
  150. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +0 -92
  151. package/docs/archive/investigations/pi-builtin-investigation.md +0 -308
  152. package/docs/archive/investigations/pre-colibri-stabilization.md +0 -329
  153. package/docs/archive/investigations/qwen38-27b-campaign-log.md +0 -1092
  154. package/docs/archive/investigations/segmented-backward-handoff.md +0 -148
  155. package/docs/archive/investigations/starter-model-port-handoff.md +0 -342
  156. package/docs/archive/investigations/steel-flash-cce-handoff.md +0 -489
  157. package/docs/archive/investigations/the-dreaming-handoff.md +0 -141
  158. package/docs/archive/investigations/trainer-validation-experiment.md +0 -173
  159. package/docs/archive/planning/chat-ui-vision-memory-first.md +0 -199
  160. package/docs/archive/planning/chat-ui-vision.md +0 -50
  161. package/docs/archive/planning/chunk-task-roadmap.md +0 -77
  162. package/docs/archive/planning/curve-sampler-research-plan.md +0 -331
  163. package/docs/archive/planning/journal.md +0 -316
  164. package/docs/archive/planning/memory-docs-and-dag-plan.md +0 -194
  165. package/docs/archive/planning/pi-integration-review.md +0 -178
  166. package/docs/archive/planning/release-notes-v0.0.10.md +0 -57
  167. package/docs/archive/planning/release-notes-v0.0.11.md +0 -239
  168. package/docs/archive/planning/release-notes-v0.0.12.md +0 -80
  169. package/docs/archive/planning/release-notes-v0.0.13.md +0 -77
  170. package/docs/archive/planning/release-notes-v0.0.9.md +0 -122
  171. package/docs/archive/planning/release-notes-v0.2.0.md +0 -49
  172. package/docs/archive/planning/release-notes-v0.3.0.md +0 -35
  173. package/docs/archive/planning/release-notes-v0.4.0.md +0 -45
  174. package/docs/archive/planning/release-notes-v0.5.0.md +0 -37
  175. package/docs/archive/planning/web-ui-pass-plan.md +0 -213
  176. package/docs/archive/planning/website-readme-pass-plan.md +0 -224
  177. package/docs/design/batching.md +0 -2054
  178. package/docs/design/decode-speed-program.md +0 -2404
  179. package/docs/design/dreaming-nightly-pipeline.md +0 -684
  180. package/docs/design/generic-model-support.md +0 -821
  181. package/docs/design/kv-cache.md +0 -993
  182. package/docs/design/orpo-training.md +0 -680
  183. package/docs/design/speculative-decoding.md +0 -2129
  184. package/docs/design/turboquant.md +0 -2225
  185. package/docs/design/unified-engine-frontier-plan.md +0 -1987
  186. package/docs/design/web-chat-redesign.md +0 -695
  187. package/docs/planning/PRODUCT_ROADMAP.md +0 -593
  188. package/docs/planning/ResearchTopics.md +0 -119
  189. package/docs/planning/public-readiness-handoff.md +0 -244
  190. package/docs/planning/public-readiness.md +0 -149
  191. package/docs/planning/video-series.md +0 -421
  192. package/docs/reference/benchmarks.md +0 -3587
  193. package/docs/reference/cli.md +0 -646
  194. package/docs/reference/distribution.md +0 -380
  195. package/docs/reference/environment.md +0 -494
  196. package/docs/reference/glossary.md +0 -39
  197. package/docs/reference/library-api.md +0 -688
  198. package/docs/reference/memory.md +0 -260
  199. package/docs/reference/models.md +0 -346
  200. package/docs/reference/server-api.md +0 -1945
  201. package/docs/reference/server-config.md +0 -973
  202. package/docs/reference/training.md +0 -462
  203. package/docs/reference/troubleshooting.md +0 -158
  204. package/src/admission-plan.ts +0 -18
  205. package/src/audio/audiotoolbox.ts +0 -112
  206. package/src/audio/conformer.ts +0 -655
  207. package/src/audio/decode.ts +0 -133
  208. package/src/audio/features.ts +0 -202
  209. package/src/audio/mic-capture.ts +0 -98
  210. package/src/audio/silero-vad.ts +0 -379
  211. package/src/audio/transcode.ts +0 -67
  212. package/src/audio/whisper-decode.ts +0 -774
  213. package/src/audio/whisper-fast.ts +0 -732
  214. package/src/audio/whisper-format.ts +0 -73
  215. package/src/audio/whisper-mel.ts +0 -185
  216. package/src/audio/whisper-timing.ts +0 -274
  217. package/src/audio/whisper-tokenizer.ts +0 -239
  218. package/src/audio/whisper-transcribe.ts +0 -410
  219. package/src/backends/mlx/assistant-target.ts +0 -46
  220. package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
  221. package/src/backends/mlx/autoregressive.ts +0 -129
  222. package/src/backends/mlx/batch-group.ts +0 -1874
  223. package/src/backends/mlx/cache-attachment.ts +0 -67
  224. package/src/backends/mlx/cache-layout.ts +0 -31
  225. package/src/backends/mlx/checkpoint-state.ts +0 -53
  226. package/src/backends/mlx/continuation-persistence.ts +0 -107
  227. package/src/backends/mlx/continuation-request.ts +0 -43
  228. package/src/backends/mlx/continuation.ts +0 -77
  229. package/src/backends/mlx/diffusion.ts +0 -39
  230. package/src/backends/mlx/draft-target.ts +0 -32
  231. package/src/backends/mlx/encoder-cache.ts +0 -25
  232. package/src/backends/mlx/evaluation.ts +0 -105
  233. package/src/backends/mlx/fill-append.ts +0 -58
  234. package/src/backends/mlx/fill-group.ts +0 -497
  235. package/src/backends/mlx/full-kv-row-append.ts +0 -32
  236. package/src/backends/mlx/full-kv-row-donor.ts +0 -54
  237. package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
  238. package/src/backends/mlx/gateway-binding.ts +0 -174
  239. package/src/backends/mlx/graph.ts +0 -38
  240. package/src/backends/mlx/kv-codec.ts +0 -9
  241. package/src/backends/mlx/kv-maintenance.ts +0 -161
  242. package/src/backends/mlx/kv-tensor-rows.ts +0 -212
  243. package/src/backends/mlx/memory-client.ts +0 -189
  244. package/src/backends/mlx/methods.ts +0 -90
  245. package/src/backends/mlx/mixed-iteration.ts +0 -62
  246. package/src/backends/mlx/model-prompt.ts +0 -178
  247. package/src/backends/mlx/model-serving.ts +0 -80
  248. package/src/backends/mlx/paged-cache-codec.ts +0 -27
  249. package/src/backends/mlx/prefill-cohort.ts +0 -100
  250. package/src/backends/mlx/prefill-policy.ts +0 -26
  251. package/src/backends/mlx/prefill-rows.ts +0 -244
  252. package/src/backends/mlx/prefill.ts +0 -33
  253. package/src/backends/mlx/projected-context-rows.ts +0 -72
  254. package/src/backends/mlx/prompt-input.ts +0 -29
  255. package/src/backends/mlx/qwen-prompt-input.ts +0 -40
  256. package/src/backends/mlx/request-policy.ts +0 -20
  257. package/src/backends/mlx/request-state-policy.ts +0 -54
  258. package/src/backends/mlx/rollback.ts +0 -92
  259. package/src/backends/mlx/row-sampling.ts +0 -49
  260. package/src/backends/mlx/serial-executor.ts +0 -251
  261. package/src/backends/mlx/speculative-group.ts +0 -438
  262. package/src/backends/mlx/speculative-round.ts +0 -161
  263. package/src/backends/mlx/speculative.ts +0 -131
  264. package/src/backends/mlx/state-rows.ts +0 -104
  265. package/src/backends/mlx/state-views.ts +0 -46
  266. package/src/backends/mlx/target-layout-capability.ts +0 -35
  267. package/src/backends/mlx/text-engine.ts +0 -73
  268. package/src/backends/mlx/transitioning-kv-rows.ts +0 -173
  269. package/src/chat-template.ts +0 -382
  270. package/src/cli.ts +0 -3490
  271. package/src/config.ts +0 -399
  272. package/src/contracts/completion.ts +0 -16
  273. package/src/contracts/execution.ts +0 -70
  274. package/src/contracts/generation.ts +0 -103
  275. package/src/contracts/host.ts +0 -7
  276. package/src/contracts/object-cache.ts +0 -8
  277. package/src/contracts/pi-provider.ts +0 -30
  278. package/src/contracts/pi.ts +0 -261
  279. package/src/contracts/prefix-cache.ts +0 -33
  280. package/src/contracts/resources.ts +0 -25
  281. package/src/contracts/sampling.ts +0 -23
  282. package/src/contracts/scheduling.ts +0 -54
  283. package/src/contracts/tsconfig.json +0 -14
  284. package/src/dataset/generators.ts +0 -1087
  285. package/src/dataset/index.ts +0 -12
  286. package/src/dataset/job.ts +0 -81
  287. package/src/dataset/llm.ts +0 -71
  288. package/src/dataset/registry.ts +0 -528
  289. package/src/diffusion/diffusion-generate.ts +0 -507
  290. package/src/download.ts +0 -505
  291. package/src/embed.ts +0 -77
  292. package/src/engine/admission.ts +0 -59
  293. package/src/engine/cancellation.ts +0 -38
  294. package/src/engine/engine.ts +0 -56
  295. package/src/engine/execution-coordinator.ts +0 -70
  296. package/src/engine/execution-plan.ts +0 -64
  297. package/src/engine/execution-tasks.ts +0 -44
  298. package/src/engine/namespaced-cache.ts +0 -13
  299. package/src/engine/resources.ts +0 -53
  300. package/src/engine/scheduler.ts +0 -92
  301. package/src/engine/session.ts +0 -247
  302. package/src/eval/README.md +0 -130
  303. package/src/eval/ifeval.ts +0 -81
  304. package/src/eval/kl-prompts.ts +0 -29
  305. package/src/eval/kl.ts +0 -590
  306. package/src/eval/perplexity.ts +0 -164
  307. package/src/eval/quality-db.ts +0 -92
  308. package/src/eval/reference-logits.ts +0 -207
  309. package/src/eval/runner.ts +0 -124
  310. package/src/eval/score.ts +0 -49
  311. package/src/eval/tasks/bfcl.ts +0 -535
  312. package/src/eval/tasks/gsm8k.ts +0 -98
  313. package/src/eval/tasks/hashhop.ts +0 -139
  314. package/src/eval/tasks/humaneval.ts +0 -275
  315. package/src/eval/tasks/ifeval.ts +0 -525
  316. package/src/eval/tasks/mmlu.ts +0 -157
  317. package/src/eval/ultrafeedback.ts +0 -60
  318. package/src/evaldb.ts +0 -199
  319. package/src/expert-io.ts +0 -393
  320. package/src/expert-offload-build.ts +0 -77
  321. package/src/expert-offload.ts +0 -76
  322. package/src/expert-residency.ts +0 -1134
  323. package/src/expert-usage.ts +0 -415
  324. package/src/fill/echo-index.ts +0 -188
  325. package/src/fill/fill-session.ts +0 -463
  326. package/src/fill/proposal.ts +0 -68
  327. package/src/fill/schema-rows.ts +0 -506
  328. package/src/fill/tool-boundary.ts +0 -103
  329. package/src/fit.ts +0 -249
  330. package/src/generate.ts +0 -1352
  331. package/src/grammar.ts +0 -604
  332. package/src/harness-pi.ts +0 -184
  333. package/src/hf-push.ts +0 -628
  334. package/src/hub-rest.ts +0 -242
  335. package/src/index.ts +0 -63
  336. package/src/inference/denoising.ts +0 -17
  337. package/src/inference/draft-acceptance.ts +0 -41
  338. package/src/inference/draft-output.ts +0 -49
  339. package/src/inference/graph.ts +0 -33
  340. package/src/inference/prefill.ts +0 -45
  341. package/src/inference/rollback.ts +0 -7
  342. package/src/jobs/index.ts +0 -31
  343. package/src/jobs/job-entry.ts +0 -107
  344. package/src/jobs/task-client.ts +0 -19
  345. package/src/jobs/types.ts +0 -2
  346. package/src/kv-scheme.ts +0 -279
  347. package/src/kv-store.ts +0 -1135
  348. package/src/lab/README.md +0 -22
  349. package/src/lab/curve/curve-designer.html +0 -303
  350. package/src/lab/curve/curve-sampler.ts +0 -122
  351. package/src/lab/expert-trace/expert-trace.ts +0 -69
  352. package/src/lab/paged-kv/paged-attention.ts +0 -117
  353. package/src/lab/paged-kv/paged-kv-rows.ts +0 -111
  354. package/src/lab/paged-kv/paged-kv.ts +0 -336
  355. package/src/library.ts +0 -40
  356. package/src/lora.ts +0 -408
  357. package/src/md-modules.d.ts +0 -54
  358. package/src/media-fetch.ts +0 -442
  359. package/src/memory/article.ts +0 -558
  360. package/src/memory/chunk-validation.ts +0 -150
  361. package/src/memory/chunk.ts +0 -318
  362. package/src/memory/cluster.ts +0 -351
  363. package/src/memory/coverage.ts +0 -134
  364. package/src/memory/crosslink.ts +0 -0
  365. package/src/memory/db.ts +0 -253
  366. package/src/memory/entity.ts +0 -203
  367. package/src/memory/gate.ts +0 -187
  368. package/src/memory/history.ts +0 -152
  369. package/src/memory/ingest.ts +0 -335
  370. package/src/memory/model.ts +0 -183
  371. package/src/memory/normalize.ts +0 -671
  372. package/src/memory/parse.ts +0 -22
  373. package/src/memory/pipeline.ts +0 -275
  374. package/src/memory/prompts.ts +0 -65
  375. package/src/memory/query.ts +0 -334
  376. package/src/memory/reconcile.ts +0 -734
  377. package/src/memory/redirect.ts +0 -108
  378. package/src/memory/reindex.ts +0 -325
  379. package/src/memory/resolve.ts +0 -307
  380. package/src/memory/rest.ts +0 -245
  381. package/src/memory/route.ts +0 -272
  382. package/src/memory/schedule.ts +0 -165
  383. package/src/memory/stages.ts +0 -777
  384. package/src/memory/synthesize.ts +0 -1638
  385. package/src/memory/tools.ts +0 -749
  386. package/src/memory/vault.ts +0 -1194
  387. package/src/memory/wikify.ts +0 -1064
  388. package/src/mlx/array.ts +0 -425
  389. package/src/mlx/autograd.ts +0 -304
  390. package/src/mlx/checkpoint.ts +0 -149
  391. package/src/mlx/compile.ts +0 -168
  392. package/src/mlx/custom-vjp.ts +0 -188
  393. package/src/mlx/ffi.ts +0 -531
  394. package/src/mlx/materialize.ts +0 -23
  395. package/src/mlx/metal-kernel.ts +0 -159
  396. package/src/mlx/normalized-argmax.ts +0 -116
  397. package/src/mlx/ops.ts +0 -1108
  398. package/src/mlx/quantized-rows.ts +0 -36
  399. package/src/mlx/token-bitmask.ts +0 -52
  400. package/src/mlx/turboquant-kv-decode.ts +0 -82
  401. package/src/mlx/turboquant-kv-inverse.ts +0 -122
  402. package/src/mlx/turboquant-ops.ts +0 -861
  403. package/src/mlx/turboquant-tables.ts +0 -237
  404. package/src/mmap.ts +0 -90
  405. package/src/model/artifact-identity.ts +0 -73
  406. package/src/model/batched-kv.ts +0 -235
  407. package/src/model/batched-mask.ts +0 -279
  408. package/src/model/batched-quant.ts +0 -212
  409. package/src/model/batched-quantized-kv.ts +0 -144
  410. package/src/model/batched-rotating-quant.ts +0 -353
  411. package/src/model/batched-rotating-state.ts +0 -149
  412. package/src/model/batched-rotating.ts +0 -338
  413. package/src/model/batched-row-storage.ts +0 -179
  414. package/src/model/batched-ssm.ts +0 -52
  415. package/src/model/batched-turboquant-kv.ts +0 -94
  416. package/src/model/compiled-decode.ts +0 -712
  417. package/src/model/decoded-kv-donor.ts +0 -12
  418. package/src/model/delayed-quantized-kv.ts +0 -72
  419. package/src/model/delayed-rotating-quantized-kv.ts +0 -93
  420. package/src/model/delayed-turboquant-kv.ts +0 -57
  421. package/src/model/diffusion-gemma.ts +0 -1026
  422. package/src/model/factory.ts +0 -215
  423. package/src/model/fingerprint.ts +0 -45
  424. package/src/model/flash-attention.ts +0 -694
  425. package/src/model/full-prefill-padding.ts +0 -56
  426. package/src/model/full-prefill-row.ts +0 -105
  427. package/src/model/gemma4-base.ts +0 -2370
  428. package/src/model/gemma4.ts +0 -1395
  429. package/src/model/generated/gemma4-12b.ts +0 -429
  430. package/src/model/generated/gemma4-26b.ts +0 -448
  431. package/src/model/generated/gemma4-e4b.ts +0 -717
  432. package/src/model/generated/index.ts +0 -18
  433. package/src/model/glm52-atlas.ts +0 -511
  434. package/src/model/glm52-cache.ts +0 -792
  435. package/src/model/glm52-config.ts +0 -262
  436. package/src/model/glm52-container.ts +0 -305
  437. package/src/model/glm52-coupling.ts +0 -388
  438. package/src/model/glm52-dsa.ts +0 -606
  439. package/src/model/glm52-expert-layout.ts +0 -253
  440. package/src/model/glm52-layout.ts +0 -152
  441. package/src/model/glm52-memory.ts +0 -500
  442. package/src/model/glm52-mla.ts +0 -792
  443. package/src/model/glm52-moe.ts +0 -223
  444. package/src/model/glm52-pilot.ts +0 -537
  445. package/src/model/glm52-reference.ts +0 -445
  446. package/src/model/glm52-residency.ts +0 -557
  447. package/src/model/glm52-resident-weights.ts +0 -135
  448. package/src/model/glm52-streamed-experts.ts +0 -453
  449. package/src/model/glm52-streamed-metal.ts +0 -610
  450. package/src/model/glm52-weights.ts +0 -503
  451. package/src/model/glm52.ts +0 -1132
  452. package/src/model/implementation.ts +0 -69
  453. package/src/model/kv-attention-view.ts +0 -70
  454. package/src/model/minicpm5.ts +0 -376
  455. package/src/model/profile.ts +0 -468
  456. package/src/model/quantized-append-attention.ts +0 -31
  457. package/src/model/qwen-append.ts +0 -12
  458. package/src/model/qwen-conv.ts +0 -52
  459. package/src/model/qwen3-delta-state.ts +0 -74
  460. package/src/model/qwen3-delta.ts +0 -451
  461. package/src/model/qwen3-moe.ts +0 -404
  462. package/src/model/qwen3-mrope.ts +0 -250
  463. package/src/model/qwen3.ts +0 -277
  464. package/src/model/qwen3_5-checkpoint.ts +0 -164
  465. package/src/model/qwen3_5.ts +0 -881
  466. package/src/model/rotating-kv-donor.ts +0 -35
  467. package/src/model/rotating-kv-layout.ts +0 -262
  468. package/src/model/rotating-row-transaction.ts +0 -35
  469. package/src/model/speculative-rotating-kv.ts +0 -101
  470. package/src/model/ssm-prefill-padding.ts +0 -39
  471. package/src/model/support.ts +0 -110
  472. package/src/model/token-groups.ts +0 -66
  473. package/src/model/trellis-balanced-scatter.ts +0 -51
  474. package/src/model/trellis-linear.ts +0 -672
  475. package/src/model/trellis-shared-m.ts +0 -151
  476. package/src/model/trellis-shared-scatter.ts +0 -144
  477. package/src/model/trellis-splitk-prefill.ts +0 -158
  478. package/src/model/trellis-tiled-prefill.ts +0 -136
  479. package/src/model/trellis-vector-expand.ts +0 -62
  480. package/src/model/trellis-wide-prefill.ts +0 -94
  481. package/src/model/turboquant-codec.ts +0 -89
  482. package/src/model/universal/archs.ts +0 -332
  483. package/src/model/universal/dense.ts +0 -612
  484. package/src/model/universal/modules.ts +0 -170
  485. package/src/model/universal/rope.ts +0 -381
  486. package/src/model/whisper.ts +0 -435
  487. package/src/native/expert_io.c +0 -523
  488. package/src/native/frame_extract.swift +0 -140
  489. package/src/native/mic_capture.swift +0 -92
  490. package/src/native-pack.ts +0 -163
  491. package/src/pi-provider.ts +0 -53
  492. package/src/pi-session.ts +0 -81
  493. package/src/pi-terminal.ts +0 -274
  494. package/src/pi-web.ts +0 -1940
  495. package/src/preflight.ts +0 -158
  496. package/src/prompt-cache.ts +0 -682
  497. package/src/quantize/allocator.ts +0 -627
  498. package/src/quantize/atomic-output.ts +0 -38
  499. package/src/quantize/calibration.ts +0 -181
  500. package/src/quantize/config-writer.ts +0 -145
  501. package/src/quantize/index.ts +0 -84
  502. package/src/quantize/job.ts +0 -162
  503. package/src/quantize/quantizer.ts +0 -666
  504. package/src/quantize/rotate.ts +0 -603
  505. package/src/quantize/safetensors-writer.ts +0 -249
  506. package/src/quantize/sensitivity.ts +0 -216
  507. package/src/quantize/trellis.ts +0 -586
  508. package/src/quantize/weight-transform.ts +0 -292
  509. package/src/registry.ts +0 -602
  510. package/src/runtime-config.ts +0 -77
  511. package/src/safetensors.ts +0 -165
  512. package/src/sampler.ts +0 -1012
  513. package/src/serve/admin-routes.ts +0 -184
  514. package/src/serve/assets/curve-terrain.html +0 -105
  515. package/src/serve/assets/training-inference-map.html +0 -575
  516. package/src/serve/audio-routes.ts +0 -323
  517. package/src/serve/aux-routes.ts +0 -132
  518. package/src/serve/batch-scheduler.ts +0 -8
  519. package/src/serve/checkpoint-identity.ts +0 -64
  520. package/src/serve/generation-gateway.ts +0 -537
  521. package/src/serve/isolate.ts +0 -609
  522. package/src/serve/kv-budget.ts +0 -14
  523. package/src/serve/lab-routes.ts +0 -341
  524. package/src/serve/lane-registry.ts +0 -45
  525. package/src/serve/model-admin-routes.ts +0 -155
  526. package/src/serve/model-binding.ts +0 -49
  527. package/src/serve/model-host.ts +0 -511
  528. package/src/serve/parent-application.ts +0 -56
  529. package/src/serve/prompt-response-trace.ts +0 -161
  530. package/src/serve/response-lease.ts +0 -24
  531. package/src/serve/session-search.ts +0 -326
  532. package/src/serve/static-routes.ts +0 -101
  533. package/src/serve/transcription-server.ts +0 -64
  534. package/src/serve/transcription-service.ts +0 -502
  535. package/src/server.ts +0 -1282
  536. package/src/spec/assistant-rows.ts +0 -93
  537. package/src/spec/assistant-source.ts +0 -126
  538. package/src/spec/cache-identity.ts +0 -22
  539. package/src/spec/deepspec-rows.ts +0 -29
  540. package/src/spec/deepspec-source.ts +0 -145
  541. package/src/spec/dflash-source.ts +0 -149
  542. package/src/spec/drafter.ts +0 -304
  543. package/src/spec/dspark/ab-stats.ts +0 -136
  544. package/src/spec/dspark/calibration.ts +0 -112
  545. package/src/spec/dspark/data-dflash.ts +0 -156
  546. package/src/spec/dspark/data.ts +0 -201
  547. package/src/spec/dspark/deepspec-module.ts +0 -776
  548. package/src/spec/dspark/generate-dflash.ts +0 -245
  549. package/src/spec/dspark/generate.ts +0 -290
  550. package/src/spec/dspark/loader.ts +0 -23
  551. package/src/spec/dspark/loss.ts +0 -213
  552. package/src/spec/dspark/module-dflash.ts +0 -560
  553. package/src/spec/dspark/module.ts +0 -540
  554. package/src/spec/dspark/quantize-drafter.ts +0 -91
  555. package/src/spec/dspark/sample.ts +0 -112
  556. package/src/spec/generate.ts +0 -276
  557. package/src/spec/glm52-mtp-graph.ts +0 -29
  558. package/src/spec/glm52-mtp-rows.ts +0 -132
  559. package/src/spec/glm52-mtp-source.ts +0 -318
  560. package/src/spec/glm52-mtp-state.ts +0 -33
  561. package/src/spec/ngram-source.ts +0 -226
  562. package/src/spec/projected-draft-rows.ts +0 -73
  563. package/src/spec/qwen-mtp-module.ts +0 -171
  564. package/src/spec/qwen-mtp-rows.ts +0 -230
  565. package/src/spec/qwen-mtp-source.ts +0 -386
  566. package/src/spec/qwen-mtp-state.ts +0 -30
  567. package/src/spec/serve-loop.ts +0 -587
  568. package/src/spec/source.ts +0 -228
  569. package/src/spec/two-model-rows.ts +0 -62
  570. package/src/spec/two-model.ts +0 -113
  571. package/src/ssd-cache.ts +0 -431
  572. package/src/ssd-durability.ts +0 -209
  573. package/src/storage/host-buffer.ts +0 -24
  574. package/src/storage/kv-writer.ts +0 -83
  575. package/src/storage/kv-writer.worker.js +0 -264
  576. package/src/storage/retention-policy.ts +0 -37
  577. package/src/tiered-prompt-cache.ts +0 -45
  578. package/src/tokenizer.ts +0 -335
  579. package/src/tool-approvals.ts +0 -131
  580. package/src/tool-call.ts +0 -695
  581. package/src/train/dataset.ts +0 -484
  582. package/src/train/diffusion-lora.ts +0 -216
  583. package/src/train/export.ts +0 -32
  584. package/src/train/flash-cce.ts +0 -1234
  585. package/src/train/forward.ts +0 -266
  586. package/src/train/fuse.ts +0 -243
  587. package/src/train/index.ts +0 -46
  588. package/src/train/job.ts +0 -205
  589. package/src/train/lora-params.ts +0 -254
  590. package/src/train/loss.ts +0 -1591
  591. package/src/train/merge.ts +0 -195
  592. package/src/train/optimizer.ts +0 -183
  593. package/src/train/prefix-shared.ts +0 -490
  594. package/src/train/rank.ts +0 -137
  595. package/src/train/segmented.ts +0 -1828
  596. package/src/train/steel-qmm-header.ts +0 -1461
  597. package/src/train/trainer.ts +0 -1221
  598. package/src/train/watch.ts +0 -542
  599. package/src/vision/diffusion-vision.ts +0 -620
  600. package/src/vision/embedder.ts +0 -176
  601. package/src/vision/preprocess.ts +0 -247
  602. package/src/vision/prompt.ts +0 -409
  603. package/src/vision/qwen3vl-preprocess.ts +0 -409
  604. package/src/vision/qwen3vl-prompt.ts +0 -233
  605. package/src/vision/qwen3vl-tower.ts +0 -566
  606. package/src/vision/siglip.ts +0 -607
  607. package/src/vision/video-frames.ts +0 -148
  608. package/src/web/app.html +0 -2155
  609. package/src/web/app.js +0 -5863
  610. package/src/web/skills/memory/SKILL.md +0 -120
  611. package/src/web/skills/web-research/SKILL.md +0 -46
  612. package/src/web/skills.ts +0 -97
  613. package/src/web/src/adapters-panel.ts +0 -288
  614. package/src/web/src/assistant.ts +0 -451
  615. package/src/web/src/chat.ts +0 -1060
  616. package/src/web/src/composer.ts +0 -962
  617. package/src/web/src/dataset.ts +0 -132
  618. package/src/web/src/finetune.ts +0 -267
  619. package/src/web/src/hub.ts +0 -428
  620. package/src/web/src/main.ts +0 -98
  621. package/src/web/src/markdown.ts +0 -0
  622. package/src/web/src/memory-panel.ts +0 -572
  623. package/src/web/src/model-picker.ts +0 -159
  624. package/src/web/src/palette.ts +0 -346
  625. package/src/web/src/protocol.ts +0 -26
  626. package/src/web/src/quantize.ts +0 -167
  627. package/src/web/src/rag.ts +0 -274
  628. package/src/web/src/sessions.ts +0 -469
  629. package/src/web/src/shell.ts +0 -804
  630. package/src/web/src/ui-catalog.ts +0 -78
  631. package/src/web/src/voice.ts +0 -141
  632. package/src/web/vendor/README.md +0 -61
  633. package/src/web-assets.ts +0 -47
  634. package/src/web-tools.ts +0 -478
  635. package/src/weights.ts +0 -255
  636. /package/src/{serve → server}/completion-sink.ts +0 -0
  637. /package/src/{serve → server}/generated-token-history.ts +0 -0
  638. /package/src/web/{icon.svg → public/icon.svg} +0 -0
  639. /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
  640. /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
@@ -1,993 +0,0 @@
1
- ---
2
- status: landed
3
- axis: ON
4
- canonical-for: kv-cache
5
- plan-anchor: "Phase 18 — Concurrent / batched serving (slots) + parallel load benchmark `[~]` (2026-06-13)"
6
- last-verified: 2026-09-12
7
- ---
8
-
9
- # KV cache — residency and layout
10
-
11
- The one doc for where KV lives and how it is arranged: the cache classes
12
- and their `signature()` capability model, the RAM prompt cache
13
- (`PromptCache.take()`), the SSD cold tier and its durability boundary, the
14
- optional paged block allocator, and how KV *schemes* plug into all of it.
15
- The TurboQuant codec itself is documented in
16
- [docs/design/turboquant.md](./turboquant.md); this doc only covers how its cache
17
- class participates in residency.
18
-
19
- Sources consolidated here: `docs/design/kv-cache.md`, `docs/design/kv-cache.md`, and
20
- the cache-integration parts of `docs/design/turboquant.md`. Status and changelog
21
- prose lives in PLAN.md; this file keeps mechanism, invariants, decisions,
22
- and open items.
23
-
24
- Code: `src/model/gemma4-base.ts` (Cache interface + the K/V cache family),
25
- `src/model/batched-*.ts`, `src/lab/paged-kv/paged-kv.ts`, `src/prompt-cache.ts`,
26
- `src/kv-store.ts`, `src/ssd-cache.ts`, `src/ssd-durability.ts`,
27
- `src/tiered-prompt-cache.ts`, `src/storage/kv-writer.ts`,
28
- `src/kv-scheme.ts`, `src/generate.ts` (`maybeQuantizeKv` / `maybePageKv`),
29
- `src/server.ts` (wiring, `/stats`, `POST /admin/cache/flush`),
30
- `src/serve/generation-gateway.ts`, `src/serve/batch-scheduler.ts`.
31
-
32
- ## 1. Layering
33
-
34
- Active generation owns mutable cache containers. Published checkpoints belong
35
- to one logical cache, with RAM and SSD storage. On Apple Silicon the GPU and
36
- CPU access unified memory; publication does not require a GPU-to-CPU copy.
37
- Each checkpoint is keyed by the exact token prefix its state encodes:
38
-
39
- | tier | owner | unit | key |
40
- |---|---|---|---|
41
- | live caches | one generation (serial lane or a batch row) | `Cache[]`, one per layer | — |
42
- | RAM prompt cache | `TieredPromptCache` over `PromptCache` | whole prefix entry (tokens + `Cache[]`) | tokens + adapter ns |
43
- | SSD cold tier | `SsdCacheStore` (`src/ssd-cache.ts`) over `src/kv-store.ts` files | one `.mlxkv` file per entry | tokens + ns + model/scheme/tokenizer fingerprint |
44
-
45
- The prompt cache tiers over the SSD store *inside* `take()` (structural
46
- `ColdTier` interface), so every consumer (serial lane, batch scheduler
47
- joiners, `/admin/drain`) gets both tiers through the same `take()`/`put()`
48
- calls. The cache queues persistence on publication or eviction. A dedicated
49
- CPU worker writes evaluated immutable buffers while inference continues.
50
- Persistence does not evict RAM; only cache residency policy does. RAM reuse
51
- does not enqueue an extra write or wait for the queue. Explicit flushes await the queued work. The opt-in `--generation-checkpoint N` path transfers owned snapshots at coarse decode boundaries and queues atomic
52
- persistence without awaiting disk in decode (section 5.7). Paged KV
53
- (section 6) is a *layout* choice for the live tier and, in v1, bypasses the
54
- other two.
55
-
56
- ## 2. Cache classes and the signature/capability model
57
-
58
- ### 2.1 The `Cache` contract (`gemma4-base.ts`)
59
-
60
- Every cache-owning layer holds one `Cache`:
61
-
62
- - `updateAndFetch(k, v)` appends and returns the attended window;
63
- `makeMask(N, windowSize)`; `state()` (arrays to eval at a prefill chunk
64
- boundary); `dispose()`.
65
- - `isTrimmable()` / `trim(n, bypass?)` — whether the last `n` tokens can be
66
- dropped. This single predicate drives prefix reuse: an entry longer than
67
- the matched prefix is usable only if every cache can trim.
68
- - `signature?()` — stable storage identity ("`kv:plain`",
69
- "`kv:quant:<bits>:<group>`", "`kv:rotating-plain`",
70
- "`kv:rotating-quant:<bits>:<group>`", "`kv:turboquant:<k>:<v>`", "`ssm`",
71
- "`kv:mla:target`", "`kv:mla:target:dsa`", "`kv:mla:mtp`"). Persistence
72
- codecs (`kv-store.ts` `CACHE_CODECS`) and the scheduler's layer-kind map
73
- dispatch on it. `cacheSignature(cache)` returns `"unknown"` when the
74
- method is absent.
75
- - `bytesPerToken?()` — physical growth per token per row (0 for recurrent
76
- state); `stateNeedsDispose` (TurboQuant's `state()` returns fresh views
77
- the caller must dispose); `ropeOffsetArr` (per-row RoPE positions for
78
- batched/compiled paths); `specRound*` (speculative rollback for
79
- untrimmable recurrent caches).
80
-
81
- Capability interfaces sit beside the signature: `RowBatchCache`
82
- (`batchSize`/`filterRows`/`extractRow`) and `BatchableCache` (caches that
83
- own their own dynamic-row batching — GLM's MLA/DSA state). Type guards
84
- (`isPlainKvCache`, `isRotatingPlainCache`, …) are signature-based;
85
- `isRowBatchCache`/`isBatchableCache` are structural.
86
-
87
- ### 2.2 The family
88
-
89
- | class | file | signature | trimmable | notes |
90
- |---|---|---|---|---|
91
- | `KVCache` | gemma4-base.ts | `kv:plain` | yes | mlx-lm port; grows in `STEP = 256` slabs, in-place `sliceUpdate` |
92
- | `QuantizedKVCache` | gemma4-base.ts | `kv:quant:b:g` | yes | affine triples (packed/scales/biases), groups along head_dim |
93
- | `RotatingKVCache` | gemma4-base.ts | `kv:rotating-plain` | **only while `offset < maxSize`** (pre-wrap) | sliding-window ring |
94
- | `RotatingQuantizedKVCache` | gemma4-base.ts | `kv:rotating-quant:b:g` | pre-wrap | quantized ring |
95
- | `TurboQuantKVCache` | gemma4-base.ts | `kv:turboquant:k:v` | yes | not a `KVCache` subclass — deliberately fails every `instanceof` gate (monolith fallback, solo-only); see docs/design/turboquant.md |
96
- | `SSMCache` | qwen3-delta.ts | `ssm` | **no** (recurrent) | `bytesPerToken()` = 0; per-row `offsets` in the batch lane |
97
- | `Glm52Cache` / `MLACache` | glm52-cache.ts | `kv:mla:*` | yes | `BatchableCache`; compressed latent + rope (+ DSA index) |
98
- | `PagedKVCache` | paged-kv.ts | **none** | yes | block pool + gather; section 6 |
99
- | `BatchedRotatingCache` | batched-rotating.ts | `kv:rotating-plain` | pre-wrap | `RowBatchCache`; batched ring, host-side scalar ring state |
100
- | `BatchedRotatingQuantCache` | batched-rotating-quant.ts | inherited (`extends RotatingQuantizedKVCache`) | pre-wrap | batched quantized ring |
101
- | `BatchedDecodeMaskCache` | batched-mask.ts | **none** | delegates | per-step wrapper over a B-generic inner (padding mask + per-row RoPE) |
102
- | `BatchedQuantDecodeMaskCache` | batched-quant.ts | inherited (`extends QuantizedKVCache`) | delegates | quantized twin of the wrapper; subclassing keeps generated forwards' `instanceof` guards happy |
103
- | compiled-decode `Trace*` | compiled-decode.ts | inherited | — | trace adapters over the four compiled-supported classes |
104
- | training caches (`TrainingCache`, `TrainingSSMCache`, `BatchedMaskCache`, `PrefixSharedCache`, `Gemma4PrefixSharedCache`, `Gemma4PrefixSharedSegCache`) | src/train/* | **none** | — | never admitted, cloned, or persisted; the interface comment explicitly allows omission here |
105
-
106
- Invariant: **quantization groups run along head_dim, never the token
107
- axis**, so token-axis slicing (trim, `cloneKvCaches` live views, batch row
108
- surgery, persistence slicing to `offset`) is byte-safe for every quantized
109
- kind. Every cache kind persisted by kv-store has a `restoreState(...)`.
110
-
111
- ### 2.3 Batching gates
112
-
113
- Two independent checks decide whether a model/scheme can batch
114
- (`generation-gateway.ts`):
115
-
116
- - `#modelCachesBatchable()` — every fresh `makeCache()` entry is a
117
- `KVCache`, `RotatingKVCache`, `BatchableCache`, or (unless
118
- `MLX_BUN_BATCH_SSM=0`) `SSMCache`. Novel classes (TurboQuant, paged) fail
119
- this by construction.
120
- - `#kvBatchable()` — `KvScheme.batchable(config, canConvert)`: per-layer
121
- `affine-config` and uniform affine KV4/KV8 batch when their cache-owning
122
- layers can convert. TurboQuant still requires merged-row layout support;
123
- a scheme-less gateway must never silently
124
- drop quantization (`kv-scheme.ts` makes the capability probe mandatory
125
- for quantized schemes).
126
-
127
- The scheduler then maps each layer to a kind ("owned-batch" / "rot" /
128
- "ssm" / "full") from the prototype caches, and joins/filters/extracts by
129
- that kind.
130
-
131
- ### 2.4 Known design gap: optional `signature()` with an `"unknown"` fallback
132
-
133
- `Cache.signature` is optional and `cacheSignature()` substitutes
134
- `"unknown"`. That makes an unsigned class *route* rather than *fail*: any
135
- signature-based conjunct silently evaluates false. This is exactly how the
136
- 2026-08-22 agg×4 regression happened (PLAN.md "agg×4 regression —
137
- root-caused and fixed", commit `443f333`; the BatchedRotatingCache fix
138
- PRs #42/#43): the scheduler's rot-merge required
139
- `isRowBatchCache(prev) && isRotatingPlainCache(prev)`, `BatchedRotatingCache`
140
- had no `signature()`, the running batch's ring was dropped from the join,
141
- and the next full-B decode step crashed in the grow-path concatenate. The
142
- quantized twin escaped only because it inherits a real signature.
143
-
144
- What holds today (verified in src, 2026-08-23):
145
-
146
- - `BatchedRotatingCache` now returns `"kv:rotating-plain"`
147
- (`batched-rotating.ts`), and the scheduler's rot branch routes by
148
- capability alone (`isRowBatchCache`). The comment at
149
- `batch-scheduler.ts` ~line 1007 still says the class has *no* override —
150
- that comment is stale; the guard order it describes (capability first)
151
- is still the rule.
152
- - Still unsigned: `PagedKVCache` and `BatchedDecodeMaskCache` in the
153
- serving tree, plus the six training-only caches listed above. Neither
154
- serving class is currently reachable by a signature-dispatched path
155
- (paged bypasses prompt-cache/kv-store/batching; the mask wrapper is
156
- rebuilt per step and the scheduler dispatches on the *inner* caches),
157
- so today the gap is latent, not live.
158
- - Rule going forward (PLAN.md lesson): **"unknown" is a bug, not a
159
- route.** Any new `Cache` that can reach the scheduler, `cloneKvCaches`,
160
- or kv-store must declare a signature; kv-store already throws on an
161
- unknown signature rather than guessing. Making `signature()` required on
162
- the interface (with training adapters returning an explicit
163
- `"training"`) is the open hardening item.
164
-
165
- ## 3. KV schemes — how they plug into the cache (`src/kv-scheme.ts`)
166
-
167
- `KvScheme` is the immutable declaration of *what bytes a token costs and
168
- which cache class holds it*: kind `bf16` | `affine-uniform` |
169
- `affine-config` | `turbo`, resolved once at server start by
170
- `resolveKvScheme` (TurboQuant wins if set; `--kv-quant off|<bits>|config`
171
- otherwise; a missing `kv_config.json` under `config` is bf16 or an error
172
- per caller). Its roles in residency:
173
-
174
- - **Conversion chokepoint.** `maybeQuantizeKv` (`generate.ts`) converts
175
- populated plain/rotating caches in place after the first prefill
176
- (`offset === 0` caches are skipped — the optiq mixed-KV convention; the
177
- affine start threshold is `quantizedKvStart`, which `resolveKvScheme`
178
- pins to 0 for every server scheme — the `generate.ts` fallback of 5000
179
- for uniform bits only reaches library callers who pass `kvBits` without
180
- a threshold) and evaluates layer by layer so the bf16 source frees
181
- before the next layer converts. TurboQuant converts
182
- full-attention `KVCache` layers only; rotating layers stay bf16 with a
183
- one-time warning.
184
- - **Byte accounting.** `kvBytesAt`/`kvGeometry` bill per layer type (full
185
- / sliding capped at the window / linear state) with
186
- `bits/8 + 4/groupSize` bytes per affine element. `fitOptions` bills
187
- TurboQuant as bf16 on purpose (conservative until its packed layout
188
- exposes a stable projector). The scheduler's KV-budget admission uses
189
- the same seam, and `BatchableCache.projectedBytes` for owned-batch
190
- caches.
191
- - **Cache compatibility key.** `cacheKey` (`bf16` | `kv<bits>` | `config`
192
- | `turbo-k<k>v<v>`) is folded into the SSD store's fingerprint
193
- (`${configFingerprint(config)}-${schemeKey}`, `server.ts`), so restored
194
- files always match what serving would have produced. `configFingerprint`
195
- itself covers the per-layer `kv_config.json` scheme but not CLI-only
196
- levers (uniform bits, TurboQuant) — the scheme key is the anti-collision
197
- mechanism for those.
198
- - **Batch placement.** `batchable()` as in 2.3.
199
-
200
- Uniform 4/8-bit affine KV is bit-exact L1 (unfused SDPA == mlx-lm
201
- `base.py`); the per-layer config scheme is the L2/optiq composition, and
202
- TurboQuant is a third, oracle-backed codec (vllm-metal). The scheme axis
203
- (which bits where) is orthogonal to the residency axis (which tier holds
204
- them); nothing in this doc changes numerics.
205
-
206
- ## 4. RAM tier — `PromptCache` (`src/prompt-cache.ts`)
207
-
208
- Byte-capped LRU of whole prefix entries (`--prompt-cache <GB>`, default
209
- 8 GB; `0` disables). The mlx-lm lesson that motivated it — a count-capped
210
- cache of multi-GB entries is an OOM footgun — still stands even though
211
- upstream is byte-capped now.
212
-
213
- **`take(prompt, ns)` — prefix sharing.** Finds the entry with the longest
214
- usable common prefix (usable = common prefix capped at `prompt.length - 1`,
215
- so at least one token is forwarded; an entry longer than the match must be
216
- trimmable). Every RAM hit lends zero-copy views (`cloneKvCaches` and
217
- `cloneAttachments`) with a ref-counted backing share; the donor remains in
218
- place. This includes exact-boundary recurrent and method state. Only
219
- trimmable state may be shortened. Decode extends its own views without
220
- changing the retained checkpoint.
221
-
222
- SSD is an eventually consistent persistence layer. RAM reuse never writes
223
- to SSD or waits for durability. Background writes retain immutable snapshot
224
- views while a CPU worker packs, hashes and writes them; explicit flush and
225
- graceful shutdown wait for persistence. A pending or failed write does not prevent RAM reuse.
226
- The byte target controls LRU eviction. With SSD enabled, an unwritten victim
227
- stays resident until storage commits; the target is soft while writes are
228
- pending or failing.
229
-
230
- The native sharing gate checks logical checkpoint bytes after repeated decode
231
- and after SSD restore. It passes on both development Macs; the M1 long-prefix
232
- case also preserves three independent 512-token continuations. The queued
233
- writer test retains snapshot state after both donor and borrower disposal,
234
- and explicit flush remains pending until the write completes.
235
-
236
- **Tier order inside `take()`.** The cold tier is consulted with
237
- `find(prompt, ns)` (index-only, no I/O) and wins only with a *strictly*
238
- longer usable prefix; then `restore(handle)` materializes it and the
239
- divergent tail is trimmed. A cold entry that would need a trim but is
240
- untrimmable (wrapped ring, SSM) is disposed loudly and the RAM candidate
241
- (or a fresh prefill) serves. Tiered restore immediately publishes shared views
242
- in RAM so another request can reuse them before the borrower finishes.
243
- `hits`/`misses` count RAM candidacy only.
244
-
245
- **`put(tokens, caches, ns, retain)` — supersession + eviction.** Exact
246
- duplicates (same tokens) are replaced regardless of trimmability. Strict
247
- prefix-ancestors in the same ns are superseded without spill — but only
248
- when the *new* entry is fully trimmable; an untrimmable new entry (wrapped
249
- ring) can only serve exact-length matches, so shorter ancestors (the
250
- prompt-boundary snapshot) must survive. `TieredPromptCache` selects the
251
- least recently used RAM entry when over budget. If it lacks durable SSD
252
- coverage, the cache queues a write and retains it until that write succeeds.
253
- It does not evict a hotter entry merely because that entry is already on SSD.
254
- An oversized checkpoint follows the same persist-before-demotion rule.
255
- Without SSD, the RAM-only cache discards evicted or oversized entries.
256
- `TieredPromptCache.put` also owns proactive persistence; `onPut` remains an
257
- independent observer for generated-token history.
258
-
259
- **Prompt-boundary snapshot.** Serial lane (`server.ts`) and batch
260
- scheduler (`snapshotAt`) both clone-and-put a trim-free *prompt-only*
261
- entry at the template probe's stable boundary during a substantial cold
262
- prefill. Rationale: the prompt+generation entry put at completion is
263
- untrimmable past the sliding window (wrapped rings) and under mid-group
264
- quantization, so any decode→encode drift in the reply the client sends
265
- back would turn the next turn into a total miss; a prompt-only entry is
266
- always an exact prefix of the next rendering.
267
-
268
- **Namespaces.** `ns` = the adapter spec (`adapters.join("+")`, `""` = base).
269
- KV computed under one adapter never seeds another's prefill.
270
-
271
- **Bypasses.** Media and optional paged-KV requests retain their documented
272
- cache qualification rules. Shared speculative execution publishes target and
273
- method state together where supported; method attachments preserve the exact
274
- boundary needed for reuse. Merged batch rows are extracted before retirement
275
- changes the batched state. The cache owns the extracted checkpoint.
276
-
277
- **Idle demotion (`demoteIdle`).** Entries unused past a threshold spill to
278
- the cold tier and free their GPU arrays; the prefix stays reachable via
279
- restore. No-op without a cold tier (demotion with nowhere to demote to is
280
- data loss). `/admin/drain` (unix-socket engine children only) is
281
- `demoteIdle(0)` under the gateway lock — the model pool's lossless
282
- pre-eviction step.
283
-
284
- **Spill contract (`SpillSink`).** Preferred `spillOwned`: the cache hands
285
- the sink zero-copy clones + copied tokens made *before* the donor is
286
- disposed, and the sink owns their disposal on every settle path. A sink
287
- that throws synchronously has not taken ownership; the spill degrades to
288
- a no-op and eviction never unwinds. `spillSync` (live entry, synchronous)
289
- remains for tests and simple embedders.
290
-
291
- Known numerics property (not a residency defect): continuing from a
292
- restored or reused prefix produces the same output as the RAM tier's own
293
- cache hit, but a bf16 prefix-reuse continuation can differ from a fresh
294
- full prefill (GEMV-vs-GEMM reduction order). Both tiers share it.
295
-
296
- ## 5. SSD cold tier
297
-
298
- ### 5.1 Decisions
299
-
300
- - **D1 — Whole-prefix-entry storage.** A checkpoint stores a complete
301
- conversation boundary. This reuses the existing cache codecs and avoids
302
- per-token storage bookkeeping, but duplicates shared attention history
303
- between files. Block deduplication is a separate layout optimization to
304
- measure against this baseline; recurrent state still needs exact boundaries.
305
- - **D2 — One store, two tiers.** `TieredPromptCache` owns RAM residency and
306
- the SSD backend. Proactive persistence after publication provides restart
307
- survival without waiting for an eviction.
308
- - **D3 — Own page-aligned format, not safetensors.** `src/kv-store.ts`
309
- writes `MLXBUNKV2\n`-magic files with a streaming writer: ordinary KV
310
- retains format v3; checkpoints with method companion state use v4.
311
- The header is sized up front with fixed-width tensor
312
- hashes, tensors materialized/hashed/written one at a time at 16 KiB
313
- aligned offsets, header patched last (peak host transient = one
314
- tensor). Header carries `formatVersion`, `modelId`,
315
- `configFingerprint`, `ns`, `tokenizerHash`, `createdAt`, `tokens`, and
316
- per-cache `{kind, offset, idx?, maxSize?, groupSize?, bits?, kBits?,
317
- vBits?, headDim?, GLM geometry, tensors[]}`. Kinds: `kv`, `rotating`,
318
- `qkv`, `rotating-qkv`, `ssm`, `turboquant`, `mla`, `mla-dsa`, `mtp-mla`.
319
- Quantized buffers are sliced to `offset` (no 256-step slack persisted).
320
- Header hash always verified; per-tensor hashes verified only under
321
- `--ssd-cache-verify` (eager reads roughly double restore cost). Writes
322
- are atomic (tmp + fsync + rename). Older v1/v2 files read as
323
- unsupported and are deleted-and-regenerated (machine-local, cheap).
324
- - **D4 — Files are the database.**
325
- `<dir>/<configFingerprint>/<nsHash>/<uuid>.mlxkv`. Startup recovery is a
326
- header-only scan of *our* fingerprint dir into an in-memory
327
- `SsdIndexEntry[]` (`{path, ns, tokens, bytes, mtimeMs, trimmable}`);
328
- foreign fingerprint dirs are ignored, never deleted. LRU = file mtime
329
- (`utimes` on hit); the byte cap is enforced at write time by oldest-mtime
330
- eviction; corrupt headers and metadata mismatches are unlinked; `.tmp`
331
- orphans reaped; disk-full is a warn-once soft-fail. Every failure path
332
- degrades to "no hit" / "not stored" — the tier can never take serving
333
- down.
334
- - **Numerical compatibility.** The serving fingerprint also includes a hash
335
- of `ModelServingBinding.stateCompatibility`. The MLX backend supplies its
336
- actual runtime version and GPU architecture at binding creation. The same
337
- artifact produces different cache tensors in the recorded 0.31.2/0.32.2
338
- RTN4 comparisons, so graph geometry and KV format alone cannot qualify
339
- automatic reuse. A runtime/architecture change selects another directory;
340
- previous snapshots remain intact. The binding owns this identity, keeping
341
- backend details out of request parsing and portable model configuration.
342
- A replacement-binding restart regression verifies matching reuse, isolation
343
- and recovery of the original snapshot. Evidence:
344
- `reports/qwen38-rd/mlx-upgrade-0.32.2/cross-runtime-cache-numerics-review.json`.
345
- The real RTN4 HTTP gate also passes all eight responses across four
346
- internal-SSD server starts. Its first requests reuse 0/352/0/352 tokens
347
- under default/default/alternate/default identities; every second request
348
- reuses 352. The alternate tag changes metadata only. All response content,
349
- tool calls and usage counts other than cached tokens match. Exit allocation
350
- includes a retained prompt cache with different fresh/restored capacity,
351
- so this is not a post-disposal memory comparison. Evidence:
352
- `reports/qwen38-rd/runtime-state-http-review.json`.
353
- - **D5 — Flags and interactions.** `--ssd-cache <dir>` (off unless set),
354
- `--ssd-cache-max <GB>` (optional; `0` or unset = unlimited), `--ssd-cache-verify`,
355
- `--ssd-demote-idle <sec>` (default 300 with the tier on; `0` disables).
356
- Requires the RAM tier (`--prompt-cache 0` + `--ssd-cache` is a startup
357
- error). Sub-flags without `--ssd-cache` warn and are ignored.
358
- `--memory-budget` is unchanged (admission already assumes worst-case
359
- bf16). `--paged-kv` warns that the tier sees nothing (section 6).
360
- - **D6 — In-flight resume is explicit.** `--generation-checkpoint <tokens>`
361
- requires `--ssd-cache` and supports eligible shared ordinary execution. It
362
- is off by default. Captured states queue for SSD persistence; an explicit
363
- flush establishes durability of queued work (section 5.7). An identical request resumes; a changed
364
- prompt or sampling policy starts normally. Ordinary adapter requests use the
365
- same continuation hooks under their existing execution context. The checkpoint
366
- identity includes the ordered adapter IDs and content-revision namespace; a
367
- base-model request or replacement adapter cannot restore that checkpoint.
368
-
369
- ### 5.2 Restore is a streamed copy (supersedes the original zero-copy plan)
370
-
371
- The first design restored by COW mmap + `fromPointer` and unmapped in the
372
- entry's `retain`. Two things killed it: the 2026-07-06 rule that mlx
373
- buffer destructors must never call into JS removed the unmap signal, so
374
- every restore became a process-lifetime mapping leak; and exactly-sized
375
- restored buffers made the first post-restore decode step concat-copy the
376
- whole entry anyway. `loadKvCache` now copies each tensor out of a
377
- read-only mmap into an mlx-owned leaf (`fromBytesCopy`), drops the clean
378
- file pages (`MADV_DONTNEED`; offsets are 16 KiB = arm64 page size),
379
- unmaps before returning, and lands plain-KV tensors in STEP-rounded
380
- capacity with slack so the first step updates in place. Peak host
381
- transient = live entry + one tensor; nothing outlives the call. The
382
- `retain` thunk stays in the entry contract as a no-op so callers' dispose
383
- ordering is unchanged (the server's `coldTier.restore` returns
384
- `() => {}`). Comments in `prompt-cache.ts` that still say "zero-copy
385
- mmap" describe the superseded mechanism.
386
-
387
- The asynchronous writer reads evaluated native storage directly. Contiguous
388
- tensors need no packing copy. Strided tensors are packed on the CPU, one
389
- tensor at a time, to omit capacity padding without a GPU `contiguous` call.
390
-
391
- ### 5.3 `find()` returns partial matches, gated on trimmability
392
-
393
- `SsdCacheStore.find` returns the longest usable prefix (capped at
394
- `prompt.length - 1`) and *skips* entries that would need a trim but are
395
- untrimmable (`trimmable` is derived from the header kinds + state at
396
- store/scan time via `cacheHeadersTrimmable`). Before this gate the big
397
- untrimmable `[prompt+gen]` file always outranked the usable boundary
398
- snapshot, got restored, and was thrown away — the 2026-07-06 "restart
399
- restores 0 tokens" defect and its silent restore-then-re-prefill cost.
400
- Store-side supersession mirrors `PromptCache.put`: exact duplicates
401
- replaced; prefix-ancestors superseded only when the new entry is
402
- trimmable.
403
-
404
- ### 5.4 Background persistence and RAM residency
405
-
406
- `TieredPromptCache` owns RAM residency, SSD writes and durable-coverage checks.
407
- `put` materializes the checkpoint before publication. `saveKvCacheAsync`
408
- uses the existing codecs to describe the file and obtains host-visible native
409
- pointers, shapes and strides on the MLX owner thread. Its optional preparation
410
- runner does not cover disk I/O. No MLX handles cross into the writer.
411
-
412
- `KvWriter` embeds a dedicated Bun CPU worker in source and compiled builds.
413
- The worker packs strided layouts, hashes tensor bytes, writes a temporary file,
414
- fsyncs it and renames it atomically. It imports no MLX or scheduler code and
415
- needs no generation lock. Contiguous tensors use direct buffer views; packing
416
- scratch is reused across strided tensors. The cache owns the retained state,
417
- and the queued write holds shared views through its completion. Those views
418
- share immutable storage rather than copying the entire checkpoint.
419
-
420
- The owner thread still prepares native views and updates the SSD metadata
421
- index after completion. A background worker removes blocking payload I/O from
422
- that thread; it does not make shared memory bandwidth or snapshot preparation
423
- free. Measure those costs with concurrent decode before claiming a speedup.
424
-
425
- Writing a copy does not remove RAM residency. Byte pressure chooses the actual
426
- LRU entry. If its SSD copy is missing, it queues persistence and waits for the
427
- completion callback to retry eviction. Failed writes leave the RAM entry
428
- usable and dirty. Age-based eviction follows the same rule and runs without
429
- acquiring the scheduler's generation lock. Neither policy refuses requests or
430
- changes context, batch size or sampling settings.
431
-
432
- `MLX_BUN_SSD_WRITEBEHIND=0` disables proactive persistence after publication;
433
- eviction still queues persistence before removing RAM. Explicit flush and
434
- shutdown await outstanding writes. A hard exit can lose uncommitted writes.
435
-
436
- ### 5.5 Persistence queue and capacity
437
-
438
- Normal prefix persistence uses one serial `SpillQueue` with no independent
439
- byte-drop policy. RAM ownership and pending write views share the checkpoint;
440
- queue completion releases only the writer's reference. RAM cache capacity
441
- and age policy decide demotion. They do not cancel an unwritten snapshot to
442
- reduce the queue's accounting. While the oldest victim is writing, RAM can
443
- exceed its target. A storage failure keeps that victim resident and retryable.
444
-
445
- The optional interrupted-generation coordinator still uses the generic
446
- bounded queue for supersedable intervals. `MLX_BUN_SSD_SPILL_QUEUE_GB`
447
- (default 2 GiB) now controls that queue only. It does not limit retained prompt
448
- history. SSD capacity is independent: no capacity eviction without an explicit
449
- positive `--ssd-cache-max`.
450
-
451
- ### 5.6 Durability boundary (`src/ssd-durability.ts`)
452
-
453
- The 2026-08-22 finding: a debounce timer (1 s), a busy re-arm (5 s), and a
454
- serial queue meant a SIGTERM shortly after a long request could outrun
455
- the write — the server implied durability it did not have.
456
- `SsdDurabilityCoordinator` turns puts into a state machine:
457
-
458
- - `schedule(tokens, ns)` records a *dirty* key that includes the exact
459
- token sequence (two same-ns, same-length conversations cannot cancel
460
- each other) and arms a next-tick attempt.
461
- - An attempt checks committed SSD coverage before cloning.
462
- Otherwise it snapshots the RAM entry (`findExact` + `cloneKvCaches`) and
463
- enqueues it without consulting scheduler activity. A failed store leaves
464
- the key dirty so it is retryable.
465
- A snapshot that vanished from RAM is "stored" only if the SSD index
466
- already covers the prefix (`hasDurablePrefix`), else "missing" and still
467
- dirty. A second flush cannot clear that failure without durable coverage.
468
- - `flush()` cancels timers, awaits in-flight attempts and `drain()`, then
469
- forces each dirty record version once, one at a time.
470
- A replacement scheduled during a write receives its own attempt. After
471
- all writes settle, the coordinator checks missing ancestors against the
472
- committed SSD index again: a later trimmable descendant may now cover a
473
- superseded RAM prefix. Uncovered or foreign-namespace prefixes stay dirty;
474
- this adds no duplicate snapshot write. Result:
475
- `durable` is true only when nothing is pending, nothing dropped or
476
- failed during this flush, and no missing prefix remains uncovered.
477
-
478
- Surfaces: `POST /admin/cache/flush` returns 200 at the boundary (503
479
- otherwise) with pending/dropped/failed/entry-count/longest-prefix
480
- evidence; `mlx-bun serve` handles SIGINT/SIGTERM by stopping admission,
481
- draining active requests, and awaiting the same flush, bounded by
482
- `MLX_BUN_SHUTDOWN_TIMEOUT_MS` (default 120000). `/stats.ssd_cache` exposes
483
- `entries`, `bytes`, `max_bytes`, `restores`, `spills`, `restore_ms_last`,
484
- `demotions`, `pending_snapshots` (includes debounce/re-arm timers),
485
- `pending_spills`, `pending_spill_bytes`, `dropped_spills`,
486
- `failed_spills`, `longest_durable_prefix_tokens`. The standard restart
487
- benchmark calls the endpoint instead of sleeping.
488
-
489
- ### 5.7 In-flight generation checkpoints
490
-
491
- At every configured token interval, `generate()` reaches a boundary where
492
- the live caches cover every emitted token and the next token has already been
493
- sampled but not emitted. The SSD entry stores both pieces atomically:
494
-
495
- - the normal token list and cache tensors encode the emitted prefix;
496
- - `generationCheckpoint` in the KV header records the request key, original
497
- prompt length, emitted-token count, adapter namespace, and pending token.
498
-
499
- Saving the pending token is what makes the mechanism work for Qwen hybrid
500
- models: recurrent `SSMCache` state cannot trim one token to recompute logits.
501
- After restart, an identical request restores the full cache, replays the saved
502
- tokens through the normal completion sink so the client receives one ordinary
503
- assistant response, then continues directly from the pending token with the
504
- original sampler step and history. The request key includes rendered prompt
505
- IDs and every sampling/KV policy field. Grammar, token-fill, media, paged KV,
506
- and logprob requests are not checkpointed in v1 because they carry additional
507
- state or response data that cannot yet be reconstructed by token replay.
508
-
509
- Ordinary continuation adds an optional request port to
510
- that same ordinary driver. Checkpoint lookup, keys and persistence are shared
511
- with the compatibility executor; the numerical driver supplies owned row
512
- snapshots at an emitted-prefix/pending-token boundary. Restored rows complete
513
- preparation without a target forward, before merging cold prefill rows, and
514
- recover sampler seed, history and sample index. The server binding qualifies
515
- ordinary requests through supported row-cache layouts, including recurrent Qwen
516
- and full-attention Llama/MiniCPM as well as Gemma4. It transfers owned row
517
- snapshots to a bounded persistence coordinator without awaiting disk in decode.
518
- Snapshot preparation runs on the owner thread; payload serialization and
519
- writes use the CPU worker. Completion
520
- invalidates reuse immediately and queues cleanup after any in-flight write;
521
- new attempts cannot be erased by older cleanup. Cancellation retains eventual
522
- persistence, and explicit flush reports failed or pending durability. A hard
523
- process exit may lose queued intervals. Plain greedy retains the ordinary
524
- vectorized sampler when no history-dependent processors are active. Supported KV layouts retain their existing affine, per-layer and full-attention
525
- TurboQuant conversion policy. Requests remain without
526
- media, grammar, fill, paging, logprobs or speculative methods. Native tests
527
- compare interrupted/restored runs at the same batch geometry; arbitrary
528
- cohort changes are not an exact numerical identity guarantee. The HTTP fixture
529
- checks serial B1, shared B1 and shared B4 with identical seeded requests and
530
- held arrivals, and inspects the generated checkpoint before restart.
531
- The configured spill queue budget applies to continuation snapshots; normal
532
- prefix persistence follows the RAM cache residency policy. Flush duration and pending/failed
533
- statistics cover both queues.
534
-
535
- Only the newest successful checkpoint for a request is retained. The old file
536
- remains valid until the new file's fsync+rename completes. Queue backpressure
537
- can leave more than one interval undurable; a crash loses work since the latest
538
- successful write. Normal completion immediately invalidates the checkpoint and
539
- queues its disk removal.
540
- Client cancellation throws through the serial token callback and preserves the
541
- last checkpoint, so terminating a long-lived client does not erase its restart
542
- point.
543
-
544
- Fresh-model compiled continuation preserves the device sampler's uint32 pending
545
- register. The Gemma model materializes its computed FP32 RoPE frequencies once
546
- before compiled graph capture: a restored request may skip the prefill that
547
- normally evaluates those constants. Capturing their unevaluated construction
548
- graph can change arithmetic on M1. The forced-token continuation regression
549
- compares a warmed model and a fresh model with cloned state, including every
550
- active cache byte and full logits across delayed KV conversion. Live delayed
551
- row layouts publish compact state through their extraction interface before
552
- persistence; they do not need storage codecs for the live wrapper itself.
553
-
554
- ### Method companion state
555
-
556
- `PrefixCache<State, Attachment>` is the shared consumer contract for ordinary
557
- and batched execution. It has no scheduling or storage-tier operations. The
558
- MLX binding supplies target cache layers plus optional `CheckpointAttachment`
559
- records containing a method schema, scalar metadata and tensor snapshots.
560
- The method owns their meaning and alignment; the shared cache owns retention.
561
- Method and drafter compatibility must be part of the namespace before a
562
- producer publishes these records. Storage does not interpret draft state as
563
- extra target-model layers.
564
-
565
- RAM accounting, eviction, idle demotion and SSD persistence include
566
- companion tensors. Checkpoints containing them require an exact prefix
567
- boundary and preserve shorter fallback entries. Write-behind captures companion
568
- views together with target-cache views under the existing exclusive boundary.
569
- The writer persists them in the same atomic file and the same per-tensor write
570
- loop. Restore copies and verifies them through the existing tensor reader.
571
- Ordinary files retain v3 compatibility; older readers reject v4 files rather
572
- than restore an incomplete checkpoint.
573
-
574
- The storage implementation is in the working tree. Synthetic gates cover RAM
575
- ownership, divergent-prefix fallback, SSD eviction, fresh-process restore,
576
- corruption, queue drops/failures and durability on both development Macs.
577
- Qwen MTP now uses this interface for prefill snapshots; its private store and
578
- separate budget option are removed. `DraftSource.checkpoint` supplies the draft
579
- KV and pending true hidden row. The namespace includes a SHA-256 of draft
580
- configuration and shard bytes, resolved at provider load, plus the mounted
581
- adapter revision and KV policy. Model loading resolves that digest through
582
- `artifactIdentity`, independently of inference and scheduling. A memo key includes
583
- the configuration seed, sorted shard names, resolved paths, device/inode, size,
584
- and nanosecond modification/change times. Unchanged revisions reuse the exact
585
- existing SHA-256; a miss reads every shard byte. Same-size writes with restored
586
- mtime and changed symlink targets invalidate the memo. Rechecking revisions after
587
- hashing prevents persisting a digest under a revision that changed during reading.
588
-
589
- `ArtifactIdentityStore` owns memo retention and persistence. Its file adapter uses
590
- `~/.cache/mlx-bun/artifact-identities/`, keeps RAM hits and queues atomic file writes
591
- without awaiting them in model loading. Missing, malformed or unwritable memo
592
- storage does not prevent computing the identity. It stores only digests, not
593
- weights, prompts or KV. Qwen MTP, standalone, assistant, DeepSpec and DSpark use
594
- the same identity service; existing namespaces and SSD entries remain compatible.
595
- Tests cover byte identity, no weight reads after a fresh-store memo hit, file
596
- mutation, configuration changes, symlink replacement and write failure.
597
-
598
- Native bf16/KV4 gates on M1 Max and M4 Pro
599
- preserve live target/companion state hashes, emitted IDs and acceptance decisions
600
- through repeated RAM reuse and SSD cache/provider restart. SSD allocation
601
- padding is excluded from the live-state comparison. Generated-output acceptance
602
- is described below; long-conversation timing remains R17 work.
603
-
604
- The shared method publishes completed row state before
605
- retirement through the same `PrefixCache.put` port. It records only inputs
606
- retained by committed target/draft rounds; an unprocessed correction or bonus
607
- token is absent from the key. The target row layout and draft provider supply
608
- their snapshots independently. Failed and cancelled consumers publish no
609
- generated checkpoint. Persistence continues through the existing queue.
610
-
611
- Generated BPE IDs are not necessarily the canonical encoding of their decoded
612
- text. `GeneratedTokenHistory` retains bounded text/ID provenance from published
613
- prefixes and recovered SSD headers. Request preparation uses the original IDs
614
- when the rendered conversation contains that exact text prefix, encodes only
615
- the remaining suffix without another BOS, and checks that the combined IDs
616
- decode to the complete rendered request. Edited history or an unsafe decoder
617
- boundary keeps canonical tokenization. KV lookup still requires exact IDs and
618
- the existing execution namespace; no state is relabeled under different IDs.
619
- The template's primer length is measured canonically and applied to the actual
620
- history-preserving token sequence. This can change input token counts, and
621
- therefore continuation logits, relative to re-encoding all generated text.
622
- Logit parity remains a contract for the same input IDs and execution shape.
623
- The stable-boundary probe includes both text and tool replies followed by a
624
- new user turn. Qwen can omit an empty thinking primer when rendering a prior
625
- tool turn; a text-only probe retained three extra tokens in the small-model
626
- HTTP fixture and prevented recurrent-cache reuse. The corrected probe keeps
627
- the last shared prefix. It never rewrites existing KV under the changed text.
628
- Republishing an identical token sequence refreshes its existing provenance
629
- entry without decoding the whole conversation again. Equality covers every ID;
630
- equal text from different IDs still replaces provenance. This avoids repeated
631
- text conversion during request completion and preserves the retention limit.
632
-
633
- Cache pressure maintenance uses 85% of the smaller of the recommended device
634
- working set and an explicit allocator limit as a residency target. It includes
635
- known weight bytes when file-backed weights have not appeared in MLX active
636
- allocation. `PrefixCache.reclaim` asks the cache to demote LRU donors. With SSD,
637
- only durable donors can leave RAM; pending writes remain queued. A selected
638
- request retains its own views. Shared buffer ownership is not counted twice as
639
- RAM plus queued data. Lookup, prefill boundaries and periodic decode maintenance
640
- call this interface; decode checks every 256 steps. It does not constrain
641
- request admission, context, sampling or batch size.
642
-
643
- M1 and M4 native bf16, affine KV4 and fused TurboQuant K8/V3 tests cover four active
644
- rows, early retirement, exact sampled-ID coverage, unchanged retained bytes
645
- after sibling and follow-up work, shorter-prefix selection after an edit, and
646
- matching RAM/SSD continuation and acceptance. Existing paired-prefill tests
647
- also pass. A second native gate records actual sampled IDs for an echo tool
648
- turn with thinking off/on, passes them through the production output parsers,
649
- and renders the subsequent tool-result request with the same chat template.
650
- Both published checkpoints are exact token prefixes of that next request.
651
- A production HTTP gate now passes on both Macs for bf16, KV4 and fused K8/V3, with
652
- thinking off/on. The next tool-result request reuses generated content in RAM.
653
- After a durable flush, a separate server process restores the first turn's
654
- SSD state and reproduces the same response, logprobs and acceptance. The test
655
- copies only the first turn's flushed files before running the RAM continuation,
656
- so no later prefill checkpoint can explain the restart hit. All six cases
657
- pass, including the actual wire `reasoning` alias and template options.
658
- The five-file change is adopted alongside the captured prefill-policy fix.
659
- Native acceptance of that final composition, long saved Kanban replay,
660
- pressure and complete next-turn timing remain open.
661
- It does not resolve the older serial prototype's bf16 comparison with fresh
662
- full prefill, whose operation shapes differ from a generated history.
663
-
664
- ### 5.8 Invalidation
665
-
666
- Compatibility key = `configFingerprint(config)` + `KvScheme.cacheKey` +
667
- tokenizer hash (`Bun.hash(tokenizer.json)`) + adapter ns; all enforced on
668
- scan and load (`KvLoadExpect`). Chat-template drift degrades to a shorter
669
- matched prefix (safe). GLM compressed caches additionally validate
670
- geometry (`kvLoraRank`, `ropeHeadDim`, `dsaHeadDim`, `maxTokens`) against
671
- the model's prototype cache before opening the tensor mmap.
672
-
673
- ### 5.9 Measured (kept with labels)
674
-
675
- - 2026-07-02, M1 Max, MiniCPM5, 13.7k-token prefix, `kv_config` quant:
676
- restart TTFT 12,083 ms (full prefill) → 236 ms restored; steady-state
677
- decode 192.0 vs 192.3 tok/s (flag off/on). Restored + trimmed
678
- continuation identical to the RAM tier's cache-hit output.
679
- - 2026-07-07, streamed-copy restore, 512 MB synthetic entry: mlx peak
680
- during restore + first step = live entry + one tensor (552 vs 520 MB
681
- active); `vmmap` shows no `.mlxkv` mapping survives; 12B real-model cold
682
- cache-load → first token 277 ms.
683
- - 2026-07-07, e4b 9.5k-token entry: repeat decode 37.9 tok/s with the
684
- flush overlapping vs 47.1 cold; flat 44–46 across three samples after
685
- the idle gate.
686
- - 2026-08-22, unified e4b, dirty machine (correctness evidence only):
687
- 4,026-token prompt flushed in 408 ms with `durable=true`, then 4,025
688
- cached tokens restored after SIGTERM + respawn.
689
-
690
- ### 5.9 Open items
691
-
692
- - Full clean 16k e4b / 12B / Qwen3.8 restart matrix (batch 8 and
693
- `--batch 1`, `cached_tokens ≥ prompt_tokens - 1`) is the acceptance run
694
- for the durability fix; only the dirty-machine smoke has run.
695
- - P4 hardening extras never closed: kill-during-write crash test; a
696
- `THIRD_PARTY_LICENSES.md` note if any oMLX-derived code (not just ideas)
697
- ever lands.
698
- - Block-granular dedup for multi-user shared prefixes stays out of scope
699
- (D1); revisit only with the paged follow-ups in 6.4.
700
-
701
- ### 5.10 Comparison with other serving caches
702
-
703
- Reviewed 2026-09-12 against published designs, not private provider internals.
704
-
705
- | System | Algorithm and ownership | Application here |
706
- | --- | --- | --- |
707
- | [Mooncake / Kimi](https://github.com/kvcache-ai/Mooncake), [SSD offload](https://kvcache-ai.github.io/Mooncake/design/store/ssd-offload.html) | A cache service owns RAM/SSD placement. Background offload records a disk replica on successful write; the same lookup can restore from SSD. | One cache API with independent storage work. No application-managed demotion. |
708
- | [SGLang HiCache](https://docs.sglang.io/docs/advanced_features/hicache_design) | A radix tree shares token spans and tracks memory placement. Supports proactive, selective and eviction-triggered writes, plus storage prefetch. Decode output can populate storage. | Keep output-produced checkpoints. Evaluate block sharing and prefetch after the persistence correction. |
709
- | [LMCache MP](https://docs.lmcache.ai/mp/) | Independent store, eviction and prefetch controllers. Watermark-triggered RAM eviction uses LRU; asynchronous storage adapters handle lower tiers. | Separate write completion from residency policy. Keep the storage interface independent of inference. |
710
- | [vLLM prefix caching](https://docs.vllm.ai/en/latest/design/prefix_caching/) | Token blocks use hashes including their preceding prefix. Unused blocks are evicted by recency. | Shared prefix blocks could reduce whole-entry duplication. |
711
- | [Anthropic prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) | Public API documents exact-prefix reuse and expiration. | The same avoided-prefill benefit; this documentation does not establish its internal storage policy. |
712
-
713
- The baseline storage unit is a complete checkpoint. Repeated conversation
714
- snapshots duplicate attention history on SSD. Block deduplication in §5.11
715
- preserves the existing state-kind contract: Qwen recurrent state and method
716
- attachments cannot be trimmed to arbitrary earlier tokens. They need valid
717
- checkpoint boundaries alongside any shared attention blocks. RAM LRU is the
718
- initial policy; §5.11 records the measured frequency-weighting, prefetch and
719
- block-layout alternatives.
720
-
721
- ### 5.11 Cache expansion program
722
-
723
- Authorized scope: implement and measure all five opportunities from §5.10.
724
- Baseline is `c953f2f`, with an independent CPU writer and one RAM/SSD cache.
725
- Preserve existing cache files and explicit serial support. Storage format,
726
- residency policy, transfer work and attention kernels remain independently
727
- selectable for paired comparisons; select defaults from measured results.
728
-
729
- | Step | Implementation | Measurement and completion |
730
- | --- | --- | --- |
731
- | C1 | Immutable content-addressed blocks shared by checkpoint manifests. Attention token spans share storage; recurrent and method state preserve exact boundaries. | Same restored bytes/continuations, atomic publication and shared-block lifetime; bytes written and retained across growing/divergent conversations. |
732
- | C2 | Cache-owned asynchronous reads and preparation-time prefetch with promotion into the existing RAM tier. | Same selected prefix and continuation; restore/TTFT, event-loop delay and concurrent decode against synchronous restore. |
733
- | C3 | Pluggable retention policy, with LRU baseline and an alternative using reuse frequency, state size and saved processing cost. | Replay identical access traces; compare misses, avoided prefill, eviction/reload traffic and peak residency. |
734
- | C4 | Segmented transfers and bounded packing scratch, preserving tensor layout contracts. | Copied bytes, temporary memory, write/read time and inference overlap on the same payload. |
735
- | C5 | Direct paged-attention Metal execution through cache/attention interfaces, including shared row layouts and quantized representations. | Same-B numerical controls, B1/B>1 prefill/decode, gather/padding allocations and complete-request timing. |
736
-
737
- Implementation is in `storage/kv-writer.worker.js` (CPU transfers), `kv-store`
738
- (codec reconstruction), `PromptCache.prefetch` (request preparation),
739
- `storage/retention-policy` (RAM choices), and the paged attention/layout modules.
740
- Format 5 manifests reference SHA-256 content blocks. Block boundaries reset per
741
- leading row so growing token axes preserve earlier head blocks. Blocks are
742
- committed before their manifest; garbage collection is serialized with writes
743
- and removes only content no remaining manifest references. Explicit SSD caps
744
- account unique committed blocks rather than charging every checkpoint for a
745
- shared copy. Existing format 3/4 files remain readable.
746
-
747
- Restore reads use a separate worker queue and page-aligned host allocations.
748
- After reads finish, the owner thread transfers allocation ownership to MLX
749
- through a native destructor; no JavaScript callback runs on GPU completion.
750
- Cache codecs are shared with synchronous restore. Plain KV still uses its
751
- established capacity-growth operation; the async transport does not change
752
- cached values or recurrent/checkpoint boundaries.
753
-
754
- Use the saved Kanban token histories and seeded synthetic branches to separate
755
- cache-policy effects from model-generated changes in task length. Existing
756
- native oracles remain authoritative. Each implementation records paired M4
757
- measurements and applicable M1 correctness; a slower arm stays explicit rather
758
- than being promoted on architectural grounds alone. Raw reports stay under
759
- `reports/`; published measurements belong in benchmarks.md.
760
-
761
- The bounded C1–C5 implementation and measurements are complete. Shared blocks
762
- trade write/read latency for space; segmentation removes packing copies for
763
- contiguous spans. Asynchronous transport overlaps ongoing decode, while a lone
764
- restart request still pays restore completion. Cost-size retention loses on the
765
- saved Kanban boundary replay; direct paged attention has shape-dependent wins
766
- and regressions. The measured selection keeps whole files and LRU, enables
767
- asynchronous preparation, and leaves direct attention and block storage explicit.
768
- [Measurements and reproduction](../reference/benchmarks.md#cache-expansion-c1c5-storage-restore-retention-and-paged-attention)
769
- include the negative results and limits. Full long-task retention acceptance
770
- and the paging extensions below remain separate work.
771
-
772
- ### 5.12 Session checkpoint index
773
-
774
- The cache owns a second index: application session ID plus numerical namespace
775
- maps to its latest immutable checkpoint. Numerical/content identity remains
776
- independent, so session and anonymous requests can share the same state.
777
- The request carries a session hint through the existing cache port's lookup,
778
- preparation and publication calls. Ordinary, speculative and paged methods
779
- forward the hint; scheduling does not own the index or residency decisions.
780
-
781
- A matching resident checkpoint bypasses candidate scans. For an evicted known
782
- checkpoint, the SSD exact index selects its manifest before asynchronous
783
- preparation adopts state through the existing codecs. Hashes accelerate SSD
784
- selection; the selected token sequence still establishes identity. A missing
785
- or incompatible session checkpoint uses ordinary prefix matching, allowing
786
- history edits, branches and retries without rejecting the request.
787
-
788
- New publications advance the session's reference. Explicit sessions also retain
789
- ordinary generated checkpoints below the anonymous publication threshold.
790
- RAM eviction first considers
791
- entries with no session references, then applies the configured policy to
792
- referenced entries when needed. This is a preference, not an unlimited memory
793
- pin. Request-scoped preparation interests retain their existing short-lived
794
- ownership. Closing a session removes its references without deleting cached
795
- state; clearing the cache removes the session index. Session metadata is
796
- process-local, and restart rebuilds associations through ordinary SSD hits.
797
-
798
- The current API still accepts full histories. This change removes candidate
799
- search and supplies retention information; it does not claim delta-only input,
800
- zero tokenization, or automatic reconstruction from a session label. The Pi
801
- provider passes the session identity its application already owns.
802
- The index, native retention and fixed-input M4 HTTP comparisons are complete.
803
- Lookup and reload work decrease; warmed model throughput is effectively
804
- unchanged. The full Kanban task now completes with all follow-up session hits
805
- and a durable final flush; independent app acceptance retains two defects.
806
- [Measurements](../reference/benchmarks.md#session-checkpoint-index-c6) preserve
807
- both the benefits and the limits. Existing idle-demotion settings remain in
808
- effect; session preference changes budget/pressure victim selection.
809
-
810
-
811
- ### 5.13 Exact tensor objects in the shared cache
812
-
813
- `ObjectCache<Value>` exposes exact-key lookup and ownership transfer separately
814
- from `PrefixCache` matching. `PromptCache.objects` uses the same resident records,
815
- byte accounting, retention policy, pressure callback and SSD durability queue.
816
- An object has an empty conversation history, no target KV layers and a private
817
- namespace containing its full key. Its versioned tensor attachment owns the
818
- payload. It never supplies a language-model prefix. No synthetic language token
819
- IDs or second RAM budget are introduced.
820
-
821
- RAM reads lend immutable native views. Evicting the resident entry cannot
822
- invalidate a request's borrowed tensors. A cold read uses the existing async
823
- serializer with an empty target-layer schema, outside the execution queue;
824
- publication joins the same RAM policy. Cache clear invalidates pending restore
825
- publication. A newer resident publication wins over an older in-flight restore.
826
- The durability coordinator includes attachment-only objects; RAM eviction waits
827
- for the shared writer to finish as it does for conversation checkpoints.
828
-
829
- The Qwen encoder adapter hashes exact preprocessed pixel bytes, grid dimensions
830
- and its schema revision. A SHA-256 of the actual loaded encoder tensors supplies weight identity,
831
- computed once under native preparation. The containing SSD store also supplies
832
- configuration, tokenizer and native numerical identity. Different image bytes or grids miss;
833
- repeated media can reuse the tensor across request histories. Prompt rendering,
834
- video timestamps, mRoPE positions, sampling and scheduling remain independent.
835
- The producer retains its own result while storage takes a separate native view.
836
- The object lookup finishes before the prompt builder enters the native queue.
837
- Preprocessing and video extraction are not cached by this change.
838
-
839
- Gemma's encoder-free and SigLIP vision towers and its audio tower use the same
840
- `EncoderCache` adapter. Each tower hashes its immutable weight names, shapes,
841
- dtypes, bytes and computation settings once. Image/WAV content hashes and a
842
- producer revision identify individual objects. Vision retains its existing
843
- pre-divided features; audio retains raw float32 features, then each prompt casts
844
- them to its embedding dtype before dividing by the embedding scale. That order
845
- is unchanged. CPU image preprocessing and audio mel extraction precede the
846
- native execution lease, as do RAM/SSD lookups. The producer never caches chat
847
- tokens, bidirectional masks or positions. Custom vision encoders without an
848
- immutable `cacheIdentity` continue to encode normally.
849
-
850
- [vLLM's encoder cache](https://docs.vllm.ai/en/v0.18.0/api/vllm/v1/core/encoder_cache_manager/)
851
- also identifies individual media items by hash and retains encoder outputs
852
- across requests. Here the existing cache owns both RAM residency and queued
853
- SSD persistence on unified memory. The bounded Qwen screen and integrated
854
- acceptance pass on M4; both-machine native/HTTP checks and fresh-process SSD
855
- reuse preserve their recorded outputs. [Measurements](../reference/benchmarks.md#qwen-encoder-reuse-through-shared-ram-and-ssd-storage-2026-09-13)
856
- include the one-time weight-fingerprint cost. Gemma prepared inputs and mixed
857
- RAM/SSD HTTP reuse pass both-machine identity checks. Its M4 ABBA preserves all
858
- 168 paired responses/usage and improves every measured workload; first-use
859
- cost and changed image overlap are recorded in the
860
- [Gemma measurements](../reference/benchmarks.md#gemma-encoder-reuse-through-shared-ram-and-ssd-storage-2026-09-13).
861
- Prepared media conversation state uses the same cache, as described below.
862
-
863
-
864
- ### 5.14 Prepared media prefix identity
865
-
866
- The Gemma producer can supply a versioned `prefixIdentity` containing every
867
- media item's exact input/encoder identity, its causal policy, and the rendered
868
- token prefix through the last media span. The ordinary cache namespace combines
869
- this identity with the adapter namespace. Consequently, matching histories
870
- include identical media and preceding tokens; a changed media set or a change
871
- from image-only bidirectional attention to mixed causal input cannot collide.
872
- Appending later text leaves the identity stable.
873
-
874
- By default, the shared Gemma binding uses ordinary
875
- RAM/SSD lookup, generated snapshots, prefetch and persistence. The prepared-input
876
- interface receives the cache's starting position. An uncached input performs
877
- its atomic media prefill; a restored prefix resumes text after all media through
878
- the model's ordinary forward method. Scheduling and sampling are unchanged.
879
- Qwen supplies the same opaque identity with prepared image/video hashes,
880
- rendered timestamps and all mRoPE positions through its final media span. Its
881
- resumed text forward uses the current request positions and preserves the
882
- recurrent state. Recurrent layers still require an exact retained prefix;
883
- explicit serial prefix reuse remains unqualified. `MLX_BUN_MEDIA_PREFIX_CACHE=0`
884
- selects encoder-only reuse for comparisons.
885
-
886
- The existing minimum reusable offset still identifies a precision transition.
887
- If delayed quantization occurs after an atomic media prefill, its cached state
888
- can serve a later conversation turn but cannot rewind before that conversion.
889
- A shorter request recomputes normally. Start-zero affine/TurboQuant and bf16
890
- retain their applicable prefix-trimming behavior. This is cache selection,
891
- not request admission or a memory refusal.
892
-
893
-
894
- ## 6. Optional paged KV
895
-
896
- `PagedKVCache` stores full-attention planes in per-request block pools.
897
- `PagedKvRows` composes those pools through the existing row-state interface.
898
- Scheduling sees request state and attention ports, not block tables or codecs.
899
- Flags, defaults and supported combinations are documented in
900
- [server-config.md](../reference/server-config.md).
901
-
902
- ### 6.1 Storage and ownership
903
-
904
- Pools hold bf16 planes or affine packed/scales/biases planes. New tokens alone
905
- are quantized and written into their destination blocks. Logical block tables
906
- map sequence positions to physical slots. Trimming returns unused tail blocks;
907
- restored prefixes can grow beyond their original request's capacity.
908
-
909
- Published attention views, cache borrowers and persistence jobs own independent
910
- MLX handles. A subsequent functional append cannot overwrite their buffers.
911
- The backend's `paged-cache-codec` preserves block tables, encoded planes and
912
- layout metadata through the common RAM/SSD cache. `namespacedCache` separates
913
- paged numerical settings from ordinary prefixes. Neither persistence nor a
914
- successful write removes a RAM donor.
915
-
916
- The current pools are separate per request. Their snapshots share immutable
917
- MLX storage, but divergent writes can detach a whole pool. Shared physical
918
- arenas with per-block copy-on-write remain a further optimization.
919
-
920
- ### 6.2 Attention
921
-
922
- The established attention arm gathers blocks and uses stock SDPA. Its bf16
923
- storage-layout contract remains bit-exact against plain KVCache.
924
-
925
- The optional direct Metal arm reads block tables for short causal queries.
926
- Each SIMD group processes one query/head/sequence partition, retaining a
927
- softmax maximum, denominator and partial value vector. A second kernel merges
928
- those partials. Affine variants unpack KV4/KV8 values in the read operation;
929
- no full-history dequantization or contiguous KV gather is materialized.
930
- Shared rows retain their own lengths, so direct attention does not pad KV to
931
- the longest row. Long prefill and explicit masks use the established SDPA
932
- shape dispatch through the same attention view.
933
-
934
- This changes reduction order. Direct attention is Lab, with numerical tolerance
935
- checks against the same encoded values; it is not a bit-exact mlx-lm claim.
936
- Tests cover unequal K/V dimensions, grouped heads, partial blocks, multiple
937
- queries, row retirement, immutable views and restored growth. Performance
938
- measurements retain both short-context losses and long-context wins in
939
- [benchmarks.md](../reference/benchmarks.md).
940
-
941
- ### 6.3 Remaining extensions
942
-
943
- TurboQuant page encoding, paged speculative transactions, media/adapter paging
944
- and shared physical arenas are outside the implemented bf16/affine page layout.
945
- Existing non-paged TurboQuant and speculative RAM/SSD state remain supported
946
- through their own codecs and attention interfaces. Extending a page codec or
947
- kernel does not require a second scheduler.
948
-
949
- ## History
950
-
951
- - 2026-06-14 — `BatchedRotatingCache` (sliding-window batching) landed.
952
- - 2026-07-02 — SSD cold tier P1–P3 landed: kv-store v2 (all cache kinds,
953
- invalidation metadata, atomic writes), `SsdCacheStore`, tier
954
- integration, flags.
955
- - 2026-07-04 — prompt-boundary snapshot (multi-turn agent reuse fix).
956
- - 2026-07-05 — non-consuming prefix sharing in `take()`; Layer-0 tiering
957
- moved inside `PromptCache.take()` (both lanes); idle demotion
958
- (`--ssd-demote-idle`).
959
- - 2026-07-06 — TurboQuant v1 cache class + kv-store `turboquant` kind;
960
- RAM cap default 2 GB → 8 GB; non-blocking `spillOwned` spills; `find()`
961
- trimmability gate (restart-0 defect).
962
- - 2026-07-07 — streamed-copy restore + zero-copy write view (A7 RSS);
963
- idle-gated write-behind (`MLX_BUN_SSD_WRITEBEHIND`); bounded
964
- `SpillQueue` (`MLX_BUN_SSD_SPILL_QUEUE_GB`); admission-safe snapshot
965
- timer.
966
- - Phase 21 (closed 2026-08-17) — GLM `mla` / `mla-dsa` / `mtp-mla`
967
- kv-store kinds.
968
- - 2026-08-21 — `KvScheme` seam: immutable scheme drives conversion,
969
- admission billing, SSD key, and batch placement.
970
- - 2026-08-22 — agg×4 regression root-caused to the unsigned
971
- `BatchedRotatingCache` (capability-only routing fix, signature added);
972
- `SsdDurabilityCoordinator`, `POST /admin/cache/flush`, graceful
973
- SIGINT/SIGTERM flush.
974
- - Paged KV v1 (serial, Gemma4, bf16, gather-then-SDPA) landed with
975
- bit-exact gates; flag rows in cli.md / server-config.md (landing date
976
- not recorded in the source doc, which still reads "in progress").
977
-
978
- ## Minimum reusable offsets
979
-
980
- Physical trimmability does not prove that an older prefix has a valid
981
- numerical representation. Caches may expose `minimumReusableOffset` after
982
- an irreversible transition, such as delayed TurboQuant conversion. Prefix
983
- selection requires the matched offset to meet every layer's bound. A
984
- trimmable descendant supersedes an ancestor only when it can serve that
985
- ancestor's offset; otherwise the earlier donor remains.
986
-
987
- Cloning and per-row extraction preserve this state metadata. New SSD
988
- headers record it, and indexing, durable-prefix checks and restore use the
989
- same rule as RAM. Legacy TurboQuant headers without a recorded boundary
990
- are conservatively reusable from their full stored offset only. Other
991
- legacy cache kinds retain their existing interpretation. This changes
992
- reuse eligibility, not scheduling, quantization arithmetic or memory
993
- admission policy.