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,528 @@
1
+ // Native Qwen3.8 MTP draft source — the Qwen-TRAINED multi-step prediction
2
+ // head, split from the raw release's last shard and published as
3
+ // mlx-community/Qwen3.8-27B-MTP-* (model_type "qwen3_5_mtp", block_size 3).
4
+ //
5
+ // Reference implementation (locate + port + gate, don't design):
6
+ // mlx_vlm/speculative/drafters/qwen3_5_mtp/qwen3_5_mtp.py. Mechanism, per
7
+ // step: h = fc(concat(rms_emb(embed(token)), rms_hidden(hidden))) → ONE
8
+ // full-attention decoder layer (own KVCache, positions continue the target
9
+ // sequence) → norm → the TARGET's lm_head → next draft token; recursive for
10
+ // the block ("trained with multiple steps"). The drafter is NOT standalone:
11
+ // it binds the target's embed_tokens + lm_head (mtp_use_dedicated_embeddings
12
+ // false) and consumes the target's PRE-final-norm last-layer hidden (mlx-vlm
13
+ // captures it with skip_final_norm=True) — carried here by the tapLayers
14
+ // machinery, NOT the seam's post-norm anchorHidden.
15
+ //
16
+ // Row convention (predict-2-ahead): drafter KV row at position p is built
17
+ // from (embed(token at position p+1), hidden at position p). Invariant
18
+ // across rounds: after commit, the drafter offset equals the next pending
19
+ // token's position minus zero — i.e. draft() always opens by building the
20
+ // pending token's row from the TARGET's TRUE hidden at the emitted position
21
+ // (held over from the verify tap). Computationally identical to mlx-vlm's
22
+ // commit-time "seed" append, just performed at the top of the next round.
23
+ //
24
+ // Companion projections may be dense or quantized according to their metadata.
25
+ // Published drafter norms are already sanitized to runtime layout; no +1.0 shift.
26
+
27
+ import { MlxArray } from "../mlx/array";
28
+ import * as ops from "../mlx/ops";
29
+ import { materializeCopy } from "../mlx/materialize";
30
+ import { toLogprobs } from "../sampler";
31
+ import { loadModelConfig, type ModelConfig } from "../config";
32
+ import { Weights } from "../weights";
33
+ import { disposing, KVCache, QuantizedLinear, RMSNorm, type Mask } from "../model/gemma4-base";
34
+ import { DenseLinear } from "../model/universal/modules";
35
+ import type { DraftProvider, DraftSource, QwenMtpTarget } from "./source";
36
+ import type { Cache } from "../model/gemma4";
37
+ import { cloneKvCaches } from "../kv-store";
38
+ import { cacheBytes } from "../prompt-cache";
39
+ import { disposeResources } from "../engine/resources";
40
+ import { SpeculativePrefixStore, type SpeculativePrefixState } from "./prefix-state";
41
+
42
+ type Sampler = (logprobs: MlxArray, step: number) => MlxArray;
43
+
44
+ const DRAFT_PREFILL_CHUNK = 2048;
45
+
46
+ type MtpLinear = DenseLinear | QuantizedLinear;
47
+
48
+ /** Projection tensors belong to Weights; dense transpose views belong to
49
+ * the provider's resource stack. Quantized heads keep no dense copy. */
50
+ function loadMtpLinear(
51
+ weights: Weights, path: string, config: ModelConfig, resources: DisposableStack,
52
+ ): MtpLinear {
53
+ if (weights.has(`${path}.scales`)) return QuantizedLinear.load(weights, path, config);
54
+ const layer = new DenseLinear(weights.tensor(`${path}.weight`), null);
55
+ resources.use(layer.wT);
56
+ return layer;
57
+ }
58
+
59
+ /** Qwen3Attention.forward with the companion's dense or quantized projections.
60
+ * Attention operations follow src/model/qwen3_5.ts verbatim. */
61
+ class MtpAttention {
62
+ readonly qProj: MtpLinear;
63
+ readonly kProj: MtpLinear;
64
+ readonly vProj: MtpLinear;
65
+ readonly oProj: MtpLinear;
66
+ readonly qNorm: RMSNorm;
67
+ readonly kNorm: RMSNorm;
68
+ readonly nHeads: number;
69
+ readonly nKvHeads: number;
70
+ readonly headDim: number;
71
+ readonly scale: number;
72
+ readonly ropeDims: number;
73
+ readonly ropeBase: number;
74
+
75
+ constructor(weights: Weights, config: ModelConfig, prefix: string, resources: DisposableStack) {
76
+ const t = config.text;
77
+ this.nHeads = t.numAttentionHeads;
78
+ this.nKvHeads = t.numKeyValueHeads;
79
+ this.headDim = t.headDim;
80
+ this.scale = Math.pow(this.headDim, -0.5);
81
+ this.ropeDims = Math.trunc(this.headDim * t.partialRotaryFactor);
82
+ this.ropeBase = t.ropeParameters.full_attention?.ropeTheta ?? 10000;
83
+ this.qProj = loadMtpLinear(weights, `${prefix}.q_proj`, config, resources);
84
+ this.kProj = loadMtpLinear(weights, `${prefix}.k_proj`, config, resources);
85
+ this.vProj = loadMtpLinear(weights, `${prefix}.v_proj`, config, resources);
86
+ this.oProj = loadMtpLinear(weights, `${prefix}.o_proj`, config, resources);
87
+ this.qNorm = new RMSNorm(weights.tensor(`${prefix}.q_norm.weight`), t.rmsNormEps);
88
+ this.kNorm = new RMSNorm(weights.tensor(`${prefix}.k_norm.weight`), t.rmsNormEps);
89
+ }
90
+
91
+ forward(x: MlxArray, mask: Mask, cache: KVCache): MlxArray {
92
+ const [B, L] = x.shape as [number, number, number];
93
+ const qp = this.qProj.forward(x);
94
+ const qpr = disposing(qp, ops.reshape(qp, [B, L, this.nHeads, this.headDim * 2]));
95
+ const [qHeads, gateHeads] = ops.split(qpr, [this.headDim], -1) as [MlxArray, MlxArray];
96
+ qpr.dispose();
97
+ const gate = disposing(gateHeads, ops.reshape(gateHeads, [B, L, this.nHeads * this.headDim]));
98
+
99
+ let k = this.kProj.forward(x);
100
+ let v = this.vProj.forward(x);
101
+
102
+ let q = this.qNorm.forward(qHeads);
103
+ qHeads.dispose();
104
+ q = disposing(q, ops.transposeAxes(q, [0, 2, 1, 3]));
105
+ k = disposing(k, ops.reshape(k, [B, L, this.nKvHeads, this.headDim]));
106
+ k = disposing(k, this.kNorm.forward(k));
107
+ k = disposing(k, ops.transposeAxes(k, [0, 2, 1, 3]));
108
+ v = disposing(v, ops.reshape(v, [B, L, this.nKvHeads, this.headDim]));
109
+ v = disposing(v, ops.transposeAxes(v, [0, 2, 1, 3]));
110
+
111
+ q = disposing(q, ops.rope(q, this.ropeDims, this.ropeBase, cache.offset, null));
112
+ k = disposing(k, ops.rope(k, this.ropeDims, this.ropeBase, cache.offset, null));
113
+
114
+ const [keys, values] = cache.updateAndFetch(k, v);
115
+ k.dispose();
116
+ v.dispose();
117
+ const attn = ops.sdpa(q, keys, values, this.scale, mask.mode, mask.arr);
118
+ keys.dispose();
119
+ values.dispose();
120
+ q.dispose();
121
+
122
+ const attnT = ops.transposeAxes(attn, [0, 2, 1, 3]);
123
+ attn.dispose();
124
+ const merged = ops.reshape(attnT, [B, L, -1]);
125
+ attnT.dispose();
126
+ const sig = ops.sigmoid(gate);
127
+ gate.dispose();
128
+ const gated = ops.mul(merged, sig);
129
+ merged.dispose();
130
+ sig.dispose();
131
+ const out = this.oProj.forward(gated);
132
+ gated.dispose();
133
+ return out;
134
+ }
135
+ }
136
+
137
+ /** The one MTP decoder block: fc-merge → attention → swiglu MLP → norm. */
138
+ class MtpModule {
139
+ readonly fc: MtpLinear;
140
+ readonly preFcNormEmbedding: RMSNorm;
141
+ readonly preFcNormHidden: RMSNorm;
142
+ readonly attn: MtpAttention;
143
+ readonly mlpGate: MtpLinear;
144
+ readonly mlpUp: MtpLinear;
145
+ readonly mlpDown: MtpLinear;
146
+ readonly inputNorm: RMSNorm;
147
+ readonly postAttnNorm: RMSNorm;
148
+ readonly finalNorm: RMSNorm;
149
+
150
+ constructor(weights: Weights, config: ModelConfig, resources: DisposableStack) {
151
+ const eps = config.text.rmsNormEps;
152
+ this.fc = loadMtpLinear(weights, "fc", config, resources);
153
+ this.preFcNormEmbedding = new RMSNorm(weights.tensor("pre_fc_norm_embedding.weight"), eps);
154
+ this.preFcNormHidden = new RMSNorm(weights.tensor("pre_fc_norm_hidden.weight"), eps);
155
+ this.attn = new MtpAttention(weights, config, "layers.0.self_attn", resources);
156
+ this.mlpGate = loadMtpLinear(weights, "layers.0.mlp.gate_proj", config, resources);
157
+ this.mlpUp = loadMtpLinear(weights, "layers.0.mlp.up_proj", config, resources);
158
+ this.mlpDown = loadMtpLinear(weights, "layers.0.mlp.down_proj", config, resources);
159
+ this.inputNorm = new RMSNorm(weights.tensor("layers.0.input_layernorm.weight"), eps);
160
+ this.postAttnNorm = new RMSNorm(weights.tensor("layers.0.post_attention_layernorm.weight"), eps);
161
+ this.finalNorm = new RMSNorm(weights.tensor("norm.weight"), eps);
162
+ }
163
+
164
+ /** One block forward over [1,S,·]: token embeddings ([1,S,H], target
165
+ * embed_tokens output) paired with hiddens ([1,S,H], target pre-final-norm
166
+ * or the module's own chained output). Appends S rows to `cache`; returns
167
+ * the module output [1,S,H] (post final norm — what the target lm_head
168
+ * consumes AND what chains into the next step's `hidden`). */
169
+ forward(tokenEmbeds: MlxArray, hiddens: MlxArray, cache: KVCache): MlxArray {
170
+ const embNorm = this.preFcNormEmbedding.forward(tokenEmbeds);
171
+ const hidNorm = this.preFcNormHidden.forward(hiddens);
172
+ const joined = ops.concatAxis([embNorm, hidNorm], 2);
173
+ embNorm.dispose();
174
+ hidNorm.dispose();
175
+ const x = this.fc.forward(joined);
176
+ joined.dispose();
177
+
178
+ // Decoder layer (Qwen3Layer.forward shape).
179
+ const L = x.shape[1]!;
180
+ const mask = cache.makeMask(L, null);
181
+ const xn = this.inputNorm.forward(x);
182
+ const r = this.attn.forward(xn, mask, cache);
183
+ xn.dispose();
184
+ mask.arr?.dispose();
185
+ const h = ops.add(x, r);
186
+ x.dispose();
187
+ r.dispose();
188
+ const hn = this.postAttnNorm.forward(h);
189
+ const g = this.mlpGate.forward(hn);
190
+ const u = this.mlpUp.forward(hn);
191
+ hn.dispose();
192
+ const silu = ops.silu(g);
193
+ g.dispose();
194
+ const act = ops.mul(silu, u);
195
+ silu.dispose();
196
+ u.dispose();
197
+ const m = this.mlpDown.forward(act);
198
+ act.dispose();
199
+ const out = ops.add(h, m);
200
+ h.dispose();
201
+ m.dispose();
202
+ return disposing(out, this.finalNorm.forward(out));
203
+ }
204
+ }
205
+
206
+ class QwenMtpPrefixState implements SpeculativePrefixState {
207
+ constructor(readonly tokens: number[], readonly targetIdentity: object,
208
+ readonly namespace: string, readonly bytes: number, readonly target: Cache[],
209
+ public draft: KVCache | null, public hidden: MlxArray | null) {}
210
+
211
+ dispose(): void {
212
+ const resources = [...this.target.splice(0), this.draft, this.hidden];
213
+ this.draft = null;
214
+ this.hidden = null;
215
+ disposeResources(resources.filter((r) => r != null));
216
+ }
217
+ }
218
+
219
+ export class QwenMtpProvider implements DraftProvider {
220
+ readonly id: string;
221
+ readonly weightsBytes: number;
222
+ readonly #module: MtpModule;
223
+ readonly #config: ModelConfig;
224
+ readonly #resources: DisposableStack;
225
+ readonly #prefixStore = new SpeculativePrefixStore<QwenMtpPrefixState>();
226
+
227
+ private constructor(id: string, config: ModelConfig, weightsBytes: number, module: MtpModule, resources: DisposableStack) {
228
+ this.id = id;
229
+ this.#config = config;
230
+ this.#module = module;
231
+ this.weightsBytes = weightsBytes;
232
+ this.#resources = resources;
233
+ }
234
+
235
+ static async load(dir: string): Promise<QwenMtpProvider> {
236
+ const config = await loadModelConfig(dir);
237
+ if (config.modelType !== "qwen3_5_mtp")
238
+ throw new Error(`${dir}: not a qwen3_5_mtp drafter (model_type ${config.modelType})`);
239
+ const weights = await Weights.open(dir);
240
+ using resources = new DisposableStack();
241
+ resources.defer(() => weights.dispose());
242
+ const weightsBytes = [...weights.shards.files.values()]
243
+ .reduce((a, f) => a + f.mmap.size, 0);
244
+ const module = new MtpModule(weights, config, resources);
245
+ return new QwenMtpProvider(
246
+ dir.split("/").filter(Boolean).at(-1) ?? "qwen-mtp",
247
+ config, weightsBytes, module, resources.move(),
248
+ );
249
+ }
250
+
251
+ open(opts: Parameters<DraftProvider["open"]>[0]): DraftSource {
252
+ if (this.#resources.disposed)
253
+ throw new Error("qwen MTP provider is disposed");
254
+ const target = opts.target.qwenMtp;
255
+ if (!target)
256
+ throw new Error("qwen MTP drafting requires a qwen3_5-family target");
257
+ if (target.hiddenSize !== this.#config.text.hiddenSize) {
258
+ throw new Error(
259
+ `qwen MTP drafter hidden ${this.#config.text.hiddenSize} != target ` +
260
+ `${target.hiddenSize} — split from a different checkpoint?`,
261
+ );
262
+ }
263
+ return new QwenMtpSource(target, this.#module, opts.sampler,
264
+ { store: this.#prefixStore, identity: opts.target.identity });
265
+ }
266
+
267
+ dispose(): void {
268
+ // Release cached transpose views before their native weight maps. MLX
269
+ // retains buffers needed by outstanding GPU commands until completion.
270
+ disposeResources([this.#prefixStore, { dispose: () => this.#resources.dispose() }]);
271
+ }
272
+ }
273
+
274
+ export class QwenMtpSource implements DraftSource {
275
+ // Full-prompt target prefill (the bonus token exists before round 1, and
276
+ // the tap covers every prompt position — mlx-vlm's flow).
277
+ readonly prefillMode = "full" as const;
278
+ readonly weightsBytes = 0; // provider owns the drafter weights
279
+ /** Pre-final-norm tap: the LAST layer's output stream (index nLayers-1).
280
+ * The seam's anchorHidden is post-final-norm and is deliberately unused. */
281
+ readonly tapLayers: number[];
282
+
283
+ readonly #target: QwenMtpTarget;
284
+ readonly #module: MtpModule;
285
+ readonly #sampler: Sampler;
286
+ #cache = new KVCache();
287
+ readonly prefix: DraftSource["prefix"];
288
+ #prefilledTokens = 0;
289
+ #hasDrafted = false;
290
+ /** Target pre-norm hidden at the position preceding the next pending
291
+ * token: prefill's last tapped row, then each commit's vCtx row at the
292
+ * emitted position. draft() consumes it to build the pending row. [1,1,H] */
293
+ #pendingTrueHidden: MlxArray | null = null;
294
+ #roundAppended = 0;
295
+ #closed = false;
296
+
297
+ constructor(target: QwenMtpTarget, module: MtpModule, sampler: Sampler,
298
+ prefix?: { store: SpeculativePrefixStore<QwenMtpPrefixState>; identity: object }) {
299
+ this.#target = target;
300
+ this.#module = module;
301
+ this.#sampler = sampler;
302
+ this.tapLayers = [target.layerCount - 1];
303
+ if (prefix) this.prefix = {
304
+ restore: (prompt, caches, namespace, maxBytes) => {
305
+ this.#checkOpen();
306
+ if (this.#prefilledTokens !== 0 || this.#hasDrafted)
307
+ throw new Error("Qwen MTP prefix restore requires a fresh source");
308
+ const entry = prefix.store.take(prompt, prefix.identity, namespace, maxBytes);
309
+ if (!entry) return 0;
310
+ try {
311
+ if (entry.target.length !== caches.length || !entry.draft || !entry.hidden ||
312
+ entry.draft.offset !== entry.tokens.length - 1)
313
+ throw new Error("invalid paired Qwen MTP prefix state");
314
+ disposeResources(caches);
315
+ caches.splice(0, caches.length, ...entry.target.splice(0));
316
+ this.#cache.dispose();
317
+ this.#cache = entry.draft;
318
+ entry.draft = null;
319
+ this.#pendingTrueHidden?.dispose();
320
+ this.#pendingTrueHidden = entry.hidden;
321
+ entry.hidden = null;
322
+ return this.#prefilledTokens = entry.tokens.length;
323
+ } finally { entry.dispose(); }
324
+ },
325
+ capture: (tokens, caches, namespace, maxBytes) => {
326
+ this.#checkOpen();
327
+ if (tokens.length !== this.#prefilledTokens || !this.#pendingTrueHidden ||
328
+ this.#cache.offset !== tokens.length - 1 || this.#roundAppended !== 0)
329
+ throw new Error("Qwen MTP snapshot requires an aligned prefill boundary");
330
+ const bytes = cacheBytes([...caches, this.#cache]) + this.#pendingTrueHidden.nbytes;
331
+ if (bytes > maxBytes) { prefix.store.dispose(); return; }
332
+ const retained: Cache[] = [];
333
+ let draft: KVCache | null = null, hidden: MlxArray | null = null;
334
+ try {
335
+ retained.push(...cloneKvCaches(caches));
336
+ draft = cloneKvCaches([this.#cache])[0] as KVCache;
337
+ hidden = this.#pendingTrueHidden.slice([0, 0, 0], this.#pendingTrueHidden.shape);
338
+ const entry = new QwenMtpPrefixState([...tokens], prefix.identity, namespace,
339
+ bytes, retained.splice(0), draft, hidden);
340
+ draft = null;
341
+ hidden = null;
342
+ prefix.store.put(entry, maxBytes);
343
+ } finally { disposeResources([...retained, draft, hidden].filter((r) => r != null)); }
344
+ },
345
+ };
346
+ }
347
+
348
+ /** Drafter prefill: rows for positions 0..L-2, keyed (token_{p+1}, h_p) —
349
+ * the (pending, h_{L-1}) row is NOT built here (the pending token is
350
+ * sampled after target prefill); its true hidden is kept for round 1. */
351
+ async prefill(promptIds: number[], ctxML?: MlxArray): Promise<void> {
352
+ this.#checkOpen();
353
+ if (!ctxML)
354
+ throw new Error("qwen MTP prefill requires the tapped pre-final-norm context");
355
+ try {
356
+ if (this.#hasDrafted)
357
+ throw new Error("qwen MTP prefill cannot follow drafting");
358
+ const L = promptIds.length;
359
+ const start = this.#prefilledTokens;
360
+ const H = ctxML.shape[2]!;
361
+ if (L <= start || ctxML.shape[1]! !== L - start)
362
+ throw new Error(`qwen MTP tap covered ${ctxML.shape[1]} of ${L - start} new prompt positions`);
363
+ if (start > 0) {
364
+ if (!this.#pendingTrueHidden || this.#cache.offset !== start - 1)
365
+ throw new Error("qwen MTP prefix state is not aligned");
366
+ this.#stepOne(promptIds[start]!, this.#pendingTrueHidden).dispose();
367
+ }
368
+ this.#pendingTrueHidden?.dispose();
369
+ // A slice would retain the complete prompt buffer in the saved prefix.
370
+ // Materialize this one row before handing off the prefill state.
371
+ using tail = ctxML.slice([0, L - start - 1, 0], [1, L - start, H]);
372
+ this.#pendingTrueHidden = materializeCopy(tail);
373
+ for (let pos = start; pos + 1 < L; pos += DRAFT_PREFILL_CHUNK) {
374
+ const n = Math.min(DRAFT_PREFILL_CHUNK, L - 1 - pos);
375
+ const shifted = promptIds.slice(pos + 1, pos + 1 + n);
376
+ const ids = ops.fromInt32(shifted, [1, n]);
377
+ const embeds = this.#target.embed(ids);
378
+ ids.dispose();
379
+ const hiddens = ctxML.slice([0, pos - start, 0], [1, pos + n - start, H]);
380
+ const out = this.#module.forward(embeds, hiddens, this.#cache);
381
+ embeds.dispose();
382
+ hiddens.dispose();
383
+ out.dispose(); // prefill outputs are not seeds; only KV matters here
384
+ // Bound the lazy KV graph at each chunk. Evaluating the unused output
385
+ // would also execute attention and the MLP, which prefill does not need.
386
+ ops.evalAll(this.#cache.state());
387
+ }
388
+ if (this.#cache.offset !== L - 1)
389
+ throw new Error(`qwen MTP prefill offset ${this.#cache.offset}, expected ${L - 1}`);
390
+ // Also covers a restored-prefix bridge with no subsequent full chunk.
391
+ ops.evalAll([this.#pendingTrueHidden, ...this.#cache.state()]);
392
+ this.#prefilledTokens = L;
393
+ } finally {
394
+ ctxML?.dispose(); // ownership per the seam contract
395
+ }
396
+ }
397
+
398
+ async draft(feed: number[], n: number, stepBase: number): Promise<number[]> {
399
+ this.#checkOpen();
400
+ this.#hasDrafted = true;
401
+ if (this.#roundAppended !== 0)
402
+ throw new Error("qwen MTP draft called before the prior round committed");
403
+ if (n <= 0) return [];
404
+ const pending = feed.at(-1);
405
+ if (!Number.isSafeInteger(pending) || pending! < 0)
406
+ throw new Error("qwen MTP requires a non-empty token feed");
407
+
408
+ const drafts: number[] = [];
409
+ let chained: MlxArray | null = null;
410
+ const startOffset = this.#cache.offset;
411
+ try {
412
+ // Build the pending token's row from the TRUE target hidden at the
413
+ // preceding position (prefill tail on round 1, verify tap afterwards).
414
+ if (!this.#pendingTrueHidden)
415
+ throw new Error("qwen MTP draft before prefill/commit");
416
+ const out = this.#stepOne(pending!, this.#pendingTrueHidden);
417
+ this.#pendingTrueHidden.dispose();
418
+ this.#pendingTrueHidden = null;
419
+ this.#roundAppended++;
420
+ chained = out;
421
+ drafts.push(this.#sample(out, stepBase));
422
+ while (drafts.length < n) {
423
+ const out = this.#stepOne(drafts.at(-1)!, chained!);
424
+ chained!.dispose();
425
+ chained = out;
426
+ this.#roundAppended++;
427
+ drafts.push(this.#sample(out, stepBase + drafts.length));
428
+ }
429
+ return drafts;
430
+ } catch (error) {
431
+ const appended = this.#cache.offset - startOffset;
432
+ if (appended > 0) this.#cache.trim(appended);
433
+ this.#roundAppended = 0;
434
+ throw error;
435
+ } finally {
436
+ chained?.dispose();
437
+ }
438
+ }
439
+
440
+ /** mlx-vlm accept semantics: keep drafted rows for accepted positions,
441
+ * trim the rejected tail, then append the missing accepted row (all-accept
442
+ * case) and the correction/bonus row using the TARGET's verified pre-norm
443
+ * hiddens — harvesting the last output as the next round's seed. */
444
+ async commit(
445
+ d: number,
446
+ kAccept: number,
447
+ vCtxML?: MlxArray,
448
+ _verifiedHidden?: MlxArray,
449
+ acceptedTokens: readonly number[] = [],
450
+ ): Promise<void> {
451
+ this.#checkOpen();
452
+ try {
453
+ if (!vCtxML)
454
+ throw new Error("qwen MTP commit requires the tapped verify context");
455
+ if (kAccept > 0 && acceptedTokens.length !== kAccept)
456
+ throw new Error(
457
+ `qwen MTP received ${acceptedTokens.length} accepted tokens for k=${kAccept}`,
458
+ );
459
+ // Rows appended this round cover window tokens 0..d-1 (pending +
460
+ // drafts[0..d-2]); rows keyed by ACCEPTED window tokens (0..kAccept)
461
+ // survive — trim the rejected tail.
462
+ const trim = Math.max(this.#roundAppended - 1 - kAccept, 0);
463
+ if (trim > 0) this.#cache.trim(trim);
464
+
465
+ // All-accept case: window token d (= drafts[d-1]) has no drafted row —
466
+ // append it from the TRUE verify hidden (vCtx row d-1, the hidden at
467
+ // its preceding position). The correction/bonus row is NOT built here:
468
+ // the serve loop re-feeds that token in the next draft()'s `feed`, and
469
+ // draft() opens by building its row from #pendingTrueHidden below.
470
+ const H = vCtxML.shape[2]!;
471
+ if (kAccept === d && d > 0) {
472
+ const ids = ops.fromInt32([acceptedTokens[kAccept - 1]!], [1, 1]);
473
+ const embeds = this.#target.embed(ids);
474
+ ids.dispose();
475
+ const hidden = vCtxML.slice([0, d - 1, 0], [1, d, H]);
476
+ const out = this.#module.forward(embeds, hidden, this.#cache);
477
+ embeds.dispose();
478
+ hidden.dispose();
479
+ out.dispose();
480
+ }
481
+ // Next round's pending row input: the target's verified pre-norm
482
+ // hidden at the emitted position.
483
+ this.#pendingTrueHidden?.dispose();
484
+ this.#pendingTrueHidden = vCtxML.slice([0, kAccept, 0], [1, kAccept + 1, H]);
485
+ } finally {
486
+ vCtxML?.dispose(); // seam: commit takes ownership
487
+ this.#roundAppended = 0;
488
+ }
489
+ }
490
+
491
+ dispose(): void {
492
+ if (this.#closed) return;
493
+ this.#closed = true;
494
+ this.#cache.dispose();
495
+ this.#pendingTrueHidden?.dispose();
496
+ }
497
+
498
+ /** One module forward for (token, hidden) — appends one KV row. */
499
+ #stepOne(token: number, hidden: MlxArray): MlxArray {
500
+ using ids = ops.fromInt32([token], [1, 1]);
501
+ using embed = this.#target.embed(ids);
502
+ ids.dispose();
503
+ return this.#module.forward(embed, hidden, this.#cache);
504
+ }
505
+
506
+ /** Sample a draft token from the module output via the TARGET's lm head
507
+ * and the request sampler (per-step RNG stream discipline). */
508
+ #sample(moduleOut: MlxArray, step: number): number {
509
+ using logits = this.#target.logitsFromHidden(moduleOut);
510
+ // Sampler contract is [1, V] (the main decode loop's shape). moduleOut
511
+ // is [1, 1, H] → logits [1, 1, V]; without this reshape any sampler
512
+ // that slices 2-D (top-k) throws "[slice] Invalid number of indices…
513
+ // dimension 3" — the serve-lane MTP 500 (chat defaults carry the
514
+ // model's top_k=20; the greedy bench harness never hit it).
515
+ const V = logits.shape[logits.shape.length - 1]!;
516
+ using flat = ops.reshape(logits, [1, V]);
517
+ logits.dispose();
518
+ using logprobs = toLogprobs(flat);
519
+ flat.dispose();
520
+ using tok = this.#sampler(logprobs, step);
521
+ logprobs.dispose();
522
+ return ops.itemUint32(tok);
523
+ }
524
+
525
+ #checkOpen(): void {
526
+ if (this.#closed) throw new Error("qwen MTP source is closed");
527
+ }
528
+ }