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
package/src/weights.ts CHANGED
@@ -59,12 +59,30 @@ const NATIVE_WEIGHTS: WeightsNativeBindings = {
59
59
  ),
60
60
  };
61
61
 
62
+ /**
63
+ * Checkpoint-generation normalization, installed once at load.
64
+ *
65
+ * `names` maps the GRAPH's canonical tensor name onto the name the artifact
66
+ * actually stores; a canonical name absent from the map does not exist (the
67
+ * same way a reference `sanitize()` drops a tensor). `fixup` is an optional
68
+ * lazy value repair applied to the loaded array — return null to keep it.
69
+ *
70
+ * Weights only APPLIES a view; the rules belong to the architecture that owns
71
+ * the naming (see `qwen35WeightsView` in model/qwen3_5-checkpoint.ts).
72
+ */
73
+ export interface WeightsView {
74
+ readonly names: ReadonlyMap<string, string>;
75
+ fixup?(canonical: string, arr: MlxArray): MlxArray | null;
76
+ }
77
+
62
78
  export class Weights {
63
79
  readonly shards: ShardedSafetensors;
64
80
  /** shard filename → native mlx map handle (string → lazy array). */
65
81
  readonly #maps = new Map<string, bigint>();
82
+ /** Cached arrays, keyed by CANONICAL name (post-view). */
66
83
  readonly #arrays = new Map<string, MlxArray>();
67
84
  readonly #native: WeightsNativeBindings;
85
+ #view: WeightsView | null = null;
68
86
 
69
87
  private constructor(
70
88
  shards: ShardedSafetensors,
@@ -82,30 +100,7 @@ export class Weights {
82
100
  const shards = await (options.openShards ?? ShardedSafetensors.open)(modelDir);
83
101
  const self = new Weights(shards, native);
84
102
  try {
85
- for (const [file, sf] of self.shards.files) {
86
- // out-param slots read back via read.u64, not [0] (DFG stale-read
87
- // bug — see outArray in mlx/ffi.ts). Cold path, but the rule is
88
- // uniform: native wrote it, read.* reads it.
89
- const arrMap = new BigUint64Array([native.newArrayMap()]);
90
- const arrMapPtr = ptr(arrMap);
91
- let arrayMapTransferred = false;
92
- try {
93
- const metaMap = new BigUint64Array([native.newStringMap()]);
94
- const metaMapPtr = ptr(metaMap);
95
- try {
96
- const status = native.loadSafetensors(arrMap, metaMap, sf.path);
97
- if (status !== 0)
98
- throw new Error(`mlx_load_safetensors(${sf.path}) failed`);
99
- self.#maps.set(file, read.u64(arrMapPtr, 0));
100
- arrayMapTransferred = true;
101
- } finally {
102
- native.freeStringMap(read.u64(metaMapPtr, 0));
103
- }
104
- } finally {
105
- if (!arrayMapTransferred)
106
- native.freeArrayMap(read.u64(arrMapPtr, 0));
107
- }
108
- }
103
+ for (const [file] of self.shards.files) self.#openShard(file);
109
104
  return self;
110
105
  } catch (error) {
111
106
  // A failure on shard N must release every array map transferred by
@@ -115,36 +110,142 @@ export class Weights {
115
110
  }
116
111
  }
117
112
 
113
+ /** Load one shard's native string→array map (lazy re-open path too). */
114
+ #openShard(file: string): void {
115
+ const sf = this.shards.files.get(file);
116
+ if (!sf) throw new Error(`no shard file ${file}`);
117
+ // out-param slots read back via read.u64, not [0] (DFG stale-read
118
+ // bug — see outArray in mlx/ffi.ts). Cold path, but the rule is
119
+ // uniform: native wrote it, read.* reads it.
120
+ const arrMap = new BigUint64Array([this.#native.newArrayMap()]);
121
+ const arrMapPtr = ptr(arrMap);
122
+ let arrayMapTransferred = false;
123
+ try {
124
+ const metaMap = new BigUint64Array([this.#native.newStringMap()]);
125
+ const metaMapPtr = ptr(metaMap);
126
+ try {
127
+ const status = this.#native.loadSafetensors(arrMap, metaMap, sf.path);
128
+ if (status !== 0)
129
+ throw new Error(`mlx_load_safetensors(${sf.path}) failed`);
130
+ this.#maps.set(file, read.u64(arrMapPtr, 0));
131
+ arrayMapTransferred = true;
132
+ } finally {
133
+ this.#native.freeStringMap(read.u64(metaMapPtr, 0));
134
+ }
135
+ } finally {
136
+ if (!arrayMapTransferred)
137
+ this.#native.freeArrayMap(read.u64(arrMapPtr, 0));
138
+ }
139
+ }
140
+
141
+ /** Install the checkpoint normalization. Must precede the first tensor()
142
+ * call: the array cache and every shard lookup below are keyed through it. */
143
+ setView(view: WeightsView): void {
144
+ if (this.#arrays.size > 0)
145
+ throw new Error("Weights.setView after tensors were materialized");
146
+ this.#view = view;
147
+ }
148
+
149
+ /** Canonical name → the stored name (identity when no view is installed). */
150
+ #source(name: string): string {
151
+ return this.#view ? this.#view.names.get(name) ?? name : name;
152
+ }
153
+
154
+ /** Free one shard's native map AND every cached tensor from it, releasing
155
+ * all bytes materialized while consuming that shard. The shard transparently
156
+ * re-opens on the next tensor() touching it (header parse only — lazy).
157
+ * Required by streaming whole-model transforms at 27B scale: the native map
158
+ * retains every evaluated source array, so per-tensor dispose alone frees
159
+ * nothing — the whole model accumulates in RAM (2026-08-18 fold OOM). */
160
+ releaseShard(file: string): void {
161
+ const sf = this.shards.files.get(file);
162
+ if (!sf) return;
163
+ for (const [name, arr] of this.#arrays) {
164
+ if (this.shards.tensorToFile.get(this.#source(name)) === sf) {
165
+ arr.dispose();
166
+ this.#arrays.delete(name);
167
+ }
168
+ }
169
+ const map = this.#maps.get(file);
170
+ if (map !== undefined) {
171
+ this.#native.freeArrayMap(map);
172
+ this.#maps.delete(file);
173
+ }
174
+ }
175
+
176
+ /** The shard filename holding `name` (for releaseShard scheduling). */
177
+ fileOf(name: string): string | undefined {
178
+ const sf = this.shards.tensorToFile.get(this.#source(name));
179
+ if (!sf) return undefined;
180
+ for (const [file, cand] of this.shards.files) if (cand === sf) return file;
181
+ return undefined;
182
+ }
183
+
184
+ /** Canonical names — with a view installed, exactly the tensors the graph
185
+ * can ask for (renamed, drops excluded). */
118
186
  get tensorNames(): string[] {
119
- return this.shards.tensorNames;
187
+ return this.#view ? [...this.#view.names.keys()] : this.shards.tensorNames;
120
188
  }
121
189
 
122
190
  info(name: string): TensorInfo {
123
- return this.shards.info(name);
191
+ return this.shards.info(this.#source(name));
124
192
  }
125
193
 
126
194
  has(name: string): boolean {
127
- return this.shards.tensorToFile.has(name);
195
+ return this.#view
196
+ ? this.#view.names.has(name)
197
+ : this.shards.tensorToFile.has(name);
128
198
  }
129
199
 
130
- /** Lazy mlx array for a tensor; cached per name. */
200
+ /** Lazy mlx array for a tensor; cached per canonical name. */
131
201
  tensor(name: string): MlxArray {
132
202
  let arr = this.#arrays.get(name);
133
203
  if (!arr) {
134
- const sf = this.shards.tensorToFile.get(name);
204
+ if (this.#view && !this.#view.names.has(name))
205
+ throw new Error(`no tensor named ${name}`);
206
+ const source = this.#source(name);
207
+ const sf = this.shards.tensorToFile.get(source);
135
208
  if (!sf) throw new Error(`no tensor named ${name}`);
136
- const mapHandle = [...this.#maps.entries()]
137
- .find(([file]) => this.shards.files.get(file) === sf)![1];
209
+ let entry = [...this.#maps.entries()]
210
+ .find(([file]) => this.shards.files.get(file) === sf);
211
+ if (!entry) {
212
+ // Shard was releaseShard()'d — transparently re-open it.
213
+ const file = this.fileOf(name)!;
214
+ this.#openShard(file);
215
+ entry = [file, this.#maps.get(file)!];
216
+ }
217
+ const mapHandle = entry[1];
138
218
  const slot = new BigUint64Array([C.mlx_array_new()]);
139
219
  const slotPtr = ptr(slot);
140
- if (C.mlx_map_string_to_array_get(slotPtr, mapHandle, ptr(cstr(name))) !== 0)
141
- throw new Error(`tensor ${name} missing from native map`);
220
+ if (C.mlx_map_string_to_array_get(slotPtr, mapHandle, ptr(cstr(source))) !== 0)
221
+ throw new Error(`tensor ${source} missing from native map`);
142
222
  arr = new MlxArray(read.u64(slotPtr, 0));
223
+ // Value repair (γ−1 → γ, conv layout): the fixed array is a lazy graph
224
+ // node over the source, so the raw handle is released immediately — the
225
+ // node keeps its own reference to the underlying data.
226
+ const fixed = this.#view?.fixup?.(name, arr) ?? null;
227
+ if (fixed) {
228
+ arr.dispose();
229
+ arr = fixed;
230
+ }
143
231
  this.#arrays.set(name, arr);
144
232
  }
145
233
  return arr;
146
234
  }
147
235
 
236
+ /** Dispose + evict one cached tensor so its materialized bytes are freed
237
+ * mid-walk. Streaming whole-model transforms (fold/quantize at 27B scale)
238
+ * MUST release each source tensor after its consumer evaluates, or the
239
+ * cache accumulates the entire model in RAM. Safe to call for names never
240
+ * accessed; the next tensor(name) call re-opens it from the map. */
241
+ release(name: string): void {
242
+ const arr = this.#arrays.get(name);
243
+ if (arr) {
244
+ arr.dispose();
245
+ this.#arrays.delete(name);
246
+ }
247
+ }
248
+
148
249
  dispose(): void {
149
250
  for (const arr of this.#arrays.values()) arr.dispose();
150
251
  this.#arrays.clear();
@@ -1,275 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>mlx-bun Lab — build report</title>
7
- <style>
8
- :root{
9
- --bg:#000; --ink:#f5f5f7; --dim:#a1a1a6; --dimmer:#6e6e73;
10
- --hairline:rgba(255,255,255,.13); --card:rgba(255,255,255,.045);
11
- --card2:rgba(255,255,255,.07);
12
- --blue:#0a84ff; --cyan:#64d2ff; --indigo:#5e5ce6; --purple:#bf5af2;
13
- --pink:#ff375f; --orange:#ff9f0a; --yellow:#ffd60a; --green:#30d158; --red:#ff453a;
14
- --ease:cubic-bezier(.22,.61,.21,1);
15
- --mono:ui-monospace,"SF Mono",Menlo,monospace;
16
- }
17
- *{box-sizing:border-box;margin:0;padding:0}
18
- html{scroll-behavior:smooth}
19
- body{background:var(--bg);color:var(--ink);
20
- font:16px/1.65 -apple-system,"SF Pro Text",system-ui,sans-serif;
21
- -webkit-font-smoothing:antialiased;overflow-x:hidden}
22
- .bloom{position:fixed;border-radius:50%;filter:blur(120px);opacity:.5;z-index:-1;pointer-events:none}
23
- .b1{width:560px;height:560px;background:radial-gradient(circle,rgba(94,92,230,.5),transparent 70%);top:-160px;left:-120px}
24
- .b2{width:620px;height:620px;background:radial-gradient(circle,rgba(255,55,95,.32),transparent 70%);top:380px;right:-200px}
25
- .b3{width:680px;height:680px;background:radial-gradient(circle,rgba(48,209,88,.22),transparent 70%);top:1500px;left:-220px}
26
- .wrap{max-width:1000px;margin:0 auto;padding:0 24px}
27
- .grad{background-clip:text;-webkit-background-clip:text;color:transparent}
28
- .g-hero{background-image:linear-gradient(96deg,var(--orange),var(--pink) 34%,var(--purple) 64%,var(--blue))}
29
- .g-green{background-image:linear-gradient(96deg,var(--green),var(--cyan))}
30
- .g-pink{background-image:linear-gradient(96deg,var(--pink),var(--purple))}
31
- .g-blue{background-image:linear-gradient(96deg,var(--cyan),var(--blue))}
32
-
33
- header.hero{padding:120px 0 70px;text-align:center}
34
- .eyebrow{font:600 13px var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--dim)}
35
- h1{font:800 clamp(40px,7vw,76px)/1.04 "SF Pro Display",-apple-system,sans-serif;letter-spacing:-.03em;margin:18px 0}
36
- .lede{font-size:clamp(17px,2.2vw,21px);color:var(--dim);max-width:720px;margin:0 auto;line-height:1.5}
37
- .meta{margin-top:26px;font:13px var(--mono);color:var(--dimmer)}
38
-
39
- section{padding:54px 0;border-top:1px solid var(--hairline)}
40
- h2{font:700 clamp(26px,3.6vw,38px)/1.1 "SF Pro Display",sans-serif;letter-spacing:-.02em;margin-bottom:8px}
41
- h2 + .sub{color:var(--dim);margin-bottom:30px;font-size:16px;max-width:760px}
42
- h3{font:650 19px "SF Pro Display",sans-serif;letter-spacing:-.01em;margin-bottom:6px}
43
-
44
- .stats{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-top:38px}
45
- .stat{background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:20px}
46
- .stat .n{font:800 30px "SF Pro Display",sans-serif;letter-spacing:-.02em}
47
- .stat .l{font-size:12.5px;color:var(--dim);margin-top:3px;line-height:1.35}
48
-
49
- .cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px}
50
- .card{background:var(--card);border:1px solid var(--hairline);border-radius:18px;padding:24px;transition:border-color .3s var(--ease),transform .3s var(--ease)}
51
- .card:hover{border-color:rgba(255,255,255,.28);transform:translateY(-2px)}
52
- .card .ic{font-size:22px;margin-bottom:12px}
53
- .card p{color:var(--dim);font-size:14.5px;margin-top:6px}
54
- .tag{display:inline-block;font:600 11px var(--mono);color:var(--green);background:rgba(48,209,88,.12);
55
- border:1px solid rgba(48,209,88,.3);border-radius:999px;padding:2px 9px;margin-top:12px}
56
- .tag.warn{color:var(--orange);background:rgba(255,159,10,.12);border-color:rgba(255,159,10,.3)}
57
-
58
- .flow{font:13px var(--mono);color:var(--dim);background:#0b0b0d;border:1px solid var(--hairline);
59
- border-radius:16px;padding:22px;white-space:pre;overflow-x:auto;line-height:1.6}
60
- .flow b{color:var(--ink)} .flow .k{color:var(--cyan)} .flow .g{color:var(--green)} .flow .p{color:var(--purple)}
61
-
62
- table{width:100%;border-collapse:collapse;font-size:14px;margin-top:6px}
63
- th,td{text-align:left;padding:11px 12px;border-bottom:1px solid var(--hairline);vertical-align:top}
64
- th{font:600 12px var(--mono);letter-spacing:.04em;text-transform:uppercase;color:var(--dim)}
65
- td code,.mono{font:12.5px var(--mono);color:var(--cyan)}
66
- td .ok{color:var(--green);font-weight:600}
67
- tr:last-child td{border-bottom:none}
68
-
69
- .showcase{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:20px}
70
- .term{background:#0b0b0d;border:1px solid var(--hairline);border-radius:16px;overflow:hidden}
71
- .term .bar{display:flex;gap:7px;padding:12px 16px;border-bottom:1px solid var(--hairline);align-items:center}
72
- .term .bar i{width:11px;height:11px;border-radius:50%;display:inline-block}
73
- .term .bar .t{margin-left:8px;font:12px var(--mono);color:var(--dimmer)}
74
- .term .body{padding:18px;font:13.5px/1.6 var(--mono);color:var(--ink);white-space:pre-wrap;word-break:break-word;min-height:96px}
75
- .lbl{font:600 12px var(--mono);letter-spacing:.05em;text-transform:uppercase;margin-bottom:8px}
76
-
77
- .loss{background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:22px;margin-top:18px}
78
- ul.j{list-style:none;margin-top:8px}
79
- ul.j li{padding:14px 0;border-bottom:1px solid var(--hairline);display:flex;gap:14px}
80
- ul.j li:last-child{border-bottom:none}
81
- ul.j .b{flex:0 0 auto;width:30px;height:30px;border-radius:9px;display:grid;place-items:center;font-size:15px;
82
- background:var(--card2);border:1px solid var(--hairline)}
83
- ul.j .c h4{font:650 15.5px "SF Pro Display",sans-serif;margin-bottom:3px}
84
- ul.j .c p{color:var(--dim);font-size:14px}
85
- ul.j .c code{font:12px var(--mono);color:var(--cyan)}
86
- .note{color:var(--dimmer);font-size:13px;margin-top:18px;line-height:1.55}
87
- code.cmd{display:block;background:#0b0b0d;border:1px solid var(--hairline);border-radius:12px;
88
- padding:14px 16px;font:13px var(--mono);color:var(--green);margin-top:10px;overflow-x:auto;white-space:pre}
89
- footer{padding:60px 0 90px;text-align:center;color:var(--dimmer);font-size:13px;border-top:1px solid var(--hairline)}
90
- @media(max-width:760px){.stats{grid-template-columns:repeat(2,1fr)}.showcase{grid-template-columns:1fr}}
91
- </style>
92
- </head>
93
- <body>
94
- <div class="bloom b1"></div><div class="bloom b2"></div><div class="bloom b3"></div>
95
-
96
- <header class="hero"><div class="wrap">
97
- <div class="eyebrow">build report · branch web-ui-and-native-lab</div>
98
- <h1>mlx-bun <span class="grad g-hero">Lab</span></h1>
99
- <p class="lede">A complete, beautiful web workbench for local AI on Apple silicon —
100
- a full pi agent in the browser, plus <b>native</b> Quantize, Fine-tune, and
101
- Build-Dataset. No Python anywhere. Every engine is mlx-c through bun:ffi.</p>
102
- <p class="meta">M4 Pro · 24 GB · MiniCPM5-1B-OptiQ-4bit · 2026-06-14</p>
103
- <div class="stats">
104
- <div class="stat"><div class="n grad g-blue">5</div><div class="l">unified sections: Chat · Quantize · Fine-tune · Dataset · Status</div></div>
105
- <div class="stat"><div class="n grad g-green">0</div><div class="l">lines of Python — native quantize &amp; LoRA training via mlx-c</div></div>
106
- <div class="stat"><div class="n grad g-pink">150+</div><div class="l">new tests, all green (incl. real-model e2e)</div></div>
107
- <div class="stat"><div class="n grad g-hero">50+</div><div class="l">new source files across 11 subsystems</div></div>
108
- </div>
109
- </div></header>
110
-
111
- <section><div class="wrap">
112
- <h2>What shipped</h2>
113
- <p class="sub">One self-contained, keynote-styled SPA served from the same binary, backed by four new engines and a shared job system. The status page is kept and folded in.</p>
114
- <div class="cards">
115
- <div class="card"><div class="ic">💬</div><h3>Chat</h3><p>The full pi coding-agent embedded server-side, streamed to the browser over WebSocket: live tokens, tool calls (read/bash/edit/write/grep/find/ls), steering, and a real pre-execution approval gate for the dangerous tools.</p><span class="tag">pi SDK · verified streaming + tools</span></div>
116
- <div class="card"><div class="ic">🧮</div><h3>Quantize</h3><p>Reads a model, quantizes every eligible Linear + embedding via <span class="mono">mlx_quantize</span>, writes a real HF-layout 4/8-bit model dir with <span class="mono">mlx_save_safetensors</span> the existing loader serves unchanged.</p><span class="tag">native · 170 modules → 4.50 bpw</span></div>
117
- <div class="card"><div class="ic">🎛️</div><h3>Fine-tune</h3><p>Native LoRA + DPO. <span class="mono">mlx_value_and_grad</span> driven by a bun:ffi closure, hand-rolled AdamW, masked cross-entropy. Saves an adapter the server hot-mounts.</p><span class="tag">native · loss 4.48 → 0.68</span></div>
118
- <div class="card"><div class="ic">📦</div><h3>Build Dataset</h3><p>All 13 OptIQ-Lab templates ported to TypeScript — pure-data ones run locally, LLM-driven ones call the server's own loopback. Splits to train/valid JSONL.</p><span class="tag">13 templates · 90/10 split</span></div>
119
- <div class="card"><div class="ic">📊</div><h3>Status</h3><p>The existing keynote dashboard, kept and integrated: live memory meters, prompt-cache hit rate, KV-quant split, the fit solver + Apple-silicon SKU matrix, downloads.</p><span class="tag">kept &amp; unified</span></div>
120
- <div class="card"><div class="ic">⚙️</div><h3>Job system</h3><p>Shared SQLite-backed jobs with NDJSON-log SSE streaming. GPU jobs run as crash-isolated subprocesses (uncatchable Metal OOM can't take the server down); dataset jobs run in-process.</p><span class="tag">SSE · crash-isolated</span></div>
121
- </div>
122
- </div></section>
123
-
124
- <section><div class="wrap">
125
- <h2>Architecture</h2>
126
- <p class="sub">Five sections over one <span class="mono">Bun.serve</span>. The chat WebSocket and the dataset LLM templates both ride the server's own loopback <span class="mono">/v1</span>, so everything serializes through the single generation queue — one GPU, no second path.</p>
127
- <div class="flow"><b>src/web/app.html</b> <span class="k">── unified SPA (hash routes #/chat #/quantize #/finetune #/dataset #/status)</span>
128
- │
129
- ▼
130
- <b>src/server.ts</b> <span class="k">── Bun.serve({ fetch, websocket })</span>
131
- ├─ <span class="g">/ws/chat</span> .............. src/pi-web.ts <span class="k">→ embedded pi AgentSession → loopback /v1</span>
132
- ├─ <span class="g">/api/dataset/*</span> ........ src/dataset/* <span class="k">→ jobs (in-process)</span>
133
- ├─ <span class="g">/api/quantize/*</span> ....... src/quantize/* <span class="k">→ jobs (GPU subprocess)</span>
134
- ├─ <span class="g">/api/finetune/*</span> ....... src/train/* <span class="k">→ jobs (GPU subprocess)</span>
135
- ├─ <span class="g">/api/jobs/:id[/stream]</span> .. src/jobs/* <span class="k">→ SQLite rows + NDJSON-log SSE</span>
136
- └─ <span class="p">/v1/* · /stats · /fit · /library</span> <span class="k">(unchanged)</span>
137
- │
138
- ▼
139
- <b>src/mlx/{ffi,ops,autograd}.ts</b> <span class="k">→ mlx-c via bun:ffi</span>
140
- <span class="k">mlx_quantize · mlx_save_safetensors · mlx_value_and_grad</span></div>
141
- </div></section>
142
-
143
- <section><div class="wrap">
144
- <h2>The headline: <span class="grad g-green">native, no Python</span></h2>
145
- <p class="sub">mlx-bun's thesis is that the buggy layer (loading, tokenizing, the training loop, serving) doesn't need Python. The two hardest pieces — weight quantization and gradient training — are now native, proven on a real model.</p>
146
-
147
- <h3 style="margin-top:8px">It actually trains</h3>
148
- <p style="color:var(--dim);font-size:14.5px;max-width:780px">A 20-iteration LoRA on a 3-row "always answer in UPPERCASE" fixture, run entirely in Bun/MLX. The base model and the freshly-trained adapter, on a held-out prompt — <i>"Tell me about your day."</i>:</p>
149
- <div class="showcase">
150
- <div><div class="lbl" style="color:var(--dimmer)">base model (adapter off)</div>
151
- <div class="term"><div class="bar"><i style="background:#ff5f57"></i><i style="background:#febc2e"></i><i style="background:#28c840"></i><span class="t">before</span></div>
152
- <div class="body" id="before"></div></div></div>
153
- <div><div class="lbl grad g-green">after 20 native LoRA steps</div>
154
- <div class="term" style="border-color:rgba(48,209,88,.4)"><div class="bar"><i style="background:#ff5f57"></i><i style="background:#febc2e"></i><i style="background:#28c840"></i><span class="t">after</span></div>
155
- <div class="body grad g-green" id="after" style="font-weight:600"></div></div></div>
156
- </div>
157
-
158
- <div class="loss">
159
- <div style="display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:10px">
160
- <div class="lbl grad g-blue">training loss · 20 iters</div>
161
- <div class="mono" style="color:var(--dim)">4.48 → 0.68 · 4.6 s · 168 adapters mounted</div>
162
- </div>
163
- <svg id="spark" viewBox="0 0 900 160" preserveAspectRatio="none" style="width:100%;height:160px;margin-top:12px"></svg>
164
- </div>
165
-
166
- <div class="cards" style="margin-top:22px">
167
- <div class="card"><div class="ic">🧮</div><h3>Quantize, measured</h3>
168
- <p>Re-quantized MiniCPM5-1B to uniform 4-bit g64: <b>170 modules</b> → <b>4.50 bits/weight</b>, a <b>0.62 GB</b> model dir written in <b>7.0 s</b>, reopened bit-correctly through the existing loader. Stages: resolving → quantizing → writing.</p>
169
- <span class="tag">mlx_quantize + mlx_save_safetensors</span></div>
170
- <div class="card"><div class="ic">🔬</div><h3>The autograd spike</h3>
171
- <p>Before writing any trainer, a feasibility gate proved <span class="mono">mlx_value_and_grad</span> driven by a <span class="mono">bun:ffi</span> JSCallback closure returns correct gradients (finite-difference &lt; 1e-2) for the LoRA A/B leaves, with the quantized base frozen.</p>
172
- <span class="tag">spikes/phase-train-vag.ts · PASS</span></div>
173
- </div>
174
- </div></section>
175
-
176
- <section><div class="wrap">
177
- <h2>Round 2 — the leftover seams, <span class="grad g-pink">ported 1:1</span></h2>
178
- <p class="sub">The five "designed-but-not-built" items, finished as faithful ports of the OptIQ / mlx-lm source (open book) — built in parallel on non-overlapping files, then integrated.</p>
179
- <div class="cards">
180
- <div class="card"><div class="ic">🎚️</div><h3>Mixed-precision quantization</h3><p>OptIQ's sensitivity + greedy-knapsack allocator ported <b>1:1</b> — per-layer bit choices are <b>bit-identical to OptIQ</b> on shared inputs (checked against the Python in the venv). Per-layer KL sensitivity runs on our own graph.</p><span class="tag">170 modules → 80×4-bit + 90×8-bit · 5.19 bpw · 32 s</span></div>
181
- <div class="card"><div class="ic">📚</div><h3>Batched (B&gt;1) training</h3><p>Ragged padding + a per-row attention mask through the forward; B=1 stays bit-identical (separate path). Verified: a batched row's loss equals its single-row forward (shape-held) within bf16 noise.</p><span class="tag">B=2 loss↓ + masking parity ✓</span></div>
182
- <div class="card"><div class="ic">🤗</div><h3>hf_dataset_import</h3><p>The 13th template — pulls any HF dataset via the datasets-server REST API (config / split / column / label-filter / min-chars / cap), no Python <span class="mono">datasets</span> lib.</p><span class="tag">13/13 templates real</span></div>
183
- <div class="card"><div class="ic">⬆️</div><h3>HF push-to-hub</h3><p>Native Hub upload — create repo → LFS preupload → S3 PUT → NDJSON commit — for model, adapter, and dataset repos. Wired into all three wizards + an HF-token settings gear.</p><span class="tag">11 protocol tests ✓ (mock; no live push)</span></div>
184
- <div class="card"><div class="ic">📦</div><h3>Single-binary web chat</h3><p>pi's JS is already bundled by <span class="mono">bun build --compile</span>; only the photon wasm is sidecarred. The compiled binary's headless pi session was verified to construct under the binary.</p><span class="tag">+1.8 MB sidecar</span></div>
185
- </div>
186
- <p class="note">The web UI now exposes all of it: a Uniform/Mixed quantize toggle with a target-bpw slider, live merge/export/push on the fine-tune done step, push on quantize &amp; dataset, and a Hugging-Face token settings gear in the nav.</p>
187
- </div></section>
188
-
189
- <section><div class="wrap">
190
- <h2>Verification</h2>
191
- <p class="sub">No browser screenshots (per request) — proof is the test suite. Integration tests spin a server up <i>inside the test process</i> and exit; the two real-model runs were explicitly approved.</p>
192
- <table>
193
- <thead><tr><th>area</th><th>evidence</th><th>result</th></tr></thead>
194
- <tbody>
195
- <tr><td>Web UI + routes</td><td><code>tests/lab-routes.test.ts</code> — SPA served, dataset build → JSONL, jobs SSE, quantize inspect, <b>real pi chat turn over WebSocket</b></td><td class="ok">7/7 ✓</td></tr>
196
- <tr><td>Chat (pi embed)</td><td>WS turn streamed 25+ text deltas, executed a tool, completed; <code>tests/pi-web.test.ts</code> event mapping</td><td class="ok">9/9 ✓</td></tr>
197
- <tr><td>Native quantize</td><td><code>MLX_BUN_TEST_QUANTIZE=1</code> e2e — real 4-bit model written + reopened; + 5 unit</td><td class="ok">6/6 ✓</td></tr>
198
- <tr><td>Native train</td><td><code>MLX_BUN_TEST_TRAIN=1</code> e2e — loss↓, adapter mounts, output changes; + 19 fast (autograd FD, AdamW, dataset)</td><td class="ok">20/20 ✓</td></tr>
199
- <tr><td>Dataset engine</td><td><code>tests/dataset.test.ts</code> — generators, CSV parse, 90/10 split, 13 templates</td><td class="ok">32/32 ✓</td></tr>
200
- <tr><td>Job system</td><td><code>tests/jobs.test.ts</code> — lifecycle, GPU lease, <b>subprocess crash isolation</b>, zombie recovery, SSE</td><td class="ok">12/12 ✓</td></tr>
201
- <tr><td>No regressions</td><td><code>tests/server.test.ts</code> — existing OpenAI/Anthropic/admission surface after the integration</td><td class="ok">17/17 ✓</td></tr>
202
- <tr><td>Types</td><td><code>bunx tsc --noEmit</code> across the whole project</td><td class="ok">clean ✓</td></tr>
203
- </tbody>
204
- </table>
205
- </div></section>
206
-
207
- <section><div class="wrap">
208
- <h2>Open-book ports</h2>
209
- <p class="sub">Every engine follows a readable source — the OptIQ-Lab Python in the installed venv and mlx-lm — rather than being invented.</p>
210
- <table>
211
- <thead><tr><th>our module</th><th>ported from</th></tr></thead>
212
- <tbody>
213
- <tr><td><code>src/dataset/*</code></td><td><code>optiq/lab/dataset_templates.py</code> — 13 templates, field-for-field</td></tr>
214
- <tr><td><code>src/train/{trainer,loss,dataset}.ts</code></td><td><code>optiq/lora/{trainer,dpo,config}.py</code> + mlx-lm <code>default_loss</code> / <code>iterate_batches</code></td></tr>
215
- <tr><td><code>src/quantize/*</code></td><td>mlx-lm <code>convert.py</code> rules + the on-disk OptIQ model format (config block, sharding, eligibility)</td></tr>
216
- <tr><td><code>src/jobs/*</code></td><td><code>optiq/lab/{jobs,db}.py</code> — job lifecycle + SSE event grammar</td></tr>
217
- <tr><td><code>src/pi-web.ts</code></td><td>pi SDK (<code>@earendil-works/pi-coding-agent</code>) <code>examples/sdk/12-full-control.ts</code></td></tr>
218
- <tr><td><code>src/web/app.html</code></td><td>OptIQ-Lab wizard UX + mlx-bun's own <code>status-page.html</code> design system</td></tr>
219
- </tbody>
220
- </table>
221
- </div></section>
222
-
223
- <section><div class="wrap">
224
- <h2>Build journal</h2>
225
- <p class="sub">How it was built, the decisions, and the bugs found along the way.</p>
226
- <ul class="j">
227
- <li><div class="b">🔭</div><div class="c"><h4>Plan first, fork the big decision to the user</h4><p>Surfaced the one decision that changed everything — native compute vs. orchestrating the installed Python vs. proxying — and you chose <b>native, no Python</b>. Full pi agent for chat; real tiny runs for verification.</p></div></li>
228
- <li><div class="b">🔬</div><div class="c"><h4>Gated the riskiest unknown before building</h4><p>Native training needs autograd through <span class="mono">bun:ffi</span>. A feasibility spike proved <code>mlx_value_and_grad</code> + a JSCallback closure returns correct grads (finite-diff &lt; 1e-2) before a line of trainer code — de-risking the whole subsystem.</p></div></li>
229
- <li><div class="b">⚡</div><div class="c"><h4>Parallel sub-agents on non-overlapping files</h4><p>After laying a shared job contract (<code>src/jobs/types.ts</code>), fanned out six subsystems concurrently — jobs, dataset, quantizer, trainer, web SPA, pi embed — each owning its own directory, then integrated <code>server.ts</code>/<code>cli.ts</code> centrally to avoid conflicts.</p></div></li>
230
- <li><div class="b">🧱</div><div class="c"><h4>Found: <code>mlx_save_safetensors</code> exists</h4><p>The quantizer needed a safetensors writer; reading the mlx-c header (open book) showed a native writer already exists — no hand-rolled byte layout, eliminating a whole class of dtype/alignment bugs.</p></div></li>
231
- <li><div class="b">🛡️</div><div class="c"><h4>Crash isolation by design</h4><p>Async Metal OOM is uncatchable (it std::terminates the process). So GPU jobs run as child <code>mlx-bun</code> subprocesses with a kernel fault boundary; a job that OOMs dies alone and the server survives. Verified with a deliberate-crash test.</p></div></li>
232
- <li><div class="b">🐛</div><div class="c"><h4>Bug: LoRA init key shape</h4><p>The first real training run failed — <code>random_normal</code> got a <code>[1,2]</code> key but mlx wants <code>[2]</code>. Reshaped the per-module subkey after slicing the split-keys array. Loss curve immediately worked.</p></div></li>
233
- <li><div class="b">🐛</div><div class="c"><h4>Bug: flaky chat test</h4><p>The pi agent sometimes takes a tool-call turn <i>before</i> producing text; the test resolved on the first <code>turn_end</code> and saw no tokens. Fixed to settle on the first turn that actually streams text — the embed was always correct.</p></div></li>
234
- <li><div class="b">🔁</div><div class="c"><h4>Test, fix, re-run</h4><p>Each real-model e2e surfaced a small integration gap (a path not resolved, a key shape) — fixed at the source and re-run green, rather than worked around in the test.</p></div></li>
235
- </ul>
236
- </div></section>
237
-
238
- <section><div class="wrap">
239
- <h2>Run it live</h2>
240
- <p class="sub">Screenshots were skipped by request; here's how to see it yourself. The unified app is served from the same binary.</p>
241
- <code class="cmd">bun src/cli.ts serve cpm <span style="color:var(--dimmer)"># serve MiniCPM5-1B; app at http://localhost:8090/</span>
242
- <span style="color:var(--dimmer)"># then open http://localhost:8090/ → Chat · Quantize · Fine-tune · Dataset · Status</span></code>
243
- <p class="note">Re-run the native engines headless (no server):</p>
244
- <code class="cmd">bun scripts/verify-lab.ts <span style="color:var(--dimmer)"># quantize + train + dataset, prints evidence</span>
245
- MLX_BUN_TEST_QUANTIZE=1 bun test tests/quantize.test.ts
246
- MLX_BUN_TEST_TRAIN=1 bun test tests/train-e2e.test.ts</code>
247
- <p class="note">Everything lives on branch <span class="mono">web-ui-and-native-lab</span>. The OptIQ sensitivity+knapsack mixed-precision path, DPO at scale, batched (B&gt;1) training, and HF push-to-hub are designed seams left for follow-ups.</p>
248
- </div></section>
249
-
250
- <footer><div class="wrap">mlx-bun Lab · native MLX inference, quantization &amp; training for Bun on Apple silicon · no Python</div></footer>
251
-
252
- <script>
253
- const before = "<think>\nOkay, the user is asking about my day. Hmm, I need to clarify my nature first. I";
254
- const after = "MY DAY AHEAD.\nAPPROVE AND PROPERLY END THE DAY.\nAPPROVE AND";
255
- document.getElementById("before").textContent = before;
256
- document.getElementById("after").textContent = after;
257
-
258
- // loss sparkline
259
- const loss = [4.4836,2.7138,2.5195,1.8625,0.8894,4.877,2.2799,1.1992,1.1923,1.4514,1.9102,0.6184,0.9046,0.6076,0.601,0.4917,0.3558,2.4688,0.25,0.6766];
260
- const W=900,H=160,pad=14, max=Math.max(...loss), min=Math.min(...loss);
261
- const x=i=>pad+i*(W-2*pad)/(loss.length-1);
262
- const y=v=>pad+(1-(v-min)/(max-min))*(H-2*pad);
263
- const pts=loss.map((v,i)=>`${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" ");
264
- const area=`M ${x(0)},${H-pad} L ${pts.split(" ").join(" L ")} L ${x(loss.length-1)},${H-pad} Z`;
265
- const svg=document.getElementById("spark");
266
- svg.innerHTML=`
267
- <defs><linearGradient id="lg" x1="0" y1="0" x2="0" y2="1">
268
- <stop offset="0" stop-color="rgba(100,210,255,.35)"/><stop offset="1" stop-color="rgba(100,210,255,0)"/>
269
- </linearGradient></defs>
270
- <path d="${area}" fill="url(#lg)"/>
271
- <polyline points="${pts}" fill="none" stroke="#64d2ff" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round"/>
272
- ${loss.map((v,i)=>`<circle cx="${x(i).toFixed(1)}" cy="${y(v).toFixed(1)}" r="2.6" fill="${i===loss.length-1?'#30d158':'#64d2ff'}"/>`).join("")}`;
273
- </script>
274
- </body>
275
- </html>