mlx-bun 0.0.13 → 0.3.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 (427) hide show
  1. package/README.md +71 -578
  2. package/bin/mlx-bun.mjs +1 -1
  3. package/docs/README.md +91 -0
  4. package/docs/{design → archive}/hlg-sampling.md +3 -3
  5. package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
  6. package/docs/{design → archive/investigations}/colibri-glm52-port.md +1 -1
  7. package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
  8. package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
  9. package/docs/{investigations → archive/investigations}/hlg-sampling-investigation.md +1 -1
  10. package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
  11. package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
  12. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +92 -0
  13. package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
  14. package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
  15. package/docs/{planning → archive/planning}/chat-ui-vision.md +1 -1
  16. package/docs/{planning → archive/planning}/chunk-task-roadmap.md +1 -1
  17. package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +4 -4
  18. package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +1 -1
  19. package/docs/{planning → archive/planning}/release-notes-v0.0.9.md +1 -1
  20. package/docs/archive/planning/release-notes-v0.2.0.md +49 -0
  21. package/docs/{planning → archive/planning}/website-readme-pass-plan.md +1 -1
  22. package/docs/design/batching.md +569 -0
  23. package/docs/design/decode-speed-program.md +3178 -169
  24. package/docs/design/dreaming-nightly-pipeline.md +675 -282
  25. package/docs/design/generic-model-support.md +579 -334
  26. package/docs/design/kv-cache.md +680 -0
  27. package/docs/design/orpo-training.md +673 -1239
  28. package/docs/design/speculative-decoding.md +1627 -0
  29. package/docs/design/turboquant.md +2102 -0
  30. package/docs/design/unified-engine-frontier-plan.md +1831 -727
  31. package/docs/design/web-chat-redesign.md +666 -1083
  32. package/docs/planning/PRODUCT_ROADMAP.md +34 -1
  33. package/docs/planning/release-notes-v0.3.0.md +35 -0
  34. package/docs/planning/video-series.md +421 -0
  35. package/docs/reference/benchmarks.md +1162 -0
  36. package/docs/reference/cli.md +395 -149
  37. package/docs/reference/distribution.md +321 -181
  38. package/docs/reference/environment.md +348 -0
  39. package/docs/reference/glossary.md +39 -0
  40. package/docs/reference/library-api.md +580 -142
  41. package/docs/reference/memory.md +227 -64
  42. package/docs/reference/models.md +137 -6
  43. package/docs/reference/server-api.md +938 -422
  44. package/docs/reference/server-config.md +652 -320
  45. package/docs/reference/training.md +393 -254
  46. package/docs/reference/troubleshooting.md +166 -0
  47. package/package.json +10 -5
  48. package/src/admission-plan.ts +18 -0
  49. package/src/anthropic.ts +92 -3
  50. package/src/audio/decode.ts +2 -2
  51. package/src/audio/features.ts +2 -2
  52. package/src/backends/mlx/autoregressive.ts +108 -0
  53. package/src/backends/mlx/batch-group.ts +1646 -0
  54. package/src/backends/mlx/diffusion.ts +39 -0
  55. package/src/backends/mlx/draft-target.ts +47 -0
  56. package/src/backends/mlx/evaluation.ts +105 -0
  57. package/src/backends/mlx/fill-append.ts +45 -0
  58. package/src/backends/mlx/gateway-binding.ts +71 -0
  59. package/src/backends/mlx/graph.ts +38 -0
  60. package/src/backends/mlx/memory-client.ts +189 -0
  61. package/src/backends/mlx/memory-guard.ts +40 -0
  62. package/src/backends/mlx/methods.ts +90 -0
  63. package/src/backends/mlx/model-prompt.ts +164 -0
  64. package/src/backends/mlx/model-serving.ts +79 -0
  65. package/src/backends/mlx/prefill.ts +33 -0
  66. package/src/backends/mlx/request-policy.ts +20 -0
  67. package/src/backends/mlx/rollback.ts +51 -0
  68. package/src/backends/mlx/serial-executor.ts +273 -0
  69. package/src/backends/mlx/speculative.ts +114 -0
  70. package/src/backends/mlx/state-views.ts +26 -0
  71. package/src/backends/mlx/text-engine.ts +73 -0
  72. package/src/cli.ts +189 -61
  73. package/src/client.ts +66 -0
  74. package/src/config.ts +21 -4
  75. package/src/contracts/completion.ts +16 -0
  76. package/src/contracts/execution.ts +49 -0
  77. package/src/contracts/generation.ts +103 -0
  78. package/src/contracts/host.ts +7 -0
  79. package/src/contracts/jobs.ts +66 -0
  80. package/src/contracts/pi-provider.ts +30 -0
  81. package/src/contracts/pi.ts +259 -0
  82. package/src/contracts/resources.ts +18 -0
  83. package/src/contracts/scheduling.ts +27 -0
  84. package/src/contracts/tsconfig.json +14 -0
  85. package/src/diffusion/diffusion-generate.ts +218 -153
  86. package/src/embed.ts +1 -1
  87. package/src/engine/admission.ts +59 -0
  88. package/src/engine/cancellation.ts +38 -0
  89. package/src/engine/engine.ts +56 -0
  90. package/src/engine/execution-coordinator.ts +70 -0
  91. package/src/engine/execution-plan.ts +51 -0
  92. package/src/engine/resources.ts +42 -0
  93. package/src/engine/scheduler.ts +67 -0
  94. package/src/engine/session.ts +247 -0
  95. package/src/eval/README.md +44 -1
  96. package/src/eval/kl.ts +263 -23
  97. package/src/eval/reference-logits.ts +207 -0
  98. package/src/eval/runner.ts +10 -79
  99. package/src/eval/tasks/bfcl.ts +2 -1
  100. package/src/eval/tasks/gsm8k.ts +2 -1
  101. package/src/eval/tasks/hashhop.ts +2 -1
  102. package/src/eval/tasks/humaneval.ts +5 -4
  103. package/src/eval/tasks/ifeval.ts +2 -1
  104. package/src/eval/tasks/mmlu.ts +2 -1
  105. package/src/expert-io.ts +3 -1
  106. package/src/expert-offload.ts +3 -1
  107. package/src/fill/echo-index.ts +188 -0
  108. package/src/fill/fill-session.ts +452 -0
  109. package/src/fill/proposal.ts +68 -0
  110. package/src/fill/schema-rows.ts +506 -0
  111. package/src/fill/tool-boundary.ts +103 -0
  112. package/src/fit.ts +24 -43
  113. package/src/generate.ts +740 -337
  114. package/src/grammar.ts +14 -8
  115. package/src/harness-pi.ts +15 -34
  116. package/src/hub-rest.ts +4 -4
  117. package/src/index.ts +27 -0
  118. package/src/inference/denoising.ts +17 -0
  119. package/src/inference/graph.ts +33 -0
  120. package/src/inference/prefill.ts +39 -0
  121. package/src/inference/rollback.ts +7 -0
  122. package/src/jobs/db.ts +4 -0
  123. package/src/jobs/index.ts +2 -0
  124. package/src/jobs/job-entry.ts +15 -14
  125. package/src/jobs/runner.ts +98 -13
  126. package/src/jobs/task-client.ts +19 -0
  127. package/src/jobs/types.ts +2 -58
  128. package/src/kv-scheme.ts +199 -0
  129. package/src/kv-store.ts +459 -234
  130. package/src/lab/README.md +18 -0
  131. package/src/{assets → lab/curve}/curve-designer.html +2 -2
  132. package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
  133. package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +7 -5
  134. package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
  135. package/src/library.ts +40 -0
  136. package/src/lora.ts +18 -0
  137. package/src/media-fetch.ts +20 -4
  138. package/src/memory/article.ts +1 -1
  139. package/src/memory/chunk.ts +1 -1
  140. package/src/memory/model.ts +32 -170
  141. package/src/memory/prompts.ts +1 -1
  142. package/src/memory/rest.ts +1 -1
  143. package/src/memory/schedule.ts +1 -1
  144. package/src/memory/stages.ts +4 -1
  145. package/src/memory/vault.ts +3 -4
  146. package/src/mlx/array.ts +9 -10
  147. package/src/mlx/compile.ts +12 -2
  148. package/src/mlx/ffi.ts +66 -22
  149. package/src/mlx/materialize.ts +23 -0
  150. package/src/mlx/metal-kernel.ts +10 -11
  151. package/src/mlx/ops.ts +64 -19
  152. package/src/mlx/quantized-rows.ts +36 -0
  153. package/src/mlx/token-bitmask.ts +52 -0
  154. package/src/mlx/turboquant-kv-decode.ts +82 -0
  155. package/src/mlx/turboquant-kv-inverse.ts +122 -0
  156. package/src/mlx/turboquant-ops.ts +33 -3
  157. package/src/mlx/turboquant-tables.ts +2 -2
  158. package/src/model/batched-mask.ts +9 -3
  159. package/src/model/batched-quant.ts +2 -2
  160. package/src/model/batched-rotating-quant.ts +45 -99
  161. package/src/model/batched-rotating-state.ts +73 -0
  162. package/src/model/batched-rotating.ts +57 -106
  163. package/src/model/batched-row-storage.ts +127 -0
  164. package/src/model/compiled-decode.ts +4 -3
  165. package/src/model/diffusion-gemma.ts +8 -8
  166. package/src/model/factory.ts +87 -49
  167. package/src/model/fingerprint.ts +1 -1
  168. package/src/model/flash-attention.ts +3 -3
  169. package/src/model/gemma4-base.ts +187 -17
  170. package/src/model/gemma4.ts +6 -6
  171. package/src/model/generated/gemma4-12b.ts +52 -148
  172. package/src/model/generated/gemma4-26b.ts +34 -94
  173. package/src/model/generated/gemma4-e4b.ts +28 -76
  174. package/src/model/glm52-cache.ts +19 -9
  175. package/src/model/glm52-dsa.ts +13 -9
  176. package/src/model/glm52-memory.ts +28 -1
  177. package/src/model/implementation.ts +69 -0
  178. package/src/model/minicpm5.ts +7 -8
  179. package/src/model/profile.ts +443 -0
  180. package/src/model/qwen-append.ts +12 -0
  181. package/src/model/qwen-conv.ts +52 -0
  182. package/src/model/qwen3-delta.ts +98 -2
  183. package/src/model/qwen3-mrope.ts +252 -0
  184. package/src/model/qwen3_5-checkpoint.ts +164 -0
  185. package/src/model/qwen3_5.ts +336 -60
  186. package/src/model/support.ts +1 -1
  187. package/src/model/trellis-balanced-scatter.ts +51 -0
  188. package/src/model/trellis-linear.ts +672 -0
  189. package/src/model/trellis-shared-m.ts +151 -0
  190. package/src/model/trellis-shared-scatter.ts +144 -0
  191. package/src/model/trellis-splitk-prefill.ts +158 -0
  192. package/src/model/trellis-tiled-prefill.ts +136 -0
  193. package/src/model/trellis-vector-expand.ts +62 -0
  194. package/src/model/trellis-wide-prefill.ts +94 -0
  195. package/src/model/universal/dense.ts +1 -1
  196. package/src/model/universal/modules.ts +8 -1
  197. package/src/native/frame_extract.swift +140 -0
  198. package/src/native-pack.ts +15 -7
  199. package/src/pi-provider.ts +6 -64
  200. package/src/pi-web.ts +14 -320
  201. package/src/prompt-cache.ts +97 -54
  202. package/src/quantize/allocator.ts +1 -1
  203. package/src/quantize/atomic-output.ts +38 -0
  204. package/src/quantize/calibration.ts +2 -1
  205. package/src/quantize/config-writer.ts +16 -3
  206. package/src/quantize/index.ts +28 -1
  207. package/src/quantize/job.ts +11 -0
  208. package/src/quantize/quantizer.ts +265 -100
  209. package/src/quantize/rotate.ts +603 -0
  210. package/src/quantize/safetensors-writer.ts +79 -1
  211. package/src/quantize/sensitivity.ts +41 -59
  212. package/src/quantize/trellis.ts +586 -0
  213. package/src/quantize/weight-transform.ts +292 -0
  214. package/src/responses.ts +130 -3
  215. package/src/runtime-config.ts +81 -0
  216. package/src/sampler.ts +276 -4
  217. package/src/serve/admin-routes.ts +184 -0
  218. package/src/serve/aux-routes.ts +132 -0
  219. package/src/serve/batch-scheduler.ts +8 -1558
  220. package/src/serve/chat-request.ts +391 -0
  221. package/src/serve/chat-stage.ts +195 -0
  222. package/src/serve/checkpoint-identity.ts +64 -0
  223. package/src/serve/completion-executor.ts +354 -0
  224. package/src/serve/completion-sink.ts +157 -0
  225. package/src/serve/discovery-routes.ts +204 -0
  226. package/src/serve/generation-gateway.ts +211 -243
  227. package/src/serve/http.ts +159 -0
  228. package/src/serve/inference-request.ts +79 -0
  229. package/src/serve/isolate.ts +297 -105
  230. package/src/serve/kv-budget.ts +14 -0
  231. package/src/serve/lab-routes.ts +341 -0
  232. package/src/serve/lane-registry.ts +2 -1
  233. package/src/serve/model-admin-routes.ts +155 -0
  234. package/src/serve/model-binding.ts +43 -0
  235. package/src/serve/model-host.ts +512 -0
  236. package/src/serve/openai-wire.ts +103 -0
  237. package/src/serve/parent-application.ts +56 -0
  238. package/src/serve/pipeline.ts +33 -0
  239. package/src/serve/preparation.ts +33 -0
  240. package/src/serve/prompt-response-trace.ts +147 -0
  241. package/src/serve/request-plan.ts +162 -0
  242. package/src/serve/request-prep.ts +346 -0
  243. package/src/serve/response-lease.ts +24 -0
  244. package/src/serve/responses-client.ts +69 -0
  245. package/src/serve/session-completion-engine.ts +110 -0
  246. package/src/serve/session-search.ts +1 -1
  247. package/src/serve/static-routes.ts +101 -0
  248. package/src/serve/text-completion-stage.ts +104 -0
  249. package/src/serve/token-streams.ts +519 -0
  250. package/src/server.ts +523 -3551
  251. package/src/spec/assistant-source.ts +10 -44
  252. package/src/spec/deepspec-source.ts +6 -6
  253. package/src/spec/dflash-source.ts +9 -8
  254. package/src/spec/dspark/ab-stats.ts +3 -3
  255. package/src/spec/dspark/calibration.ts +2 -2
  256. package/src/spec/dspark/deepspec-module.ts +2 -2
  257. package/src/spec/dspark/generate-dflash.ts +2 -2
  258. package/src/spec/dspark/generate.ts +2 -2
  259. package/src/spec/dspark/loader.ts +1 -1
  260. package/src/spec/dspark/module-dflash.ts +5 -4
  261. package/src/spec/dspark/module.ts +3 -3
  262. package/src/spec/dspark/quantize-drafter.ts +1 -1
  263. package/src/spec/generate.ts +1 -1
  264. package/src/spec/glm52-mtp-source.ts +9 -1
  265. package/src/spec/ngram-source.ts +1 -1
  266. package/src/spec/prefix-state.ts +45 -0
  267. package/src/spec/qwen-mtp-source.ts +528 -0
  268. package/src/spec/serve-loop.ts +179 -213
  269. package/src/spec/source.ts +42 -10
  270. package/src/spec/two-model.ts +1 -1
  271. package/src/ssd-cache.ts +95 -24
  272. package/src/ssd-durability.ts +207 -0
  273. package/src/tool-call.ts +51 -14
  274. package/src/train/flash-cce.ts +14 -13
  275. package/src/train/forward.ts +38 -1
  276. package/src/train/loss.ts +3 -2
  277. package/src/train/prefix-shared.ts +5 -1
  278. package/src/train/segmented.ts +9 -6
  279. package/src/train/trainer.ts +9 -8
  280. package/src/vision/preprocess.ts +1 -1
  281. package/src/vision/prompt.ts +48 -2
  282. package/src/vision/qwen3vl-preprocess.ts +409 -0
  283. package/src/vision/qwen3vl-prompt.ts +196 -0
  284. package/src/vision/qwen3vl-tower.ts +555 -0
  285. package/src/vision/video-frames.ts +148 -0
  286. package/src/web/app.html +1 -1
  287. package/src/web/app.js +15 -6
  288. package/src/web/src/adapters-panel.ts +3 -3
  289. package/src/web/src/assistant.ts +2 -36
  290. package/src/web/src/chat.ts +16 -9
  291. package/src/web/src/composer.ts +4 -4
  292. package/src/web/src/hub.ts +1 -1
  293. package/src/web/src/markdown.ts +0 -0
  294. package/src/web/src/memory-panel.ts +1 -1
  295. package/src/web/src/model-picker.ts +1 -1
  296. package/src/web/src/palette.ts +1 -1
  297. package/src/web/src/protocol.ts +3 -39
  298. package/src/web/src/sessions.ts +1 -1
  299. package/src/web/src/ui-catalog.ts +4 -18
  300. package/src/web-assets.ts +47 -0
  301. package/src/weights.ts +134 -33
  302. package/docs/archive/mlx-bun-lab-report.html +0 -275
  303. package/docs/archive/wwdc-mlx-bun.html +0 -848
  304. package/docs/design/adapters-end-to-end.md +0 -205
  305. package/docs/design/batching-perf-path.md +0 -255
  306. package/docs/design/batching-v2-plan.md +0 -529
  307. package/docs/design/bucketing-stage.md +0 -197
  308. package/docs/design/compat-cli-surface-design.md +0 -230
  309. package/docs/design/docs-reorg-plan.md +0 -165
  310. package/docs/design/dspark-serving-program.md +0 -371
  311. package/docs/design/dspark-speculative-decoding.md +0 -268
  312. package/docs/design/grammar-spec-batching-integration.md +0 -301
  313. package/docs/design/memory-inference-path.md +0 -422
  314. package/docs/design/memory-synthesis.md +0 -287
  315. package/docs/design/memory-system.md +0 -237
  316. package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
  317. package/docs/design/omlx-adoption-map.md +0 -98
  318. package/docs/design/orpo-dynamic-lambda.md +0 -220
  319. package/docs/design/orpo-future-enhancements.md +0 -340
  320. package/docs/design/paged-kv-cache.md +0 -214
  321. package/docs/design/parallel-slots.md +0 -567
  322. package/docs/design/parity-tier-dag.md +0 -161
  323. package/docs/design/repo-cleanup-plan.md +0 -189
  324. package/docs/design/runtime-isolation.md +0 -263
  325. package/docs/design/segmented-backward-training.md +0 -543
  326. package/docs/design/spec-decode-larger-targets.md +0 -229
  327. package/docs/design/ssd-kv-cold-tier.md +0 -288
  328. package/docs/design/structured-output.md +0 -484
  329. package/docs/design/tauri-desktop-app.md +0 -127
  330. package/docs/design/the-dreaming-master-plan.md +0 -445
  331. package/docs/design/turboquant-kv.md +0 -235
  332. package/docs/design/w4a16-compute-precision-spike.md +0 -232
  333. package/docs/design/web-chat-beat-matrix.md +0 -315
  334. package/docs/design/write-pipeline-entity.md +0 -468
  335. package/docs/investigations/curve-runs/bisector-route.json +0 -98
  336. package/docs/investigations/curve-runs/bisector-trace.json +0 -1393
  337. package/docs/investigations/curve-runs/curve-terrain.json +0 -1179
  338. package/docs/investigations/curve-runs/curve-terrain.svg +0 -42
  339. package/docs/investigations/hlg-ab.html +0 -34
  340. package/docs/investigations/hlg-explorer.html +0 -238
  341. package/docs/investigations/hlg-figs/cliff.svg +0 -1
  342. package/docs/investigations/hlg-figs/cross.svg +0 -1
  343. package/docs/investigations/hlg-figs/edges.svg +0 -1
  344. package/docs/investigations/hlg-figs/k20-grids.svg +0 -1
  345. package/docs/investigations/hlg-figs/k20-peaks.svg +0 -1
  346. package/docs/investigations/hlg-figs/lw-curve.svg +0 -1
  347. package/docs/investigations/hlg-figs/terrain.svg +0 -1
  348. package/docs/investigations/hlg-report.html +0 -262
  349. package/docs/investigations/hlg-runs/hlg-ab.json +0 -149
  350. package/docs/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
  351. package/docs/investigations/hlg-runs/hlg-blind.json +0 -107
  352. package/docs/investigations/hlg-runs/hlg-cliff.json +0 -197
  353. package/docs/investigations/hlg-runs/hlg-cross.json +0 -505
  354. package/docs/investigations/hlg-runs/hlg-edge.json +0 -388
  355. package/docs/investigations/hlg-runs/hlg-eval1.json +0 -25
  356. package/docs/investigations/hlg-runs/hlg-frontier.json +0 -86
  357. package/docs/investigations/hlg-runs/hlg-grid.json +0 -242
  358. package/docs/investigations/hlg-runs/hlg-grid2.json +0 -343
  359. package/docs/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
  360. package/docs/investigations/hlg-runs/hlg-map.json +0 -383
  361. package/docs/investigations/hlg-runs/run0-original.log +0 -159
  362. package/docs/investigations/hlg-runs/runA-recommended.log +0 -131
  363. package/docs/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
  364. package/docs/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
  365. package/docs/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
  366. package/docs/investigations/hlg-runs/runAD-ab-samples.log +0 -8
  367. package/docs/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
  368. package/docs/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
  369. package/docs/investigations/hlg-runs/runAG-eval-config2.log +0 -16
  370. package/docs/investigations/hlg-runs/runB-gain-variants.log +0 -192
  371. package/docs/investigations/hlg-runs/runC-pivots.log +0 -162
  372. package/docs/investigations/hlg-runs/runD-oetf.log +0 -132
  373. package/docs/investigations/hlg-runs/runE-eotf.log +0 -162
  374. package/docs/investigations/hlg-runs/runF-shaper-working.log +0 -86
  375. package/docs/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
  376. package/docs/investigations/hlg-runs/runH-wsweep.log +0 -176
  377. package/docs/investigations/hlg-runs/runI-asweep.log +0 -176
  378. package/docs/investigations/hlg-runs/runJ-knobmap.log +0 -49
  379. package/docs/investigations/hlg-runs/runK-frontier.log +0 -19
  380. package/docs/investigations/hlg-runs/runL-widemap.log +0 -65
  381. package/docs/investigations/hlg-runs/runM-frontier-emb.log +0 -15
  382. package/docs/investigations/hlg-runs/runN-verify.log +0 -116
  383. package/docs/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
  384. package/docs/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
  385. package/docs/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
  386. package/docs/investigations/hlg-runs/runR-blind-transcript.log +0 -79
  387. package/docs/investigations/hlg-runs/runS-cross-coherence.log +0 -33
  388. package/docs/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
  389. package/docs/investigations/hlg-runs/runU-cross-12b.log +0 -33
  390. package/docs/investigations/hlg-runs/runV-lw-e4b.log +0 -22
  391. package/docs/investigations/hlg-runs/runW-lw-12b.log +0 -22
  392. package/docs/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
  393. package/docs/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
  394. package/docs/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
  395. package/docs/planning/IDEAS.md +0 -69
  396. package/docs/reference/embedding.md +0 -197
  397. package/docs/reference/features-matrix.md +0 -100
  398. package/docs/reference/orpo-quickstart.md +0 -135
  399. package/src/flags.ts +0 -13
  400. /package/docs/{investigations → archive/investigations}/chunk-finetune-experiment.md +0 -0
  401. /package/docs/{investigations → archive/investigations}/colibri-oracle-pin.md +0 -0
  402. /package/docs/{investigations → archive/investigations}/curve-bisector-routing.md +0 -0
  403. /package/docs/{investigations → archive/investigations}/decode-roofline-lookagain.md +0 -0
  404. /package/docs/{investigations → archive/investigations}/dspark-handoff.md +0 -0
  405. /package/docs/{investigations → archive/investigations}/expert-offload-single-user-moe.md +0 -0
  406. /package/docs/{investigations → archive/investigations}/indexshare-performance-spike.md +0 -0
  407. /package/docs/{investigations → archive/investigations}/kernel-perf-review-2026-07.md +0 -0
  408. /package/docs/{investigations → archive/investigations}/lab-build-journal.md +0 -0
  409. /package/docs/{investigations → archive/investigations}/openwiki-evaluation.md +0 -0
  410. /package/docs/{investigations → archive/investigations}/orpo-base-uf-experiment-and-directions.md +0 -0
  411. /package/docs/{investigations → archive/investigations}/orpo-flash-cce-pin-leak.md +0 -0
  412. /package/docs/{investigations → archive/investigations}/orpo-uf-testing-handoff.md +0 -0
  413. /package/docs/{investigations → archive/investigations}/pi-builtin-investigation.md +0 -0
  414. /package/docs/{investigations → archive/investigations}/segmented-backward-handoff.md +0 -0
  415. /package/docs/{investigations → archive/investigations}/starter-model-port-handoff.md +0 -0
  416. /package/docs/{investigations → archive/investigations}/steel-flash-cce-handoff.md +0 -0
  417. /package/docs/{investigations → archive/investigations}/trainer-validation-experiment.md +0 -0
  418. /package/docs/{planning → archive/planning}/chat-ui-vision-memory-first.md +0 -0
  419. /package/docs/{planning → archive/planning}/journal.md +0 -0
  420. /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
  421. /package/docs/{planning → archive/planning}/release-notes-v0.0.10.md +0 -0
  422. /package/docs/{planning → archive/planning}/release-notes-v0.0.11.md +0 -0
  423. /package/docs/{planning → archive/planning}/release-notes-v0.0.12.md +0 -0
  424. /package/docs/{planning → archive/planning}/release-notes-v0.0.13.md +0 -0
  425. /package/docs/{planning → archive/planning}/web-ui-pass-plan.md +0 -0
  426. /package/{docs/investigations → src/serve/assets}/curve-terrain.html +0 -0
  427. /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
@@ -0,0 +1,18 @@
1
+ # src/lab — quarantined, no-oracle code
2
+
3
+ Everything here is a Lab-tier module (docs/design/unified-engine-frontier-plan.md):
4
+ no external oracle, off by default, kept only while a measurement decides its
5
+ fate (PLAN.md "Maintainability program", D6 darlings). Rules, gate-enforced by
6
+ `scripts/check-hygiene.ts` (check 11):
7
+
8
+ - nothing outside `src/lab/` may import from `src/lab/` except the edges listed
9
+ in `LAB_IMPORT_ALLOWLIST` — those are recorded debt, not permission;
10
+ - a new production dependency on a Lab module is a decision, not a default:
11
+ either promote the module out of the lab with an oracle/measurement, or
12
+ route it through an option seam.
13
+
14
+ | module | flag / entry | oracle |
15
+ |---|---|---|
16
+ | `curve/` (curve sampler + designer page) | `--curve-*`, `/curves` | none (Lab) |
17
+ | `paged-kv/` | `--paged-kv` (serial-only) | parity vs plain KVCache (bit-exact), no measured win |
18
+ | `expert-trace/` | `MLX_BUN_EXPERT_TRACE` hook in gemma4 | closed study (expert-offload) |
@@ -88,7 +88,7 @@ button.ghost{background:var(--card);border:1px solid var(--bd);border-radius:8px
88
88
 
89
89
  <script>
90
90
  const ln=Math.log;
91
- const UMIN=ln(1e-6); // bottom-left / plot floor (matches src/curve-sampler.ts CURVE_UMIN)
91
+ const UMIN=ln(1e-6); // bottom-left / plot floor (matches src/lab/curve/curve-sampler.ts CURVE_UMIN)
92
92
  const ENGINE_BASE = location.protocol.startsWith("http") ? location.origin : "http://localhost:8080";
93
93
  // ---- curve state: 5 movable control points, sorted ascending in p_in ----
94
94
  function identityPoints(){ return [{x_pct:1e-4,y_pct:1e-4},{x_pct:0.1,y_pct:0.1},{x_pct:1,y_pct:1},{x_pct:9,y_pct:9},{x_pct:100,y_pct:100}]; }
@@ -102,7 +102,7 @@ const PROMPTS=[
102
102
  "Describe the feeling of a city at 3am in one vivid sentence.",
103
103
  ];
104
104
 
105
- // ---- math: monotone cubic (PCHIP), IDENTICAL to src/curve-sampler.ts ----
105
+ // ---- math: monotone cubic (PCHIP), IDENTICAL to src/lab/curve/curve-sampler.ts ----
106
106
  const Lp=pt=>[ln(Math.max(pt.x_pct,1e-9)/100),ln(Math.max(pt.y_pct,1e-9)/100)];
107
107
  function knotArrays(c){ const k=c.points.map(Lp).sort((a,b)=>a[0]-b[0]); return {X:k.map(q=>q[0]),Y:k.map(q=>q[1])}; }
108
108
  function secants(c){ const {X,Y}=knotArrays(c),s=[]; for(let i=X.length-1;i>0;i--)s.push((Y[i]-Y[i-1])/(X[i]-X[i-1])); return s; }
@@ -16,8 +16,8 @@
16
16
  // BELOW lowers them (renorm makes the literal number an intent); the SLOPE across
17
17
  // a region sets how evenly the tokens inside it spread (flat<1 even, steep>1 separated).
18
18
 
19
- import { MlxArray } from "./mlx/array";
20
- import * as ops from "./mlx/ops";
19
+ import { MlxArray } from "../../mlx/array";
20
+ import * as ops from "../../mlx/ops";
21
21
 
22
22
  export interface CurvePoint { x_pct: number; y_pct: number; }
23
23
  export interface CurveParams {
@@ -2,13 +2,13 @@
2
2
  //
3
3
  // Records every MoE router decision as JSONL — one record per router call:
4
4
  // { c: callSeq, l: layerIdx, s: [B, L, k], i: [flattened uint32 expert ids] }
5
- // Analyse with scripts/analyze-expert-trace.ts (coverage curve, working-set
5
+ // Analyse with analyze-expert-trace.ts (deleted 2026-08-23; git history) (coverage curve, working-set
6
6
  // size, within-task stability, cross-task specialisation).
7
7
  //
8
8
  // Two ways to drive it:
9
9
  // - env: `MLX_BUN_EXPERT_TRACE=<path> mlx-bun serve ...` (auto-starts here)
10
10
  // - programmatic: beginExpertTrace(path) / endExpertTrace() per domain
11
- // (scripts/run-expert-trace.ts uses this to trace several domains from
11
+ // (run-expert-trace.ts (deleted 2026-08-23; git history) uses this to trace several domains from
12
12
  // one model load).
13
13
  //
14
14
  // Recording adds a per-call GPU->host sync (it reads the routed indices back),
@@ -17,8 +17,9 @@
17
17
  // Router.forward is a single branch (zero overhead) — inert by default, same
18
18
  // discipline as the slots flag.
19
19
 
20
- import { Dtype } from "./mlx/ffi";
21
- import type { MlxArray } from "./mlx/array";
20
+ import { Dtype } from "../../mlx/ffi";
21
+ import type { MlxArray } from "../../mlx/array";
22
+ import { runtimeValue } from "../../runtime-config";
22
23
 
23
24
  let sink: Bun.FileSink | null = null;
24
25
  let seq = 0;
@@ -61,7 +62,8 @@ export function recordRouting(layer: number, indices: MlxArray): void {
61
62
  if (++written % 2000 === 0) sink.flush();
62
63
  }
63
64
 
64
- if (process.env.MLX_BUN_EXPERT_TRACE) beginExpertTrace(process.env.MLX_BUN_EXPERT_TRACE);
65
+ const expertTracePath = runtimeValue("MLX_BUN_EXPERT_TRACE");
66
+ if (expertTracePath) beginExpertTrace(expertTracePath);
65
67
 
66
68
  process.on("exit", () => { try { endExpertTrace(); } catch { /* best-effort */ } });
67
69
  process.on("SIGINT", () => { endExpertTrace(); process.exit(0); });
@@ -1,5 +1,5 @@
1
1
  // Paged KV cache — vLLM-style block-pool storage behind the standard Cache
2
- // interface (docs/design/paged-kv-cache.md). OPTIONAL and default-off
2
+ // interface (docs/design/kv-cache.md). OPTIONAL and default-off
3
3
  // (`--paged-kv` / GenerateOptions.pagedKv); v1 scope is serial batch=1
4
4
  // decode on Gemma4-family plain full-attention layers, bf16 only.
5
5
  //
@@ -11,7 +11,7 @@
11
11
  // block axis + transpose + reshape) and hand that to the unchanged
12
12
  // ops.sdpa call site — identical bytes to what a plain KVCache would have
13
13
  // fetched, so the paged path is gated bit-exact, not KL-tolerated
14
- // (tests/paged-kv-parity.test.ts). The gather IS the cost: a full K/V copy
14
+ // (tests/parity/paged-kv-parity.test.ts). The gather IS the cost: a full K/V copy
15
15
  // per step, pure bandwidth tax at batch=1 — v1 ships the abstraction for
16
16
  // the batched/CoW follow-ups, not a speed win (see the design doc's
17
17
  // Motivation, which says this honestly).
@@ -21,10 +21,10 @@
21
21
  // gateway's #modelCachesBatchable, generated forwards' #matches() — must
22
22
  // EXCLUDE paged caches and fall back to the monolith/uncompiled path.
23
23
 
24
- import { MlxArray } from "../mlx/array";
25
- import { Dtype } from "../mlx/ffi";
26
- import * as ops from "../mlx/ops";
27
- import { createCausalMask, type Cache, type Mask } from "./gemma4-base";
24
+ import { MlxArray } from "../../mlx/array";
25
+ import { Dtype } from "../../mlx/ffi";
26
+ import * as ops from "../../mlx/ops";
27
+ import { createCausalMask, type Cache, type Mask } from "../../model/gemma4-base";
28
28
 
29
29
  /** Typed pool-exhaustion error: a generation outgrew its pool. Sizing from
30
30
  * prompt+maxTokens at construction makes this unreachable in practice;
@@ -134,6 +134,9 @@ export class BlockPool {
134
134
  * shape): head count / head dims / dtype come from the first k/v pair,
135
135
  * so the wiring (maybePageKv) needs no per-model shape plumbing. */
136
136
  export class PagedKVCache implements Cache {
137
+ /** Distinct kind: paged layout is serial-only and never merges into a
138
+ * batch, so no capability guard should ever match it as plain. */
139
+ signature(): string { return "kv:paged"; }
137
140
  /** Matches KVCache.STEP: v1's growth granularity is a permutation of
138
141
  * today's 256-token step into fixed reusable slots, not a new tuning
139
142
  * axis (--paged-kv-block-size overrides for experiments). */
package/src/library.ts ADDED
@@ -0,0 +1,40 @@
1
+ /** CPU-safe high-level entry. Native modules load only after bootstrap. */
2
+ export { createInferenceEngine } from "./engine/engine";
3
+ export { CancellationSource } from "./engine/cancellation";
4
+ export { createCompletionClient, createDirectHost } from "./client";
5
+ export type * from "./contracts/generation";
6
+ export type * from "./contracts/completion";
7
+ export type * from "./contracts/host";
8
+
9
+ /** Install/locate the native pack before importing the compatibility API. */
10
+ export async function initializeMlx(): Promise<typeof import("./index")> {
11
+ const { ensureNativeRuntime } = await import("./native-pack");
12
+ await ensureNativeRuntime();
13
+ return import("./index");
14
+ }
15
+
16
+ /** Own one local worker. Model resolution and native initialization remain in
17
+ * the existing CLI; importing this module does not start it or download weights. */
18
+ export async function openIsolatedHost(model: string, options: {
19
+ arguments?: readonly string[];
20
+ command?: readonly string[];
21
+ readyTimeoutMs?: number;
22
+ } = {}): Promise<import("./contracts/host").EngineHost<Request, Response>> {
23
+ const { EngineChild } = await import("./serve/isolate");
24
+ const { tmpdir } = await import("node:os");
25
+ const { join } = await import("node:path");
26
+ const { fileURLToPath } = await import("node:url");
27
+ const source = fileURLToPath(new URL("./cli.ts", import.meta.url));
28
+ if (!options.command && source.includes("$bunfs"))
29
+ throw new Error("a compiled library consumer must supply the mlx-bun executable as command");
30
+ const command = options.command ?? [process.execPath, source];
31
+ if (!command.length) throw new Error("engine command must not be empty");
32
+ const socketPath = join(tmpdir(), `mlx-bun-library-${crypto.randomUUID()}.sock`);
33
+ const host = new EngineChild({
34
+ argv: [...command,
35
+ "serve", "--model", model, ...options.arguments ?? [], "--unix", socketPath, "--no-open"],
36
+ socketPath, readyTimeoutMs: options.readyTimeoutMs,
37
+ });
38
+ try { await host.ready; return host; }
39
+ catch (error) { await host.close(); throw error; }
40
+ }
package/src/lora.ts CHANGED
@@ -12,6 +12,7 @@
12
12
 
13
13
  import { existsSync, readdirSync, statSync } from "node:fs";
14
14
  import { resolve } from "node:path";
15
+ import { createHash } from "node:crypto";
15
16
  import { ptr, read } from "bun:ffi";
16
17
  import { MlxArray, cpuStream } from "./mlx/array";
17
18
  import { C } from "./mlx/ffi";
@@ -198,6 +199,7 @@ export class AdapterManager {
198
199
  readonly #mounted = new Map<string, AdapterInfo>();
199
200
  /** Owned adapter arrays per id (disposed on unmount). */
200
201
  readonly #arrays = new Map<string, MlxArray[]>();
202
+ readonly #revisions = new Map<string, string>();
201
203
 
202
204
  constructor(model: RuntimeModel) {
203
205
  this.#model = model;
@@ -216,6 +218,11 @@ export class AdapterManager {
216
218
 
217
219
  const weightsFile = adapterWeightsFile(path);
218
220
  const { scale, rank: configRank, rsLora } = await readAdapterScale(path);
221
+ // Hash once at mount, before native allocation. Reusing an adapter name
222
+ // for different weights or scale must never reuse its prefix/checkpoint.
223
+ const digest = createHash("sha256").update(JSON.stringify({ scale, rank: configRank, rsLora }));
224
+ for await (const chunk of Bun.file(weightsFile).stream()) digest.update(chunk);
225
+ const revision = digest.digest("hex");
219
226
  const tensors = loadAdapterTensors(weightsFile);
220
227
  const targets = this.#model.loraTargets();
221
228
 
@@ -347,6 +354,7 @@ export class AdapterManager {
347
354
  ramBytes,
348
355
  };
349
356
  this.#mounted.set(id, info);
357
+ this.#revisions.set(id, revision);
350
358
  this.#arrays.set(id, validated.flatMap(({ lw }) => [lw.a, lw.b]));
351
359
  return info;
352
360
  }
@@ -355,6 +363,7 @@ export class AdapterManager {
355
363
  * stay in place for other adapters, like the reference. */
356
364
  unmount(id: string): number {
357
365
  if (!this.#mounted.delete(id)) return 0;
366
+ this.#revisions.delete(id);
358
367
  let removed = 0;
359
368
  for (const linear of this.#model.loraTargets().values()) {
360
369
  if (linear.adapters?.delete(id)) removed++;
@@ -366,6 +375,15 @@ export class AdapterManager {
366
375
  return removed;
367
376
  }
368
377
 
378
+ /** Ordered, content-addressed namespace for the actual mounted composition. */
379
+ cacheNamespace(ids: readonly string[]): string {
380
+ return ids.map((id) => {
381
+ const revision = this.#revisions.get(id);
382
+ if (!revision) throw new Error(`adapter ${id} is no longer mounted`);
383
+ return `${id}@${revision}`;
384
+ }).join("+");
385
+ }
386
+
369
387
  list(): AdapterInfo[] {
370
388
  return [...this.#mounted.values()];
371
389
  }
@@ -17,10 +17,11 @@
17
17
  // preserving the logical Host header and HTTPS SNI, so the transport cannot
18
18
  // re-resolve to a private address between validation and connect.
19
19
  //
20
- // This module must stay free of mlx imports so tests/media-fetch.test.ts
20
+ // This module must stay free of mlx imports so tests/unit/media-fetch.test.ts
21
21
  // runs model-free.
22
22
 
23
23
  import { lookup } from "node:dns/promises";
24
+ import { runtimeValue } from "./runtime-config";
24
25
 
25
26
  export interface MediaFetchPolicy {
26
27
  /** Permit private/loopback/link-local destinations (LAN hosts). */
@@ -71,7 +72,7 @@ export interface RestrictedFetchResult {
71
72
 
72
73
  export function defaultMediaFetchPolicy(): MediaFetchPolicy {
73
74
  return {
74
- allowPrivate: process.env.MLX_BUN_ALLOW_PRIVATE_MEDIA === "1",
75
+ allowPrivate: runtimeValue("MLX_BUN_ALLOW_PRIVATE_MEDIA") === "1",
75
76
  // 10 s / 64 MB: well above what a 30 s audio clip (~5 MB of 16 kHz
76
77
  // WAV, less compressed) or any supported image needs.
77
78
  timeoutMs: 10_000,
@@ -405,10 +406,19 @@ export async function fetchRestrictedHttpBytes(
405
406
  * redirect hop re-validated, one wall-clock timeout, and a streaming
406
407
  * size cap. Throws Error with a client-presentable message — callers
407
408
  * surface it as a 400 (the prompt-build catch in server.ts). */
409
+ /** Video clips are legitimately larger than images/audio: same SSRF guard
410
+ * and timeout discipline, quadruple the body cap (a 30 s 1080p H.264 clip
411
+ * runs tens of MB; frame sampling truncates long clips server-side). */
412
+ export function videoMediaFetchPolicy(): MediaFetchPolicy {
413
+ return { ...defaultMediaFetchPolicy(), maxBytes: 256 * 1024 * 1024 };
414
+ }
415
+
408
416
  export async function fetchMediaBytes(
409
417
  url: string,
410
- kind: "image" | "audio",
411
- policy: MediaFetchPolicy = defaultMediaFetchPolicy(),
418
+ kind: "image" | "audio" | "video",
419
+ policy: MediaFetchPolicy = kind === "video"
420
+ ? videoMediaFetchPolicy()
421
+ : defaultMediaFetchPolicy(),
412
422
  ): Promise<Uint8Array> {
413
423
  if (url.startsWith("data:")) {
414
424
  const comma = url.indexOf(",");
@@ -416,6 +426,12 @@ export async function fetchMediaBytes(
416
426
  const meta = url.slice(0, comma);
417
427
  const body = url.slice(comma + 1);
418
428
  if (!meta.includes("base64")) throw new Error("data: URL must be base64");
429
+ // The same body cap as the HTTP path — inline payloads must not become
430
+ // the uncapped route (base64 inflates 4/3, so check pre-decode too).
431
+ if (body.length * 0.75 > policy.maxBytes)
432
+ throw new Error(
433
+ `${kind} data: URL exceeds the ${Math.round(policy.maxBytes / 1024 / 1024)} MB cap`,
434
+ );
419
435
  return Uint8Array.from(Buffer.from(body, "base64"));
420
436
  }
421
437
  return (
@@ -3,7 +3,7 @@
3
3
  // The Dreaming write pipeline emits Wikipedia-shaped articles: each entity gets
4
4
  // one Markdown file with a fixed top-to-bottom skeleton —
5
5
  // H1 → series banner → infobox → lead → sections → See also → Notes → References
6
- // (see docs/design/the-dreaming-master-plan.md and write-pipeline-entity.md).
6
+ // (see docs/design/dreaming-nightly-pipeline.md and docs/design/dreaming-nightly-pipeline.md).
7
7
  // This module reads that grammar deterministically: the structured outputs the
8
8
  // read side traverses (infobox key-value facts, the [[wikilink]] graph, footnote
9
9
  // bijection) are parsed here, never by the model.
@@ -16,7 +16,7 @@
16
16
  //
17
17
  // Re-chunk only conversations whose updated_at > chunked_at (NULL chunked_at = a
18
18
  // never-segmented conversation). Setting chunked_at after a successful run makes
19
- // re-running a no-op. See docs/design/the-dreaming-master-plan.md → P5-T2.
19
+ // re-running a no-op. See docs/design/dreaming-nightly-pipeline.md → P5-T2.
20
20
 
21
21
  import { MemoryStore, chunkId } from "./db";
22
22
  import { callLocal, type LocalInput } from "./model";
@@ -24,12 +24,9 @@
24
24
  // server and to the trainer's prompt-region render (dataset.ts:98-101).
25
25
 
26
26
  import { existsSync, readdirSync } from "node:fs";
27
- import { loadTaskModel, greedyDecodeBitExact, type TaskModel } from "../eval/runner";
28
27
  import type { ChatMessage, ChatTemplate } from "../chat-template";
29
28
  import type { LoadedTokenizer } from "../tokenizer";
30
- import { BatchScheduler, type RowSampler } from "../serve/batch-scheduler";
31
- import * as ops from "../mlx/ops";
32
- import type { MlxArray } from "../mlx/array";
29
+ import { runtimeValue } from "../runtime-config";
33
30
 
34
31
  const HF_HUB = `${process.env.HOME}/.cache/huggingface/hub`;
35
32
  const E4B_REPO = "models--mlx-community--gemma-4-e4b-it-OptiQ-4bit";
@@ -37,7 +34,7 @@ const E4B_REPO = "models--mlx-community--gemma-4-e4b-it-OptiQ-4bit";
37
34
  /** Resolve the e4b snapshot dir by globbing snapshots/ for the one carrying
38
35
  * config.json — so a freshly-downloaded model needs no hardcoded commit hash.
39
36
  * Returns an _unresolved sentinel (precheck → STOP) until the download lands.
40
- * Mirrors tests/paths.ts hfSnapshot / SNAPSHOT_E4B, inlined to keep src self-
37
+ * Mirrors tests/support/paths.ts hfSnapshot / SNAPSHOT_E4B, inlined to keep src self-
41
38
  * contained (no src→tests dependency). */
42
39
  function resolveE4bSnapshot(): string {
43
40
  const base = `${HF_HUB}/${E4B_REPO}/snapshots`;
@@ -139,183 +136,48 @@ export function memoryPromptIds(
139
136
  /** Async mutex: acquire() resolves to a release fn; releases run FIFO. Serializes
140
137
  * memory generation ops so the global `loraState.active` is owned exclusively for
141
138
  * one op's lifetime (set for the chunk batch, reset after). */
142
- class AsyncMutex {
143
- #tail: Promise<void> = Promise.resolve();
144
- acquire(): Promise<() => void> {
145
- let release!: () => void;
146
- const gate = new Promise<void>((r) => { release = r; });
147
- const wait = this.#tail;
148
- this.#tail = this.#tail.then(() => gate);
149
- return wait.then(() => release);
150
- }
151
- }
152
-
153
- interface MemoryRuntime {
154
- tm: TaskModel;
155
- template: ChatTemplate;
156
- scheduler: BatchScheduler;
157
- /** True once the `memory-chunk` adapter is mounted on the shared model. */
158
- hasChunkAdapter: boolean;
139
+ export interface MemoryCompletionRequest {
140
+ readonly stage: string;
141
+ readonly input: LocalInput;
142
+ readonly maxTokens: number;
159
143
  }
144
+ export type MemoryCompletionClient = import("../contracts/completion").BatchCompletionClient<MemoryCompletionRequest, string>;
160
145
 
161
146
  /** Max rows in a memory batch (continuous-batching width). 1 disables batching
162
147
  * (callLocalBatch then loops on the bit-exact greedy fallback).
163
148
  * Default 1 (serial): batching measured 1.7-1.9x SLOWER for the real
164
149
  * extract/chunk workload (heterogeneous prefills pad) and can diverge on
165
- * near-ties — see docs/design/memory-inference-path.md "Verification results".
150
+ * near-ties — see docs/design/dreaming-nightly-pipeline.md "Verification results".
166
151
  * Opt back in with MLX_BUN_MEMORY_BATCH=8 if a length-bucketed scheduler
167
152
  * lands. (Decision: Josh, 2026-07-01.) */
168
153
  export function memoryBatchSize(): number {
169
- const n = Number(process.env.MLX_BUN_MEMORY_BATCH ?? "1");
154
+ const n = Number(runtimeValue("MLX_BUN_MEMORY_BATCH") ?? "1");
170
155
  return Number.isFinite(n) && n >= 1 ? Math.floor(n) : 1;
171
156
  }
172
157
 
173
- function requireModel(): void {
174
- if (!existsSync(`${MODEL_ID}/config.json`)) {
175
- throw new Error(
176
- `memory: Gemma-4-e4b is not downloaded (looked under ${HF_HUB}/${E4B_REPO}/snapshots). ` +
177
- `Fetch it first:\n HF_HUB_DISABLE_XET=1 hf download mlx-community/gemma-4-e4b-it-OptiQ-4bit`,
178
- );
179
- }
180
- }
181
-
182
- let runtimePromise: Promise<MemoryRuntime> | null = null;
183
- const opLock = new AsyncMutex(); // serializes whole memory ops (loraState ownership)
184
- const schedLock = new AsyncMutex(); // the scheduler's exclusive-GPU lock
185
-
186
- /** Lazily load the ONE shared model + mount memory-chunk + build the scheduler.
187
- * Never auto-downloads; throws (with the fetch hint) until the snapshot lands. */
188
- async function getRuntime(): Promise<MemoryRuntime> {
189
- if (!runtimePromise) {
190
- runtimePromise = (async () => {
191
- requireModel();
192
- const tm = await loadTaskModel(MODEL_ID); // base e4b, no adapter
193
- if (!tm.template) throw new Error(`memory: ${MODEL_ID} has no chat template`);
194
- let hasChunkAdapter = false;
195
- const chunkDir = adapterDirFor("chunk");
196
- if (chunkDir) {
197
- // Mount ONCE; activation is per-call via loraState.active (see decodeGreedy
198
- // / batchDecode). Mounting alone runs base — it must be activated.
199
- const { AdapterManager } = await import("../lora");
200
- await new AdapterManager(tm.model).mount("memory-chunk", chunkDir);
201
- hasChunkAdapter = true;
202
- }
203
- const scheduler = new BatchScheduler(tm.model, {
204
- maxBatch: memoryBatchSize(),
205
- lock: { acquire: () => schedLock.acquire() },
206
- });
207
- return { tm, template: tm.template, scheduler, hasChunkAdapter };
208
- })();
209
- }
210
- return runtimePromise;
211
- }
212
-
213
- /** The adapters to activate for a stage. Only `chunk` is adapter-bound (and only
214
- * when `memory-chunk` is mounted); every base stage runs with `active = []`. */
215
- function activeFor(stage: string, rt: MemoryRuntime): string[] {
216
- return stage === "chunk" && rt.hasChunkAdapter ? ["memory-chunk"] : [];
217
- }
218
-
219
- /** One bit-exact greedy decode for a stage (raw-forward argmax, runner parity).
220
- * Sets the stage's adapter for the call and resets `active` to [] after. */
221
- function decodeGreedy(rt: MemoryRuntime, stage: string, input: LocalInput, maxTokens: number): string {
222
- const active = activeFor(stage, rt);
223
- rt.tm.model.loraState.active = active;
224
- const ids = memoryPromptIds(stage, input, rt.tm.tokenizer, rt.template);
225
- try {
226
- return greedyDecodeBitExact(
227
- { ...rt.tm, activeAdapters: active.length ? active : undefined },
228
- ids,
229
- maxTokens,
230
- );
231
- } finally {
232
- rt.tm.model.loraState.active = [];
233
- }
234
- }
235
-
236
- /** Batched decode for N independent inputs of ONE stage. Sets the stage adapter
237
- * globally for the whole batch (the scheduler reads `loraState.active` per
238
- * forward, so all rows get it uniformly — a batch is necessarily single-adapter),
239
- * submits every row to the shared scheduler with a greedy per-row sampler, and
240
- * resets `active` after all rows finish. Order-preserving: out[i] ↔ inputs[i].
241
- *
242
- * Decode-path caveat: this samples via forwardHidden/logitsFromHidden (the
243
- * scheduler), which can diverge from the runner's raw-forward greedy on near-ties
244
- * past ~32 tokens. For the JSON/binary memory stages (validated by
245
- * well-formedness) this is acceptable; the single-call fallback stays bit-exact. */
246
- async function batchDecode(
247
- rt: MemoryRuntime,
248
- stage: string,
249
- inputs: LocalInput[],
250
- maxTokens: number,
251
- ): Promise<string[]> {
252
- const active = activeFor(stage, rt);
253
- rt.tm.model.loraState.active = active;
254
- const eos = rt.tm.config.eosTokenIds;
255
- const greedySample: RowSampler = (logits1V: MlxArray) => ops.argmaxAxis(logits1V, -1);
256
- try {
257
- return await Promise.all(
258
- inputs.map((inp) => {
259
- const ids = memoryPromptIds(stage, inp, rt.tm.tokenizer, rt.template);
260
- const toks: number[] = [];
261
- return rt.scheduler
262
- .submit({
263
- promptIds: ids,
264
- maxTokens,
265
- eosTokenIds: eos,
266
- sample: greedySample,
267
- onToken: (t) => { toks.push(t); },
268
- })
269
- .then(() => rt.tm.tokenizer.decode(toks, true));
270
- }),
271
- );
272
- } finally {
273
- rt.tm.model.loraState.active = [];
274
- }
275
- }
276
-
277
- // ---------------------------------------------------------------------------
278
- // Public seams
279
- // ---------------------------------------------------------------------------
280
158
 
281
- /** Call the local model for a pipeline `stage` with a {system?, user} input.
282
- * Renders through the shared chat-template path, runs the MODEL precheck, and
283
- * decodes ONE completion on the bit-exact greedy path (the safe fallback used by
284
- * every single-call site). NEVER auto-downloads. */
285
- export async function callLocal(
286
- stage: string,
287
- input: LocalInput,
288
- opts?: { maxTokens?: number },
289
- ): Promise<string> {
290
- const rt = await getRuntime();
291
- const release = await opLock.acquire();
292
- try {
293
- return decodeGreedy(rt, stage, input, opts?.maxTokens ?? 256);
294
- } finally {
295
- release();
296
- }
159
+ /** One configured client per pipeline. Importing this module does not load MLX. */
160
+ export function createMemoryCalls(client: MemoryCompletionClient) {
161
+ return {
162
+ callLocal(stage: string, input: LocalInput, opts?: { maxTokens?: number }): Promise<string> {
163
+ return client.complete({ stage, input, maxTokens: opts?.maxTokens ?? 256 });
164
+ },
165
+ callLocalBatch(stage: string, inputs: LocalInput[], opts?: { maxTokens?: number }): Promise<string[]> {
166
+ if (!inputs.length) return Promise.resolve([]);
167
+ return client.completeBatch(inputs.map((input) => ({ stage, input, maxTokens: opts?.maxTokens ?? 256 })));
168
+ },
169
+ };
297
170
  }
298
171
 
299
- /** N independent calls for ONE stage (homogeneous adapter by construction).
300
- * Routed through the shared in-process BatchScheduler when batch>1 AND N>1, else
301
- * looped on the bit-exact greedy fallback. Order-preserving: out[i] ↔ inputs[i]. */
302
- export async function callLocalBatch(
303
- stage: string,
304
- inputs: LocalInput[],
305
- opts?: { maxTokens?: number },
306
- ): Promise<string[]> {
307
- if (inputs.length === 0) return [];
308
- const rt = await getRuntime();
309
- const maxTokens = opts?.maxTokens ?? 256;
310
- const release = await opLock.acquire();
311
- try {
312
- if (inputs.length === 1 || memoryBatchSize() <= 1) {
313
- const out: string[] = [];
314
- for (const inp of inputs) out.push(decodeGreedy(rt, stage, inp, maxTokens));
315
- return out;
316
- }
317
- return await batchDecode(rt, stage, inputs, maxTokens);
318
- } finally {
319
- release();
320
- }
321
- }
172
+ const defaultCalls = createMemoryCalls({
173
+ async complete(request) {
174
+ const { memoryCompletionClient } = await import("../backends/mlx/memory-client");
175
+ return memoryCompletionClient.complete(request);
176
+ },
177
+ async completeBatch(requests) {
178
+ const { memoryCompletionClient } = await import("../backends/mlx/memory-client");
179
+ return memoryCompletionClient.completeBatch(requests);
180
+ },
181
+ });
182
+ export const callLocal = defaultCalls.callLocal;
183
+ export const callLocalBatch = defaultCalls.callLocalBatch;
@@ -7,7 +7,7 @@
7
7
  // Topics_to_Ignore.md) and are read in at synthesis time so the system governs
8
8
  // itself in its own substrate.
9
9
  //
10
- // See docs/design/memory-system.md → "Synthesis runs on the local model".
10
+ // See docs/design/dreaming-nightly-pipeline.md → "Synthesis runs on the local model".
11
11
 
12
12
  import { readFileSync } from "node:fs";
13
13
  import { join } from "node:path";
@@ -11,7 +11,7 @@
11
11
  // rest of server.ts's /api/* conventions. Route dispatch (matching
12
12
  // url.pathname + method) lives in server.ts; this file is the pure handler
13
13
  // bodies so they're unit-testable without booting a model context (see
14
- // tests/memory-rest.test.ts).
14
+ // tests/using/memory-rest.test.ts).
15
15
 
16
16
  import { realpath } from "node:fs/promises";
17
17
  import { tmpdir } from "node:os";
@@ -11,7 +11,7 @@
11
11
  // it. That job runs the full synthesis DAG and produces/updates articles on the
12
12
  // schedule with no further configuration.
13
13
  //
14
- // See docs/design/memory-system.md → "Scheduling (launchd)".
14
+ // See docs/design/dreaming-nightly-pipeline.md → "Scheduling (launchd)".
15
15
 
16
16
  import { spawn } from "node:child_process";
17
17
  import { mkdir, rm, writeFile } from "node:fs/promises";
@@ -356,7 +356,10 @@ export async function runExtractStage(
356
356
  // SERIAL fallback (injected `call`, single chunk, or batching disabled).
357
357
  let extracted = 0;
358
358
  for (const id of targetIds) {
359
- await extractEntities(store, id, { resolver, policy: policy || undefined, call: opts.call });
359
+ // Pass the (possibly empty) policy through: `|| undefined` made extractEntities
360
+ // re-load Meta/Entities.md and THROW on a vault without it, while the batched
361
+ // path above proceeds with "" — the serial path must match.
362
+ await extractEntities(store, id, { resolver, policy, call: opts.call });
360
363
  extracted++;
361
364
  if (extracted % 10 === 0) {
362
365
  opts.onEvent?.({ type: "log", message: ` extracted ${extracted}/${target} chunks` });
@@ -9,7 +9,7 @@
9
9
  // an already-working system at real scale — they're pure filesystem functions
10
10
  // with no MCP coupling, so they drop straight in. We keep lucien's good
11
11
  // descriptive concepts (articles, [[wikilinks]], Meta governance pages,
12
- // conv: citations) and drop the mythology. See docs/design/memory-system.md.
12
+ // conv: citations) and drop the mythology. See docs/design/dreaming-nightly-pipeline.md.
13
13
  //
14
14
  // M0 is the read path: setup the vault, then list / read / search / index it.
15
15
  // Synthesis (writing articles from conversations) is M1 and lives elsewhere.
@@ -19,12 +19,13 @@ import { homedir } from "node:os";
19
19
  import { isAbsolute, join } from "node:path";
20
20
  import { spawn } from "node:child_process";
21
21
  import { isFenceLine } from "./article";
22
+ import { runtimeValue } from "../runtime-config";
22
23
 
23
24
  // ---- paths -----------------------------------------------------------
24
25
 
25
26
  /** Root of the memory vault: ~/.mlx-bun/wiki (override with MLX_BUN_WIKI). */
26
27
  export function vaultRoot(): string {
27
- const override = process.env.MLX_BUN_WIKI?.trim();
28
+ const override = runtimeValue("MLX_BUN_WIKI")?.trim();
28
29
  if (override) return override.replace(/^~(?=$|[/\\])/, homedir());
29
30
  return join(homedir(), ".mlx-bun", "wiki");
30
31
  }
@@ -986,10 +987,8 @@ const REFERENCE_LINKS: { name: string; source: string }[] = [
986
987
  { name: "mlx-bun_Server_API", source: join(REPO_ROOT, "docs", "reference", "server-api.md") },
987
988
  { name: "mlx-bun_Server_Config", source: join(REPO_ROOT, "docs", "reference", "server-config.md") },
988
989
  { name: "mlx-bun_Library_API", source: join(REPO_ROOT, "docs", "reference", "library-api.md") },
989
- { name: "mlx-bun_Embedding", source: join(REPO_ROOT, "docs", "reference", "embedding.md") },
990
990
  { name: "mlx-bun_Distribution", source: join(REPO_ROOT, "docs", "reference", "distribution.md") },
991
991
  { name: "mlx-bun_Training", source: join(REPO_ROOT, "docs", "reference", "training.md") },
992
- { name: "mlx-bun_ORPO_Quickstart", source: join(REPO_ROOT, "docs", "reference", "orpo-quickstart.md") },
993
992
  { name: "mlx-bun_Product_Roadmap", source: join(REPO_ROOT, "docs", "planning", "PRODUCT_ROADMAP.md") },
994
993
  ];
995
994