mlx-bun 0.4.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 (601) hide show
  1. package/README.md +1453 -88
  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 +10 -8
  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 +12 -10
  78. package/src/{serve → server}/chat-stage.ts +27 -22
  79. package/src/{client.ts → server/client.ts} +28 -2
  80. package/src/{serve → server}/completion-executor.ts +42 -42
  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 +42 -83
  85. package/src/server/generated-token-history.ts +57 -0
  86. package/src/{serve → server}/http.ts +25 -13
  87. package/src/{serve → server}/inference-request.ts +5 -5
  88. package/src/server/job-routes.ts +20 -0
  89. package/src/server/media-prompt.ts +63 -0
  90. package/src/server/memory-completion-client.ts +46 -0
  91. package/src/server/model-routes.ts +192 -0
  92. package/src/{serve → server}/openai-wire.ts +3 -2
  93. package/src/{serve → server}/pipeline.ts +1 -1
  94. package/src/server/prompt-builder.ts +13 -0
  95. package/src/server/prompt-contracts.ts +17 -0
  96. package/src/server/proxy-routes.ts +187 -0
  97. package/src/server/publishing-routes.ts +41 -0
  98. package/src/{serve → server}/request-plan.ts +8 -8
  99. package/src/{serve → server}/request-prep.ts +82 -68
  100. package/src/{serve → server}/responses-client.ts +30 -13
  101. package/src/{responses.ts → server/responses.ts} +12 -93
  102. package/src/server/routes.ts +205 -0
  103. package/src/server/start.ts +72 -0
  104. package/src/server/status-routes.ts +108 -0
  105. package/src/{serve → server}/text-completion-stage.ts +25 -4
  106. package/src/server/text-prompt.ts +12 -0
  107. package/src/{serve → server}/token-streams.ts +36 -38
  108. package/src/server/worker-routes.ts +311 -0
  109. package/src/storage/paths.ts +61 -0
  110. package/src/web/assets.ts +37 -0
  111. package/src/web/{src → browser}/api.ts +5 -23
  112. package/src/web/browser/chat-host.ts +17 -0
  113. package/src/web/browser/installed-panels.ts +6 -0
  114. package/src/web/browser/main.ts +55 -0
  115. package/src/web/browser/markdown.ts +279 -0
  116. package/src/web/browser/memory-panel.ts +9 -0
  117. package/src/web/browser/palette.ts +64 -0
  118. package/src/web/browser/protocol.ts +13 -0
  119. package/src/web/browser/shell.ts +467 -0
  120. package/src/web/{src → browser}/status.ts +32 -36
  121. package/src/web/build.ts +51 -0
  122. package/src/web/public/app.html +1054 -0
  123. package/src/web/{sw.js → public/sw.js} +4 -3
  124. package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
  125. package/src/web/{vendor → public/vendor}/hljs.js +2 -2
  126. package/THIRD_PARTY_LICENSES.md +0 -51
  127. package/docs/README.md +0 -94
  128. package/docs/archive/hlg-sampling.md +0 -350
  129. package/docs/archive/investigations/audio-input-plan.md +0 -356
  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/web-ui-pass-plan.md +0 -213
  174. package/docs/archive/planning/website-readme-pass-plan.md +0 -224
  175. package/docs/design/batching.md +0 -1841
  176. package/docs/design/decode-speed-program.md +0 -2349
  177. package/docs/design/dreaming-nightly-pipeline.md +0 -684
  178. package/docs/design/generic-model-support.md +0 -705
  179. package/docs/design/kv-cache.md +0 -824
  180. package/docs/design/orpo-training.md +0 -680
  181. package/docs/design/speculative-decoding.md +0 -1990
  182. package/docs/design/turboquant.md +0 -2212
  183. package/docs/design/unified-engine-frontier-plan.md +0 -1880
  184. package/docs/design/web-chat-redesign.md +0 -695
  185. package/docs/planning/PRODUCT_ROADMAP.md +0 -594
  186. package/docs/planning/ResearchTopics.md +0 -119
  187. package/docs/planning/release-notes-v0.4.0.md +0 -45
  188. package/docs/planning/video-series.md +0 -421
  189. package/docs/reference/benchmarks.md +0 -3613
  190. package/docs/reference/cli.md +0 -585
  191. package/docs/reference/distribution.md +0 -355
  192. package/docs/reference/environment.md +0 -398
  193. package/docs/reference/glossary.md +0 -39
  194. package/docs/reference/library-api.md +0 -674
  195. package/docs/reference/memory.md +0 -260
  196. package/docs/reference/models.md +0 -334
  197. package/docs/reference/server-api.md +0 -1775
  198. package/docs/reference/server-config.md +0 -846
  199. package/docs/reference/training.md +0 -462
  200. package/docs/reference/troubleshooting.md +0 -158
  201. package/src/admission-plan.ts +0 -18
  202. package/src/audio/conformer.ts +0 -647
  203. package/src/audio/decode.ts +0 -133
  204. package/src/audio/features.ts +0 -202
  205. package/src/audio/transcode.ts +0 -67
  206. package/src/backends/mlx/assistant-target.ts +0 -46
  207. package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
  208. package/src/backends/mlx/autoregressive.ts +0 -108
  209. package/src/backends/mlx/batch-group.ts +0 -1790
  210. package/src/backends/mlx/cache-attachment.ts +0 -67
  211. package/src/backends/mlx/cache-layout.ts +0 -31
  212. package/src/backends/mlx/checkpoint-state.ts +0 -53
  213. package/src/backends/mlx/continuation-persistence.ts +0 -106
  214. package/src/backends/mlx/continuation-request.ts +0 -43
  215. package/src/backends/mlx/continuation.ts +0 -77
  216. package/src/backends/mlx/diffusion.ts +0 -39
  217. package/src/backends/mlx/draft-target.ts +0 -32
  218. package/src/backends/mlx/evaluation.ts +0 -105
  219. package/src/backends/mlx/fill-append.ts +0 -45
  220. package/src/backends/mlx/full-kv-row-append.ts +0 -32
  221. package/src/backends/mlx/full-kv-row-donor.ts +0 -54
  222. package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
  223. package/src/backends/mlx/gateway-binding.ts +0 -130
  224. package/src/backends/mlx/graph.ts +0 -38
  225. package/src/backends/mlx/kv-codec.ts +0 -9
  226. package/src/backends/mlx/kv-maintenance.ts +0 -133
  227. package/src/backends/mlx/kv-tensor-rows.ts +0 -212
  228. package/src/backends/mlx/memory-client.ts +0 -189
  229. package/src/backends/mlx/methods.ts +0 -90
  230. package/src/backends/mlx/model-prompt.ts +0 -164
  231. package/src/backends/mlx/model-serving.ts +0 -79
  232. package/src/backends/mlx/prefill-cohort.ts +0 -89
  233. package/src/backends/mlx/prefill-rows.ts +0 -200
  234. package/src/backends/mlx/prefill.ts +0 -33
  235. package/src/backends/mlx/projected-context-rows.ts +0 -72
  236. package/src/backends/mlx/request-policy.ts +0 -20
  237. package/src/backends/mlx/request-state-policy.ts +0 -41
  238. package/src/backends/mlx/rollback.ts +0 -89
  239. package/src/backends/mlx/row-sampling.ts +0 -49
  240. package/src/backends/mlx/serial-executor.ts +0 -242
  241. package/src/backends/mlx/speculative-group.ts +0 -359
  242. package/src/backends/mlx/speculative-round.ts +0 -161
  243. package/src/backends/mlx/speculative.ts +0 -114
  244. package/src/backends/mlx/state-rows.ts +0 -104
  245. package/src/backends/mlx/state-views.ts +0 -46
  246. package/src/backends/mlx/target-layout-capability.ts +0 -35
  247. package/src/backends/mlx/text-engine.ts +0 -73
  248. package/src/backends/mlx/transitioning-kv-rows.ts +0 -165
  249. package/src/chat-template.ts +0 -382
  250. package/src/cli.ts +0 -3168
  251. package/src/config.ts +0 -399
  252. package/src/contracts/completion.ts +0 -16
  253. package/src/contracts/execution.ts +0 -60
  254. package/src/contracts/generation.ts +0 -103
  255. package/src/contracts/host.ts +0 -7
  256. package/src/contracts/pi-provider.ts +0 -30
  257. package/src/contracts/pi.ts +0 -259
  258. package/src/contracts/prefix-cache.ts +0 -23
  259. package/src/contracts/resources.ts +0 -25
  260. package/src/contracts/sampling.ts +0 -23
  261. package/src/contracts/scheduling.ts +0 -45
  262. package/src/contracts/tsconfig.json +0 -14
  263. package/src/dataset/generators.ts +0 -1087
  264. package/src/dataset/index.ts +0 -12
  265. package/src/dataset/job.ts +0 -81
  266. package/src/dataset/llm.ts +0 -71
  267. package/src/dataset/registry.ts +0 -528
  268. package/src/diffusion/diffusion-generate.ts +0 -507
  269. package/src/download.ts +0 -505
  270. package/src/embed.ts +0 -77
  271. package/src/engine/admission.ts +0 -59
  272. package/src/engine/cancellation.ts +0 -38
  273. package/src/engine/engine.ts +0 -56
  274. package/src/engine/execution-coordinator.ts +0 -70
  275. package/src/engine/execution-plan.ts +0 -53
  276. package/src/engine/resources.ts +0 -53
  277. package/src/engine/scheduler.ts +0 -72
  278. package/src/engine/session.ts +0 -247
  279. package/src/eval/README.md +0 -130
  280. package/src/eval/ifeval.ts +0 -81
  281. package/src/eval/kl-prompts.ts +0 -29
  282. package/src/eval/kl.ts +0 -590
  283. package/src/eval/perplexity.ts +0 -164
  284. package/src/eval/quality-db.ts +0 -92
  285. package/src/eval/reference-logits.ts +0 -207
  286. package/src/eval/runner.ts +0 -124
  287. package/src/eval/score.ts +0 -49
  288. package/src/eval/tasks/bfcl.ts +0 -535
  289. package/src/eval/tasks/gsm8k.ts +0 -98
  290. package/src/eval/tasks/hashhop.ts +0 -139
  291. package/src/eval/tasks/humaneval.ts +0 -275
  292. package/src/eval/tasks/ifeval.ts +0 -525
  293. package/src/eval/tasks/mmlu.ts +0 -157
  294. package/src/eval/ultrafeedback.ts +0 -60
  295. package/src/evaldb.ts +0 -199
  296. package/src/expert-io.ts +0 -393
  297. package/src/expert-offload-build.ts +0 -77
  298. package/src/expert-offload.ts +0 -76
  299. package/src/expert-residency.ts +0 -1134
  300. package/src/expert-usage.ts +0 -415
  301. package/src/fill/echo-index.ts +0 -188
  302. package/src/fill/fill-session.ts +0 -452
  303. package/src/fill/proposal.ts +0 -68
  304. package/src/fill/schema-rows.ts +0 -506
  305. package/src/fill/tool-boundary.ts +0 -103
  306. package/src/fit.ts +0 -249
  307. package/src/generate.ts +0 -1346
  308. package/src/grammar.ts +0 -591
  309. package/src/harness-pi.ts +0 -184
  310. package/src/hf-push.ts +0 -628
  311. package/src/hub-rest.ts +0 -242
  312. package/src/index.ts +0 -63
  313. package/src/inference/denoising.ts +0 -17
  314. package/src/inference/draft-acceptance.ts +0 -41
  315. package/src/inference/draft-output.ts +0 -49
  316. package/src/inference/graph.ts +0 -33
  317. package/src/inference/prefill.ts +0 -39
  318. package/src/inference/rollback.ts +0 -7
  319. package/src/jobs/index.ts +0 -31
  320. package/src/jobs/job-entry.ts +0 -107
  321. package/src/jobs/task-client.ts +0 -19
  322. package/src/jobs/types.ts +0 -2
  323. package/src/kv-scheme.ts +0 -257
  324. package/src/kv-store.ts +0 -1099
  325. package/src/lab/README.md +0 -18
  326. package/src/lab/curve/curve-designer.html +0 -303
  327. package/src/lab/curve/curve-sampler.ts +0 -122
  328. package/src/lab/expert-trace/expert-trace.ts +0 -69
  329. package/src/lab/paged-kv/paged-kv-rows.ts +0 -83
  330. package/src/lab/paged-kv/paged-kv.ts +0 -270
  331. package/src/library.ts +0 -40
  332. package/src/lora.ts +0 -408
  333. package/src/md-modules.d.ts +0 -49
  334. package/src/media-fetch.ts +0 -442
  335. package/src/memory/article.ts +0 -558
  336. package/src/memory/chunk-validation.ts +0 -150
  337. package/src/memory/chunk.ts +0 -318
  338. package/src/memory/cluster.ts +0 -351
  339. package/src/memory/coverage.ts +0 -134
  340. package/src/memory/crosslink.ts +0 -0
  341. package/src/memory/db.ts +0 -253
  342. package/src/memory/entity.ts +0 -203
  343. package/src/memory/gate.ts +0 -187
  344. package/src/memory/history.ts +0 -152
  345. package/src/memory/ingest.ts +0 -335
  346. package/src/memory/model.ts +0 -183
  347. package/src/memory/normalize.ts +0 -671
  348. package/src/memory/parse.ts +0 -22
  349. package/src/memory/pipeline.ts +0 -275
  350. package/src/memory/prompts.ts +0 -65
  351. package/src/memory/query.ts +0 -334
  352. package/src/memory/reconcile.ts +0 -734
  353. package/src/memory/redirect.ts +0 -108
  354. package/src/memory/reindex.ts +0 -325
  355. package/src/memory/resolve.ts +0 -307
  356. package/src/memory/rest.ts +0 -245
  357. package/src/memory/route.ts +0 -272
  358. package/src/memory/schedule.ts +0 -165
  359. package/src/memory/stages.ts +0 -777
  360. package/src/memory/synthesize.ts +0 -1638
  361. package/src/memory/tools.ts +0 -749
  362. package/src/memory/vault.ts +0 -1194
  363. package/src/memory/wikify.ts +0 -1064
  364. package/src/mlx/array.ts +0 -388
  365. package/src/mlx/autograd.ts +0 -304
  366. package/src/mlx/checkpoint.ts +0 -149
  367. package/src/mlx/compile.ts +0 -168
  368. package/src/mlx/custom-vjp.ts +0 -188
  369. package/src/mlx/ffi.ts +0 -512
  370. package/src/mlx/materialize.ts +0 -23
  371. package/src/mlx/metal-kernel.ts +0 -159
  372. package/src/mlx/ops.ts +0 -1021
  373. package/src/mlx/quantized-rows.ts +0 -36
  374. package/src/mlx/token-bitmask.ts +0 -52
  375. package/src/mlx/turboquant-kv-decode.ts +0 -82
  376. package/src/mlx/turboquant-kv-inverse.ts +0 -122
  377. package/src/mlx/turboquant-ops.ts +0 -861
  378. package/src/mlx/turboquant-tables.ts +0 -237
  379. package/src/mmap.ts +0 -90
  380. package/src/model/artifact-identity.ts +0 -73
  381. package/src/model/batched-kv.ts +0 -235
  382. package/src/model/batched-mask.ts +0 -279
  383. package/src/model/batched-quant.ts +0 -212
  384. package/src/model/batched-quantized-kv.ts +0 -144
  385. package/src/model/batched-rotating-quant.ts +0 -353
  386. package/src/model/batched-rotating-state.ts +0 -149
  387. package/src/model/batched-rotating.ts +0 -338
  388. package/src/model/batched-row-storage.ts +0 -179
  389. package/src/model/batched-ssm.ts +0 -52
  390. package/src/model/batched-turboquant-kv.ts +0 -93
  391. package/src/model/compiled-decode.ts +0 -712
  392. package/src/model/decoded-kv-donor.ts +0 -12
  393. package/src/model/delayed-quantized-kv.ts +0 -72
  394. package/src/model/delayed-rotating-quantized-kv.ts +0 -93
  395. package/src/model/delayed-turboquant-kv.ts +0 -55
  396. package/src/model/diffusion-gemma.ts +0 -1026
  397. package/src/model/factory.ts +0 -189
  398. package/src/model/fingerprint.ts +0 -33
  399. package/src/model/flash-attention.ts +0 -694
  400. package/src/model/full-prefill-padding.ts +0 -56
  401. package/src/model/full-prefill-row.ts +0 -105
  402. package/src/model/gemma4-base.ts +0 -2351
  403. package/src/model/gemma4.ts +0 -1315
  404. package/src/model/generated/gemma4-12b.ts +0 -429
  405. package/src/model/generated/gemma4-26b.ts +0 -448
  406. package/src/model/generated/gemma4-e4b.ts +0 -717
  407. package/src/model/generated/index.ts +0 -18
  408. package/src/model/glm52-atlas.ts +0 -511
  409. package/src/model/glm52-cache.ts +0 -792
  410. package/src/model/glm52-config.ts +0 -262
  411. package/src/model/glm52-container.ts +0 -305
  412. package/src/model/glm52-coupling.ts +0 -388
  413. package/src/model/glm52-dsa.ts +0 -606
  414. package/src/model/glm52-expert-layout.ts +0 -253
  415. package/src/model/glm52-layout.ts +0 -152
  416. package/src/model/glm52-memory.ts +0 -500
  417. package/src/model/glm52-mla.ts +0 -792
  418. package/src/model/glm52-moe.ts +0 -223
  419. package/src/model/glm52-pilot.ts +0 -537
  420. package/src/model/glm52-reference.ts +0 -445
  421. package/src/model/glm52-residency.ts +0 -557
  422. package/src/model/glm52-resident-weights.ts +0 -135
  423. package/src/model/glm52-streamed-experts.ts +0 -453
  424. package/src/model/glm52-streamed-metal.ts +0 -610
  425. package/src/model/glm52-weights.ts +0 -503
  426. package/src/model/glm52.ts +0 -1132
  427. package/src/model/implementation.ts +0 -69
  428. package/src/model/kv-attention-view.ts +0 -67
  429. package/src/model/minicpm5.ts +0 -376
  430. package/src/model/profile.ts +0 -443
  431. package/src/model/qwen-append.ts +0 -12
  432. package/src/model/qwen-conv.ts +0 -52
  433. package/src/model/qwen3-delta-state.ts +0 -74
  434. package/src/model/qwen3-delta.ts +0 -451
  435. package/src/model/qwen3-moe.ts +0 -404
  436. package/src/model/qwen3-mrope.ts +0 -252
  437. package/src/model/qwen3.ts +0 -277
  438. package/src/model/qwen3_5-checkpoint.ts +0 -164
  439. package/src/model/qwen3_5.ts +0 -819
  440. package/src/model/rotating-kv-donor.ts +0 -35
  441. package/src/model/rotating-kv-layout.ts +0 -262
  442. package/src/model/rotating-row-transaction.ts +0 -35
  443. package/src/model/speculative-rotating-kv.ts +0 -101
  444. package/src/model/ssm-prefill-padding.ts +0 -39
  445. package/src/model/support.ts +0 -102
  446. package/src/model/trellis-balanced-scatter.ts +0 -51
  447. package/src/model/trellis-linear.ts +0 -672
  448. package/src/model/trellis-shared-m.ts +0 -151
  449. package/src/model/trellis-shared-scatter.ts +0 -144
  450. package/src/model/trellis-splitk-prefill.ts +0 -158
  451. package/src/model/trellis-tiled-prefill.ts +0 -136
  452. package/src/model/trellis-vector-expand.ts +0 -62
  453. package/src/model/trellis-wide-prefill.ts +0 -94
  454. package/src/model/turboquant-codec.ts +0 -83
  455. package/src/model/universal/archs.ts +0 -332
  456. package/src/model/universal/dense.ts +0 -612
  457. package/src/model/universal/modules.ts +0 -170
  458. package/src/model/universal/rope.ts +0 -381
  459. package/src/native/expert_io.c +0 -523
  460. package/src/native/frame_extract.swift +0 -140
  461. package/src/native-pack.ts +0 -162
  462. package/src/pi-provider.ts +0 -53
  463. package/src/pi-session.ts +0 -81
  464. package/src/pi-terminal.ts +0 -274
  465. package/src/pi-web.ts +0 -1932
  466. package/src/preflight.ts +0 -158
  467. package/src/prompt-cache.ts +0 -460
  468. package/src/quantize/allocator.ts +0 -627
  469. package/src/quantize/atomic-output.ts +0 -38
  470. package/src/quantize/calibration.ts +0 -181
  471. package/src/quantize/config-writer.ts +0 -145
  472. package/src/quantize/index.ts +0 -84
  473. package/src/quantize/job.ts +0 -162
  474. package/src/quantize/quantizer.ts +0 -666
  475. package/src/quantize/rotate.ts +0 -603
  476. package/src/quantize/safetensors-writer.ts +0 -249
  477. package/src/quantize/sensitivity.ts +0 -216
  478. package/src/quantize/trellis.ts +0 -586
  479. package/src/quantize/weight-transform.ts +0 -292
  480. package/src/registry.ts +0 -602
  481. package/src/runtime-config.ts +0 -81
  482. package/src/safetensors.ts +0 -158
  483. package/src/sampler.ts +0 -1011
  484. package/src/serve/admin-routes.ts +0 -184
  485. package/src/serve/assets/curve-terrain.html +0 -105
  486. package/src/serve/assets/training-inference-map.html +0 -575
  487. package/src/serve/aux-routes.ts +0 -132
  488. package/src/serve/batch-scheduler.ts +0 -8
  489. package/src/serve/checkpoint-identity.ts +0 -64
  490. package/src/serve/generation-gateway.ts +0 -496
  491. package/src/serve/isolate.ts +0 -609
  492. package/src/serve/kv-budget.ts +0 -14
  493. package/src/serve/lab-routes.ts +0 -341
  494. package/src/serve/lane-registry.ts +0 -45
  495. package/src/serve/model-admin-routes.ts +0 -155
  496. package/src/serve/model-binding.ts +0 -43
  497. package/src/serve/model-host.ts +0 -511
  498. package/src/serve/parent-application.ts +0 -56
  499. package/src/serve/prompt-response-trace.ts +0 -147
  500. package/src/serve/response-lease.ts +0 -24
  501. package/src/serve/session-search.ts +0 -326
  502. package/src/serve/static-routes.ts +0 -101
  503. package/src/server.ts +0 -1314
  504. package/src/spec/assistant-rows.ts +0 -92
  505. package/src/spec/assistant-source.ts +0 -126
  506. package/src/spec/cache-identity.ts +0 -22
  507. package/src/spec/deepspec-rows.ts +0 -29
  508. package/src/spec/deepspec-source.ts +0 -145
  509. package/src/spec/dflash-source.ts +0 -149
  510. package/src/spec/drafter.ts +0 -304
  511. package/src/spec/dspark/ab-stats.ts +0 -136
  512. package/src/spec/dspark/calibration.ts +0 -112
  513. package/src/spec/dspark/data-dflash.ts +0 -156
  514. package/src/spec/dspark/data.ts +0 -201
  515. package/src/spec/dspark/deepspec-module.ts +0 -776
  516. package/src/spec/dspark/generate-dflash.ts +0 -245
  517. package/src/spec/dspark/generate.ts +0 -290
  518. package/src/spec/dspark/loader.ts +0 -23
  519. package/src/spec/dspark/loss.ts +0 -213
  520. package/src/spec/dspark/module-dflash.ts +0 -560
  521. package/src/spec/dspark/module.ts +0 -540
  522. package/src/spec/dspark/quantize-drafter.ts +0 -91
  523. package/src/spec/dspark/sample.ts +0 -112
  524. package/src/spec/generate.ts +0 -276
  525. package/src/spec/glm52-mtp-graph.ts +0 -29
  526. package/src/spec/glm52-mtp-rows.ts +0 -132
  527. package/src/spec/glm52-mtp-source.ts +0 -317
  528. package/src/spec/glm52-mtp-state.ts +0 -33
  529. package/src/spec/ngram-source.ts +0 -204
  530. package/src/spec/projected-draft-rows.ts +0 -73
  531. package/src/spec/qwen-mtp-module.ts +0 -171
  532. package/src/spec/qwen-mtp-rows.ts +0 -219
  533. package/src/spec/qwen-mtp-source.ts +0 -383
  534. package/src/spec/qwen-mtp-state.ts +0 -30
  535. package/src/spec/serve-loop.ts +0 -585
  536. package/src/spec/source.ts +0 -215
  537. package/src/spec/two-model-rows.ts +0 -61
  538. package/src/spec/two-model.ts +0 -113
  539. package/src/ssd-cache.ts +0 -372
  540. package/src/ssd-durability.ts +0 -211
  541. package/src/tokenizer.ts +0 -335
  542. package/src/tool-approvals.ts +0 -131
  543. package/src/tool-call.ts +0 -695
  544. package/src/train/dataset.ts +0 -484
  545. package/src/train/diffusion-lora.ts +0 -216
  546. package/src/train/export.ts +0 -32
  547. package/src/train/flash-cce.ts +0 -1234
  548. package/src/train/forward.ts +0 -266
  549. package/src/train/fuse.ts +0 -243
  550. package/src/train/index.ts +0 -46
  551. package/src/train/job.ts +0 -205
  552. package/src/train/lora-params.ts +0 -254
  553. package/src/train/loss.ts +0 -1591
  554. package/src/train/merge.ts +0 -195
  555. package/src/train/optimizer.ts +0 -183
  556. package/src/train/prefix-shared.ts +0 -490
  557. package/src/train/rank.ts +0 -137
  558. package/src/train/segmented.ts +0 -1828
  559. package/src/train/steel-qmm-header.ts +0 -1461
  560. package/src/train/trainer.ts +0 -1221
  561. package/src/train/watch.ts +0 -542
  562. package/src/vision/diffusion-vision.ts +0 -620
  563. package/src/vision/embedder.ts +0 -168
  564. package/src/vision/preprocess.ts +0 -247
  565. package/src/vision/prompt.ts +0 -373
  566. package/src/vision/qwen3vl-preprocess.ts +0 -409
  567. package/src/vision/qwen3vl-prompt.ts +0 -196
  568. package/src/vision/qwen3vl-tower.ts +0 -555
  569. package/src/vision/siglip.ts +0 -599
  570. package/src/vision/video-frames.ts +0 -148
  571. package/src/web/app.html +0 -2143
  572. package/src/web/app.js +0 -5703
  573. package/src/web/skills/memory/SKILL.md +0 -120
  574. package/src/web/skills/web-research/SKILL.md +0 -46
  575. package/src/web/skills.ts +0 -97
  576. package/src/web/src/adapters-panel.ts +0 -288
  577. package/src/web/src/assistant.ts +0 -451
  578. package/src/web/src/chat.ts +0 -1057
  579. package/src/web/src/composer.ts +0 -959
  580. package/src/web/src/dataset.ts +0 -132
  581. package/src/web/src/finetune.ts +0 -267
  582. package/src/web/src/hub.ts +0 -428
  583. package/src/web/src/main.ts +0 -98
  584. package/src/web/src/markdown.ts +0 -0
  585. package/src/web/src/memory-panel.ts +0 -572
  586. package/src/web/src/model-picker.ts +0 -159
  587. package/src/web/src/palette.ts +0 -346
  588. package/src/web/src/protocol.ts +0 -26
  589. package/src/web/src/quantize.ts +0 -167
  590. package/src/web/src/rag.ts +0 -274
  591. package/src/web/src/sessions.ts +0 -469
  592. package/src/web/src/shell.ts +0 -804
  593. package/src/web/src/ui-catalog.ts +0 -78
  594. package/src/web/vendor/README.md +0 -61
  595. package/src/web-assets.ts +0 -47
  596. package/src/web-tools.ts +0 -478
  597. package/src/weights.ts +0 -255
  598. /package/src/{serve → server}/completion-sink.ts +0 -0
  599. /package/src/web/{icon.svg → public/icon.svg} +0 -0
  600. /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
  601. /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
package/src/preflight.ts DELETED
@@ -1,158 +0,0 @@
1
- // Machine-state preflight for fair benchmarks (Phase 15 method rules).
2
- // Every failure mode here was MEASURED on this machine (PLAN findings):
3
- // - 6.4 GB swap residue: 26B decode 32 tok/s → 0.02 tok/s
4
- // - session drift: identical bf16 runs 15.5 vs 14.3 tok/s an hour apart
5
- // - cold page cache: prefill timer dominated by SSD page-in
6
- // The harness refuses to record headline numbers when checks fail;
7
- // the state snapshot is stored in every eval-DB row either way.
8
- //
9
- // LIMITS of this instrument (2026-07-05 finding): the snapshot is taken
10
- // BETWEEN legs, when the bench process has exited and memory is quiet —
11
- // free_pct reads a near-constant ~92% on an otherwise-idle box and load1m
12
- // sat under every threshold while a 40-minute mid-pass slow window cut
13
- // decode rates ~40% across every stack. Preflight ok ≠ clean measurements
14
- // and preflight-failed ≠ dirty ones (the 07-04 pass was ‡-tagged yet
15
- // stable). The DIRECT quality signal is per-cell run spread — see the
16
- // run-stability policy in scripts/bench-h2h.ts (SPREAD_TOL retries +
17
- // `unstable`/`stabilized` tags). Preflight remains the coarse pre-gate.
18
-
19
- export interface MachineState {
20
- swapUsedMB: number;
21
- freePercent: number;
22
- /** pmset -g therm CPU_Speed_Limit (100 = no thermal throttle; -1 if
23
- * unavailable on this platform). */
24
- cpuSpeedLimit: number;
25
- /** iogpu.wired_limit_mb sysctl (0 = macOS default ≈75% of unified
26
- * memory). A raised ceiling helps every stack equally but MUST be
27
- * recorded — results depend on it. Resets on reboot. */
28
- gpuWiredLimitMB: number;
29
- /** 1-minute load average — post-boot indexing (Spotlight etc.) shows
30
- * up here; let it settle before benchmarking. */
31
- loadAvg1m: number;
32
- /** Foreign processes holding > bigRssMB resident (excludes kernel,
33
- * WindowServer, and our own bun/python benchmark processes). */
34
- bigProcesses: { rssMB: number; command: string }[];
35
- /** Explicitly permitted CPU-only background commands; RSS limits still apply. */
36
- allowedCpuProcesses?: string[];
37
- backgroundCpuProcesses?: { rssMB: number; command: string }[];
38
- ok: boolean;
39
- problems: string[];
40
- at: string;
41
- }
42
-
43
- export interface PreflightLimits {
44
- maxSwapUsedMB?: number; // default 512
45
- minFreePercent?: number; // default 35
46
- bigRssMB?: number; // default 2048
47
- allowCpuProcesses?: string[];
48
- }
49
-
50
- export function foreignProcessFindings(output: string, limits: PreflightLimits = {}) {
51
- const bigProcesses: { rssMB: number; command: string }[] = [];
52
- const backgroundCpuProcesses: { rssMB: number; command: string }[] = [];
53
- for (const line of output.split("\n")) {
54
- const m = line.match(/^\s*([\d.]+)\s+(\d+)\s+(.*)$/);
55
- if (!m) continue;
56
- const cpuPct = Number(m[1]);
57
- const rssMB = Number(m[2]) / 1024;
58
- const command = m[3]!;
59
- if (rssMB < (limits.bigRssMB ?? 2048) && cpuPct < 50) continue;
60
- if (/(bun|\.venv\/bin\/python|mlx_lm|optiq)/.test(command) ||
61
- /(kernel_task|WindowServer|launchd|mds|spotlight)/i.test(command)) continue;
62
- const finding = { rssMB: Math.round(rssMB),
63
- command: command.slice(0, 80) + (cpuPct >= 50 ? ` (${cpuPct.toFixed(0)}% cpu)` : "") };
64
- // Exact command match; permission to tolerate CPU activity never permits
65
- // that process to consume the benchmark's memory headroom.
66
- const allowed = rssMB < (limits.bigRssMB ?? 2048) && limits.allowCpuProcesses?.includes(command);
67
- (allowed ? backgroundCpuProcesses : bigProcesses).push(finding);
68
- }
69
- return { bigProcesses, backgroundCpuProcesses };
70
- }
71
-
72
- function sh(cmd: string[]): string {
73
- const r = Bun.spawnSync(cmd, { stdout: "pipe", stderr: "pipe" });
74
- return r.exitCode === 0 ? r.stdout.toString() : "";
75
- }
76
-
77
- export function checkMachine(limits: PreflightLimits = {}): MachineState {
78
- const maxSwap = limits.maxSwapUsedMB ?? 512;
79
- const minFree = limits.minFreePercent ?? 35;
80
- const problems: string[] = [];
81
-
82
- // swap: "vm.swapusage: total = 7168.00M used = 6413.06M free = ..."
83
- const swapOut = sh(["sysctl", "vm.swapusage"]);
84
- const swapUsedMB = Number(swapOut.match(/used = ([\d.]+)M/)?.[1] ?? -1);
85
- if (swapUsedMB < 0) problems.push("could not read vm.swapusage");
86
- else if (swapUsedMB > maxSwap)
87
- problems.push(`swap in use: ${swapUsedMB.toFixed(0)} MB (> ${maxSwap} MB) — reboot before benchmarking`);
88
-
89
- // free memory: "System-wide memory free percentage: 34%"
90
- const memOut = sh(["memory_pressure", "-Q"]);
91
- const freePercent = Number(memOut.match(/free percentage: (\d+)/)?.[1] ?? -1);
92
- if (freePercent < 0) problems.push("could not read memory_pressure");
93
- else if (freePercent < minFree)
94
- problems.push(`free memory ${freePercent}% (< ${minFree}%) — close apps / reboot`);
95
-
96
- // thermal: "CPU_Speed_Limit = 100"
97
- const thermOut = sh(["pmset", "-g", "therm"]);
98
- const speedMatch = thermOut.match(/CPU_Speed_Limit\s*=\s*(\d+)/);
99
- const cpuSpeedLimit = speedMatch ? Number(speedMatch[1]) : -1;
100
- if (cpuSpeedLimit !== -1 && cpuSpeedLimit < 100)
101
- problems.push(`thermal throttle active: CPU speed limit ${cpuSpeedLimit}% — let the machine cool`);
102
-
103
- // GPU wired ceiling: 0 = stock (~75% of unified). Recorded, not judged
104
- // — raising it (sudo sysctl iogpu.wired_limit_mb=N) helps all stacks
105
- // equally, but rows measured under different ceilings don't compare.
106
- const wiredOut = sh(["sysctl", "-n", "iogpu.wired_limit_mb"]);
107
- const gpuWiredLimitMB = Number(wiredOut.trim() || 0) || 0;
108
-
109
- // load average: "{ 4.96 3.21 1.90 }" — post-boot indexing settles fast
110
- const loadOut = sh(["sysctl", "-n", "vm.loadavg"]);
111
- const loadAvg1m = Number(loadOut.match(/([\d.]+)/)?.[1] ?? -1);
112
- if (loadAvg1m > 8)
113
- problems.push(`1-min load ${loadAvg1m.toFixed(1)} — post-boot indexing still running; wait a minute`);
114
-
115
- // big foreign processes: high RSS *or* high CPU (pcpu + rss in KB from
116
- // ps). The CPU arm is the 2026-07-05 lesson: Apple's
117
- // knowledgeconstructiond (Apple Intelligence indexing) burned 87% CPU
118
- // with a tiny RSS during the pass's 40-min slow window — a background
119
- // ML workload the RSS-only check could never flag. CPU is bursty, so
120
- // one clean sample doesn't prove absence (the run-spread stability
121
- // policy in bench-h2h.ts is the direct signal); a hot sample here is
122
- // still a cheap early warning.
123
- const { bigProcesses, backgroundCpuProcesses } = foreignProcessFindings(
124
- sh(["ps", "axo", "pcpu=,rss=,command="]), limits);
125
- for (const p of bigProcesses)
126
- problems.push(`big process: ${p.rssMB} MB — ${p.command}`);
127
-
128
- return {
129
- swapUsedMB, freePercent, cpuSpeedLimit, gpuWiredLimitMB, loadAvg1m,
130
- bigProcesses,
131
- allowedCpuProcesses: limits.allowCpuProcesses ?? [], backgroundCpuProcesses,
132
- ok: problems.length === 0,
133
- problems,
134
- at: new Date().toISOString(),
135
- };
136
- }
137
-
138
- /** Compact JSON for the eval-DB machine_state column. Includes machine
139
- * identity — rows from different Macs must never be confused (the
140
- * eval DB is per-machine, but rendered tables travel in git). */
141
- export function machineStateJson(s: MachineState): string {
142
- const host = (sh(["hostname", "-s"]).trim() || "unknown");
143
- const chip = (sh(["sysctl", "-n", "machdep.cpu.brand_string"]).trim() || "unknown");
144
- const ramGB = Math.round(Number(sh(["sysctl", "-n", "hw.memsize"]).trim() || 0) / 2 ** 30);
145
- return JSON.stringify({
146
- host, chip, ram_gb: ramGB,
147
- swap_mb: Math.round(s.swapUsedMB),
148
- free_pct: s.freePercent,
149
- cpu_limit: s.cpuSpeedLimit,
150
- gpu_wired_mb: s.gpuWiredLimitMB,
151
- load1m: s.loadAvg1m,
152
- big_procs: s.bigProcesses.length,
153
- allowed_cpu_processes: s.allowedCpuProcesses ?? [],
154
- background_cpu_processes: s.backgroundCpuProcesses ?? [],
155
- ok: s.ok,
156
- at: s.at,
157
- });
158
- }
@@ -1,460 +0,0 @@
1
- import { type CheckpointAttachment, materializeCheckpoint, attachmentBytes, cloneAttachments, disposeAttachments } from "./backends/mlx/checkpoint-state";
2
- import { leaseCacheState, minimumReusableOffset } from "./backends/mlx/state-views";
3
- import { withResource, cleanupFailure, disposeResources, ownResource } from "./engine/resources";
4
- // Byte-capped LRU prompt cache — the RAM tier of the Layer-0 KV store.
5
- //
6
- // The mlx-lm lesson (PLAN.md): a count-capped cache of multi-GB KV
7
- // entries is an OOM footgun. Ours accounts bytes (sum of KV array bytes
8
- // per entry) and evicts least-recently-used until under the cap.
9
- //
10
- // Usage pattern (single generation queue, so take/put is race-free):
11
- // const hit = cache.take(promptIds); // longest-prefix match
12
- // generate(model, promptIds, { cache: hit?.caches ?? fresh })
13
- // cache.put([...promptIds, ...generated], caches, ns, hit?.retain);
14
- // take() lends zero-copy views and leaves every RAM donor in place, including
15
- // recurrent and method state. SSD persistence runs independently of RAM reuse.
16
- // put() supersedes same-ns prefix-ancestors when the new entry is trimmable.
17
- //
18
- // TIERING (Layer 0, unified-engine plan): when a ColdTier is attached,
19
- // take() itself runs the two-tier dance — RAM peek vs cold find, restore
20
- // (zero-copy mmap) + trim when the cold tier holds a strictly longer
21
- // prefix — so EVERY consumer (the serial lane, the batch scheduler, future
22
- // prefix sharing) gets SSD restores through the same take()/put() it
23
- // already calls. Eviction spills to the tier (the #spill hook); idle
24
- // entries DEMOTE to it (demoteIdle — free the GPU memory, keep the prefix
25
- // reachable); onPut lets the server schedule its debounced write-behind
26
- // snapshot for both lanes.
27
-
28
- import type { Cache } from "./model/gemma4";
29
- import type { PrefixCache, PrefixCacheHit } from "./contracts/prefix-cache";
30
- import { cloneKvCaches } from "./kv-store";
31
-
32
- /** Reference-counted release: wraps an entry's `retain` (e.g. an mmap
33
- * unmap) so DONOR + CLONES can each hold a share — the underlying hook
34
- * runs once, after the LAST holder releases. Prefix sharing hands out
35
- * zero-copy VIEWS of a donor's buffers; if the donor's unmap ran while a
36
- * clone still referenced the mapped pages, the clone would read freed
37
- * memory. */
38
- function makeSharedRetain(retain: (() => void) | undefined): { acquire(): () => void } {
39
- let count = 0;
40
- let released = false;
41
- return {
42
- acquire() {
43
- count++;
44
- let mine = false;
45
- return () => {
46
- if (mine) return; // idempotent per share
47
- mine = true;
48
- count--;
49
- if (count === 0 && !released) {
50
- released = true;
51
- retain?.();
52
- }
53
- };
54
- },
55
- };
56
- }
57
-
58
- /** The cold (SSD) tier the RAM cache tiers over. Structural — this module
59
- * never imports ssd-cache (which imports us); the server binds
60
- * SsdCacheStore + the model into this shape (src/server.ts). Lookup and
61
- * restore failures return null; store returns false on failure. Operations
62
- * that require persistence retain their donor if storing fails. */
63
- export interface ColdTier {
64
- /** Longest stored usable prefix for prompt/ns — index-only, no I/O.
65
- * `handle` is the tier's opaque entry token, passed back to restore. */
66
- find(prompt: number[], ns: string): { prefixLen: number; handle: unknown } | null;
67
- /** Materialize a found entry as GPU-visible caches (zero-copy COW mmap;
68
- * pages fault in lazily). `retain` must run after the caches are
69
- * disposed (it unmaps the backing file). Null on any failure. */
70
- restore(handle: unknown): { tokens: number[]; caches: Cache[]; attachments?: CheckpointAttachment[]; retain: () => void } | null;
71
- /** Borrow live caches and finish persistence before returning. A false
72
- * result prevents a required demotion. */
73
- store(tokens: number[], caches: Cache[], ns: string, attachments?: CheckpointAttachment[]): boolean | void;
74
- }
75
-
76
- /** Spill sink for eviction/demotion (write-behind, 2026-07-06).
77
- *
78
- * `spillOwned` (preferred, NON-BLOCKING): the cache hands the sink an
79
- * entry it OWNS — zero-copy CLONES of the caches (made via the same
80
- * injectable cloner take() uses, BEFORE the donor is disposed; entries
81
- * are immutable so the clones stay consistent forever) plus a copy of
82
- * the tokens. The sink must dispose entry.caches when done, on BOTH
83
- * fulfill and reject paths (the server chains storeAsync -> dispose on
84
- * ssdWriteChain). A sink that throws SYNCHRONOUSLY has not taken
85
- * ownership — the cache disposes the clones and the spill degrades to
86
- * a no-op; eviction/demotion never unwinds.
87
- *
88
- * `spillSync` (fallback): called with the LIVE entry about to be
89
- * disposed; must finish reading before returning. This is the old
90
- * contract — a full synchronous serialize+write under the generation
91
- * lock — kept for tests and simple embedders. */
92
- export interface SpillSink {
93
- spillOwned?: (entry: PromptCacheEntry) => void;
94
- spillSync?: (entry: PromptCacheEntry) => void;
95
- }
96
-
97
- export interface PromptCacheEntry extends PrefixCacheHit<Cache[], CheckpointAttachment[]> {
98
- /** Namespace key — adapter spec for LoRA requests ("" = base model).
99
- * KV computed under one adapter must never seed another's prefill. */
100
- ns: string;
101
- /** Release hook for entries restored from the SSD cold tier: unmaps the
102
- * backing COW file mapping. MUST run only after `caches` are disposed
103
- * (they may alias file pages); every dispose path here honors that, and
104
- * callers who take() an entry carry the thunk with it. */
105
- retain?: () => void;
106
- }
107
-
108
- export function cacheBytes(caches: Cache[]): number {
109
- let total = 0;
110
- for (const c of caches) {
111
- total += withResource(leaseCacheState(c), (state) => state.reduce((bytes, a) => bytes + a.nbytes, 0));
112
- }
113
- return total;
114
- }
115
-
116
- /** Length of the longest common prefix of two token sequences. */
117
- export function commonPrefixLength(a: number[], b: number[]): number {
118
- const n = Math.min(a.length, b.length);
119
- let i = 0;
120
- while (i < n && a[i] === b[i]) i++;
121
- return i;
122
- }
123
-
124
- interface EntryRecord {
125
- entry: PromptCacheEntry;
126
- bytes: number;
127
- lastUsed: number;
128
- lastUsedMs: number;
129
- /** Ref-counted retain: the donor holds one share (entry.retain); every
130
- * clone handed out by take() holds another. */
131
- share: { acquire(): () => void };
132
- }
133
-
134
- export class PromptCache implements PrefixCache<Cache[], CheckpointAttachment[]> {
135
- readonly maxBytes: number;
136
- #entries: EntryRecord[] = [];
137
- #clock = 0;
138
- hits = 0;
139
- misses = 0;
140
- demotions = 0;
141
- /** Cold-tier spill sinks (see SpillSink): spillOwned gets clones and
142
- * runs the write off-lock; spillSync gets the live entry inline.
143
- * Failures are the sink's problem; eviction proceeds regardless. */
144
- readonly #spillOwned: ((entry: PromptCacheEntry) => void) | null;
145
- readonly #spillSync: ((entry: PromptCacheEntry) => void) | null;
146
- /** Optional cold tier: take() tiers over it, demoteIdle() spills into it. */
147
- readonly #cold: ColdTier | null;
148
- /** Fired after every successful put() — the server hangs its debounced
149
- * write-behind SSD snapshot here so BOTH lanes' entries persist. */
150
- onPut: ((tokens: number[], ns: string) => void) | null = null;
151
-
152
- /** Zero-copy view cloner — injectable so model-free tests can stub it. */
153
- readonly #clone: (caches: Cache[]) => Cache[];
154
-
155
- constructor(
156
- maxBytes: number,
157
- // Bare function = legacy sync spill (tests, simple embedders).
158
- spill: SpillSink | ((entry: PromptCacheEntry) => void) | null = null,
159
- cold: ColdTier | null = null,
160
- clone: (caches: Cache[]) => Cache[] = cloneKvCaches,
161
- private readonly materialize: (caches: Cache[], attachments?: CheckpointAttachment[]) => void = materializeCheckpoint,
162
- ) {
163
- this.maxBytes = maxBytes;
164
- this.#spillOwned = typeof spill === "function" ? null : spill?.spillOwned ?? null;
165
- this.#spillSync = typeof spill === "function" ? spill : spill?.spillSync ?? null;
166
- this.#cold = cold;
167
- this.#clone = clone;
168
- }
169
-
170
- #disposeEntry(entry: PromptCacheEntry, spillFirst: boolean): void {
171
- if (spillFirst) {
172
- if (this.#spillOwned) {
173
- // NON-BLOCKING spill: clone BEFORE disposing (clones are zero-copy
174
- // views of the live arrays) and hand ownership to the sink, which
175
- // flushes off the generation lock. The underlying GPU buffers stay
176
- // alive until the sink disposes the clones — for demoteIdle that
177
- // means the memory is freed one bounded write-chain hop later, not
178
- // instantly; acceptable because the chain is serial and each write
179
- // disposes its clones on settle (fulfill AND reject).
180
- // Retain contract: entry.retain still runs below, possibly while
181
- // the clones are in flight — safe because cold-restored entries
182
- // OWN their bytes (streamed copy-restore, 2026-07-07): there is
183
- // no backing mmap for retain to unmap, so nothing the clones
184
- // could alias goes away.
185
- let clones: Cache[] | null = null;
186
- let attachments: CheckpointAttachment[] | undefined;
187
- try {
188
- clones = this.#clone(entry.caches);
189
- attachments = cloneAttachments(entry.attachments);
190
- this.#spillOwned({ tokens: [...entry.tokens], caches: clones, ns: entry.ns, attachments });
191
- } catch {
192
- // Failure degrades to no-spill; eviction/demotion never unwinds.
193
- if (clones) for (const c of clones) try { c.dispose(); } catch {}
194
- try { disposeAttachments(attachments); } catch {}
195
- }
196
- } else {
197
- const spill =
198
- this.#spillSync ??
199
- (this.#cold
200
- ? (e: PromptCacheEntry) => this.#cold!.store(e.tokens, e.caches, e.ns, e.attachments)
201
- : null);
202
- if (spill) try { spill(entry); } catch {}
203
- }
204
- }
205
- disposeResources([...entry.caches, { dispose: () => disposeAttachments(entry.attachments) }]);
206
- entry.retain?.(); // backing release requires successful cache disposal
207
- }
208
-
209
- get totalBytes(): number {
210
- return this.#entries.reduce((a, e) => a + e.bytes, 0);
211
- }
212
-
213
- get size(): number {
214
- return this.#entries.length;
215
- }
216
-
217
- /** Release LRU entries until the caller's live-memory check is satisfied.
218
- * Unlike background eviction, pressure relief must finish the SSD write
219
- * and release the buffers now, not enqueue clones that keep them alive.
220
- * Call under the engine lock, after taking the request's reusable prefix. */
221
- relievePressure(overBudget: () => boolean): number {
222
- let count = 0;
223
- while (this.#entries.length && overBudget()) {
224
- let oldest = 0;
225
- for (let i = 1; i < this.#entries.length; i++)
226
- if (this.#entries[i]!.lastUsed < this.#entries[oldest]!.lastUsed) oldest = i;
227
- const { entry } = this.#entries[oldest]!;
228
- // Keep the entry resident if persistence throws. A failed SSD write
229
- // must not be presented as a successful demotion.
230
- if (this.#cold) {
231
- if (this.#cold.store(entry.tokens, entry.caches, entry.ns, entry.attachments) === false) break;
232
- }
233
- else this.#spillSync?.(entry);
234
- this.#entries.splice(oldest, 1);
235
- this.#disposeEntry(entry, false);
236
- this.demotions++;
237
- count++;
238
- }
239
- return count;
240
- }
241
-
242
- /** Find the entry with the longest usable common prefix of `prompt`
243
- * across BOTH tiers. Every RAM entry lends zero-copy views and retains its
244
- * donor, including exact-boundary recurrent state and method attachments.
245
- * The caller owns the returned views —
246
- * dispose them or put() an extended entry, honoring `retain` either way.
247
- *
248
- * Usable prefix = common prefix capped at prompt.length - 1 (at least
249
- * one token must be forwarded to produce logits). Entries longer than
250
- * the prefix need cache.trim(); ring caches lose trimability once
251
- * wrapped — those entries only match in full.
252
- *
253
- * Tier order: the cold tier wins only with a STRICTLY longer usable
254
- * prefix (its restore costs a mmap + lazy fault-in; RAM is free). A
255
- * cold entry with an untrimmable divergent tail (ring post-wrap, SSM)
256
- * is dropped and the RAM candidate (or a fresh prefill) serves.
257
- * hits/misses count RAM candidacy only (cold restores are counted by
258
- * the tier itself), preserving the /stats meaning. */
259
- take(prompt: number[], ns = ""): PromptCacheEntry | null {
260
- let bestIdx = -1;
261
- let bestLen = 0;
262
- for (let i = 0; i < this.#entries.length; i++) {
263
- const e = this.#entries[i]!.entry;
264
- if (e.ns !== ns) continue;
265
- const p = Math.min(commonPrefixLength(e.tokens, prompt), prompt.length - 1);
266
- if (p <= bestLen || p < minimumReusableOffset(e.caches)) continue;
267
- const trimNeeded = e.tokens.length - p;
268
- if (trimNeeded > 0 && (!!e.attachments?.length || !e.caches.every((c) => c.isTrimmable()))) continue;
269
- bestLen = p;
270
- bestIdx = i;
271
- }
272
- if (bestIdx === -1) this.misses++;
273
- else this.hits++;
274
-
275
- // Cold tier: strictly longer usable prefix ⇒ restore + trim; the RAM
276
- // candidate stays put UNTOUCHED (the old serial-lane flow trimmed it
277
- // before comparing — a needless entry degradation, gone now).
278
- if (this.#cold) {
279
- const hit = this.#cold.find(prompt, ns);
280
- if (hit && hit.prefixLen > bestLen) {
281
- const loaded = this.#cold.restore(hit.handle);
282
- if (loaded) {
283
- const owner = ownResource(loaded, (entry) =>
284
- this.#disposeEntry({ ...entry, ns }, false));
285
- const result = withResource(owner, (entry) => {
286
- const trimNeeded = entry.tokens.length - hit.prefixLen;
287
- if (hit.prefixLen < minimumReusableOffset(entry.caches)) return null;
288
- if (trimNeeded > 0 && (!!entry.attachments?.length || !entry.caches.every((cache) => cache.isTrimmable()))) {
289
- console.warn(
290
- `[prompt-cache] cold entry unusable: ${entry.tokens.length} tokens, ` +
291
- `needs trim ${trimNeeded} but untrimmable (wrapped ring/SSM) — re-prefilling`,
292
- );
293
- return null;
294
- }
295
- if (trimNeeded > 0) for (const cache of entry.caches) cache.trim(trimNeeded);
296
- const tokens = entry.tokens.slice(0, hit.prefixLen);
297
- return { ...owner.transfer(), tokens, ns };
298
- });
299
- if (result) return result;
300
- }
301
- }
302
- }
303
-
304
- if (bestIdx === -1) return null;
305
- // PREFIX SHARING (non-consuming serve): the caller gets zero-copy
306
- // CLONES of the donor's caches, trimmed to the matched prefix; the
307
- // donor stays in the cache untouched, ready to serve the next agent /
308
- // session with the same prefix. Safe because mlx cache updates are
309
- // FUNCTIONAL (updateAndFetch reassigns fresh arrays; buffer donation
310
- // only fires at refcount 1, and the donor always holds a ref), so a
311
- // clone extending itself can never mutate the donor's bytes. The old
312
- // consume-and-trim semantics CANNIBALIZED donors: agent B borrowing
313
- // agent A's 2k system prompt destroyed A's 10k entry to do it.
314
- const rec = this.#entries[bestIdx]!;
315
- rec.lastUsed = ++this.#clock;
316
- rec.lastUsedMs = Date.now();
317
- // Lend immutable views for every backend state. RAM retention and SSD
318
- // durability remain cache responsibilities while the caller extends its
319
- // own views. This keeps synchronous persistence out of a RAM hit.
320
- const clones = this.#clone(rec.entry.caches);
321
- let attachments: CheckpointAttachment[] | undefined;
322
- const trimNeeded = rec.entry.tokens.length - bestLen;
323
- try {
324
- attachments = cloneAttachments(rec.entry.attachments);
325
- if (trimNeeded > 0) for (const c of clones) c.trim(trimNeeded);
326
- } catch (error) {
327
- cleanupFailure(error, () => disposeResources([...clones, { dispose: () => disposeAttachments(attachments) }]));
328
- }
329
- return {
330
- tokens: rec.entry.tokens.slice(0, bestLen),
331
- caches: clones,
332
- attachments,
333
- ns,
334
- retain: rec.share.acquire(),
335
- };
336
- }
337
-
338
- /** Longest usable prefix length available for `prompt` WITHOUT taking
339
- * (the SSD tier compares this against its own best before restoring). */
340
- peekPrefixLen(prompt: number[], ns = ""): number {
341
- let best = 0;
342
- for (const { entry: e } of this.#entries) {
343
- if (e.ns !== ns) continue;
344
- const p = Math.min(commonPrefixLength(e.tokens, prompt), prompt.length - 1);
345
- if (p <= best || p < minimumReusableOffset(e.caches)) continue;
346
- const trimNeeded = e.tokens.length - p; // same usability rule as take()
347
- if (trimNeeded > 0 && (!!e.attachments?.length || !e.caches.every((c) => c.isTrimmable()))) continue;
348
- best = p;
349
- }
350
- return best;
351
- }
352
-
353
- /** Read-only exact-token lookup (the write-behind snapshot path: the
354
- * entry stays owned by the cache; the caller only reads array state,
355
- * under the gateway lock so no generation is mutating it). */
356
- findExact(tokens: number[], ns = ""): PromptCacheEntry | null {
357
- for (const { entry: e } of this.#entries) {
358
- if (e.ns !== ns || e.tokens.length !== tokens.length) continue;
359
- if (commonPrefixLength(e.tokens, tokens) === tokens.length) return e;
360
- }
361
- return null;
362
- }
363
-
364
- /** Insert (or reinsert) an entry; evicts LRU entries over the byte cap
365
- * (spilling each to the cold tier first, when one is attached). If the
366
- * entry itself exceeds the cap it is spilled + disposed, not stored. */
367
- put(tokens: number[], caches: Cache[], ns = "", retain?: () => void, attachments?: CheckpointAttachment[]): void {
368
- // All cache-provided validation runs before ownership is adopted.
369
- const bytes = cacheBytes(caches) + attachmentBytes(attachments);
370
- const trimmable = !attachments?.length && caches.every((cache) => cache.isTrimmable());
371
- // Storage takes resolved snapshots, independently of their producer.
372
- this.materialize(caches, attachments);
373
- const discard = (entry: PromptCacheEntry, spill: boolean) => {
374
- try { this.#disposeEntry(entry, spill); }
375
- catch (error) {
376
- // The new entry is already owned here. Throwing back to its previous
377
- // owner would invite disposal of state still held by this store.
378
- console.warn(`[prompt-cache] entry cleanup failed: ${error}`);
379
- }
380
- };
381
- const entry: PromptCacheEntry = { tokens, caches, ns, retain, attachments };
382
- if (bytes > this.maxBytes) {
383
- discard(entry, true);
384
- return;
385
- }
386
- // The donor's retain becomes ref-counted so take()'s clones can hold
387
- // shares; the entry's own retain is the donor's share.
388
- const share = makeSharedRetain(retain);
389
- entry.retain = share.acquire();
390
- const rec: EntryRecord = { entry, bytes, lastUsed: ++this.#clock, lastUsedMs: Date.now(), share };
391
- this.#entries.push(rec);
392
- // Exact duplicates (equal tokens) are redundant REGARDLESS of
393
- // trimmability — the new entry serves exactly the matches the old one
394
- // did (mlx-lm's trie replaces in place). Without this, every gemma ctx
395
- // repeat left another full-size wrapped-ring entry behind (measured
396
- // ~0.4 GB retained per repeat on the 12B bench).
397
- for (const old of [...this.#entries]) {
398
- if (old === rec || old.entry.ns !== ns) continue;
399
- if (old.entry.tokens.length !== tokens.length) continue;
400
- if (commonPrefixLength(old.entry.tokens, tokens) !== tokens.length) continue;
401
- this.#entries = this.#entries.filter((r) => r !== old);
402
- discard(old.entry, false);
403
- }
404
- // SUPERSEDE strict prefix-ancestors (the SSD store's rule, brought to
405
- // RAM — prefix sharing would otherwise grow an entry per turn): same-ns
406
- // entries whose tokens are a prefix of the new entry are redundant
407
- // and disposed WITHOUT spill (the new entry serves their prefixes and
408
- // its own write-behind persists them) — but ONLY when the new entry's
409
- // caches are all trimmable. An untrimmable new entry (wrapped ring)
410
- // can only serve exact-length matches, so shorter ancestors — e.g.
411
- // the prompt-boundary snapshot that makes drifted multi-turn agents
412
- // hit — must survive.
413
- if (trimmable) {
414
- for (const old of [...this.#entries]) {
415
- if (old === rec || old.entry.ns !== ns) continue;
416
- if (old.entry.tokens.length >= tokens.length || old.entry.tokens.length < minimumReusableOffset(caches)) continue;
417
- if (commonPrefixLength(old.entry.tokens, tokens) !== old.entry.tokens.length) continue;
418
- this.#entries = this.#entries.filter((r) => r !== old);
419
- discard(old.entry, false);
420
- }
421
- }
422
- while (this.totalBytes > this.maxBytes && this.#entries.length > 1) {
423
- let lruIdx = 0;
424
- for (let i = 1; i < this.#entries.length; i++)
425
- if (this.#entries[i]!.lastUsed < this.#entries[lruIdx]!.lastUsed) lruIdx = i;
426
- const [evicted] = this.#entries.splice(lruIdx, 1);
427
- discard(evicted!.entry, true);
428
- }
429
- try { this.onPut?.(tokens, ns); } catch {}
430
- }
431
-
432
- /** Layer-0 idle demotion: spill every entry unused for `idleMs` to the
433
- * cold tier and FREE its GPU memory. The prefix stays reachable — the
434
- * next take() restores it via zero-copy mmap (~0.25 s for a 13.7k-token
435
- * entry, vs a 12 s re-prefill) — but between bursts the RAM tier drains
436
- * toward empty, returning unified memory to the system. Caller must
437
- * hold the generation lock (entries' arrays are disposed here) — with
438
- * a spillOwned sink the lock covers only the zero-copy clone; the
439
- * write runs off-lock and the buffers free when it settles. No-op
440
- * without a cold tier — demotion without a place to demote TO would
441
- * just be data loss. Returns entries demoted. */
442
- demoteIdle(idleMs: number, now = Date.now()): number {
443
- if (!this.#cold) return 0;
444
- let n = 0;
445
- for (const rec of [...this.#entries]) {
446
- if (now - rec.lastUsedMs < idleMs) continue;
447
- this.#entries = this.#entries.filter((r) => r !== rec);
448
- this.#disposeEntry(rec.entry, true); // spill-first, then dispose
449
- this.demotions++;
450
- n++;
451
- }
452
- return n;
453
- }
454
-
455
- clear(): void {
456
- const entries = this.#entries;
457
- this.#entries = [];
458
- disposeResources(entries.map(({ entry }) => ({ dispose: () => this.#disposeEntry(entry, false) })));
459
- }
460
- }