mlx-bun 0.5.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (640) hide show
  1. package/README.md +1449 -109
  2. package/THIRD_PARTY_NOTICES.md +882 -0
  3. package/bin/mlx-bun.convert.mjs +4 -0
  4. package/bin/mlx-bun.fuse.mjs +4 -0
  5. package/bin/mlx-bun.generate.mjs +4 -0
  6. package/bin/mlx-bun.lora.mjs +4 -0
  7. package/bin/mlx-bun.mjs +13 -21
  8. package/bin/mlx-bun.server.mjs +4 -0
  9. package/bin/mlx-bun.upload.mjs +4 -0
  10. package/dist/web/app.js +9584 -0
  11. package/package.json +50 -53
  12. package/src/cli/ane-install.ts +76 -0
  13. package/src/cli/args.ts +179 -0
  14. package/src/cli/engine-entry.ts +68 -0
  15. package/src/cli/inference.ts +214 -0
  16. package/src/cli/job-entry.ts +97 -0
  17. package/src/cli/library-host.ts +117 -0
  18. package/src/cli/main.ts +57 -0
  19. package/src/cli/memory-engine.ts +163 -0
  20. package/src/cli/memory.ts +36 -0
  21. package/src/cli/mlx-lm-aliases.ts +309 -0
  22. package/src/cli/model-choice.ts +33 -0
  23. package/src/cli/model-selection.ts +84 -0
  24. package/src/cli/module-host.ts +11 -0
  25. package/src/cli/module-verbs.ts +50 -0
  26. package/src/cli/numerical-policy.ts +12 -0
  27. package/src/cli/perplexity.ts +89 -0
  28. package/src/cli/serve-host.ts +423 -0
  29. package/src/cli/serve-isolated.ts +217 -0
  30. package/src/cli/serve-options.ts +106 -0
  31. package/src/cli/serve-state.ts +205 -0
  32. package/src/cli/serve.ts +500 -0
  33. package/src/cli/served-model-host.ts +102 -0
  34. package/src/cli/server-entry.ts +148 -0
  35. package/src/cli/serving-unit.ts +177 -0
  36. package/src/{tui.ts → cli/terminal.ts} +13 -8
  37. package/src/cli/worker-adapters.ts +34 -0
  38. package/src/cli/worker-entry.ts +272 -0
  39. package/src/cli/worker-unit.ts +179 -0
  40. package/src/engine/adapter-operation.ts +26 -0
  41. package/src/engine/cache-services.ts +114 -0
  42. package/src/engine/completion.ts +39 -0
  43. package/src/engine/generation-gateway.ts +358 -0
  44. package/src/engine/index.ts +59 -0
  45. package/src/engine/kv-budget.ts +120 -0
  46. package/src/engine/media-preparation.ts +232 -0
  47. package/src/engine/model-binding.ts +24 -0
  48. package/src/engine/model-host.ts +513 -0
  49. package/src/engine/model-serving.ts +89 -0
  50. package/src/{serve → engine}/preparation.ts +7 -5
  51. package/src/{serve → engine}/session-completion-engine.ts +6 -6
  52. package/src/engine/telemetry.ts +109 -0
  53. package/src/hub/downloads.ts +116 -0
  54. package/src/jobs/db.ts +16 -7
  55. package/src/jobs/events.ts +29 -0
  56. package/src/jobs/executable.ts +5 -0
  57. package/src/jobs/host.ts +51 -0
  58. package/src/jobs/in-process.ts +55 -0
  59. package/src/{contracts/jobs.ts → jobs/protocol.ts} +7 -9
  60. package/src/jobs/runner.ts +150 -231
  61. package/src/jobs/service.ts +75 -0
  62. package/src/jobs/sse.ts +59 -23
  63. package/src/jobs/worker-memory.ts +46 -0
  64. package/src/jobs/worker-process.ts +163 -0
  65. package/src/jobs/worker-supervisor.ts +258 -0
  66. package/src/modules.ts +58 -0
  67. package/src/publishing/catalog-hub.ts +18 -0
  68. package/src/publishing/credentials.ts +31 -0
  69. package/src/publishing/upload.ts +37 -0
  70. package/src/residency/leased-adapters.ts +21 -0
  71. package/src/residency/local-records.ts +16 -0
  72. package/src/residency/model-residency.ts +405 -0
  73. package/src/residency/record-index.ts +28 -0
  74. package/src/residency/resident-estimate.ts +50 -0
  75. package/src/{anthropic.ts → server/anthropic.ts} +3 -81
  76. package/src/server/cache-routes.ts +36 -0
  77. package/src/{serve → server}/chat-request.ts +10 -10
  78. package/src/{serve → server}/chat-stage.ts +14 -14
  79. package/src/{client.ts → server/client.ts} +28 -2
  80. package/src/{serve → server}/completion-executor.ts +33 -41
  81. package/src/server/decision-body.ts +55 -0
  82. package/src/server/decision-routes.ts +134 -0
  83. package/src/server/decision-wire.ts +228 -0
  84. package/src/{serve → server}/discovery-routes.ts +25 -82
  85. package/src/{serve → server}/http.ts +25 -13
  86. package/src/{serve → server}/inference-request.ts +5 -5
  87. package/src/server/job-routes.ts +20 -0
  88. package/src/server/media-prompt.ts +63 -0
  89. package/src/server/memory-completion-client.ts +46 -0
  90. package/src/server/model-routes.ts +192 -0
  91. package/src/{serve → server}/openai-wire.ts +3 -2
  92. package/src/{serve → server}/pipeline.ts +1 -1
  93. package/src/server/prompt-builder.ts +13 -0
  94. package/src/server/prompt-contracts.ts +17 -0
  95. package/src/server/proxy-routes.ts +187 -0
  96. package/src/server/publishing-routes.ts +41 -0
  97. package/src/{serve → server}/request-plan.ts +6 -8
  98. package/src/{serve → server}/request-prep.ts +47 -50
  99. package/src/{serve → server}/responses-client.ts +30 -13
  100. package/src/{responses.ts → server/responses.ts} +12 -93
  101. package/src/server/routes.ts +205 -0
  102. package/src/server/start.ts +72 -0
  103. package/src/server/status-routes.ts +108 -0
  104. package/src/{serve → server}/text-completion-stage.ts +25 -4
  105. package/src/server/text-prompt.ts +12 -0
  106. package/src/{serve → server}/token-streams.ts +36 -38
  107. package/src/server/worker-routes.ts +311 -0
  108. package/src/storage/paths.ts +61 -0
  109. package/src/web/assets.ts +37 -0
  110. package/src/web/{src → browser}/api.ts +5 -23
  111. package/src/web/browser/chat-host.ts +17 -0
  112. package/src/web/browser/installed-panels.ts +6 -0
  113. package/src/web/browser/main.ts +55 -0
  114. package/src/web/browser/markdown.ts +279 -0
  115. package/src/web/browser/memory-panel.ts +9 -0
  116. package/src/web/browser/palette.ts +64 -0
  117. package/src/web/browser/protocol.ts +13 -0
  118. package/src/web/browser/shell.ts +467 -0
  119. package/src/web/{src → browser}/status.ts +32 -36
  120. package/src/web/build.ts +51 -0
  121. package/src/web/public/app.html +1054 -0
  122. package/src/web/{sw.js → public/sw.js} +4 -3
  123. package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
  124. package/src/web/{vendor → public/vendor}/hljs.js +2 -2
  125. package/THIRD_PARTY_LICENSES.md +0 -51
  126. package/docs/README.md +0 -107
  127. package/docs/archive/hlg-sampling.md +0 -350
  128. package/docs/archive/investigations/audio-input-plan.md +0 -356
  129. package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +0 -3617
  130. package/docs/archive/investigations/chunk-finetune-experiment.md +0 -84
  131. package/docs/archive/investigations/colibri-glm52-port.md +0 -1486
  132. package/docs/archive/investigations/colibri-oracle-pin.md +0 -1284
  133. package/docs/archive/investigations/curve-bisector-routing.md +0 -123
  134. package/docs/archive/investigations/decode-roofline-lookagain.md +0 -348
  135. package/docs/archive/investigations/diffusion-gemma-port.md +0 -602
  136. package/docs/archive/investigations/dspark-handoff.md +0 -224
  137. package/docs/archive/investigations/expert-offload-single-user-moe.md +0 -292
  138. package/docs/archive/investigations/faithful-l1-consolidation.md +0 -310
  139. package/docs/archive/investigations/hlg-sampling-investigation.md +0 -970
  140. package/docs/archive/investigations/indexshare-performance-spike.md +0 -357
  141. package/docs/archive/investigations/kernel-perf-review-2026-07.md +0 -171
  142. package/docs/archive/investigations/lab-build-journal.md +0 -157
  143. package/docs/archive/investigations/minicpm5-decode-megakernel.md +0 -489
  144. package/docs/archive/investigations/openjarvis-fit.md +0 -198
  145. package/docs/archive/investigations/openwiki-evaluation.md +0 -249
  146. package/docs/archive/investigations/optimization_plan.md +0 -346
  147. package/docs/archive/investigations/orpo-base-uf-experiment-and-directions.md +0 -120
  148. package/docs/archive/investigations/orpo-flash-cce-pin-leak.md +0 -167
  149. package/docs/archive/investigations/orpo-uf-testing-handoff.md +0 -126
  150. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +0 -92
  151. package/docs/archive/investigations/pi-builtin-investigation.md +0 -308
  152. package/docs/archive/investigations/pre-colibri-stabilization.md +0 -329
  153. package/docs/archive/investigations/qwen38-27b-campaign-log.md +0 -1092
  154. package/docs/archive/investigations/segmented-backward-handoff.md +0 -148
  155. package/docs/archive/investigations/starter-model-port-handoff.md +0 -342
  156. package/docs/archive/investigations/steel-flash-cce-handoff.md +0 -489
  157. package/docs/archive/investigations/the-dreaming-handoff.md +0 -141
  158. package/docs/archive/investigations/trainer-validation-experiment.md +0 -173
  159. package/docs/archive/planning/chat-ui-vision-memory-first.md +0 -199
  160. package/docs/archive/planning/chat-ui-vision.md +0 -50
  161. package/docs/archive/planning/chunk-task-roadmap.md +0 -77
  162. package/docs/archive/planning/curve-sampler-research-plan.md +0 -331
  163. package/docs/archive/planning/journal.md +0 -316
  164. package/docs/archive/planning/memory-docs-and-dag-plan.md +0 -194
  165. package/docs/archive/planning/pi-integration-review.md +0 -178
  166. package/docs/archive/planning/release-notes-v0.0.10.md +0 -57
  167. package/docs/archive/planning/release-notes-v0.0.11.md +0 -239
  168. package/docs/archive/planning/release-notes-v0.0.12.md +0 -80
  169. package/docs/archive/planning/release-notes-v0.0.13.md +0 -77
  170. package/docs/archive/planning/release-notes-v0.0.9.md +0 -122
  171. package/docs/archive/planning/release-notes-v0.2.0.md +0 -49
  172. package/docs/archive/planning/release-notes-v0.3.0.md +0 -35
  173. package/docs/archive/planning/release-notes-v0.4.0.md +0 -45
  174. package/docs/archive/planning/release-notes-v0.5.0.md +0 -37
  175. package/docs/archive/planning/web-ui-pass-plan.md +0 -213
  176. package/docs/archive/planning/website-readme-pass-plan.md +0 -224
  177. package/docs/design/batching.md +0 -2054
  178. package/docs/design/decode-speed-program.md +0 -2404
  179. package/docs/design/dreaming-nightly-pipeline.md +0 -684
  180. package/docs/design/generic-model-support.md +0 -821
  181. package/docs/design/kv-cache.md +0 -993
  182. package/docs/design/orpo-training.md +0 -680
  183. package/docs/design/speculative-decoding.md +0 -2129
  184. package/docs/design/turboquant.md +0 -2225
  185. package/docs/design/unified-engine-frontier-plan.md +0 -1987
  186. package/docs/design/web-chat-redesign.md +0 -695
  187. package/docs/planning/PRODUCT_ROADMAP.md +0 -593
  188. package/docs/planning/ResearchTopics.md +0 -119
  189. package/docs/planning/public-readiness-handoff.md +0 -244
  190. package/docs/planning/public-readiness.md +0 -149
  191. package/docs/planning/video-series.md +0 -421
  192. package/docs/reference/benchmarks.md +0 -3587
  193. package/docs/reference/cli.md +0 -646
  194. package/docs/reference/distribution.md +0 -380
  195. package/docs/reference/environment.md +0 -494
  196. package/docs/reference/glossary.md +0 -39
  197. package/docs/reference/library-api.md +0 -688
  198. package/docs/reference/memory.md +0 -260
  199. package/docs/reference/models.md +0 -346
  200. package/docs/reference/server-api.md +0 -1945
  201. package/docs/reference/server-config.md +0 -973
  202. package/docs/reference/training.md +0 -462
  203. package/docs/reference/troubleshooting.md +0 -158
  204. package/src/admission-plan.ts +0 -18
  205. package/src/audio/audiotoolbox.ts +0 -112
  206. package/src/audio/conformer.ts +0 -655
  207. package/src/audio/decode.ts +0 -133
  208. package/src/audio/features.ts +0 -202
  209. package/src/audio/mic-capture.ts +0 -98
  210. package/src/audio/silero-vad.ts +0 -379
  211. package/src/audio/transcode.ts +0 -67
  212. package/src/audio/whisper-decode.ts +0 -774
  213. package/src/audio/whisper-fast.ts +0 -732
  214. package/src/audio/whisper-format.ts +0 -73
  215. package/src/audio/whisper-mel.ts +0 -185
  216. package/src/audio/whisper-timing.ts +0 -274
  217. package/src/audio/whisper-tokenizer.ts +0 -239
  218. package/src/audio/whisper-transcribe.ts +0 -410
  219. package/src/backends/mlx/assistant-target.ts +0 -46
  220. package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
  221. package/src/backends/mlx/autoregressive.ts +0 -129
  222. package/src/backends/mlx/batch-group.ts +0 -1874
  223. package/src/backends/mlx/cache-attachment.ts +0 -67
  224. package/src/backends/mlx/cache-layout.ts +0 -31
  225. package/src/backends/mlx/checkpoint-state.ts +0 -53
  226. package/src/backends/mlx/continuation-persistence.ts +0 -107
  227. package/src/backends/mlx/continuation-request.ts +0 -43
  228. package/src/backends/mlx/continuation.ts +0 -77
  229. package/src/backends/mlx/diffusion.ts +0 -39
  230. package/src/backends/mlx/draft-target.ts +0 -32
  231. package/src/backends/mlx/encoder-cache.ts +0 -25
  232. package/src/backends/mlx/evaluation.ts +0 -105
  233. package/src/backends/mlx/fill-append.ts +0 -58
  234. package/src/backends/mlx/fill-group.ts +0 -497
  235. package/src/backends/mlx/full-kv-row-append.ts +0 -32
  236. package/src/backends/mlx/full-kv-row-donor.ts +0 -54
  237. package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
  238. package/src/backends/mlx/gateway-binding.ts +0 -174
  239. package/src/backends/mlx/graph.ts +0 -38
  240. package/src/backends/mlx/kv-codec.ts +0 -9
  241. package/src/backends/mlx/kv-maintenance.ts +0 -161
  242. package/src/backends/mlx/kv-tensor-rows.ts +0 -212
  243. package/src/backends/mlx/memory-client.ts +0 -189
  244. package/src/backends/mlx/methods.ts +0 -90
  245. package/src/backends/mlx/mixed-iteration.ts +0 -62
  246. package/src/backends/mlx/model-prompt.ts +0 -178
  247. package/src/backends/mlx/model-serving.ts +0 -80
  248. package/src/backends/mlx/paged-cache-codec.ts +0 -27
  249. package/src/backends/mlx/prefill-cohort.ts +0 -100
  250. package/src/backends/mlx/prefill-policy.ts +0 -26
  251. package/src/backends/mlx/prefill-rows.ts +0 -244
  252. package/src/backends/mlx/prefill.ts +0 -33
  253. package/src/backends/mlx/projected-context-rows.ts +0 -72
  254. package/src/backends/mlx/prompt-input.ts +0 -29
  255. package/src/backends/mlx/qwen-prompt-input.ts +0 -40
  256. package/src/backends/mlx/request-policy.ts +0 -20
  257. package/src/backends/mlx/request-state-policy.ts +0 -54
  258. package/src/backends/mlx/rollback.ts +0 -92
  259. package/src/backends/mlx/row-sampling.ts +0 -49
  260. package/src/backends/mlx/serial-executor.ts +0 -251
  261. package/src/backends/mlx/speculative-group.ts +0 -438
  262. package/src/backends/mlx/speculative-round.ts +0 -161
  263. package/src/backends/mlx/speculative.ts +0 -131
  264. package/src/backends/mlx/state-rows.ts +0 -104
  265. package/src/backends/mlx/state-views.ts +0 -46
  266. package/src/backends/mlx/target-layout-capability.ts +0 -35
  267. package/src/backends/mlx/text-engine.ts +0 -73
  268. package/src/backends/mlx/transitioning-kv-rows.ts +0 -173
  269. package/src/chat-template.ts +0 -382
  270. package/src/cli.ts +0 -3490
  271. package/src/config.ts +0 -399
  272. package/src/contracts/completion.ts +0 -16
  273. package/src/contracts/execution.ts +0 -70
  274. package/src/contracts/generation.ts +0 -103
  275. package/src/contracts/host.ts +0 -7
  276. package/src/contracts/object-cache.ts +0 -8
  277. package/src/contracts/pi-provider.ts +0 -30
  278. package/src/contracts/pi.ts +0 -261
  279. package/src/contracts/prefix-cache.ts +0 -33
  280. package/src/contracts/resources.ts +0 -25
  281. package/src/contracts/sampling.ts +0 -23
  282. package/src/contracts/scheduling.ts +0 -54
  283. package/src/contracts/tsconfig.json +0 -14
  284. package/src/dataset/generators.ts +0 -1087
  285. package/src/dataset/index.ts +0 -12
  286. package/src/dataset/job.ts +0 -81
  287. package/src/dataset/llm.ts +0 -71
  288. package/src/dataset/registry.ts +0 -528
  289. package/src/diffusion/diffusion-generate.ts +0 -507
  290. package/src/download.ts +0 -505
  291. package/src/embed.ts +0 -77
  292. package/src/engine/admission.ts +0 -59
  293. package/src/engine/cancellation.ts +0 -38
  294. package/src/engine/engine.ts +0 -56
  295. package/src/engine/execution-coordinator.ts +0 -70
  296. package/src/engine/execution-plan.ts +0 -64
  297. package/src/engine/execution-tasks.ts +0 -44
  298. package/src/engine/namespaced-cache.ts +0 -13
  299. package/src/engine/resources.ts +0 -53
  300. package/src/engine/scheduler.ts +0 -92
  301. package/src/engine/session.ts +0 -247
  302. package/src/eval/README.md +0 -130
  303. package/src/eval/ifeval.ts +0 -81
  304. package/src/eval/kl-prompts.ts +0 -29
  305. package/src/eval/kl.ts +0 -590
  306. package/src/eval/perplexity.ts +0 -164
  307. package/src/eval/quality-db.ts +0 -92
  308. package/src/eval/reference-logits.ts +0 -207
  309. package/src/eval/runner.ts +0 -124
  310. package/src/eval/score.ts +0 -49
  311. package/src/eval/tasks/bfcl.ts +0 -535
  312. package/src/eval/tasks/gsm8k.ts +0 -98
  313. package/src/eval/tasks/hashhop.ts +0 -139
  314. package/src/eval/tasks/humaneval.ts +0 -275
  315. package/src/eval/tasks/ifeval.ts +0 -525
  316. package/src/eval/tasks/mmlu.ts +0 -157
  317. package/src/eval/ultrafeedback.ts +0 -60
  318. package/src/evaldb.ts +0 -199
  319. package/src/expert-io.ts +0 -393
  320. package/src/expert-offload-build.ts +0 -77
  321. package/src/expert-offload.ts +0 -76
  322. package/src/expert-residency.ts +0 -1134
  323. package/src/expert-usage.ts +0 -415
  324. package/src/fill/echo-index.ts +0 -188
  325. package/src/fill/fill-session.ts +0 -463
  326. package/src/fill/proposal.ts +0 -68
  327. package/src/fill/schema-rows.ts +0 -506
  328. package/src/fill/tool-boundary.ts +0 -103
  329. package/src/fit.ts +0 -249
  330. package/src/generate.ts +0 -1352
  331. package/src/grammar.ts +0 -604
  332. package/src/harness-pi.ts +0 -184
  333. package/src/hf-push.ts +0 -628
  334. package/src/hub-rest.ts +0 -242
  335. package/src/index.ts +0 -63
  336. package/src/inference/denoising.ts +0 -17
  337. package/src/inference/draft-acceptance.ts +0 -41
  338. package/src/inference/draft-output.ts +0 -49
  339. package/src/inference/graph.ts +0 -33
  340. package/src/inference/prefill.ts +0 -45
  341. package/src/inference/rollback.ts +0 -7
  342. package/src/jobs/index.ts +0 -31
  343. package/src/jobs/job-entry.ts +0 -107
  344. package/src/jobs/task-client.ts +0 -19
  345. package/src/jobs/types.ts +0 -2
  346. package/src/kv-scheme.ts +0 -279
  347. package/src/kv-store.ts +0 -1135
  348. package/src/lab/README.md +0 -22
  349. package/src/lab/curve/curve-designer.html +0 -303
  350. package/src/lab/curve/curve-sampler.ts +0 -122
  351. package/src/lab/expert-trace/expert-trace.ts +0 -69
  352. package/src/lab/paged-kv/paged-attention.ts +0 -117
  353. package/src/lab/paged-kv/paged-kv-rows.ts +0 -111
  354. package/src/lab/paged-kv/paged-kv.ts +0 -336
  355. package/src/library.ts +0 -40
  356. package/src/lora.ts +0 -408
  357. package/src/md-modules.d.ts +0 -54
  358. package/src/media-fetch.ts +0 -442
  359. package/src/memory/article.ts +0 -558
  360. package/src/memory/chunk-validation.ts +0 -150
  361. package/src/memory/chunk.ts +0 -318
  362. package/src/memory/cluster.ts +0 -351
  363. package/src/memory/coverage.ts +0 -134
  364. package/src/memory/crosslink.ts +0 -0
  365. package/src/memory/db.ts +0 -253
  366. package/src/memory/entity.ts +0 -203
  367. package/src/memory/gate.ts +0 -187
  368. package/src/memory/history.ts +0 -152
  369. package/src/memory/ingest.ts +0 -335
  370. package/src/memory/model.ts +0 -183
  371. package/src/memory/normalize.ts +0 -671
  372. package/src/memory/parse.ts +0 -22
  373. package/src/memory/pipeline.ts +0 -275
  374. package/src/memory/prompts.ts +0 -65
  375. package/src/memory/query.ts +0 -334
  376. package/src/memory/reconcile.ts +0 -734
  377. package/src/memory/redirect.ts +0 -108
  378. package/src/memory/reindex.ts +0 -325
  379. package/src/memory/resolve.ts +0 -307
  380. package/src/memory/rest.ts +0 -245
  381. package/src/memory/route.ts +0 -272
  382. package/src/memory/schedule.ts +0 -165
  383. package/src/memory/stages.ts +0 -777
  384. package/src/memory/synthesize.ts +0 -1638
  385. package/src/memory/tools.ts +0 -749
  386. package/src/memory/vault.ts +0 -1194
  387. package/src/memory/wikify.ts +0 -1064
  388. package/src/mlx/array.ts +0 -425
  389. package/src/mlx/autograd.ts +0 -304
  390. package/src/mlx/checkpoint.ts +0 -149
  391. package/src/mlx/compile.ts +0 -168
  392. package/src/mlx/custom-vjp.ts +0 -188
  393. package/src/mlx/ffi.ts +0 -531
  394. package/src/mlx/materialize.ts +0 -23
  395. package/src/mlx/metal-kernel.ts +0 -159
  396. package/src/mlx/normalized-argmax.ts +0 -116
  397. package/src/mlx/ops.ts +0 -1108
  398. package/src/mlx/quantized-rows.ts +0 -36
  399. package/src/mlx/token-bitmask.ts +0 -52
  400. package/src/mlx/turboquant-kv-decode.ts +0 -82
  401. package/src/mlx/turboquant-kv-inverse.ts +0 -122
  402. package/src/mlx/turboquant-ops.ts +0 -861
  403. package/src/mlx/turboquant-tables.ts +0 -237
  404. package/src/mmap.ts +0 -90
  405. package/src/model/artifact-identity.ts +0 -73
  406. package/src/model/batched-kv.ts +0 -235
  407. package/src/model/batched-mask.ts +0 -279
  408. package/src/model/batched-quant.ts +0 -212
  409. package/src/model/batched-quantized-kv.ts +0 -144
  410. package/src/model/batched-rotating-quant.ts +0 -353
  411. package/src/model/batched-rotating-state.ts +0 -149
  412. package/src/model/batched-rotating.ts +0 -338
  413. package/src/model/batched-row-storage.ts +0 -179
  414. package/src/model/batched-ssm.ts +0 -52
  415. package/src/model/batched-turboquant-kv.ts +0 -94
  416. package/src/model/compiled-decode.ts +0 -712
  417. package/src/model/decoded-kv-donor.ts +0 -12
  418. package/src/model/delayed-quantized-kv.ts +0 -72
  419. package/src/model/delayed-rotating-quantized-kv.ts +0 -93
  420. package/src/model/delayed-turboquant-kv.ts +0 -57
  421. package/src/model/diffusion-gemma.ts +0 -1026
  422. package/src/model/factory.ts +0 -215
  423. package/src/model/fingerprint.ts +0 -45
  424. package/src/model/flash-attention.ts +0 -694
  425. package/src/model/full-prefill-padding.ts +0 -56
  426. package/src/model/full-prefill-row.ts +0 -105
  427. package/src/model/gemma4-base.ts +0 -2370
  428. package/src/model/gemma4.ts +0 -1395
  429. package/src/model/generated/gemma4-12b.ts +0 -429
  430. package/src/model/generated/gemma4-26b.ts +0 -448
  431. package/src/model/generated/gemma4-e4b.ts +0 -717
  432. package/src/model/generated/index.ts +0 -18
  433. package/src/model/glm52-atlas.ts +0 -511
  434. package/src/model/glm52-cache.ts +0 -792
  435. package/src/model/glm52-config.ts +0 -262
  436. package/src/model/glm52-container.ts +0 -305
  437. package/src/model/glm52-coupling.ts +0 -388
  438. package/src/model/glm52-dsa.ts +0 -606
  439. package/src/model/glm52-expert-layout.ts +0 -253
  440. package/src/model/glm52-layout.ts +0 -152
  441. package/src/model/glm52-memory.ts +0 -500
  442. package/src/model/glm52-mla.ts +0 -792
  443. package/src/model/glm52-moe.ts +0 -223
  444. package/src/model/glm52-pilot.ts +0 -537
  445. package/src/model/glm52-reference.ts +0 -445
  446. package/src/model/glm52-residency.ts +0 -557
  447. package/src/model/glm52-resident-weights.ts +0 -135
  448. package/src/model/glm52-streamed-experts.ts +0 -453
  449. package/src/model/glm52-streamed-metal.ts +0 -610
  450. package/src/model/glm52-weights.ts +0 -503
  451. package/src/model/glm52.ts +0 -1132
  452. package/src/model/implementation.ts +0 -69
  453. package/src/model/kv-attention-view.ts +0 -70
  454. package/src/model/minicpm5.ts +0 -376
  455. package/src/model/profile.ts +0 -468
  456. package/src/model/quantized-append-attention.ts +0 -31
  457. package/src/model/qwen-append.ts +0 -12
  458. package/src/model/qwen-conv.ts +0 -52
  459. package/src/model/qwen3-delta-state.ts +0 -74
  460. package/src/model/qwen3-delta.ts +0 -451
  461. package/src/model/qwen3-moe.ts +0 -404
  462. package/src/model/qwen3-mrope.ts +0 -250
  463. package/src/model/qwen3.ts +0 -277
  464. package/src/model/qwen3_5-checkpoint.ts +0 -164
  465. package/src/model/qwen3_5.ts +0 -881
  466. package/src/model/rotating-kv-donor.ts +0 -35
  467. package/src/model/rotating-kv-layout.ts +0 -262
  468. package/src/model/rotating-row-transaction.ts +0 -35
  469. package/src/model/speculative-rotating-kv.ts +0 -101
  470. package/src/model/ssm-prefill-padding.ts +0 -39
  471. package/src/model/support.ts +0 -110
  472. package/src/model/token-groups.ts +0 -66
  473. package/src/model/trellis-balanced-scatter.ts +0 -51
  474. package/src/model/trellis-linear.ts +0 -672
  475. package/src/model/trellis-shared-m.ts +0 -151
  476. package/src/model/trellis-shared-scatter.ts +0 -144
  477. package/src/model/trellis-splitk-prefill.ts +0 -158
  478. package/src/model/trellis-tiled-prefill.ts +0 -136
  479. package/src/model/trellis-vector-expand.ts +0 -62
  480. package/src/model/trellis-wide-prefill.ts +0 -94
  481. package/src/model/turboquant-codec.ts +0 -89
  482. package/src/model/universal/archs.ts +0 -332
  483. package/src/model/universal/dense.ts +0 -612
  484. package/src/model/universal/modules.ts +0 -170
  485. package/src/model/universal/rope.ts +0 -381
  486. package/src/model/whisper.ts +0 -435
  487. package/src/native/expert_io.c +0 -523
  488. package/src/native/frame_extract.swift +0 -140
  489. package/src/native/mic_capture.swift +0 -92
  490. package/src/native-pack.ts +0 -163
  491. package/src/pi-provider.ts +0 -53
  492. package/src/pi-session.ts +0 -81
  493. package/src/pi-terminal.ts +0 -274
  494. package/src/pi-web.ts +0 -1940
  495. package/src/preflight.ts +0 -158
  496. package/src/prompt-cache.ts +0 -682
  497. package/src/quantize/allocator.ts +0 -627
  498. package/src/quantize/atomic-output.ts +0 -38
  499. package/src/quantize/calibration.ts +0 -181
  500. package/src/quantize/config-writer.ts +0 -145
  501. package/src/quantize/index.ts +0 -84
  502. package/src/quantize/job.ts +0 -162
  503. package/src/quantize/quantizer.ts +0 -666
  504. package/src/quantize/rotate.ts +0 -603
  505. package/src/quantize/safetensors-writer.ts +0 -249
  506. package/src/quantize/sensitivity.ts +0 -216
  507. package/src/quantize/trellis.ts +0 -586
  508. package/src/quantize/weight-transform.ts +0 -292
  509. package/src/registry.ts +0 -602
  510. package/src/runtime-config.ts +0 -77
  511. package/src/safetensors.ts +0 -165
  512. package/src/sampler.ts +0 -1012
  513. package/src/serve/admin-routes.ts +0 -184
  514. package/src/serve/assets/curve-terrain.html +0 -105
  515. package/src/serve/assets/training-inference-map.html +0 -575
  516. package/src/serve/audio-routes.ts +0 -323
  517. package/src/serve/aux-routes.ts +0 -132
  518. package/src/serve/batch-scheduler.ts +0 -8
  519. package/src/serve/checkpoint-identity.ts +0 -64
  520. package/src/serve/generation-gateway.ts +0 -537
  521. package/src/serve/isolate.ts +0 -609
  522. package/src/serve/kv-budget.ts +0 -14
  523. package/src/serve/lab-routes.ts +0 -341
  524. package/src/serve/lane-registry.ts +0 -45
  525. package/src/serve/model-admin-routes.ts +0 -155
  526. package/src/serve/model-binding.ts +0 -49
  527. package/src/serve/model-host.ts +0 -511
  528. package/src/serve/parent-application.ts +0 -56
  529. package/src/serve/prompt-response-trace.ts +0 -161
  530. package/src/serve/response-lease.ts +0 -24
  531. package/src/serve/session-search.ts +0 -326
  532. package/src/serve/static-routes.ts +0 -101
  533. package/src/serve/transcription-server.ts +0 -64
  534. package/src/serve/transcription-service.ts +0 -502
  535. package/src/server.ts +0 -1282
  536. package/src/spec/assistant-rows.ts +0 -93
  537. package/src/spec/assistant-source.ts +0 -126
  538. package/src/spec/cache-identity.ts +0 -22
  539. package/src/spec/deepspec-rows.ts +0 -29
  540. package/src/spec/deepspec-source.ts +0 -145
  541. package/src/spec/dflash-source.ts +0 -149
  542. package/src/spec/drafter.ts +0 -304
  543. package/src/spec/dspark/ab-stats.ts +0 -136
  544. package/src/spec/dspark/calibration.ts +0 -112
  545. package/src/spec/dspark/data-dflash.ts +0 -156
  546. package/src/spec/dspark/data.ts +0 -201
  547. package/src/spec/dspark/deepspec-module.ts +0 -776
  548. package/src/spec/dspark/generate-dflash.ts +0 -245
  549. package/src/spec/dspark/generate.ts +0 -290
  550. package/src/spec/dspark/loader.ts +0 -23
  551. package/src/spec/dspark/loss.ts +0 -213
  552. package/src/spec/dspark/module-dflash.ts +0 -560
  553. package/src/spec/dspark/module.ts +0 -540
  554. package/src/spec/dspark/quantize-drafter.ts +0 -91
  555. package/src/spec/dspark/sample.ts +0 -112
  556. package/src/spec/generate.ts +0 -276
  557. package/src/spec/glm52-mtp-graph.ts +0 -29
  558. package/src/spec/glm52-mtp-rows.ts +0 -132
  559. package/src/spec/glm52-mtp-source.ts +0 -318
  560. package/src/spec/glm52-mtp-state.ts +0 -33
  561. package/src/spec/ngram-source.ts +0 -226
  562. package/src/spec/projected-draft-rows.ts +0 -73
  563. package/src/spec/qwen-mtp-module.ts +0 -171
  564. package/src/spec/qwen-mtp-rows.ts +0 -230
  565. package/src/spec/qwen-mtp-source.ts +0 -386
  566. package/src/spec/qwen-mtp-state.ts +0 -30
  567. package/src/spec/serve-loop.ts +0 -587
  568. package/src/spec/source.ts +0 -228
  569. package/src/spec/two-model-rows.ts +0 -62
  570. package/src/spec/two-model.ts +0 -113
  571. package/src/ssd-cache.ts +0 -431
  572. package/src/ssd-durability.ts +0 -209
  573. package/src/storage/host-buffer.ts +0 -24
  574. package/src/storage/kv-writer.ts +0 -83
  575. package/src/storage/kv-writer.worker.js +0 -264
  576. package/src/storage/retention-policy.ts +0 -37
  577. package/src/tiered-prompt-cache.ts +0 -45
  578. package/src/tokenizer.ts +0 -335
  579. package/src/tool-approvals.ts +0 -131
  580. package/src/tool-call.ts +0 -695
  581. package/src/train/dataset.ts +0 -484
  582. package/src/train/diffusion-lora.ts +0 -216
  583. package/src/train/export.ts +0 -32
  584. package/src/train/flash-cce.ts +0 -1234
  585. package/src/train/forward.ts +0 -266
  586. package/src/train/fuse.ts +0 -243
  587. package/src/train/index.ts +0 -46
  588. package/src/train/job.ts +0 -205
  589. package/src/train/lora-params.ts +0 -254
  590. package/src/train/loss.ts +0 -1591
  591. package/src/train/merge.ts +0 -195
  592. package/src/train/optimizer.ts +0 -183
  593. package/src/train/prefix-shared.ts +0 -490
  594. package/src/train/rank.ts +0 -137
  595. package/src/train/segmented.ts +0 -1828
  596. package/src/train/steel-qmm-header.ts +0 -1461
  597. package/src/train/trainer.ts +0 -1221
  598. package/src/train/watch.ts +0 -542
  599. package/src/vision/diffusion-vision.ts +0 -620
  600. package/src/vision/embedder.ts +0 -176
  601. package/src/vision/preprocess.ts +0 -247
  602. package/src/vision/prompt.ts +0 -409
  603. package/src/vision/qwen3vl-preprocess.ts +0 -409
  604. package/src/vision/qwen3vl-prompt.ts +0 -233
  605. package/src/vision/qwen3vl-tower.ts +0 -566
  606. package/src/vision/siglip.ts +0 -607
  607. package/src/vision/video-frames.ts +0 -148
  608. package/src/web/app.html +0 -2155
  609. package/src/web/app.js +0 -5863
  610. package/src/web/skills/memory/SKILL.md +0 -120
  611. package/src/web/skills/web-research/SKILL.md +0 -46
  612. package/src/web/skills.ts +0 -97
  613. package/src/web/src/adapters-panel.ts +0 -288
  614. package/src/web/src/assistant.ts +0 -451
  615. package/src/web/src/chat.ts +0 -1060
  616. package/src/web/src/composer.ts +0 -962
  617. package/src/web/src/dataset.ts +0 -132
  618. package/src/web/src/finetune.ts +0 -267
  619. package/src/web/src/hub.ts +0 -428
  620. package/src/web/src/main.ts +0 -98
  621. package/src/web/src/markdown.ts +0 -0
  622. package/src/web/src/memory-panel.ts +0 -572
  623. package/src/web/src/model-picker.ts +0 -159
  624. package/src/web/src/palette.ts +0 -346
  625. package/src/web/src/protocol.ts +0 -26
  626. package/src/web/src/quantize.ts +0 -167
  627. package/src/web/src/rag.ts +0 -274
  628. package/src/web/src/sessions.ts +0 -469
  629. package/src/web/src/shell.ts +0 -804
  630. package/src/web/src/ui-catalog.ts +0 -78
  631. package/src/web/src/voice.ts +0 -141
  632. package/src/web/vendor/README.md +0 -61
  633. package/src/web-assets.ts +0 -47
  634. package/src/web-tools.ts +0 -478
  635. package/src/weights.ts +0 -255
  636. /package/src/{serve → server}/completion-sink.ts +0 -0
  637. /package/src/{serve → server}/generated-token-history.ts +0 -0
  638. /package/src/web/{icon.svg → public/icon.svg} +0 -0
  639. /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
  640. /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
@@ -1,2054 +0,0 @@
1
- ---
2
- status: active
3
- axis: ON
4
- canonical-for: batching
5
- plan-anchor: "Phase 18 — Concurrent / batched serving (slots) + parallel load benchmark `[~]` (2026-06-13)"
6
- last-verified: 2026-09-10
7
- ---
8
-
9
- # Batching — continuous scheduling for `--batch N`
10
-
11
- The canonical design for concurrent serving: the scheduling seam
12
- (`src/serve/generation-gateway.ts`), the continuous-batching engine
13
- (`src/serve/batch-scheduler.ts`), the dynamic-B cache primitives
14
- (`src/model/batched-*.ts`), admission, and the parity discipline that
15
- gates all of it. Consolidates docs/design/batching.md, docs/design/batching.md,
16
- docs/design/batching.md, the batching items of
17
- docs/design/batching.md, and the scheduler tie-in from
18
- docs/design/kv-cache.md. Status and dated progress live in PLAN.md (see the
19
- plan anchor above); this file describes the mechanism as it exists and
20
- what is still open.
21
-
22
- Phase 6 now requires shared method/session execution at B=1 and B>1, full
23
- batch feature parity, and configuration organized by owning interface. The
24
- boundaries are defined in [engine architecture §12.5–12.7](unified-engine-frontier-plan.md#125-capability-negotiation-and-planning).
25
- The separate serial mechanism described below is the current implementation;
26
- retiring its duplicate lifecycle is open work. Backend specializations may
27
- remain when they improve measured performance. R17 owns output checkpoint
28
- publication to the shared RAM/SSD cache.
29
-
30
- User-facing mirror: docs/reference/server-config.md ("Execution modes",
31
- compatibility matrix, known limitations) and docs/reference/cli.md.
32
-
33
- ## 1. Why batch, and why continuous
34
-
35
- Decode on Apple Silicon is bandwidth-bound: every token streams the whole
36
- weight set through the GPU to produce one token. Batch B sequences and
37
- the weights stream once for B tokens — aggregate tok/s scales roughly
38
- linearly until the compute roofline.
39
-
40
- The motivating workload is one person's agent harness: a coding agent
41
- fans out sub-agents against one local server. Serially, the Nth agent's
42
- first token waits for N−1 full generations. Batched, 4–8 agents decode
43
- together — each stream somewhat slower, nobody queued. "Single-user" in
44
- this project means single user, many agents. Consequences that shape the
45
- design: arrivals are uncoordinated (so scheduling is concurrency-driven),
46
- sub-agents share long system prompts (so prefix reuse on the batch lane
47
- matters), and agent fleets want long contexts (so quantized KV must
48
- compose with batching).
49
-
50
- The scheduler is **continuous** (iteration-level), never static: it
51
- works per decode step, admitting a newly arrived request after a short
52
- prefill and retiring finished rows between steps. A late request joins at
53
- the next step instead of waiting for the in-flight batch to finish. By
54
- Little's law requests overlap even at modest rates because generations
55
- last seconds. When traffic never overlaps there is only ever one live row
56
- and the engine runs its B=1 fast path (§4.4).
57
-
58
- ## 2. Decisions and rationale
59
-
60
- - **Concurrency IS the batch size.** `--batch N` is a concurrency cap
61
- (mlx-lm's `--decode-concurrency` twin, accepted as an alias), not a
62
- mode switch. Placement declares a *mechanism* per request; the
63
- scheduler picks B=1 or B=N from its live row count. Default cap is 8
64
- (`server.ts`: `serverOptions.batch ?? 8`), flipped from 1 on
65
- 2026-07-05 once a lone scheduler request measured within 0.992–0.996
66
- of the strict serial decode (paired A/B, byte-identical output). The
67
- earlier "mode switch, auto-batching rejected for determinism" decision
68
- was reversed with an answer to its objection: mlx_lm.server itself
69
- batches by arrival, so drop-in parity *requires* it, and anyone who
70
- needs arrival-independent numerics pins `--batch 1`.
71
- - **`--batch 1` is the determinism pin.** It runs the strict serialized
72
- single-queue path; a request's bits never depend on what else was in
73
- flight. Use it for golden regeneration.
74
- - **bf16 batching IS the mlx-lm-parity contract.** mlx-lm's batched
75
- path is bf16 (`BatchRotatingKVCache.to_quantized` raises), so the
76
- oracle for a batched row is **mlx-lm's own batch mode at the same B**,
77
- bit-exact — never our own B=1 (batching changes the attention
78
- kernel's reduction order, so B=N vs B=1 measures the wrong thing).
79
- KV-quant unset ⇒ bf16 on both lanes.
80
- - **Compositions inherit the scheme's oracle.** Batched per-layer
81
- quantized KV (`--kv-quant config`) has no mlx-lm oracle; each row is
82
- gated against the *serial* composition of the same scheme (unpadded
83
- rows bit-exact / KL 0; padded rows within a calibrated bf16
84
- reduction-order envelope). Lab-style KL/eval gates are for no-oracle
85
- schemes only, never a substitute for an oracle that exists.
86
- - **Compat mode: no parity-breaking levers on the batched step.** The
87
- scheduler drives `forwardHidden`/`logitsFromHidden` directly. Only
88
- bit-exact machinery engages: compiled decode at B=1 on adopted
89
- serial-class caches (same kill switch as serial), the compiled
90
- activations, the pipelined readback. `--fused-sdpa` and `--force-wire`
91
- never reach the batched lane.
92
- - **Never downgrade a composition to make it batchable.** Placement is a
93
- support check only: it does not strip MTP/drafting, KV schemes,
94
- TurboQuant, grammar, adapters, or sampling. A composition the scheduler
95
- does not implement runs unchanged on the serial mechanism today. That
96
- fallback preserves behavior during migration; it does not meet the new
97
- batch feature-parity requirement.
98
- - **Drain, never starve.** A serial-lane waiter pauses admission; running
99
- rows finish; the lock hands over FIFO; admission resumes (mlx-lm
100
- `drain_batch`). One `AsyncMutex` is the single exclusion domain for
101
- the GPU and shared model state (`loraState`): serial generation, the
102
- scheduler's whole active period, curve `/generate` + `/signal`, and
103
- adapter mount/unmount all go through it.
104
- - **Byte-budget admission is ours, not mlx-lm's.** mlx-lm caps counts
105
- only; we project bytes (`--kv-budget`) because GPU OOM is uncatchable.
106
- Budget accounting is total bytes, mirroring the byte-capped
107
- `PromptCache` precedent. Queue-don't-OOM; never evict a running row.
108
- - **Solo prefill, then join.** Joiners prefill alone (chunked,
109
- interleaved with decode steps) and merge/extend into the running
110
- batch. This is why SSM state needs no `ssm_mask` under left-padding:
111
- state never sees a pad token, and the token-exact match vs mlx-lm's
112
- masked-pad prefill proves the equivalence.
113
- - **oMLX-style burst decode is refuted here.** Their `_step_burst` runs
114
- K steps per event-loop hand-off to dodge Python's per-token GIL
115
- ping-pong (~1 ms/token). Ported faithfully and measured 2026-07-02
116
- (M1 Max 32 GB): cpm5 B=4 aggregate 345→289 tok/s, batch-lane B=1
117
- 149→121, TTFT +~100 ms (SSE flushes wait out the budget). Bun's
118
- `setImmediate` hop costs microseconds; there is nothing to dodge.
119
- Reverted; the per-yield step is the measured optimum. Don't re-add
120
- without new evidence.
121
- - **Spec decode and scheduling are separate concerns.** A mounted
122
- `--draft-model` selects speculation only for compatible requests. Ordinary
123
- fallback requests can batch when their actual features permit it. Concurrent
124
- speculation is an explicit feature gap in the shared-execution program;
125
- accepted lengths and target/draft state belong to the decode method.
126
-
127
- ## 3. Scheduling seam — `GenerationGateway`
128
-
129
- `place(shape)` freezes one `GenerationPlacement { shape, mechanism }`
130
- with `mechanism ∈ {"serial", "continuous"}`; `run()` rejects a placement
131
- made for another shape. The predicate, exactly as implemented
132
- (`#supportsContinuous`):
133
-
134
- ```
135
- continuous iff
136
- model is not DiffusionGemmaModel (non-autoregressive)
137
- AND batch > 1
138
- AND #modelCachesBatchable() (cache-capability gate, below)
139
- AND (!hasVision OR model supports prepared media input)
140
- AND adapter state supports compatible groups (when adapters are selected)
141
- AND KV scheme supports the loaded cache layouts
142
- AND selected method has a grouped implementation
143
- AND !(hasGrammar AND MLX_BUN_GRAMMAR_BATCH=0)
144
- ```
145
-
146
- `hasRepetitionPenalty` and `hasLogitsExtras` (min_p, XTC, logit_bias,
147
- presence/frequency) are carried on `RequestShape` for stats only — they
148
- do **not** gate. The load-bearing reason: Qwen3.5 ships a default
149
- repetition penalty in `generation_config.json`, which once routed every
150
- request serial. Per-row `StepSampler`s fold the processors over a per-row
151
- device-side token history, so all of these batch.
152
-
153
- **Cache-capability gate** (`#modelCachesBatchable`, mirrors mlx-lm
154
- server.py's all-caches-have-`merge` check), memoized from a fresh
155
- `makeCache()`:
156
- - `UniversalDenseModel`: batchable iff `!maskArray` and no
157
- `sliding_attention` layer type — plain full-attention Tier-0 archs
158
- (Llama) only; gemma2-family (pad-blind causal mask in `forwardLayers`)
159
- and sliding-window universal archs are unvalidated cells → serial.
160
- - Otherwise every cache must be `KVCache`, `RotatingKVCache`, a
161
- `BatchableCache` implementer (GLM-5.2's `MLACache`, which owns its own
162
- merge/extract/filter and byte projection), or `SSMCache` (Qwen3.5's
163
- gated-DeltaNet state; `MLX_BUN_BATCH_SSM=0` removes it → serial).
164
-
165
- **KV-scheme capability:** the resolved `KvScheme` qualifies per-layer affine,
166
- start-zero uniform affine, and ordinary TurboQuant with immediate or delayed
167
- conversion through actual cache and layout support. The MLX layout binding
168
- creates encoded rows; scheduling does not encode or decode KV. Qualified methods
169
- share delayed affine/TurboQuant conversion and per-layer affine policies through
170
- these row interfaces; TurboQuant sliding layers remain bf16.
171
- Unsupported compositions retain the existing serial route until their shared implementation and acceptance are complete.
172
-
173
- Paged Gemma4 bf16 requests compose through `PagedKvRows`; the request state
174
- policy supplies block storage and prompt-cache bypass without changing scheduling.
175
- GLM `--mtp on` (its default) mounts the MTP
176
- drafter and therefore routes serial+spec — `--mtp off` exposes ordinary
177
- GLM batching.
178
-
179
- **Lanes and the lock.** Serial requests run under `runExclusive`, which
180
- counts itself as a serial waiter; the scheduler's `admissionHeld()` reads
181
- that count. The serial branch also wraps `onToken` in a ≥25 ms
182
- rate-limited macrotask hop: serial decode is an unbroken microtask chain
183
- and without the hop `/stats`, `/health`, and accepts stall for the whole
184
- generation (measured 2.5 s on a 512-token cpm5 run, 2026-07-02; after:
185
- 10–44 ms). The batch lane needs none — its drive loop yields per step.
186
-
187
- `/stats.batch` exposes `{configured, mode, batched, active_rows,
188
- pending_rows, submitted_rows, kv_bytes, kv_budget_bytes}`; `mode` is
189
- `off | serial | batch` (cap ≤1 / model not batchable / batchable).
190
- `MLX_BUN_LANE_DEBUG=1` logs each placement with its shape.
191
-
192
- ## 4. The engine — `BatchScheduler`
193
-
194
- One detached Bun-async driver loop (no threads) owns one running batch.
195
- Per iteration: purge aborted pending rows → decide `hasWork` (running
196
- rows, an in-flight prefill — finished even under drain — or admissible
197
- pending) → sleep on `#wake` when idle, releasing the lock and the wired
198
- limit → otherwise acquire both, start/advance at most one joiner prefill,
199
- burst-admit further queued rows that fit before the next step, run one
200
- decode step, yield.
201
-
202
- ### 4.1 Admission
203
-
204
- FIFO from `#pending`, head-of-line, never reordered. A joiner starts iff
205
- no prefill is in flight, admission isn't held, `running < maxBatch`, and
206
- `#kvAdmits` passes:
207
- - context limit: `prompt + max_tokens > batchCacheMaxTokens` (GLM's
208
- compressed caches declare one) rejects with `RangeError`;
209
- - no `--kv-budget` ⇒ admit;
210
- - a row whose projected bytes exceed the budget *alone* (empty batch, no
211
- prefill) rejects with an actionable error — never deadlocks the queue;
212
- - otherwise admit iff `projectedKvBytes + need ≤ budget`, else wait.
213
-
214
- Projection is worst-case: `batchRowKvBytes(config, prompt, max_tokens,
215
- scheme)` = the scheme's `bytesAt` (window-capped for rotating layers,
216
- quantized bytes under a config scheme), or per-cache `projectedBytes()`
217
- when every cache is a `BatchableCache`. `/stats.batch.kv_bytes` is the
218
- running + mid-prefill sum.
219
-
220
- ### 4.2 Joiner prefill (chunked, interleaved, cache-aware)
221
-
222
- - **Prompt-cache take at admission** (`RowPromptCache.take`): the solo
223
- prefill starts from the longest usable cached prefix (namespace `""`
224
- — adapter requests never reach this lane); `cachedTokens` is real.
225
- Serves are non-consuming zero-copy clones, so N agents sharing a
226
- system prompt reuse one prefill. The SSD tier lives inside `take()`,
227
- so restores reach batch joiners too.
228
- - **Boundary snapshot**: for a cold-ish prefill ≥256 tokens, chunking
229
- splits exactly at `min(snapshotAt ?? len, len−1)` and a trim-free
230
- strict-prefix entry is cloned + put there — the same invariant as the
231
- serial lane's `snapshotAt`. Without it the lane's only entry was the
232
- untrimmable `[prompt+gen]` finish-time put, a total miss once rings
233
- wrap (12B batched ctx-repeat 84 s vs serial 0.4 s, 2026-07-06).
234
- - **One chunk per loop iteration** (`prefillChunkSize`, default 2048 =
235
- the serial constant) with `evalAll(state)` + `#quantizeSolo` +
236
- `clearCache()` at every chunk boundary, one decode step between
237
- chunks. Running rows stall at most one chunk per joiner; the prefill
238
- transient stays bounded.
239
- - **Oracle tail convention** (`MLX_BUN_PREFILL_TAIL_SPLIT`, default on):
240
- drain to `len−1`, then an L=1 forward of the last prompt token yields
241
- step-0 logits — mlx-lm's `insert_segments` + `GenerationBatch._step`
242
- shape, and what keeps `--batch 1 == unified == oracle` bit-exact.
243
- - **Slice before the head**: the last hidden position is sliced before
244
- `logitsFromHidden` (a whole-prompt `[1,Lp,V]` would be ~4.3 GB bf16 at
245
- Gemma's V=262k and an 8k prompt).
246
- - **Token 0 is sampled on the solo caches before merge**, so per-row
247
- serial equivalence holds by construction. A row that stops at token 0
248
- (EOS, stop sequence, 1-token grammar) never joins `#running`; its
249
- prompt-only caches go back to the prompt cache.
250
- - **Mixed-precision conversion** (`#quantizeSolo`): the scheduler-side
251
- mirror of serial `maybeQuantizeKv` — same per-layer map, same skip
252
- rules, same chunk-boundary placement (`KVCache.toQuantized` /
253
- `RotatingKVCache.toQuantized`). That placement is what makes a row's
254
- quantized bytes bit-exact vs serial `--kv-quant config`.
255
-
256
- ### 4.3 Join: adopt, extend, or merge — by layer kind
257
-
258
- `#kinds[layer] ∈ {full, rot, ssm, owned-batch}` from the prototype
259
- caches. Before any merge the decode pipeline is flushed (read + emit) so
260
- the row set is settled and the next step starts cold.
261
-
262
- **Adopt-don't-copy.** A row joining an *empty* batch keeps its solo
263
- caches as the inners (pointer handoff, zero bytes moved). The lone row's
264
- caches stay serial-class (`KVCache` / `RotatingKVCache` /
265
- `QuantizedKVCache` / `RotatingQuantizedKVCache`), which is what makes the
266
- B=1 step literally the serial graph and lets compiled decode and
267
- prompt-cache put/take apply to it.
268
-
269
- When a second row joins, per layer:
270
-
271
- | kind | running batch cache | join op | file |
272
- | --- | --- | --- | --- |
273
- | full (bf16) | `KVCache` `[B,H,S,D]` + shared `#fullLeftPad` | `extendKVRows` — one pad + one B-axis concat, running rows untouched, pads grow (mlx-lm `BatchKVCache.extend`); `MLX_BUN_BATCH_EXTEND=0` = `mergeKVRows` re-merge (per-row slices, pads re-normalized; O(B·S)) | `src/model/batched-mask.ts` |
274
- | full (quantized) | `QuantizedKVCache` over (packed, scales, biases) | `extendQuantRows` / `mergeQuantRows` — the same surgery on all three triples | `src/model/batched-quant.ts` |
275
- | rot (bf16) | `BatchedRotatingCache` (persistent; self-tracks per-row leftPad/offsetArr through ring wrap) | always re-merge: extract each running row's temporal view + the new row → `BatchedRotatingCache.merge(rows, offsets, maxSize)`; an adopted lone `RotatingKVCache` is the first row (pad 0) | `src/model/batched-rotating.ts` |
276
- | rot (quantized) | `BatchedRotatingQuantCache` | same recipe over triples, `merge(rows, offsets, maxSize, groupSize, bits)` | `src/model/batched-rotating-quant.ts` |
277
- | ssm | `SSMCache` `[B,…]` state, no temporal axis, no padding | `SSMCache.mergeRows(prev, solo)` — B-axis concat | `src/model/qwen3-delta.ts` |
278
- | owned-batch | `BatchableCache` (GLM `MLACache`) | `solo.makeEmptyBatch().mergeRows([prev, solo])`; the cache owns leftPad/rowOffsets | `src/model/glm52-cache.ts` |
279
-
280
- Routing among these branches is **by capability only** (`isRowBatchCache`,
281
- `isBatchableCache`, `isQuantizedKvCache`, …). `BatchedRotatingCache` has
282
- no `signature()` override, so any signature-based conjunct silently drops
283
- the running batch from the merge — the 2026-08-22 agg×4 outage from
284
- 443f333 (joiners built a B<max ring; the next full-B step crashed in the
285
- grow-path concatenate; whole-batch drop). `tests/batch-rotating-join.test.ts`
286
- pins this.
287
-
288
- After a real merge every row is marked `merged`: its KV is interleaved in
289
- batched buffers and must be *extracted*, not adopted, at finish.
290
-
291
- ### 4.4 The decode step
292
-
293
- Pipelined, mirroring mlx-lm `GenerationBatch._step`: (1) forward all rows
294
- from the still-unread `[B]` token register (or, cold, from each row's
295
- `current`), sample, `asyncEvalAll` the new register; (2) then read the
296
- *previous* register with `toIntTokens()` (an eval + raw int read — never
297
- `toFloat32()`, whose cast kernel queued behind the whole next step and
298
- stalled every token), emit, evict. Rows that finish get one extra
299
- harmless KV write from the already-built step; `filter` drops the row
300
- (mlx-lm behaves identically). `MLX_BUN_BATCH_NO_PIPELINE=1` reads
301
- synchronously (same math, slower).
302
-
303
- **Unpadded fast path.** When every `#fullLeftPad` is 0 the full layers'
304
- per-step `BatchedDecodeMaskCache` / `BatchedQuantDecodeMaskCache`
305
- wrapper is skipped: `KVCache.makeMask(1)` is already the empty mask and
306
- every row sits at the shared scalar offset, so the bare cache dispatches
307
- the same per-step graph serial builds. The wrapper otherwise costs a
308
- host mask build + ~8 device nodes per full layer per token. Rot,
309
- owned-batch, and ssm caches always pass through unwrapped.
310
-
311
- **Compiled decode at B=1.** With exactly one row, unpadded, a uint32
312
- register, serial-class inners (`CompiledDecode.supports`; a
313
- filtered-to-one batched rot-quant cache is excluded), and a Gemma4
314
- dense model, the scheduler replays the serial engine's compiled step
315
- (`MLX_BUN_COMPILED_DECODE` is the same kill switch). A failed step is
316
- transactional: the scheduler permanently falls back to the graph path.
317
-
318
- **Vectorized homogeneous sampling.** When every live row is plain greedy
319
- (`plainGreedy` from the gateway: temperature 0, no logprob capture, no processors, no
320
- grammar), one `toLogprobs` + `argmaxAxis` over `[B,V]` replaces B
321
- slice/sample/concat graphs — per-row identical math, tie behavior
322
- included. `MLX_BUN_BATCH_VEC_SAMPLE=0` forces the per-row closure path.
323
- Length-doomed rows get a placeholder slot (never emitted; one KV write
324
- on their own about-to-evict row).
325
-
326
- **Sampling boundary.** `contracts/sampling.ts` defines `SamplingSession`:
327
- borrow model logits, apply request-owned processing/selection, return the
328
- chosen token and optional probability tensors. Device and host-token MLX
329
- samplers implement the same generic contract. `createRowSampling` accepts a
330
- sampler through that interface and binds its captures to output delivery.
331
- The scheduler receives token tensors and a callback, with no probability
332
- retention policy. Captures are evaluated with their token and retained by
333
- request-local step until emission; cancellation and unconsumed lookahead
334
- release them. Token-only requests retain the existing vectorized shortcut.
335
- Explicit seeds no longer determine scheduler placement. The seed and local
336
- sample index select the random key, independently of sibling requests.
337
- Reproducibility is conditional on the same numerical execution composition,
338
- not a promise of equal logits across different batch shapes. The preserved
339
- `--batch 1` control supplies the earlier fixed-shape behavior.
340
- Vectorized logprob capture and the broader sampling-kernel interface remain
341
- optimization work; this change establishes no performance dominance.
342
-
343
- **Grammar rows** (`#stepGrammar`): read the previous register *before*
344
- building the graph (the matcher's `accept` needs JS numbers), `accept()`
345
- per live grammar row (fires async bitmask fills that overlap the graph
346
- build), `await ready()`, sample per row with the mask applied in the
347
- closure, emit the values already read. While any grammar row is live the
348
- batch effectively runs unpipelined — bounded by the ~0.1 ms readback +
349
- 0.004–0.19 ms/row fills. Flushes must `accept()` the flushed tokens too,
350
- or matchers run one token behind on every mid-decode join (found by the
351
- conformance gate, 2026-07-03).
352
-
353
- **Housekeeping.** `clearCache()` every 256 steps (serial's cadence;
354
- mlx-lm batched uses 512), not per step. The per-step `setImmediate` hop
355
- is rate-limited to 25 ms when exactly one row is running with an empty
356
- queue and no drain — the 0.4–0.8% B=1 drive-loop tax, paired-measured
357
- 2026-07-05. `MLX_BUN_BATCH_STEP_TRACE=1` prints per-step build/read/
358
- emit/gap timings.
359
-
360
- ### 4.5 Emit, evict, extract
361
-
362
- `#emit` mirrors `generate()`: EOS terminates **without** an `onToken`
363
- call; `onToken` returning `false` → `stop`; grammar terminated → `stop`
364
- (the final token was delivered; never sample into an all-`-inf` mask);
365
- `generated ≥ max_tokens` → `length`. Per-row `StopMatcher`, tool router,
366
- and SSE fan-out live in each request's `onToken` closure.
367
-
368
- **Failure containment.** A row's `onToken` throwing rejects and evicts
369
- *that* row (mlx-lm `remove`); siblings continue. A forward/sampling error
370
- cannot be attributed to a row: every row rejects and the batch KV is
371
- dropped (`dropOnly`, never put). A prefill error rejects only the joiner.
372
-
373
- **Eviction** (`#applyFilter(keep)`): `filterRows` on batched/owned
374
- caches; `filterKVRows` / `filterQuantRows` (B-axis `takeAxis`) on full
375
- layers; the pending register and `#pendingReal` flags are filtered in
376
- lockstep. Full layers also remove the minimum left padding shared by all
377
- survivors, matching `mlx-lm BatchKVCache.filter`. Buffer width and per-row
378
- padding shrink together, preserving absolute RoPE positions. Quantized full
379
- layers slice the token axis of packed/scales/biases together; rotating layers
380
- retain their separate layout. Keeping those unused columns can change logits
381
- through a different attention reduction shape.
382
-
383
- **Finish-time prompt-cache put.** A lone never-merged row hands its
384
- adopted serial-class caches back zero-copy, keyed by `promptIds + fed`
385
- (`fed` = generated tokens whose KV actually entered the cache, per-row
386
- exact via the register's real/placeholder flags; a placeholder that ever
387
- fed taints the row and extraction refuses). A merged row with ≥256
388
- prompt tokens is **extracted** per layer into fresh owned serial caches
389
- before the filter mutates the batch (`#extractRowCaches`: `extractRow`,
390
- `extractKVRow`, `extractQuantRow`; SSM only when the row's advance count
391
- equals the key exactly — recurrent state is untrimmable), evaluated
392
- asynchronously so the batched source buffers free once the copies land,
393
- then put. Extract is a pure slice+copy of byte-preserved rows
394
- (`tests/batched-extract.test.ts`), so an extracted row's bytes equal the
395
- solo run's. All take/put happens inside the gateway's exclusion domain —
396
- the scheduler is the sole owner of cache mutations on its lane.
397
-
398
- ## 5. The one hard numerics problem: per-sequence position
399
-
400
- Rows are different lengths at different positions, but a cache has one
401
- `offset`, RoPE takes one offset, and the mask is built from it. mlx-lm's
402
- solution, and ours: **left-padding + per-row mask + per-row RoPE**.
403
-
404
- - Right-align every row so "the current token" is one column → one
405
- advancing offset still works. Full layers share `#fullLeftPad`; rot
406
- caches self-track per-row `leftPad`/`offsetArr` across the ring wrap.
407
- - Mask padding with a real `[B,1,1,S]` boolean mask (`j >= leftPad[b]`,
408
- `buildBatchedDecodeMask`) even at the N=1 decode step, and use the same
409
- fused bool-mask `scaled_dot_product_attention` mlx-lm uses. An additive
410
- mask would deviate.
411
- - RoPE positions are a per-row array (`ropeOffsetArr`,
412
- `ops.ropeDynamic`; `UniversalRope.applyDynamic` for Tier-0 archs).
413
- Two traps every attention implementation must respect: capture the
414
- offset array **once** per forward (reading it before and after
415
- `updateAndFetch` gives K and Q different positions — the 2026-06-14
416
- Gemma fix; the generator `scripts/gen-model.ts` carries it for
417
- generated handlers), and a batched cache's `ropeOffsetArr` must be
418
- **stable within a step** and refreshed only in `releaseRopeArr` (the
419
- rope-array step-stability contract from milestone 2). MiniCPM5
420
- (2026-06-14) and universal dense (2026-07-03, latent since v0.0.9)
421
- each shipped a scalar-offset bug that only the batched oracle caught:
422
- every model family wires the per-row path separately.
423
- - Batched prefill numerics differ from B=1 prefill (GEMV vs GEMM
424
- reduction order), and a padded row's attention sum accumulates in a
425
- different order than its solo run. The unpadded row is bit-exact vs
426
- solo; the padded row carries bounded bf16 noise (≤0.23 logit diff over
427
- 8 steps, CPM, 2026-06-14). Calibrate the envelope per model; don't
428
- chase it — mlx-lm B=2 diverges from its own B=1 the same way.
429
-
430
- ## 6. KV memory model
431
-
432
- Three rungs, in increasing fidelity of "who needs it most":
433
-
434
- 1. **Static per-slot partition** — rejected, never built (wastes memory,
435
- arbitrary per-slot context cap).
436
- 2. **Dynamic shared budget, contiguous padded caches** — what ships.
437
- Each layer holds one `[B,H,S,D]` buffer (growing in `KVCache.STEP`
438
- = 256 granules) whose width tracks the *longest* live row; short rows
439
- pay padding to that width. Admission is by projected total bytes
440
- (§4.1). Quantized KV multiplies the budget (~4× at 4-bit), and
441
- batching × quantized KV compounds.
442
- 3. **Paged KV** — the density upgrade. `PagedKVCache`/`BlockPool`
443
- (`src/lab/paged-kv/paged-kv.ts`, `--paged-kv`) shipped as a serial-only,
444
- bf16, Gemma4 full-attention block manager, bit-exact vs the plain
445
- path. The batching payoff is **not built**: paged caches into the
446
- scheduler's `LayerInner` union, block-count admission replacing byte
447
- projection, `#mergeJoiner`/`#applyFilter`/`#extractAndPut` allocating
448
- and freeing blocks instead of pad+concat, then block-level CoW prefix
449
- sharing and a fused paged-attention kernel. That is where the
450
- padded-batch-waste win lives (a 50-token row cohabiting with a
451
- 4000-token row pays ~4000 tokens of KV today).
452
-
453
- The `--prompt-cache` budget is the *prefix* cache; live decode KV is a
454
- separate pool. mlx-lm trims its prompt cache to `total − live batch
455
- bytes`; sharing one ceiling between the LRU and the live batch is still
456
- open (§8).
457
-
458
- ## 7. Verification
459
-
460
- Oracle discipline: every batched mechanism lands with a token-for-token
461
- gate against whoever ships the same protocol, generated from the oracle
462
- venv (`scripts/oracle/gen-batched-golden.py` static B=N;
463
- `gen-batched-dynamic-golden.py` merge/extract/filter join+leave;
464
- `gen-batched-extend-golden.py` — extend's pad layout differs from
465
- re-merge, so it has its own golden; `gen-rotating-golden.py`
466
- `BatchRotatingKVCache` model-free incl. ring wrap). Goldens are
467
- machine-layered (`goldens/*.json`, per-GPU overrides under
468
- `goldens/<machine>/`). The B=N-vs-our-B=1 KL harness is an internal
469
- consistency check only, never the gate.
470
-
471
- | Behavior | Test | Tier |
472
- | --- | --- | --- |
473
- | rotating merge/decode/mask/filter incl. ring wrap vs mlx-lm; generic row-storage state | `tests/batched-rotating.test.ts`, `batched-rotating-state.test.ts` | fast, oracle |
474
- | static B=2 + dynamic join/leave + extend-join vs mlx-lm (CPM, Gemma 12B/e4b/26B, Llama 3B Tier-0) | `tests/batched-decode-parity.test.ts` (`MLX_BUN_TEST_BATCH_DECODE=1`) | gated, oracle |
475
- | Qwen3.5 SSM batched vs mlx-lm B=2 | `tests/qwen35-batched-parity.test.ts` | gated, oracle |
476
- | scheduler orchestration (admit/step/evict/join) | `tests/batch-scheduler.test.ts` | gated, exact live same-B protocol oracle (CPM and Gemma 12B) |
477
- | per-row containment, drain, full-attention join routing | `tests/batch-containment.test.ts` | gated |
478
- | rotating-layer join mid-decode (2026-08-22 regression) | `tests/batch-rotating-join.test.ts` | gated |
479
- | KV-budget queueing + projection math | `tests/batch-kv-budget.test.ts`, `kv-budget-projection.test.ts` | gated / fast |
480
- | vectorized sampling bit-equal A/B | `tests/batch-vec-sample.test.ts` | gated |
481
- | grammar under batching (mixed masks, 4 schemas at B=4, churn, joiner, truncation) | `tests/batch-grammar.test.ts` | gated |
482
- | quantized dynamic-B ops; batched mixed-KV per row vs serial composition | `tests/batched-quant.test.ts`, `batched-rotating-quant.test.ts`, `batched-kv-quant-parity.test.ts` | fast / gated |
483
- | per-row extract == solo bytes | `tests/batched-extract.test.ts` | fast |
484
- | live server: engage, `/stats`, SSE fan-out, serial coexistence | `tests/batch-serving.test.ts` | gated smoke |
485
-
486
- Closeout recheck (2026-09-08, Apple M1 Max 32 GB, Bun 1.4.0,
487
- MLX 0.32.2): the old Gemma solo-KL bound failed even though all 24 captured
488
- simultaneous-admission vectors matched the same-B oracle exactly. Replaying
489
- the mid-stream join exposed a separate mismatch in two later vectors: the
490
- native full cache retained padding that `BatchKVCache.filter` removes.
491
- The scheduler now performs that removal. The live protocol gate passes all
492
- 48 full-vector hashes on each of MiniCPM5 and Gemma 12B, including row
493
- retirement and a join triggered after two emitted tokens. The oracle receives
494
- inputs and row/step identities, never the native expected hashes. It uses
495
- mlx-lm merge/extend/filter operations and ordinary full-attention KV caches
496
- when the reference state has no padding, matching serving's scalar-position
497
- policy. No tolerance was increased and no golden was regenerated.
498
-
499
- Production oracle: `scripts/oracle/batch-scheduler-reference.py`. Initial
500
- capture and comparison: `reports/qwen38-closeout/gemma-protocol/`.
501
- Before/after gates: `gemma-protocol-gate.log`,
502
- `gemma-protocol-filter-trim-gate.log`, `batch-protocol-final-gate.log`.
503
- The original unchanged-HEAD failure remains in
504
- `memory-baseline-batch-scheduler.log`. Quantized slice and broader serving
505
- checks accompany the filter change; M4 campaign acceptance remains separate.
506
-
507
-
508
- Benchmark harness: `scripts/bench-matrix.ts features` (six-cell
509
- composition matrix over live SSE: TTFT p50/p95 at the first SSE token,
510
- wall-clock aggregate tok/s, schema conformance as a hard gate);
511
- `scripts/bench-serving-load.ts` is the client-only stack-vs-stack tool.
512
- Quotable numbers come from a clean machine only; SSE bursts inflate
513
- naive tok/s — use wall-clock.
514
-
515
- ## 8. Open items
516
-
517
- ### Consolidation matrix
518
-
519
- This is the implementation gap inventory, not a claim that all combinations
520
- have passed acceptance. Phase 6 coordinates the work; Phase 18 tracks its
521
- batch cells. Current placement rules are in `src/engine/execution-plan.ts`.
522
-
523
- The window-crossing scheduler gate in `tests/parity/batch-scheduler.test.ts`
524
- now passes on Gemma 12B and e4b on both development machines. Two rows cross
525
- the sliding window during decode; a third begins beyond it and joins late.
526
- The pinned oracle replays the same admissions, merges and retirements and
527
- compares every sampled full float32 logit vector. Run with
528
- `MLX_BUN_TEST_BATCH_RING_WRAP=1`; `MLX_BUN_TEST_BATCH_RING_MODEL` selects the
529
- existing artifact. Evidence is recorded in benchmarks.md.
530
-
531
- Josh revised the immediate objective to shared batching by default with
532
- effectively equal measured single-request performance. Serving already defaults
533
- to a concurrency cap of eight, with B1 execution for a lone eligible request.
534
- Explicit `--batch 1` and compatibility routing retain serial. Removing that
535
- executor is deferred; complete feature parity is follow-up work, not a blocker
536
- for the current default. Supported ordinary requests use the shared method.
537
- Compare single-request B=1 latency, cold/cached
538
- prefill and sustained decode as well as concurrent throughput. Record memory,
539
- cache reuse and actual admitted rows. Keep kernel settings fixed for the
540
- refactor comparison; measure new kernel optimizations in separate experiments.
541
- Keep measured single-request regressions and unsupported features explicit.
542
- They do not require repeating completed checks or withholding the current default.
543
-
544
- | Concern | Current implementation | Required completion |
545
- |---|---|---|
546
- | Scheduling and ordinary decode | Default cap eight uses shared execution at B1/B>1; explicit `--batch 1` retains serial | Keep the batched default using existing measurements; address specific regressions separately. Serial removal is deferred |
547
- | Speculative decode | Qwen MTP, prompt lookup, standalone, assistant, DeepSpec and seeded DSpark/DFlash providers are integrated through shared B1/B>1 interfaces, with the native/HTTP/cache checks recorded below | Finish specific unsupported combinations and measured regressions; GLM artifact testing is deferred and no trained DSpark/DFlash checkpoint exists |
548
- | Sampling and logprobs | Shared sampler contract; ordinary groups capture logprobs and accept explicit seeds | Extend same-B oracle/feature compositions and matched performance; preserve per-request RNG/history |
549
- | Grammar and fill | Shared grammar proposals use grouped verification. Shared strict fill composes prefill, rows, sampling and committed append; native and HTTP/cache/timing gates pass | Shared echo uses grouped verification and row rollback; Qwen MTP consumes verified echo through the provider interface. Asserted fill with speculation, other providers and other named combinations remain. Keep grammar proposals opt-in after mixed timing results |
550
- | Adapters and media | Compatible adapter groups use shared execution; Gemma4 image/audio and Qwen image/video inputs, encoder caching and generated media-prefix reuse pass native/HTTP and M4 comparisons | Chunked media prefill, lone-media latency costs, adapter performance and broader compositions |
551
- | KV layout | Full and rotating affine/TurboQuant layouts, delayed/per-layer transitions, speculative donors and ordinary paged storage are integrated and tested below | C1–C5 add shared SSD blocks and optional direct bf16/affine paged attention with native/HTTP evidence. TQ pages and paged speculation remain extensions; reuse completed row-layout gates |
552
- | Prefix and output reuse | Ordinary and grouped methods publish generated target/companion state to one RAM/SSD cache | Native/HTTP and full Kanban retention/durability acceptance are complete; investigate new regressions without reopening unchanged gates |
553
- | Generation resume | Shared ordinary resume is integrated after both-machine native/HTTP compiled, quantized/delayed and mixed-grammar checks, combined suites and eight M4 timing arms. The packed-model fixture now owns fresh weights per server | Adapter resume is integrated through the same policy; native/HTTP composition checks accompany it. Reuse the completed ordinary resume evidence |
554
- | Usage and cleanup | The gateway forwards timing/rates; the missing first-token timestamp in ordinary batch admission is now fixed | Consistent events/accounting, row cancellation, failure cleanup and persistence on shutdown |
555
- | Configuration | Bound configuration reaches each owner, including delayed KV conversion, copied codecs and paged cache identity; the flag-owner inventory is complete | Preserve the contract when adding settings; completed policy tests and fixed-settings performance remain accepted |
556
-
557
- ### Prepared media and execution tasks
558
-
559
- `MlxPromptInput` lets the model binding consume prepared embeddings and return
560
- an owned final hidden row. `MlxPrefillCohort` retains initialization, precision
561
- maintenance, projection and completion. An indivisible media attention span
562
- declines prefill joins and mixed token packing; it processes the whole prompt
563
- before entering ordinary shared decode. Sampling, row merging, retirement and
564
- cancellation remain in their existing owners. Prepared media producers supply
565
- an opaque identity for their exact media, encoder and preceding token prefix.
566
- The ordinary cache combines this with the adapter namespace to retain generated
567
- conversation state in RAM/SSD. Inputs without that identity remain uncached;
568
- token IDs alone do not identify media contents. The input binding resumes
569
- positioned text from the retained offset. Cache ownership and precision boundaries
570
- are described in [kv-cache](kv-cache.md#514-prepared-media-prefix-identity).
571
-
572
- Native preparation uses `ExecutionTasks` through the portable scheduler's
573
- optional task boundary. The queue owns cancellation and failure settlement;
574
- the scheduler owns when to advance it under the existing execution lease.
575
- A task cannot replace model state. Exclusive mutations retain drain priority,
576
- including a waiter arriving while preparation is running. Started work settles
577
- before its owner releases native resources. The queue uses the portable
578
- cancellation contract; the MLX binding adapts request AbortSignals.
579
- `ModelPromptBuilder` receives a `PromptNativeWork` callback. Media downloads
580
- and container transcoding finish outside that callback; lazy tower loading
581
- and native prompt construction enter it. Encoder-local preprocessing remains
582
- part of prompt construction. Grammar compilation enters the same execution
583
- domain without putting template rendering or tokenization on the queue.
584
-
585
- Gemma4 binds image/audio embeddings to this input interface. Qwen binds
586
- image/video embeddings and a `MlxDecodeState` carrying each request's position
587
- delta. The backend passes the current row order to that state after joins and
588
- retirement. Its forward uses the cache's logical position array on device;
589
- attention receives explicit positions without a module-global variable.
590
- Input-state compatibility remains separate from cache-layout compatibility.
591
- Media forwards with additional position state decline mixed-token packing.
592
- Qwen image/video preprocessing completes outside the native execution lease.
593
- Exact encoder features now use the shared cache object port; SSD reads finish
594
- before native preparation. Cache placement remains independent of batching.
595
- Diffusion retains its separate method. Qwen's source-controlled native comparison
596
- and padded B1/B2/B4 affine/TurboQuant checks pass on both Macs, as do real image
597
- HTTP checks on the packed 27B. The M4 image/video ABBA comparison preserves full
598
- usage with faster concurrent completion; different batch geometries change some
599
- responses. See [Qwen media evidence](../reference/benchmarks.md#shared-qwen-image-and-video-execution-2026-09-13)
600
- for the source identity checks, probe correction and latency costs.
601
- The input and task tests cover atomic boundaries, final projection geometry,
602
- full same-machine Gemma logits/cache/continuation, active B1/B2 logit identity,
603
- cancellation and mutation priority. Both-machine audio HTTP checks preserve the existing
604
- transcription golden, exercise actual B2 decode and complete text generation
605
- while an audio download is held open. The complete model-free suite and all
606
- three TypeScript projects pass. The M4 ABBA media comparison preserves every
607
- response and token count; concurrent completion improves with documented lone-
608
- request and first-submitted-request costs in [benchmarks.md](../reference/benchmarks.md#shared-gemma-media-execution-2026-09-13);
609
- see `tests/parity/shared-media-input.test.ts` and
610
- `reports/prefill-observation/shared-media-*`.
611
-
612
- Tests must exercise actual B>1 work, not requests routed back to serial.
613
- Completed checks remain evidence for their recorded source and settings. A new
614
- run needs a named changed behavior, uncovered case or reproduced failure;
615
- an open umbrella row is not a reason to repeat all its passing checks.
616
- The working tree now has a shared transaction lifecycle for request-owned
617
- acceptance counts. Recurrent state and attention KV implement the same round
618
- operations. Attention rollback changes logical coverage without moving KV;
619
- subsequent appends overwrite rejected tips. Positions, masks, extraction,
620
- empty draft prefixes, late joins and retirement belong to that storage layout.
621
- A singleton preserves allocated prefill capacity and uses the existing scalar
622
- RoPE and causal-mask operations. Removing the longest recurrent row now updates
623
- the group's maximum coverage to its survivors.
624
-
625
- `MlxStateRows` applies admission, filtering and extraction through layer-owned
626
- row layouts. Attention KV and `BatchedSSMCache` implement the same port; the
627
- collection contains no model-family branches or persistence policy. Admission
628
- borrows prepared state and builds all layer replacements before releasing the
629
- active collection. Unit checks cover reorder, extraction, removal of every row,
630
- reuse, donor ownership and failed admission. Full-target MTP controls preserve
631
- all retained state at B=1/2/4 on both Macs through this collection.
632
-
633
- The affine row layout composes those same position/retention
634
- operations for affine KV's packed values, scales and biases. It adds no
635
- dequantization to joins, rollback or extraction. Qwen attention consumes a
636
- quantized numerical port rather than selecting the concrete cache class.
637
- Focused KV4/KV8 storage checks pass, including unequal acceptance, overwritten
638
- rejected tips, joins, growth, removal and compact extraction. Full-target MTP
639
- bf16/KV4/KV8 round checks pass on both Macs, including output-aligned stopping
640
- and survivor continuation at smaller B. Both complete model-free suites and
641
- typechecks pass. `BatchedQuantizedKVCache` and the numerical attention port are
642
- adopted in the working tree. The tested source matches the adopted source;
643
- native logs and the `batched-quantized-state-manifest.json` live in the
644
- composition-baseline reports. Same-B external quantized-oracle coverage and
645
- actual speculative serving acceptance remain open.
646
-
647
- The row method also handles a zero-proposal round through the
648
- same graph/commit interfaces. It processes the pending companion KV row without
649
- sampling a draft or projecting draft logits, then retains the target hidden
650
- row needed for later speculation. The old request producer skips this update;
651
- the native control instead discards a depth-one proposal to obtain the same
652
- processed state. The change is adopted after full-round bf16/KV4/KV8 checks at B=1/2/4 on
653
- both Macs, companion checks that forbid draft sampling/head projection in the
654
- zero round, both complete model-free suites and typechecks. The source manifest
655
- and native logs live in the composition-baseline reports. This does not adopt
656
- the separately measured request-producer replacement or B=1 hidden slice.
657
-
658
- The provider exposes grouped companion state through the existing
659
- checkpoint representation. `PreparedStateChange` lets target and draft owners
660
- stage a join independently; `applyStateChanges` publishes both only after all
661
- preparation succeeds, then releases their old state. A failed preparation
662
- leaves active membership intact. Publication performs no graph construction or
663
- validation, and adds no operation to the decode round. Focused checks cover
664
- failed joins, cleanup errors and released allocations. Full bf16/KV4/KV8
665
- rounds through coordinated admission and both complete suites pass on both
666
- Macs. This interface is adopted; serving must now use it before exposing a new
667
- request to scheduling. Source identity and checks live in the
668
- composition-baseline `mtp-admission-manifest.json`. Companion ownership
669
- details live in speculative-decoding §4.8.
670
-
671
- The MTP numerical graph is separate from request lifecycle and sampling. It
672
- uses the cache's positions for both singleton and batched forwards. Native
673
- checks on both Macs preserve full-target state and subsequent output at B=2,
674
- plus the companion graph's output and KV after unequal prefix retention.
675
- Uniform acceptance uses the existing recurrent kernel; unequal acceptance uses
676
- the row-length-aware state-only kernel. The separate uniform state-only replay
677
- experiment did not establish a serving win, so it is not promoted.
678
-
679
- The exact-token acceptance walk is now a portable request state machine.
680
- It tracks accepted drafts, correction/bonus tokens, EOS, token budgets and
681
- grammar termination. Sampling still owns score processing and request history.
682
- A group can stop sampling a finished walk while advancing the others.
683
-
684
- The MTP row method keeps draft-token arrays on device between steps and reads
685
- one complete proposal block. Its sampler is injected; committed state can be
686
- extracted, filtered and joined between rounds. Greedy and seeded sampling pass
687
- at depths 1/2/4 on both Macs, including retirement, reorder, rejoin and reuse
688
- after removing every row. B=1 preserves the existing producer's proposals and
689
- state. At B=2, per-step host-read and device-chain controls match at the same B.
690
- The M4's B=2 KV differs from independent B=1 controls; cross-B byte equality is
691
- not claimed, and the initial result remains in the raw logs.
692
-
693
- `advanceSpeculativeRows` composes injected drafting, target verification,
694
- request samplers and state transactions into one committed round. It has no
695
- admission, output delivery or persistence policy. Each sampler walks only its
696
- accepted prefix, recording logprobs and respecting EOS, grammar and remaining
697
- token budget. The existing request adapter and grouped method now use the same
698
- `sampleSpeculativeRows` operation. Failure tests cover disposal of verification
699
- tensors when sampling or target/draft retention fails; the caller discards the
700
- failed method state.
701
-
702
- Native complete-round tests pass on both Macs at B=1/2/4 and depths 1/2/4,
703
- with seeded depth-two coverage. They compare proposals, target samples,
704
- logprobs, every retained target layer and draft KV/hidden state across three
705
- rounds. B=1 uses the prior request producer and manual accept loop as control;
706
- B=2/4 use a manual same-B loop with host reads between draft steps. These are
707
- internal refactor controls, not a replacement for the external numerical oracle.
708
- The row method is adopted in the working tree but not yet selected by serving;
709
- matched complete-request performance remains an acceptance gate.
710
-
711
- The next isolated candidate replaces the request producer's draft/commit loop
712
- with the shared row method at B=1. The frozen `mtp-shared-draft-reference`
713
- checkout retains the previous producer for native continuation and paired
714
- serving comparisons. A zero-proposal round must retain its pending companion
715
- KV row before resuming speculation; the old producer skipped that state update.
716
- Native continuation checks pass on both Macs. Both serving orders preserve
717
- responses but do not establish equal-or-better performance; the candidate is
718
- not adopted. Numbers and frozen source identities live in benchmarks.md.
719
- The subsequent isolated change replaces the B=1 hidden gather with a slice of
720
- the same verification context, retaining the shared method. Its first serving
721
- block is essentially flat in decode, but the reverse-order block regresses;
722
- neither the source replacement nor the slice specialization is adopted.
723
-
724
- The isolated round interface also separates verification/sampling from state
725
- commit. `advanceSpeculativeOutputs` composes that round with the portable
726
- `deliverDraftOutputs` operation. Each consumer receives verified tokens through
727
- `GenerationOutput`; its stop, failure or cancellation affects its own request.
728
- Both target and draft state then retain the same published, processed prefix,
729
- excluding unpublished EOS and unprocessed correction/bonus tokens. Failed or
730
- cancelled state must be discarded. Continuing requests keep their complete
731
- accepted prefix. Verification context is an opaque disposable resource,
732
- allowing graph-specific hidden taps without putting model details into
733
- scheduling. The output method is adopted in the working tree; it is not wired
734
- to serving. Focused tests and the complete isolated model-free suite pass.
735
- Final-source native controls pass on both Macs, including removal of stopped
736
- requests and continued generation at B=4→2 and B=2→1. They retain the old
737
- request producer as the independent B=1 control. Same-B survivor comparisons
738
- check tokens, logprobs and every retained target/companion state. Logs and the
739
- source manifest live in the composition-baseline reports.
740
-
741
- These are method/state dependencies, not served batched MTP acceptance. Grouped
742
- draft generation, verification and sampling still need to be integrated into
743
- the shared executor, with actual concurrent serving, cancellation, persistence,
744
- quantized-layout composition and matched performance checks.
745
- Parallel tool execution in Pi only exercises inference batching if model
746
- requests overlap; record admitted rows and arrival schedules in the evidence.
747
- Josh's September 8 M1 Max and M4 Pro suites are the ordinary-execution
748
- baseline, recorded in [benchmarks.md](../reference/benchmarks.md#direct-scheduler-b1-versus-serial--both-machines-2026-09-08).
749
- Retain those results and target new measurements at changed implementations
750
- and newly supported features. They justify consolidation, but do not prove
751
- the missing feature combinations or removal of host overhead.
752
-
753
- ### Remaining mechanisms
754
-
755
- - **`--prompt-concurrency`**: not accepted by the CLI (mlx-lm's cap on
756
- joiners per iteration; the drop-in alias slot). Today at most one
757
- joiner prefills at a time.
758
- - **Rotating-layer `extend`**: Gemma joins extend their full layers but
759
- re-merge their sliding layers (`BatchedRotatingCache.merge` only).
760
- - **Logprobs acceptance**: ordinary batch capture is implemented through
761
- the sampler interface. Unit coverage checks interleaved pipelines,
762
- processor-adjusted probabilities, top-only capture and abandoned outputs;
763
- `tests/parity/batch-logprobs.test.ts` exercises real B=1/B=2 and retirement.
764
- On M4 Pro, the packed Qwen campaign artifact passes native and HTTP
765
- serial/B=1 identity, actual B=2 capture on/off token identity, row retirement,
766
- selected/top-only wire shapes and SSE omission. This is an internal
767
- composition check, not a same-B external oracle or performance comparison.
768
- Explicit-seed gateway tests also reproduce serial/B=1 outputs and repeat
769
- fixed B=2 and controlled B=3 joins on M1 Gemma e4b and M4 packed Qwen.
770
- These keep the numerical composition fixed; they do not assert
771
- arrival-independent output across different batch shapes.
772
- Broader compositions and matched performance remain open.
773
- Cached-request diagnostics found synchronous recurrent-boundary persistence
774
- inside a RAM hit and a serial request-time durability flush. Both are
775
- removed: the shared cache lends immutable state views, and SSD writes run
776
- independently of requests, acquiring idle ownership without draining active
777
- batches. Explicit flush and shutdown remain durability
778
- boundaries. Native repeated-prefix and SSD-restart continuation checks pass
779
- on M1 and M4, including the saved long-prefix size on M4. A stalled-write
780
- regression verifies RAM reuse and snapshot lifetime through donor eviction.
781
- Balanced Bun 1.4.2 serving preserves outputs and near-equal decode time,
782
- but a small cached first-output gap remains. First admission now seeds the existing device-token register to overlap its readback
783
- with the following forward. Same-B MiniCPM/Gemma oracle vectors, compiled
784
- B=1, grammar, first-token stop/EOS/length, callback containment, seeded
785
- sampling and native/HTTP logprobs pass. Both serving arm orders preserve
786
- outputs and close most of the remaining gap; the change is adopted in the
787
- working tree. Actual method selection now precedes scheduling: configuring
788
- a draft does not force ordinary logprob or supported affine-KV fallbacks
789
- onto the serial executor. Native B=2 fallback outputs match ordinary B=2
790
- on M1 and M4; HTTP tests with a configured draft also reach B=2 without
791
- opening the producer. HTTP correctness tests align request arrivals at the gateway
792
- to require concurrent work; serving benchmarks retain natural arrivals.
793
- Broader pressure acceptance remains open.
794
- Measurements live in benchmarks.md.
795
- The gateway forwards timing/rates, but native regression coverage found that
796
- ordinary admission never set the first-token timestamp. That caused zero decode
797
- time even for multi-token responses. The timestamp is now recorded at first-token
798
- emission; repeated shared-prefix B=2 tests check positive decode time and rate.
799
- Both execution lanes already use the same omitted-library-cap fallback. Broader
800
- usage and stopping compositions remain in the matrix.
801
- - **LoRA-group batching**: ordered adapter sets now select compatible groups
802
- through a backend-owned execution context. The scheduling driver sees only
803
- readiness. Context changes and prefix identity bind under the execution
804
- lease; adapter replacement while queued cannot reuse stale KV. Gemma e4b
805
- native B=1/B=2, mixed queues, callback failure, separate prefix reuse and
806
- queued replacement pass on M1/M4. Both machines match the pinned same-B
807
- oracle through retirement and late joins; KV4/logprobs composition also passes.
808
- Both M4 serving arm orders preserve single-request output. A small cached
809
- first-output gap keeps strict performance dominance open;
810
- simultaneous different adapters within a single forward remain future work.
811
- Shared adapter speculation is qualified by the grouped provider's
812
- `supportsTargetAdapters` capability. Prompt lookup declares it because its
813
- draft state contains token history, with no separate learned graph to adapt.
814
- Target prefill and verification retain the existing group adapter context;
815
- adapter revision namespaces protect both target and lookup checkpoints.
816
- Unqualified combinations retain ordinary execution. Learned providers do not
817
- declare this capability. The native `adapter-lookup.test.ts` gate covers
818
- compatible rows, seeded logprobs, failure cleanup and adapter prefix isolation;
819
- both-machine bf16/KV4/TurboQuant checks pass, including existing ordinary-adapter
820
- regressions. HTTP tests verify actual generated token alignment, exact seeded
821
- RAM/SSD restart choices and speculation metadata, adapter namespace isolation
822
- and concurrent requests. Concatenating a second newline can change the rendered
823
- token boundary; the negative tokenization control preserves that distinction.
824
- Full suites/typechecks pass; eight M4 comparisons are complete. Ordinary source cost is
825
- effectively flat. Enabled lookup has low draft acceptance and loses concurrent
826
- throughput on this workload; stricter matching is under separate measurement.
827
- The feature remains opt-in and does not qualify a new default.
828
- Evidence: `reports/qwen38-closeout/composition-baseline/adapter-lookup/`.
829
- - **Prompt-cache byte interplay**: mirror mlx-lm's
830
- `trim_to(total − activeBatchBytes)` so the LRU and the live batch
831
- share one ceiling. Derived default for `--kv-budget` (usable − weights
832
- − prompt-cache cap − headroom) instead of unlimited-when-unset.
833
- - **Long-context batched golden is complete** for the recorded Gemma 12B/e4b
834
- same-B window-crossing, retirement and late-join cases on both machines.
835
- See the ring-wrap gate above; it is not outstanding implementation work.
836
- - **Unvalidated model cells** stay serial by the capability gate:
837
- gemma2-family (`maskArray`) and sliding-window universal archs.
838
- - **Paged KV**: ordinary shared storage is integrated and measured. Direct
839
- paged attention, block sharing and quantized/speculative paging remain;
840
- the state/cache interfaces own storage, independently of scheduling.
841
- - **GLM MTP with concurrent requests**: the isolated shared provider passes
842
- tiny-model and same-B Python state checks. Josh deferred final real-artifact
843
- testing; see the GLM section below.
844
- - **Grammar jump-forward** (`MLX_BUN_GRAMMAR_JUMP`) is serial-only;
845
- `#stepGrammar` does not jump. Native XGrammar-2 via TVM-FFI has a
846
- B≫8 trigger.
847
- - **Device-side step chaining (depth-k pipelining)**: serial-decode
848
- work, orthogonal to the scheduler; the matrix's serial baselines show
849
- its headroom.
850
- - **Batch-invariant kernels** (identical numerics regardless of B): Lab
851
- research item; would restore determinism under load for free.
852
- - **Qwen MTP with concurrent requests is integrated**: variable accepted
853
- lengths, method-owned state alignment, shared sampling and generated RAM/SSD
854
- reuse have passed the controls recorded below. Further work must identify a
855
- specific unsupported composition or regression.
856
- - **Stale source comments**: `batch-scheduler.ts` header still says
857
- joins re-merge (extend landed 2026-07-04); `generation-gateway.ts`
858
- header and server-config.md say merged rows are never re-put
859
- (`#extractAndPut` now extracts merged rows ≥256 prompt tokens).
860
-
861
- ## 9. Measured numbers (labels required)
862
-
863
- - oMLX head-to-head, **M1 Max 32 GB, loaded machine, 2026-07-02**, 4
864
- concurrent × 128 tok, median of 3, wall-clock aggregate tok/s, oMLX
865
- 0.4.5.dev1 on the same OptiQ snapshots: MiniCPM5-1B 339.0 vs mlx-bun
866
- `--batch 4` 345.4 (mean TTFT 397 vs 119 ms); gemma-4-e4b 89.8 vs 87.1;
867
- Qwen3.5-4B 100.9 vs 99.5 (TTFT 848 vs 368 ms). Before P5/logits
868
- processors landed the same day, oMLX led 1.46–1.89× and our TTFT
869
- queue-inflated to 1.1–3.8 s.
870
- - Batch-lane B=1 gap before Phase 2, **cpm5, M1 Max, 2026-07-05**: ~149
871
- tok/s vs 267 serial (1.8×). mlx-lm's own `BatchGenerator` at B=1:
872
- 256.5 vs 264.6 for `stream_generate` (3.3% tax) — the unified design
873
- was proven achievable before the gap was closed.
874
- - After Phase 3.2, **2026-07-05 paired A/B**: unified B=1 decode ratios
875
- 0.992–0.996 vs strict serial, byte-identical output (the default-flip
876
- evidence).
877
- - Batched mixed-KV, **cpm5, 2026-07-05**: `--batch 2 --kv-quant config`
878
- 400 tok @ 240 tok/s aggregate; B=1 bit-exact vs the optiq golden;
879
- padded-row KL within the 5e-2 envelope (bf16 same-harness ~9e-3).
880
- Gemma 12B rotating-quant B=2 join: unpadded row KL 0 every step, padded
881
- ≤4e-3.
882
-
883
- ## 10. History
884
-
885
- - 2026-06-13 — Phase 18 opened; batched padded prefill found already
886
- proven in the training path (`buildBatchedPadMask`, `BatchedMaskCache`).
887
- - 2026-06-14 — `--batch N` flag (designed as `--slots`; `--decode-concurrency`
888
- alias); `BatchedDecodeMaskCache` + decode-parity harness; dynamic-B
889
- `mergeKVRows`/`filterKVRows` vs mlx-lm dynamic golden;
890
- `BatchedRotatingCache`; `BatchScheduler` + `GenerationGateway` wired;
891
- L1 row green — CPM, Gemma 12B, e4b, 26B bit-exact vs mlx-lm B=2.
892
- - 2026-07-01 — v2 hotfix bundle: slice-before-head, cache-capability
893
- gate, one lock domain (`runExclusive`); per-row containment +
894
- drain-on-serial-waiter; engine hygiene (pipelined decode, clearCache/256,
895
- chunked interleaved admit).
896
- - 2026-07-02 — SSM batched path (Qwen3.5, token-exact vs mlx-lm B=2);
897
- per-row logits processors; `MLX_BUN_LANE_DEBUG`; serial-lane macrotask
898
- hop; oMLX burst port refuted; `--model` override honored by serve/bench.
899
- - 2026-07-03 — grammar B1 per-row matchers + churn gates; `--kv-budget`
900
- admission + `/stats.batch` fields; Tier-0 universal per-row RoPE fix
901
- (Llama token-exact vs mlx-lm B=2); `bench-matrix.ts features`.
902
- - 2026-07-04 — extend-join for full-attention layers (own oracle);
903
- vectorized homogeneous greedy sampling (bit-equal A/B).
904
- - 2026-07-05 — Phase 2: B=1 host tax closed (`toIntTokens`, unpadded
905
- fast path); Phase 3.1: batched per-layer quantized KV (full layers);
906
- Phase 3.2: adopt-don't-copy, compiled decode at B=1, prompt-cache
907
- take/put on the batch lane; milestone 2: batched rotating-quant (every
908
- shipped `kv_config` batches); `--batch` default 1→8; concurrency-is-the-
909
- batch-size decision.
910
- - 2026-07-06 — boundary-snapshot put on the batch lane (12B ctx repeat
911
- 84 s → serial-class).
912
- - 2026-07-07 — rot-quant join `temporalView` leak fixed.
913
- - 2026-08-21 — Serving consolidation: immutable `GenerationPlacement`
914
- (`serial` | `continuous`); resolved `KvScheme` authoritative through
915
- placement, conversion, and projection; GLM `MLACache` as a
916
- `BatchableCache`.
917
- - 2026-08-22 — rotating-join regression from 443f333 fixed (capability-only
918
- merge routing); `tests/batch-rotating-join.test.ts`.
919
-
920
- ### Shared KV maintenance
921
-
922
- `createKvMaintenance` resolves the KV policy once when composing an execution
923
- session. The ordinary generator, speculative prefill and the MLX execution group call
924
- this same backend operation at their populated-cache boundaries. Per-layer lookup
925
- is no longer rebuilt on every serial decode step. Streaming conversion still
926
- materializes and releases each layer before converting the next. The existing
927
- single-call `maybeQuantizeKv` export remains a compatibility entry point.
928
- Server uniform KV4/KV8 now uses the same operation and existing quantized
929
- batch layouts. Capability discovery probes actual cache-owning layers, including
930
- Gemma's shorter donor prefix; it does not infer a separate scheduler from the
931
- quantization label. M1 Gemma e4b and M4 packed Qwen native tests cover seeded
932
- sampling with logprobs, exact serial/B=1 output, and repeatable controlled
933
- B=2/B=3 joins. Matched serving performance and same-B external oracle coverage
934
- remain open. Start-zero TurboQuant now has encoded row-layout support for
935
- ordinary and Qwen MTP methods. Positive ordinary TurboQuant thresholds now
936
- have merged-row transitions; Qwen delayed affine conversion is also adopted.
937
-
938
- Prefill policy is now shared too. The execution group captures its chunk
939
- default once; ordinary and speculative preparation use it, and both honor a
940
- library request override. The gateway forwards that override instead of
941
- silently using the group default for ordinary requests. The scheduler does
942
- not choose a numerical prefill chunk. M1 native seeded-sampling and MTP
943
- RAM/SSD checks pass on both Macs. The first matched M4 short control preserves
944
- all measured outputs with effectively unchanged timing; long-context checks
945
- and balanced acceptance remain open.
946
-
947
- Delayed ordinary TurboQuant conversion is now adopted after the mixed-phase,
948
- prefix-reuse, fused/unfused and composed MTP/generated-output checks pass on
949
- M4 as well as the original M1 family gates. The cache publishes its actual
950
- irreversible conversion boundary through clone, extraction, RAM lookup and
951
- SSD metadata; earlier plain donors remain usable. The first M4 hygiene run
952
- found only a stale generated docs map. After regeneration, full hygiene passes
953
- on both machines without changing tested code. The main full suite and typecheck
954
- pass for ordinary conversion. Delayed shared MTP is also adopted after native
955
- state/continuation, seeded B1/B3/late-admission, SSD and complete-suite gates on
956
- both machines. Conversion follows retained state after rollback and preserves
957
- physical row positions; the codec and kernels are unchanged. The main full suite, typecheck and hygiene pass. Qwen delayed affine is also adopted; these correctness gates establish
958
- no new TQ default or performance gain.
959
-
960
- ### Variable proposal lengths
961
-
962
- The shared method accepts 0..depth proposals independently per
963
- request. It sizes the rectangular target graph from the longest returned
964
- proposal, right-pads shorter rows, and samples only through each row's own
965
- bonus position. State commit retains the pending token and accepted inputs;
966
- no padding enters a reusable checkpoint. If every proposal is empty, the
967
- method runs one target step even when the configured draft depth is positive.
968
- Scheduling does not choose proposal lengths or manage their acceptance.
969
-
970
- The M1 Qwen control covers four rows with 0/1/2/3 proposals, greedy and seeded
971
- sampling, independent retirement and re-admission. Both bf16 and KV4 preserve
972
- tokens, logprobs, target state and companion state against explicit same-B
973
- controls that use a different right-padding token. These are internal
974
- composition checks, not an external oracle or a performance claim. Unit tests
975
- also prove that padding consumes no sampler steps and terminal consumers
976
- retain only delivered inputs. The complete candidate suite and typecheck pass.
977
- M4 uniform/variable native controls and the complete suite also pass. The
978
- first fixed-settings serving comparison was slower despite identical outputs
979
- and speculation statistics. Alternating frozen-source controls do not reproduce
980
- that slowdown and retain identical work. The three-file verifier patch is now
981
- adopted; the main complete suite and typecheck pass. Provider migrations must retain their own
982
- prefill/checkpoint conventions; this verifier alone does not change eligibility.
983
- Evidence: `reports/qwen38-closeout/composition-baseline/variable-proposals/`.
984
-
985
- ### Prompt lookup through the shared method
986
-
987
- The shared executor now implements prompt lookup as a provider-owned draft
988
- row group. Each row holds committed token history, proposes repeated spans,
989
- and appends only inputs retained by verification. It uses the same variable
990
- proposal verifier, sampler, target state, membership transactions and cache
991
- publication as MTP. Scheduling has no prompt-lookup branch.
992
-
993
- Preparation explicitly supplies processed-token coverage to the provider.
994
- For tail-split lookup, the last prompt token becomes the first pending input;
995
- completed-output keys include it exactly once. Integer history tensors use the
996
- existing companion-checkpoint format and byte accounting. The generic tensor
997
- reader now supports int32 for streamed SSD persistence; no separate prompt
998
- lookup writer or storage queue is introduced.
999
-
1000
- M1 Qwen bf16/KV4/KV8/fused-TQ serving checks pass B1 controls, real B4 verification, independent
1001
- consumer stops/failures, cancellation, late joins, seeded logprobs and grammar.
1002
- All four schemes pass tool turns with thinking off/on, generated-prefix RAM reuse and fresh-PID
1003
- SSD restoration with identical responses, logprobs and acceptance. The initial
1004
- SSD flush failure exposed the missing int32 raw-byte reader; its regression
1005
- now passes. MTP3 serving regressions also pass in bf16/KV4/TQ after the shared
1006
- preparation changes. Unsplit-prefill serving and the complete model-free/SSD
1007
- suite and typecheck also pass. The same planned native/HTTP scheme matrix,
1008
- MTP regressions, unsplit-prefill and complete suite now pass on M4. An initial
1009
- M4 wrapper failure supplied CLI quantization value `0` instead of `off`; it
1010
- failed before inference, was retained, and the corrected wrapper resumed
1011
- without source changes. Fixed-MTP timing is effectively unchanged. Both lookup arm orders preserve
1012
- single-request outputs with small decode improvements and new cached-prefix
1013
- reuse. Concurrent outputs differ and observed shared throughput is lower, so
1014
- strict dominance is unproven. Default depth-10 KV4 serving checks now pass on
1015
- both Macs and exercise all ten proposal positions. Serving eligibility is
1016
- currently Qwen only. The seven-file patch is adopted; its concurrent
1017
- performance gap remains open, and serial deletion is not qualified.
1018
- Evidence: `reports/qwen38-closeout/composition-baseline/ngram-group/`.
1019
-
1020
- A separate composed checkout combines the adopted variable verifier and ordinary
1021
- TQ cache with prompt lookup and delayed speculative TQ. The only production
1022
- hunk shared by the two candidate patches is the generic cache preparation hook
1023
- before state admission; the merged source is verified against both deltas.
1024
- M1 fused/unfused target/MTP and prompt-lookup native checks, threshold-crossing
1025
- SSD restart, full suite and typecheck pass. The lookup extension exercises the
1026
- same delayed-cache serving test by substituting its draft provider, including
1027
- mixed phases, seeded metadata, retirement and late joins. M4 composition now passes the same native/SSD gates, full suite, typecheck
1028
- and hygiene. Both production patches are now adopted in main, with exact
1029
- file identity against the tested composed source. Fixed-MTP composition
1030
- timing is effectively unchanged. Main full-suite, typecheck and hygiene checks pass.
1031
- Evidence: `reports/qwen38-closeout/composition-baseline/method-composed/`.
1032
-
1033
- ### One-token prefill allocator maintenance candidate
1034
-
1035
- A native first-output diagnostic found milliseconds of allocator cleanup after
1036
- a cached one-token target forward. A separate candidate retains allocator
1037
- buffers for single-token prefill work and still clears after larger chunks.
1038
- This is method-owned maintenance; the scheduler, sampler and cache-persistence
1039
- policy are unchanged. KV4 and delayed-TQ one-token suffix SSD continuations,
1040
- complete suites and typechecks pass on M1/M4. Alternating M4 fixed-MTP timing
1041
- preserves every request/output/usage record and consistently reduces cached
1042
- first-output latency. The first control has slower cold/decode timings, so
1043
- a broad throughput gain is not established. The 8k M1 allocation comparison
1044
- retains identical output/acceptance, nearly equal peak live allocation and a
1045
- larger final allocator pool. Matched serial/shared timing still shows a cached-latency regression; the
1046
- candidate remains isolated and long-context/SSD pressure remains open.
1047
- Evidence: `reports/qwen38-closeout/composition-baseline/single-token-maintenance/`.
1048
-
1049
- ### Delayed affine conversion
1050
-
1051
- The adopted delayed-affine implementation extracts `TransitioningKvRows` from the
1052
- existing TurboQuant adapter. It owns committed positions, conversion, physical
1053
- padding, merge/extract and speculative rollback. Codec-specific adapters retain
1054
- their attention arithmetic. The affine adapter uses the storage-owned
1055
- `KvAttentionState` port for mixed-precision rows and the existing packed batch
1056
- attention once every row converts. Model binding declares support once and
1057
- passes that capability to request planning and executor construction.
1058
-
1059
- Both Macs pass KV4/KV8 target rollback and continuation, ordinary serial/B1
1060
- seeded token/logprob equality, MTP B1/B3 retirement and late admission, plus SSD
1061
- restoration with conversion in prefill or decode. Fused/unfused TQ native and
1062
- serving regressions pass. Final full suites and typechecks pass on both Macs.
1063
- Positive affine thresholds and non-default group sizes have distinct cache
1064
- identities; captured conversion floors survive extraction and SSD restoration.
1065
- The original constructor-capability failure is retained in the raw evidence.
1066
-
1067
- Alternating M4 fixed-settings comparisons preserve every request output, usage
1068
- and finish reason with effectively unchanged timing. The reviewed implementation
1069
- is adopted in the unreleased working tree. Eligibility currently covers Qwen;
1070
- other model attention ports remain open. Main full-suite/typecheck/hygiene checks pass. Prompt lookup also passes KV4/KV8
1071
- B1/B3 seeded continuation, actual proposals, retirement and late admission on
1072
- both Macs without scheduler or sampler changes.
1073
- No serial deletion or performance gain is established by these checks. Evidence is under
1074
- `reports/qwen38-closeout/composition-baseline/delayed-affine/`.
1075
-
1076
- The adopted attention port preserves Gemma's donor/shared-layer contract: a donor
1077
- calls `Cache.attentionState.appendAndFetch` once, then every sharing layer calls
1078
- `KvAttentionView.attend` on the same captured state. The view owns tensor handles
1079
- and captured row alignment independently of later cache mutation or disposal.
1080
- The model retains pre-write RoPE positions through all consumers and releases
1081
- the view once at the end of the pass. Qwen and MiniCPM use one consumer per
1082
- append. Existing training attention paths remain separate from these inference
1083
- cache views. Scheduling and sampling do not manage attention-view lifetimes.
1084
-
1085
-
1086
- The captured-view implementation exposes `appendAndFetch` and a
1087
- reusable `KvAttentionView.attend`. Qwen consumes one view per append; Gemma can
1088
- retain it for sharing layers. KV4/KV8 unit checks preserve two distinct queries
1089
- after further appends, conversion, row reordering and disposal of the original
1090
- cache. Full-model Gemma 12B logits and active cache state match the existing
1091
- consumer path. The initial state test compared unused allocator capacity;
1092
- the corrected check compares every processed value, and keeps the failed raw
1093
- comparison. Gemma 12B/e4b and MiniCPM KV4/KV8 pass on both machines. The e4b checks
1094
- exercise multiple reads per append. The M4 full suite passes. The initial M1
1095
- suite used a fallback DeltaNet fixture because this worktree lacked the
1096
- already-qualified machine golden; copying that unchanged fixture makes the
1097
- focused test pass. Qwen KV4/KV8 rollback, MTP and prompt lookup regressions plus the corrected
1098
- M1 suite pass. Both-machine typechecks and KV4/KV8 SSD continuations pass.
1099
- Alternating M4 decode/cached/throughput timing is effectively unchanged. The
1100
- final control end check flags a small CPU daemon; the raw result is retained
1101
- and assessed under the campaign background-CPU policy. Startup remains less
1102
- stable and is not qualified by this screen. The reviewed change is adopted;
1103
- main integration suite, typecheck and hygiene pass.
1104
- Gateway eligibility is unchanged until the broader storage combinations pass.
1105
- Candidate evidence: `reports/qwen38-closeout/composition-baseline/kv-attention-view/`.
1106
-
1107
-
1108
- Delayed rotating affine storage remains a separate missing composition. Its
1109
- logical row positions and physical ring columns are different state: conversion
1110
- or packing must preserve the mask/column relationship. Existing
1111
- `BatchedRotatingQuantCache` has encoded row merging; the shared
1112
- `BatchedRotatingState` owns ring positions. Reuse those interfaces rather than
1113
- introducing a scheduler quantization branch. Qualify unequal offsets, conversion
1114
- before/after wrap, retirement, late admission, shared Gemma consumers and
1115
- persisted conversion floors before widening the gateway capability.
1116
-
1117
- The first M4 benchmark wrapper stopped before inference because its parent
1118
- shell inherited an older oracle path. The Qwen correctness child had already
1119
- passed with the explicit pinned environment. Only the unstarted benchmark was
1120
- resumed, with that same explicit environment; the completed four-arm comparison
1121
- and failed launch log are retained.
1122
-
1123
-
1124
- ### Multi-token rotating storage
1125
-
1126
- The adopted rotating-block implementation removes the one-token-only restriction
1127
- from both bf16 and affine batched rings. `RowStorage` supplies encoded tensor
1128
- operations; `appendRotatingStorage` retains chronological history without
1129
- unpacking affine tensors; `BatchedRotatingState.commitConcat` owns padding,
1130
- logical positions and the physical write head. One-token updates retain their
1131
- existing in-place implementation. The port follows the pinned mlx-lm
1132
- `BatchRotatingKVCache._update_concat` for left-padded inputs.
1133
-
1134
- A block temporarily holds up to window + block length - 1 columns so every
1135
- query retains its full context. Extraction preserves that active block, while
1136
- merge views select the newest window. Dropping the temporary suffix during
1137
- extraction would corrupt subsequent continuation. No scheduler or sampler
1138
- changes are needed for these storage operations.
1139
-
1140
- The same-B live oracle gate covers B=1/2/3, unequal left padding, mixed
1141
- one-token and block writes, repeated wrap, overshoot, row reordering,
1142
- retirement, late admission and extracted state. bf16 values and masks match
1143
- mlx-lm exactly on both Macs. KV4/KV8 share its geometry and preserve every
1144
- extracted affine component against the native quantizer. Existing Gemma 12B
1145
- ring-wrap serving still matches 48 full logit vectors per machine. Both-machine model-free/SSD suites and typechecks pass. The reviewed six-file
1146
- change is adopted; main typecheck/hygiene pass. No performance or full
1147
- batched-prefill claim is made.
1148
- Right-padded prefill finalization and speculative rollback of overwritten
1149
- ring entries remain part of the shared-execution work. Delayed rotating
1150
- precision conversion is implemented below.
1151
- Evidence: `reports/qwen38-closeout/composition-baseline/rotating-block/`.
1152
-
1153
-
1154
- ### Delayed rotating affine conversion
1155
-
1156
- The adopted implementation uses `TransitioningKvRows` for ordinary membership,
1157
- retirement, checkpoints and precision changes. `SpeculativeTransitioningKvRows`
1158
- adds rollback for layouts that implement it; ordinary rotating storage does not
1159
- advertise speculative rollback. `TransitioningKvPositions` supplies geometry
1160
- without a codec. Full-attention positions retain the existing `KvTensorRows`
1161
- operations, and rotating positions use `BatchedRotatingState`.
1162
-
1163
- An aligned row keeps the group's physical ring columns while exposing its own
1164
- logical position to the shared affine-maintenance policy. Conversion quantizes
1165
- the existing buffers without changing their physical layout, write head or
1166
- padding. Once every row is converted, the encoded planes concatenate
1167
- without reordering columns. `captureKvAttention` provides the same owned read
1168
- view for either representation; Gemma's donor and sharing layers can keep
1169
- reading it after the cache advances. No scheduling or sampling branch is added.
1170
-
1171
- Membership changes normalize borrowed solo rows through owned clones. Empty
1172
- plain and affine cache clones now remain valid empty caches, enabling a group
1173
- to be assembled before its first input. Extraction preserves each row's
1174
- conversion floor in the existing persistence schema. The implementation enables
1175
- positive affine thresholds for ordinary Gemma execution; other speculative
1176
- providers, rotating rollback and true batched-prefill scheduling remain open.
1177
-
1178
- Gemma 12B and e4b KV4/KV8 pass exact hidden/logit and active-state checks before
1179
- and after the real sliding window on both Macs. Serialized output snapshots
1180
- preserve token IDs, state and conversion floors; restored snapshots continue
1181
- identically to RAM snapshots in the same process. These are not fresh-process
1182
- or long Kanban acceptance. Seeded gateway checks match serial at B1 and repeat
1183
- B2/B3 admission schedules exactly on both machines. The model-free checks cover
1184
- mixed precision, empty rows, conversion before/after wrap, block updates,
1185
- retirement, late joins and attention reads after owner disposal. Final complete suites and typechecks pass on both Macs, as do Qwen affine
1186
- MTP/lookup and fused/unfused TurboQuant regressions. Artifact-defined per-layer
1187
- Gemma compression also passes both-machine hidden/logit/state and persistence
1188
- checks. Four alternating M4 default Qwen benchmark arms preserve all responses
1189
- and show effectively flat decode, cached latency and concurrent throughput;
1190
- startup equivalence remains open. The reviewed fifteen-file implementation is
1191
- adopted in the unreleased working tree. Main integration suite, typecheck and hygiene pass.
1192
- Measurements and source identities live in benchmarks.md.
1193
- Evidence: `reports/qwen38-closeout/composition-baseline/delayed-rotating/`.
1194
-
1195
-
1196
- ### Singleton admission copy candidate
1197
-
1198
- An isolated candidate replaces singleton `copyOf` calls with `contiguous` in
1199
- attention and recurrent admission. The proposed GPU-copy saving is not
1200
- supported: the M4 MLX 0.32.2 probe shows both operations share existing storage.
1201
- This agrees with the existing explanation in `mlx/materialize.ts`; a method
1202
- name is not evidence of a physical copy. The evaluated synthetic Qwen-shaped
1203
- probe shows no additional GPU allocation for either version. Also, shared MTP
1204
- publishes its first token before the singleton group merge, so that merge is
1205
- not on its first-output critical path.
1206
-
1207
- Both-machine ownership tests cover independent updates, donor immutability,
1208
- self-merge, spare capacity and source/group disposal. Typechecks pass. The M4
1209
- bf16/KV4/KV8 round matrix, fused/unfused TQ, delayed affine/TQ serving and full
1210
- suite pass. M1 native/serving and the complete suite also pass. The alternating
1211
- M4 HTTP comparison preserves all responses and records lower cached latency
1212
- in both candidate arms. The physical-copy explanation is disproven, so further
1213
- attribution/repetition remains before adoption. Startup equivalence and direct
1214
- serial dominance are still unproven. Extraction and larger group concatenation
1215
- are unchanged. Timing belongs in benchmarks.md.
1216
- Evidence: `reports/qwen38-closeout/composition-baseline/singleton-state/`.
1217
-
1218
-
1219
- ### Recurrent output-row retention
1220
-
1221
- `SSMCache.extractRow` used `copyOf` with a comment promising compact storage.
1222
- The current MLX implementation aliases that storage: after releasing a B>1
1223
- source group, one retained row still owns the entire group's backing buffers.
1224
- The M1 allocation probe reproduces this for B=2/4/8. This is separate from
1225
- singleton admission, where sharing a complete allocation is appropriate.
1226
-
1227
- The adopted extraction fix uses the existing `materializeCopy` port for
1228
- a proper subset of a batch and retains a separate array handle for B1. Row
1229
- values and logical offsets do not change. The allocation regression fails on
1230
- the unchanged control and passes on the candidate, including exact values and
1231
- disposal on both Macs. Both-machine KV4 MTP round matrices, bf16/KV4/TQ generated
1232
- RAM/SSD continuations, complete suites and typechecks pass. Four alternating
1233
- M4 HTTP arms preserve all responses and support integration without a broad
1234
- speed claim. The source and regression test are adopted; main integration
1235
- suite and typecheck pass. Long-context pressure acceptance remains open.
1236
- No scheduler, sampler, persistence queue or quantization policy changes.
1237
- Evidence: `reports/qwen38-closeout/composition-baseline/ssm-extraction/`.
1238
-
1239
-
1240
- ### Padded prefill cache interfaces
1241
-
1242
- The adopted cache-interface portion adds `PaddedPrefillCache`. Its frozen
1243
- validation checkout is `padded-prefill-validation`.
1244
- Preparation receives each prompt's lengths and left/right padding. Finalization
1245
- removes trailing padding from logical coverage and aligns retained state before
1246
- decode or checkpoint publication. The cache owns these operations; neither the
1247
- scheduler nor a quantization codec chooses token positions.
1248
-
1249
- `FullPrefillPadding` applies one row-roll index to arbitrary full-cache tensor
1250
- planes. Plain KV, affine KV4/KV8 and TurboQuant reuse it without dequantizing or
1251
- requantizing the stored values. Rotating storage instead moves completed rows'
1252
- padding ahead of valid history before each block trims its window. Both plain
1253
- and affine rings share that position logic and preserve physical alignment.
1254
-
1255
- One-token padded chunks use the existing rotating concat operation and block
1256
- mask. MLX-LM's public dispatch rejects this case before finalization. The gate
1257
- therefore invokes its existing `_update_concat` and takes the first query from
1258
- its two-token block mask for this extension. Ordinary decode retains the ring
1259
- write. This is an extension checked against oracle operations, not a claim that
1260
- the upstream public dispatcher supports the same call.
1261
-
1262
- The live geometry gate checks B1/B2/B3, left/right padding, an existing prefix,
1263
- window overflow, zero-length prefixes, one-token chunks, finalization, extraction
1264
- and continuation.
1265
- Plain tensors, masks and positions match the reference. Affine and TurboQuant
1266
- retained bytes match their existing codecs applied to oracle-retained rows.
1267
- MiniCPM5 and Gemma 12B/e4b also match full same-B logit vectors after padded
1268
- prefill and three continuations with whole-prompt, 5/5/1 and singleton chunks
1269
- on both Macs. Complete suites and typechecks pass on both machines. Qwen KV4
1270
- MTP rounds, both TurboQuant decode settings and generated-prefix bf16/KV4/TQ
1271
- RAM/SSD continuations also pass on both Macs. These regression runs use ordinary
1272
- unpadded preparation; they do not qualify Qwen padded prefill.
1273
-
1274
- The cache-interface changes are adopted; main integration suite and typecheck pass.
1275
- Qwen recurrent-state masking, delayed rotating transitions and ordinary
1276
- preparation cohorts are adopted below, including cancellation/checkpoints.
1277
- MTP preparation and full-cache delayed padding still keep S1a open.
1278
- No serving performance result or default change is claimed for this candidate.
1279
- Evidence: `reports/qwen38-closeout/composition-baseline/padded-prefill/`.
1280
-
1281
- Full-cache finalization now retains absolute valid ends, as rotating storage
1282
- already does. It removes only padding actually processed. This lets a cohort
1283
- finish early after a longer request leaves, or finalize a partial prefix before
1284
- the original planned width. Ending before any right padding leaves tensors and
1285
- positions untouched. Filtering and reordering requests also filters those ends.
1286
- The unchanged control subtracts unprocessed padding and produces negative
1287
- coverage in the shortened-cohort regression; its failure log is retained.
1288
-
1289
- Both-machine geometry checks cover completed and shortened B1/B2/B3 prefills,
1290
- cached prefixes, leading padding, request retirement/reordering before
1291
- finalization, extraction and three subsequent decode steps. The reference full
1292
- cache receives the effective padding of the processed prefix; Bun receives the
1293
- original plan. Survivor checks select the corresponding oracle rows without
1294
- changing their physical columns. Plain state/masks match exactly; affine and
1295
- TurboQuant retained bytes match encoding those oracle rows. Host lifecycle
1296
- tests also cover finalization before any work and preparation after a partial
1297
- checkpoint. Model-free suites, SSD tests and typechecks pass on both Macs.
1298
- This fixes cache preparation semantics; serving cohorts and their cancellation
1299
- handling are still unwired. No speed or default change is claimed.
1300
- Evidence: `reports/qwen38-closeout/composition-baseline/padded-partial/`.
1301
-
1302
-
1303
- ### Recurrent-state padding
1304
-
1305
- The adopted recurrent-padding change extends the same prefill port to
1306
- Qwen's recurrent cache. `SsmPrefillPadding` tracks remaining real-token lengths
1307
- and leading padding. Each cache advances coverage by real tokens only and
1308
- filters that metadata with its rows. Finalization clears preparation state.
1309
- Uniform prompts keep the unmasked kernel and ordinary convolution tail.
1310
-
1311
- The model constructs one mask for all recurrent layers. DeltaNet zeroes padded
1312
- QKV inputs before convolution, selects each row's convolution tail by its
1313
- remaining length, and uses the pinned oracle's masked recurrence. Masked steps
1314
- leave recurrent state unchanged. The existing kernel body is shared between
1315
- masked and unmasked specializations; scheduling does not choose or manipulate
1316
- these state operations. The convolution-tail selection already used by
1317
- variable speculative rollback is reused here.
1318
-
1319
- The primitive gate compares full output and state hashes with the pinned GPU
1320
- oracle at the 27B head geometry. It includes B1/B3, fully masked rows, singleton
1321
- steps and an unmasked continuation. The unchanged control fails this gate.
1322
- The small Qwen model passes padded full-model checks on M1. Both Macs pass
1323
- same-B 27B full-logit and convolution/recurrent-state checks for unequal prompts,
1324
- batch-of-one and uniform B3, with whole-prompt, singleton-tail and all-singleton
1325
- chunks. The machines use their locally installed affine 27B artifacts; these
1326
- are per-machine oracle comparisons, not an engine or artifact speed comparison.
1327
-
1328
- The isolated checkout initially retained three obsolete HEAD files and lacked
1329
- its machine-specific golden blobs. Those setup problems were corrected against
1330
- the validated base; no golden was regenerated. Initial scalar creation also
1331
- needed the existing typed-array constructor. Failure logs remain with the
1332
- candidate evidence. Both-machine MTP/TQ/generated RAM/SSD regressions, full
1333
- suites and typechecks pass. Four alternating M4 HTTP arms preserve all responses
1334
- with roughly flat decode/cached/throughput timing. Startup equivalence remains
1335
- open. The change is adopted; main integration suite and typecheck pass.
1336
- Delayed-codec preparation and actual serving cohorts remain open.
1337
- Evidence: `reports/qwen38-closeout/composition-baseline/padded-ssm/`.
1338
-
1339
- ### Delayed rotating compression during padded prefill
1340
-
1341
- Delayed rotating KV4/KV8 now implements the same preparation/finalization port.
1342
- Each physical row receives its own token lengths and the common batch layout.
1343
- All rows use the padded block operation when any sibling has right padding,
1344
- including a singleton chunk in a row without padding. An initial implementation
1345
- lost alignment by letting that row switch to a ring write while its sibling
1346
- continued block writes. The retained failing regression exposed incorrect cache
1347
- bytes when those rows later packed together.
1348
-
1349
- The shared position snapshot now combines every row's pending valid end when
1350
- format conversion packs a group. Maintenance uses valid token counts, so a
1351
- padded suffix cannot cross a precision threshold or inflate the earliest
1352
- reusable prefix. Physical write positions remain separate. Retirement and
1353
- reordering preserve pending preparation state before and after packing.
1354
-
1355
- Both-machine geometry gates include B1/B2/B3/B4/B8, KV4/KV8, multiple transition
1356
- thresholds, cached history, ring overflow, singleton chunks, early finalization
1357
- and survivor continuation. Cache geometry comes from the pinned MLX-LM oracle.
1358
- Encoded bytes match the existing codec applied to oracle-retained rows; exact
1359
- attention checks use those oracle tensors and masks with the native operation
1360
- at the same B as each mixed or packed representation. Gemma 12B/e4b also pass
1361
- same-B padded model logits through the delayed layout before conversion.
1362
- The latter is plain-KV model coverage, not a quantized padded full-model oracle.
1363
- Ordinary Gemma conversion and RAM/SSD continuations, complete model-free suites
1364
- and typechecks pass on both Macs.
1365
-
1366
- The change is adopted in the unreleased working tree. Four alternating Gemma
1367
- M4 benchmark arms preserve all responses; the small timing differences are
1368
- recorded in benchmarks.md and establish no speed win or serial dominance.
1369
- Full-attention delayed affine/TurboQuant padding remains open. Ordinary serving
1370
- now uses the real-token cohort described below.
1371
-
1372
- Ordinary serving uses a method-owned cohort of equal-length
1373
- chunks of real tokens. It admits later arrivals between forwards. Each request
1374
- retains its planned maintenance, checkpoint and tail boundaries when a peer
1375
- causes an earlier split. Scheduling chooses membership; the preparation method
1376
- runs shared forwards; cache layouts merge positions and retain precision policy.
1377
- The same target driver now serves MTP and prompt lookup; their providers own
1378
- companion prefill state through the separate method interface.
1379
-
1380
- The first lifecycle runs exposed an empty-state native evaluation and missing
1381
- initialization when a cold recurrent row joins populated state. Empty caches
1382
- submit no evaluation. The recurrent layout initializes the new row's state
1383
- with zeros, matching the pinned reference's ArraysCache.merge behavior.
1384
- Qwen 27B on the M4 then exposed a logit mismatch immediately after retirement.
1385
- Full-attention storage now removes padding shared by all survivors, matching
1386
- BatchKVCache.filter. Affine planes inherit that operation; TurboQuant's tensor
1387
- storage applies it too. These are cache membership operations, independent of
1388
- scheduler, sampler and inference method.
1389
-
1390
- Focused tests cover staggered B1→B2→B3 joins, real-token identity, native retained
1391
- bytes with immediate/delayed affine and TurboQuant conversion, checkpoint
1392
- boundaries, cancellation and completion callback failure. A scheduler-level test
1393
- submits a request during the first forward and observes it in the next chunk.
1394
- The model gate compares every full logit vector to the pinned same-B reference
1395
- through joins and retirement, then compares each extracted row's continuation.
1396
- It passes Qwen 27B and Gemma 12B on both Macs; the M1 also passes Qwen 0.8B.
1397
- Padded cache geometry/encoded-state/attention checks pass on both machines.
1398
- The padded oracle now performs retirement itself; stock filter omits pending
1399
- prefill metadata, so the fixture selects its right-padding/length metadata with
1400
- the same row indices before calling the existing finalize operation.
1401
-
1402
- MTP delayed affine/TurboQuant regressions pass on both Macs. Four-arm M4
1403
- timing and response differences are recorded in benchmarks.md.
1404
- An added token-zero check then found that the M4 Qwen 27B sampler receives
1405
- different logits when each completed row projects its hidden state separately.
1406
- The shared forward's full vectors still match; the M=1 projection changes its
1407
- arithmetic. A follow-up candidate projects the batch before selecting logits
1408
- for each sampler. Its focused lifecycle checks and the M4 Qwen 27B/Gemma 12B full-vector,
1409
- sampler-logit and continuation checks pass on both Macs. Full model-free suites
1410
- and typechecks pass on both; all 829 nonignored source/test files match across
1411
- the isolated copies. Fresh four-arm M4 timing is effectively flat. The corrected
1412
- cohort is adopted in the unreleased working tree; main integration suite, typecheck and hygiene pass. Seeded delayed-TQ/affine serving and logprob/wire checks also pass on
1413
- M1. Unsplit-prefill seeded serving passes M1 Qwen 0.8B and M4 Gemma 12B.
1414
- Validated source: `/Users/joshrossi/.cache/mlx-bun/prefill-cohort-logits-validation`.
1415
- A separate admission-control benchmark retains the measured code but disables
1416
- additional cohort admission. That control restores every baseline response,
1417
- attributing the changed concurrent text to shared prefill.
1418
- Full delayed affine/TurboQuant padded preparation and MTP prefill cohorts remain
1419
- open. The production HTTP benchmark now measures long aggregate prompts and
1420
- staggered arrivals. Its four-arm M4 comparison exposes a first-output latency
1421
- regression and no throughput win at the current chunk size. M1 traces show
1422
- internal token zero ready before a long B3 preparation chunk delays the first
1423
- response write. A matching smaller-chunk M4 screen still loses latency and
1424
- throughput. Prefill work-unit sizing and admission policy need tuning through
1425
- the shared scheduler/preparation interfaces. This is a scheduling decision
1426
- within the shared executor; it must not introduce another serial method.
1427
- See benchmarks.md for numbers and limits.
1428
- Evidence: `reports/qwen38-closeout/composition-baseline/prefill-cohort/`.
1429
-
1430
- An isolated follow-up exposes preparation work through the scheduling contract.
1431
- The preparation owner reports each admitted request's initial uncached token
1432
- count; the queue adapter reports the next prompt length as an upper bound before
1433
- cache restoration. The scheduler extends a cohort only while the combined
1434
- weight fits its internal preparation budget, currently 2,048 tokens. The first
1435
- request always enters and can exceed that budget alone. No request is rejected
1436
- or sent to another executor. Short late arrivals still join ongoing preparation.
1437
- An earlier version reduced the weight as prefill progressed. That allowed a
1438
- nearly completed request to attract another long request into its tail; the
1439
- first M4 screen restored early latency but still lost total request time. The
1440
- current candidate retains admission weight until the request leaves preparation.
1441
- This policy changes grouping, not per-request chunk or precision boundaries.
1442
- Cache lookup/restoration remains in preparation, not scheduling. The conservative
1443
- queued estimate can postpone a cached long request until current preparation
1444
- ends. A native Qwen/TurboQuant check with a long RAM prefix passes repeat output,
1445
- retirement, immutable cache bytes and actual B2 decode on M1. The original late
1446
- prefill join remains covered, alongside a group test that holds a nearly finished
1447
- row's weight and then advances its queued sibling through the same executor.
1448
- The seven-file fixed-weight policy is adopted in the unreleased working tree.
1449
- Both complete suites/types and long-prefix ownership checks pass. The M4
1450
- comparison restores the large first-output loss and all response identity,
1451
- while small unfavorable timing differences remain in benchmarks.md. Main
1452
- integration suite/typecheck pass; strict performance dominance remains open.
1453
- Candidate: `/Users/joshrossi/.cache/mlx-bun/prefill-budget-validation`.
1454
- Evidence: `reports/qwen38-closeout/composition-baseline/prefill-cohort/budget*`.
1455
-
1456
- A separate two-file lifetime candidate releases input IDs after the forward and
1457
- releases unused hidden output before cache evaluation/maintenance, matching the
1458
- existing shared prefill primitive. The cohort previously retained those handles
1459
- until the end of the work unit. Finished rows still retain hidden output through
1460
- batched projection and sampling. Focused ownership checks pass on M1, and the Qwen 27B
1461
- same-B model/sampler/continuation gates pass on both Macs. The M1 full suite and
1462
- typecheck pass. The four-arm M4 comparison preserves all responses but shows no
1463
- throughput or material measured-memory benefit. This candidate is not adopted;
1464
- its M4 full suite/typecheck was not run. Timing and limits are in benchmarks.md.
1465
- Candidate: `/Users/joshrossi/.cache/mlx-bun/prefill-drain-validation`.
1466
-
1467
- The next isolated candidate exposes `DraftPrefillGroup` from the draft provider.
1468
- It borrows each batch of new target tokens and context, and owns companion KV,
1469
- the pending true hidden row, cold/restored membership and checkpoint capture.
1470
- `QwenMtpRows` supplies preparation and draft/commit state at B1 and B>1;
1471
- its preparation port requires no sampler. A restored request bridges its next
1472
- token to the saved preceding hidden; cold members discard that bridge. The
1473
- remaining chunk pairs each shifted token with its preceding target hidden.
1474
- Preparation evaluates KV and the retained final hidden, leaving unused decoder
1475
- outputs outside the evaluated graph. Membership and state extraction reuse the
1476
- existing cache storage and checkpoint format. The scheduler is unchanged.
1477
-
1478
- Pinned same-B checks pass companion KV and full decoder continuation through
1479
- B1/B2/B3/B4, cold joins, restored prefixes, retirement and a long chunk boundary
1480
- on both Macs. Those checks exposed a pre-existing unfused activation in the MTP
1481
- MLP. Reusing the target's compiled SwiGLU matches the reference vectors. Existing
1482
- MTP draft/commit checks and both complete suites/typechecks pass. Initial suite
1483
- failures came from omitted benchmark fixtures and an old M1 DeltaNet golden in
1484
- the isolated checkouts; restoring each machine's existing inputs fixes them
1485
- without changing the implementation or regenerating goldens.
1486
-
1487
- The seven-file provider port and numerical correction are adopted in the
1488
- unreleased working tree. The M4 HTTP comparison preserves response text and
1489
- prompt/output counts with effectively flat timing, while draft acceptance
1490
- traces change. That comparison did not exercise batched MTP prefill because
1491
- shared target preparation was not yet connected to the new port. Numbers and limits are in
1492
- benchmarks.md. Main integration suite/typecheck pass.
1493
-
1494
- The isolated `MlxPrefillRows` driver now owns target membership and chunk
1495
- progression for ordinary, Qwen MTP and prompt-lookup preparation. Each method
1496
- supplies initialization, chunk planning, checkpoint capture and completion.
1497
- The draft provider owns companion prefill state and materializes restored
1498
- companion copies before target membership releases an SSD backing lease.
1499
- Final-step maintenance is explicit in the chunk plan. A fully restored
1500
- pending-token prefix completes without a zero-length model forward.
1501
- Prepared rows return to scheduling before joining active decode, preserving
1502
- the first-output publication boundary.
1503
-
1504
- The connected implementation passes full suites and typechecks on both Macs,
1505
- and the target's same-B oracle checks through staggered prefill and continuation.
1506
- The companion and Gemma 12B target oracles also pass on M1. Expanded native MTP serving checks on both
1507
- Macs prove that a request submitted during the first target chunk joins the
1508
- next chunk before any output. M4 passes this check with prompt lookup and
1509
- TurboQuant as well. These checks also cover B1 producer parity, B4 execution,
1510
- retirement, cache reuse and HTTP grammar/logprobs. Generated target/draft
1511
- checkpoints preserve RAM/SSD continuation after restart on both Macs with TQ.
1512
- An added M1 probe confirms four rows actually share prefill in that cache test,
1513
- before the first output.
1514
-
1515
- Added method lifecycle checks verify exact target/history coverage, zero-work
1516
- restored prefixes and cancellation before and after preparation completes.
1517
- The latter exposed a failure that rejected siblings before active admission.
1518
- The method now rejects and releases only the cancelled ready row. A first-token
1519
- consumer failure also leaves an unfinished sibling running. M1 delayed affine
1520
- MTP/lookup and delayed TurboQuant MTP checks preserve seeded continuation,
1521
- metadata and speculation through mixed precision, retirement and late joins.
1522
- The common driver also passes M1 ordinary TQ ownership checks with a 4096-token
1523
- RAM prefix through concurrent use, retirement and supersession.
1524
- The balanced M4 comparison is complete. Shared MTP prefill reduces concurrent
1525
- throughput. Two attribution runs retain the same shared engine but prepare
1526
- requests individually; both reproduce control responses and restore throughput.
1527
- This isolates the regression to enabling shared preparation, without requiring
1528
- a separate serial executor. A narrower policy that finishes a resumed final
1529
- prefill step before admitting new rows does not recover throughput and produces
1530
- the same responses and speculation counters as the shared-prefill candidate.
1531
- Neither scheduling variant is adopted. The common driver is adopted with the
1532
- cache ownership fix described below.
1533
- Phase/layer probes narrow the sustained gap to MLP evaluation despite identical
1534
- Trellis routes, shapes, dtypes and call counts. Settling target state and clearing
1535
- the temporary pool after preparation does not restore throughput and is not
1536
- adopted. The snapshot-lifetime investigation below recovers this loss. Numbers
1537
- and conditions are in benchmarks.md. Broader composed oracle/cache coverage and
1538
- other draft providers remain open.
1539
- Candidate: `/Users/joshrossi/.cache/mlx-bun/mtp-prefill-validation`.
1540
- Evidence: `reports/qwen38-closeout/composition-baseline/prefill-cohort/mtp-prefill*`.
1541
-
1542
- Common-driver candidate: `/Users/joshrossi/.cache/mlx-bun/mtp-cohort-validation`.
1543
- Evidence: `reports/qwen38-closeout/composition-baseline/prefill-cohort/mtp-cohort*`.
1544
- The integration patch and source hashes are recorded in
1545
- `mtp-cohort-integration.patch` and `mtp-cohort-integration-manifest.json`
1546
- under that evidence directory. Native logs identify which test expansion
1547
- ran; earlier serving checks did not include arrival during target prefill.
1548
-
1549
-
1550
- A deferred compact row snapshot retained its full parent batch until evaluated.
1551
- Resolving target snapshots before RAM retention recovers the concurrent loss
1552
- in two M4 runs while preserving every shared-prefill response and full usage
1553
- record. Failed tile-size, input-evaluation and residency experiments are closed.
1554
-
1555
- The adopted implementation moves snapshot materialization into the
1556
- RAM prefix-cache implementation. A backend operation resolves target state and
1557
- opaque companion tensors before storage accepts ownership. It performs no
1558
- serialization or disk I/O; eventual SSD persistence keeps its existing queue.
1559
- Ordinary decode, MTP and lookup publish through the same interface and need no
1560
- method-local evaluation rule. Evaluation failure leaves ownership with the
1561
- publisher. The regression test measures retained allocation before any cache
1562
- read and covers both target and companion rows. Both-machine suites/typechecks, composed native checks and the matched M4
1563
- comparison pass. Main integration suite, typechecks and hygiene pass. Strict B1/long-prefill
1564
- acceptance and remaining feature combinations are still open. Numbers belong in
1565
- benchmarks.md; source and validation records are in
1566
- `mtp-cohort-cache-boundary-manifest.json` under the cohort evidence directory.
1567
-
1568
-
1569
- ### Full-attention delayed compression during padded prefill
1570
-
1571
- The integrated full-attention implementation provides the existing padded-prefill
1572
- port for delayed affine and TurboQuant rows. Each row keeps physical write
1573
- positions, valid token coverage and pending finalization separately. Conversion
1574
- uses valid coverage; codec tensors retain their physical alignment. When all
1575
- rows share an encoding, the packed layout inherits pending valid ends. Finalizing
1576
- and retiring rows move encoded planes without re-encoding live values. The same
1577
- row wrapper and transition lifecycle serve affine and TurboQuant; model methods
1578
- and scheduling are unchanged.
1579
-
1580
- The extended pinned cache oracle passes on both Macs with KV4/KV8 and TQ K8/V3
1581
- and K4/V4, multiple conversion thresholds, cached history, shortened prefill,
1582
- retirement/reorder, continuation, and leading padding across multiple chunks.
1583
- It checks masks, positions, retained encoded bytes and same-B attention. Invalid
1584
- padding bytes are not state: moving encoded zero padding can preserve a different
1585
- signed zero than encoding it again, while retained values and attention remain
1586
- exact. Existing unpadded row/cohort regressions pass on M1. The Gemma model gate
1587
- exposes a before-conversion mismatch when affine attention runs uniform BF16
1588
- rows separately. The candidate now assembles those tensors through the same
1589
- helper as TurboQuant and runs attention at the model's B. The cache oracle
1590
- passes after that change, as does the Gemma affine full-model gate. Both
1591
- regression suites and typechecks pass. Affine MTP/lookup and delayed affine/TQ
1592
- seeded serving pass on M1; TQ MTP serving passes on M4. Generated MTP3/B4
1593
- RAM/SSD continuations pass on both Macs. All four matched M4 arms preserve responses and usage. The measured small
1594
- throughput decrease is recorded in benchmarks.md; strict B1/long-prefill
1595
- acceptance remains open. Main integration suite/typechecks pass. Candidate:
1596
- `/Users/joshrossi/.cache/mlx-bun/full-delayed-prefill-validation`; evidence:
1597
- `reports/qwen38-closeout/composition-baseline/full-delayed-prefill/`.
1598
-
1599
-
1600
- ### Standalone autoregressive draft graph integration
1601
-
1602
- The integrated implementation runs a standalone draft graph at the group's B and
1603
- hands its log-probabilities to the existing row sampler. Immutable batch
1604
- snapshots retain accepted recurrent/attention boundaries without changing
1605
- scheduler policy. Full acceptance retains the last draft token as companion
1606
- state and re-feeds it with the next target token in one forward, preserving
1607
- the reference's two-token shape. Mixed one-/two-token feeds use the existing
1608
- padding interface. Zero-proposal rounds preserve processed-token coverage.
1609
-
1610
- The lifecycle test covers mixed acceptance, removal/reordering, zero proposals
1611
- and mixed re-feed lengths. A native Qwen 0.8B gate on M1 checks exact B1
1612
- attention/recurrent state and continuation at depths 0, 1, 3 and 4. The provider is integrated; its old single-request source now
1613
- adapts the same draft engine at B1. The small Qwen target/drafter passes B1/B4
1614
- serving, late joins, consumer failures, seeded sampling/logprobs and HTTP
1615
- coverage. Companion state reuses backend cache codecs; asynchronous prefill
1616
- completion now preserves target-context ownership. The 27B target with the 0.8B
1617
- standalone drafter passes B1/B4 serving on both Macs (228 assertions each),
1618
- and depth-three TurboQuant generation restores through RAM and cold SSD
1619
- (33 assertions each). The M4 pinned Python oracle passes the Llama 3B/1B
1620
- pair, including four templated cases at draft depths two and three, grammar
1621
- and stopping. Full suites pass on M1/M4 (2,153/2,157 tests), as do all
1622
- typechecks and the existing MTP/lookup native regressions. The first matched M4 comparison improves B1 decode and cache reuse but
1623
- hits a GPU allocation failure in the four-request phase, including the
1624
- benchmark’s retry. The allocation trace explains why short correctness
1625
- coverage did not expose this workload failure. Replaying
1626
- all prior requests reproduces the failure with 4,005 MiB of accounted RAM
1627
- cache entries and a 17,987 MiB observed allocation peak. A fresh aggregate
1628
- passes. The retained entries consume the remaining working-set headroom;
1629
- the repeated comparison holds both arms at 2 GiB RAM plus SSD and completes
1630
- all four arms. Decode/cached/aggregate gains and cold/startup costs are
1631
- recorded in benchmarks.md. Main integration suite, typechecks and hygiene pass. The retained 4 GiB failure remains part of cache-settings acceptance.
1632
- Validation source:
1633
- `/Users/joshrossi/.cache/mlx-bun/two-model-batch-validation`; source hashes and
1634
- checks: `reports/qwen38-closeout/composition-baseline/two-model-batch/`.
1635
-
1636
- ### Full-attention target capability binding
1637
-
1638
- The integrated implementation moves the shared target-layout factory behind
1639
- one storage capability binding. Grouped speculation then depends on the
1640
- model graph's batch support and the storage transaction factory, replacing
1641
- the Qwen-only target selection. Scheduler and draft-provider policy are
1642
- unchanged; quantized speculation retains its existing qualification while
1643
- plain full-attention targets are tested. On M1, Llama 3B with the same model
1644
- as drafter passes shared B1/B4 serving, late joins, retirement, seeded
1645
- sampling/logprobs and grammar (224 assertions). Prompt lookup passes the
1646
- same serving harness; generated RAM/SSD continuation passes at depth three.
1647
- The pinned Llama model oracle is exact at B1/2/3/4/8 across padding, chunks
1648
- and continuation (541 assertions), and mixed-prefix target rollback passes
1649
- 1,352 state/continuation assertions. The complete M1 suite and typechecks pass.
1650
- Both-machine Llama and MiniCPM serving/lookup/cache checks pass, as do the
1651
- wide same-B oracle, Qwen MTP/TQ regressions, full suites and typechecks.
1652
- The M4 shared B1 path also passes the pinned templated speculation oracle.
1653
- Four matched M4 arms complete; gains, cold-prefill cost and the changed
1654
- concurrent response are recorded in benchmarks.md. Main integration suite, all typechecks and full hygiene pass. Rotating target transactions remain the next storage gap.
1655
- Validation source:
1656
- `/Users/joshrossi/.cache/mlx-bun/full-kv-speculation-validation`;
1657
- evidence: `reports/qwen38-closeout/composition-baseline/full-kv-speculation/`.
1658
-
1659
-
1660
- ### Rotating target transactions
1661
-
1662
- The rotating-storage adapter now exposes independent speculative acceptance
1663
- through the existing target transaction interface. It reuses the existing
1664
- ring kernels and keeps the verification rectangle: rejected suffixes move
1665
- into masked left padding. Contracting the rectangle preserved KV columns
1666
- but failed Gemma continuation equality; that candidate was replaced.
1667
- Published rows contain the newest accepted window; ordinary ring extraction
1668
- retains its pinned-oracle oversized-block behavior. No scheduler change.
1669
-
1670
- Tagged storage windows, cloning, retirement and subsequent writes pass.
1671
- Both-machine Gemma 12B lookup serving passes at B1/B4, including late joins,
1672
- consumer failures, grammar, seeded sampling/logprobs and HTTP coverage.
1673
- Both-machine Gemma target transactions pass 3,080 assertions across prompts
1674
- straddling the actual 1,024-token window, unequal acceptance and continuation.
1675
- Both-machine pinned padded-model oracle passes at B1/2/3/4/8, and
1676
- standalone draft generation restores through RAM/cold SSD. The default
1677
- lookup depth of ten exposed a singleton geometry difference; an unwrapped
1678
- single row now retains its scalar prefix geometry without adding padding.
1679
- The depth-ten serving gates now pass for Gemma 12B and e4b on both Macs.
1680
- Both final suites/typechecks pass, alongside the Qwen MTP/TurboQuant regression.
1681
- The final-source matched M4 comparison completes: concurrent throughput and
1682
- cache reuse improve, while B1 decode is effectively equal. Startup variation
1683
- and different concurrent trajectories are recorded in benchmarks.md.
1684
- The adapter is integrated; main integration suite, all typechecks and full hygiene pass. Quantized rotating
1685
- speculation and the remaining providers are still open.
1686
- Validation source: `/Users/joshrossi/.cache/mlx-bun/rotating-speculation-validation`.
1687
- Evidence: `reports/qwen38-closeout/composition-baseline/rotating-speculation/`.
1688
-
1689
-
1690
- ### Assistant draft rows
1691
-
1692
- The assistant provider now supplies the same prefill and decode row interfaces
1693
- as standalone drafting. It retains only the last true target hidden and its
1694
- processed-token count. The target layout supplies captured donor attention;
1695
- full and rotating layouts own valid intervals and masks. The draft graph does
1696
- not know the scheduler or cache geometry. Matching layers and draft steps reuse
1697
- the same donor mask. All proposals remain on device until the chain completes.
1698
- Generated checkpoints store the hidden companion alongside existing target KV,
1699
- using the same RAM ownership and queued SSD persistence.
1700
-
1701
- Both e4b centroid and 12B tied heads use one graph at B1/B2/B4. The pinned
1702
- optiq loader assumes centroid presence from config; the 12B numerical test binds
1703
- its actual tied embedding head by tensor presence while retaining upstream
1704
- layers. The e4b end-to-end test uses unmodified optiq `spec_generate`. Both
1705
- machines pass these checks, serving/retirement/late joins, grammar/logprobs and
1706
- generated RAM/SSD continuations. Tagged donor columns independently verify
1707
- padding, rejected suffixes, wrap and row removal. The old contiguous transposed
1708
- weights caused a hidden-state mismatch on M4 even before this refactor. Keeping
1709
- upstream's strided weight views restores exactness and avoids duplicate weights.
1710
-
1711
- The candidate is integrated after both regression suites/types and the four-arm
1712
- M4 comparison. Main integration suite, all typechecks and full hygiene pass. Throughput and cached
1713
- latency improve; readiness and concurrent-trajectory differences remain recorded
1714
- in benchmarks.md. DeepSpec follows through projected context rows.
1715
- Evidence: `reports/qwen38-closeout/composition-baseline/assistant-batch/`.
1716
-
1717
-
1718
- ### Shared DeepSpec context rows
1719
-
1720
- DeepSpec's context projections and block graph now accept the active row count.
1721
- The MLX projected-context owner reuses full-attention row storage, cache codecs
1722
- and coordinated membership. Its attention port hides context geometry and
1723
- validity from the block graph. The provider declares its target taps and stores
1724
- projected context as the ordinary method companion in RAM and queued SSD.
1725
- Accepted lengths advance each row independently; rejected context never survives
1726
- in a published checkpoint. The sequential proposal head stays on device through
1727
- the block, and serving skips unconsumed confidence values when truncation is off.
1728
-
1729
- Synthetic tests cover bf16/quantized projections, confidence truncation through
1730
- zero proposals, differing commit lengths, restoration and retirement. Real Gemma
1731
- 12B serving/generated RAM/SSD checks pass with both DeepSpec bf16 and its affine
1732
- 4-bit sibling on both Macs, including the trained seven-token block. Native B1
1733
- full logits and confidence match the frozen implementation; B2/B4 repeat exactly
1734
- with independent positions. These comparisons are regression controls, not a new
1735
- external DeepSpec oracle: the existing Torch oracle requires a separate reference
1736
- environment and bf16 target that are not currently installed on either laptop.
1737
- Real-window checks confirm that shared speculation remains active across wrap
1738
- while the previous serial path stops drafting there. Both-machine regression
1739
- suites and all typechecks pass. Four matched M4 arms improve aggregate throughput
1740
- and cached latency; startup cost and one changed concurrent response remain
1741
- recorded in benchmarks.md. The candidate is integrated; the main regression
1742
- suite, all typechecks and full hygiene pass. DSpark/DFlash is next and shares the projected-context lifecycle.
1743
- Evidence: `reports/qwen38-closeout/composition-baseline/deepspec-batch/`.
1744
-
1745
-
1746
- ### Shared DSpark/DFlash provider
1747
-
1748
- The draft graph accepts B1 and B>1 through one backbone and sequential proposal
1749
- head. Markov and RNN heads retain their existing numerical behavior. Confidence
1750
- can retire proposals independently for each row, and sampled proposal streams
1751
- remain independent. Serving avoids confidence and float-logit outputs it does
1752
- not consume. Scheduling does not select a draft implementation.
1753
-
1754
- DSpark and DeepSpec use the same projected-context lifecycle. The provider binds
1755
- projection and proposal operations; the shared owner handles prefill, accepted
1756
- lengths, row membership, capture and restoration. The MLX context store applies
1757
- padding and attention validity. Generated context enters the existing RAM cache
1758
- and queued SSD persistence as a method companion.
1759
-
1760
- Pinned graph outputs and both-machine Markov/RNN real-Gemma serving/generated
1761
- RAM/SSD tests pass. Those tests use seeded draft weights, so they establish
1762
- execution and state contracts, not trained acceptance or quality. Josh confirms
1763
- that no DSpark/DFlash checkpoint has been trained yet. DeepSpec's real trained provider passes again
1764
- after the extraction. Full suites and all typechecks pass on both Macs. Four M4
1765
- arms measure the extraction using the same trained DeepSpec checkpoint and
1766
- preserve all responses and usage. The measured throughput cost is recorded in
1767
- benchmarks.md. The provider is integrated; the main suite, all typechecks
1768
- and full hygiene pass. Trained DSpark performance qualification remains open. Evidence:
1769
- `reports/qwen38-closeout/composition-baseline/dflash-batch/`.
1770
-
1771
-
1772
- ### Affine rotating speculation and encoded assistant donors
1773
-
1774
- Uniform start-zero KV4/KV8 target layouts now compose with grouped draft
1775
- providers on full-attention and rotating targets. Acceptance bookkeeping is
1776
- shared by plain and encoded rings. Packed values, scales and biases use the
1777
- same row operations; rollback, retirement and snapshots never dequantize.
1778
- Ordinary rotating extraction retains the oracle's oversized-block geometry;
1779
- committed speculative snapshots retain only the newest valid window.
1780
- Unpadded verification keeps the existing fused attention mask dispatch.
1781
- Standard universal attention and MiniCPM now consume encoded cache ports
1782
- instead of assuming plain arrays or a concrete affine cache class. The
1783
- universal softcap branch retains its existing plain-only qualification.
1784
-
1785
- Assistant drafting consumes an opaque donor-attention interface. Storage owns
1786
- the encoded planes and validity ranges; the assistant only supplies queries.
1787
- Its compatibility source now adapts the same row graph at B1. The redundant
1788
- plain-only AssistantTarget interface is removed. Additive donor masks follow
1789
- the pinned affine attention operation, independently of boolean cache masks.
1790
-
1791
- Native continuation controls must hold both batch size and physical attention
1792
- width fixed. The pinned Python oracle reproduces the captured different-width
1793
- full-attention results byte for byte: one bf16 output differs despite identical
1794
- queries and valid KV. The transaction test pads only the reference's fetched
1795
- views to the compared width, retaining exact state and hidden checks.
1796
- The old executor disables speculation near the rotating boundary, so its KV4
1797
- text can differ from active shared speculation. Live-window tests record that
1798
- comparison and check repeated active execution; they do not relabel ordinary
1799
- fallback as an exact speculative oracle.
1800
-
1801
- Both-machine KV4/KV8 serving and generated RAM/SSD checks pass for DeepSpec,
1802
- assistant, standalone and lookup providers; seeded DSpark heads pass the same
1803
- state contracts. Real-window transactions, same-width target controls, pinned
1804
- regressions, full suites and all typechecks pass. Six M4 benchmark arms preserve
1805
- bf16 responses and usage with effectively unchanged throughput. KV4 improves
1806
- aggregate throughput on this workload, with different generated text and more
1807
- prefill time; the comparison is recorded in benchmarks.md. The change is
1808
- integrated; the main suite, all typechecks and full hygiene pass.
1809
- Evidence: reports/qwen38-closeout/composition-baseline/rotating-spec-quant/.
1810
- Strict performance dominance and removal of the serial control remain open.
1811
-
1812
-
1813
- ### TurboQuant donor attention and grouped draft composition
1814
-
1815
- Solo, packed and delayed TurboQuant storage expose the same owned donor-attention
1816
- interface as plain and affine caches. Captures preserve physical columns, row
1817
- validity and right-prefill padding without appending tokens, converting precision
1818
- or advancing membership. Assistant attention consumes eagerly decoded values,
1819
- preserving its existing operation order. MiniCPM retains its explicit deferred
1820
- value rotation. Storage chooses the existing codec and fused decode kernels;
1821
- scheduling does not identify the encoding or draft provider.
1822
-
1823
- The gateway advertises grouped TurboQuant from the bound target and provider
1824
- capabilities. Positive conversion thresholds use the existing row-local
1825
- transition lifecycle; sliding layers remain bf16. Per-layer affine speculative
1826
- configuration composes through the same bound row capabilities. Group binding now snapshots per-layer and
1827
- TQ options so caller mutation cannot change a live graph or its persisted
1828
- identity. Explicit per-layer schemes get distinct group/cache keys, with
1829
- canonical layer ordering; absent/empty schemes preserve existing keys exactly.
1830
-
1831
- The isolated candidate passes independent K8/V3 donor codec plus pinned assistant
1832
- graph controls at B1/B2/B4 with fused decoding on/off for both Gemma assistant
1833
- artifacts on the M4, alongside the unchanged bf16 oracle. M1 assistant and
1834
- MiniCPM lookup pass start-zero serving/generated RAM/SSD and delayed conversion,
1835
- retirement and late admission. Both-machine assistant, DeepSpec, standalone and lookup serving/generated
1836
- RAM/SSD, delayed conversion and late admission pass, along with seeded DSpark
1837
- heads, Qwen regressions, final suites and typechecks. Twelve M4 timing arms
1838
- record both source-only costs and TQ configuration tradeoffs in benchmarks.md.
1839
- The source is integrated; main suite, all typechecks and full hygiene pass.
1840
- Evidence: `reports/qwen38-closeout/composition-baseline/turboquant-donor/`.
1841
-
1842
- The independent assistant control also exposed an existing centroid-head
1843
- numerical shortcut on M1: matrix multiplication did not preserve the oracle's
1844
- bf16 multiplication followed by reduction. On identical B2 hidden state, Python
1845
- reproduced the old candidate token with matmul and the oracle token with explicit
1846
- multiply/sum. The shared head now preserves that operation order at every B and
1847
- selects the lowest vocabulary ID among tied candidate scores, matching sparse
1848
- vocabulary argmax without materializing a full-vocabulary logits buffer. The
1849
- independent codec, hidden-state and token assertions remain exact. The M1 e4b
1850
- control and unmodified end-to-end optiq generation pass after the correction;
1851
- final both-machine regressions pass. The source-only and TQ timing comparisons
1852
- are recorded in benchmarks.md. Attribution repeats recover the prior decode rate on the unchanged candidate, so they do not establish a code regression. Captured diagnostics live
1853
- with the composition evidence.
1854
-
1855
-
1856
- ### Delayed and per-layer affine rotating speculation
1857
-
1858
- Rotating storage owns an accepted-column transaction shared by plain and affine rings. The generic transition interface invokes that transaction while rows have different precisions; after conversion it packs them into the existing affine layout. Conversion sees only committed tokens. Scheduling and draft providers do not inspect the quantization scheme.
1859
-
1860
- Full and rotating caches expose owned donor-attention views. Mixed rows retain their physical columns, valid offsets and padding; assistants submit queries and masks without materializing a common decoded cache. Pending right-prefill padding is excluded before finalization, including when a physical block exceeds the sliding window. Captures survive cache conversion, row reordering and disposal.
1861
-
1862
- The gateway now advertises supported affine configurations from the target/provider binding, including model-defined per-layer schemes and positive library thresholds. The served CLI schemes retain start zero. Independent attention fixtures cover padding and ownership; exact target hidden/logit controls cross the real sliding boundary at verification widths one and eight. Native checks cover continuation, precision transitions, unequal retirement, late admission, grammar/logprobs and generated RAM/SSD companions. Legacy serving rejects some of these combinations; repeated shared execution is not relabeled as a legacy speculative oracle.
1863
-
1864
- Both-machine provider/native/suite/typechecks and six matched M4 benchmark arms are complete. Bf16 responses and usage remain exact; model-defined per-layer KV trades higher single-request decode for lower aggregate throughput on the measured workload. The change is integrated; main suite, types and full hygiene pass. Evidence: `reports/qwen38-closeout/composition-baseline/delayed-rotating-spec/`. Defaults and the serial retirement criterion remain unchanged.
1865
-
1866
-
1867
- ### GLM compressed target and native MTP rows
1868
-
1869
- The integrated implementation adds independent accepted-column transactions and cold-row membership to compressed MLA/DSA storage, without reconstructing full K/V. Native MTP exposes a shared numerical graph and a grouped provider. Its prompt prefill retains the target anchor while leaving MTP cache empty; committed accepted positions are rebuilt from true target hidden states. Target token coverage and the shorter draft-cache offset remain separate checkpoint metadata. The existing attachment interface persists both through the generic RAM/SSD cache.
1870
-
1871
- Both-machine tiny-model controls cover exact B1 proposals/absorption, zero-proposal rounds, streamed expert calls, B4 membership with DSA enabled, late admission, retirement, generated-prefix identity and identical RAM/SSD next drafts. DSA verification retains every request row and shares each logical query selection across FULL/SHARED layers. Compressed multi-token attention now masks physical left padding, including native MTP absorption. Empty DSA rows survive filtering and resumed writes. Six two-layer dense/sparse boundary cases per machine match independent pinned Python/MLX hidden and every cache plane byte-for-byte. Python uses the uncompiled sigmoid/multiply activation that this model graph calls. Different-B controls retain greedy IDs but can differ in small f32 values because physical widths select absorbed/reconstructed attention and different matrix kernels; they are not the exact numerical oracle. Both full model-free suites, the SSD suite and all typechecks pass. The tested implementation is integrated in the PR. Josh has deferred final GLM-5.2 Colibri artifact testing. This does not establish real-artifact numerical or performance acceptance. Evidence: `reports/qwen38-closeout/composition-baseline/mla-transaction/`.
1872
-
1873
-
1874
- ### Paged storage through shared row and request-state interfaces
1875
-
1876
- `PagedKvRows` owns per-request block pools, immutable snapshots, gathering, masks, offsets and row membership. The existing executor binds it through the cache-layout interface. `MlxRequestStatePolicy` supplies compatible state construction and a prompt-cache port; paging selects no prefix store. Switching back to ordinary requests restores their cache port. The same paging constructor serves the compatibility entry point and shared prefill. B1 keeps the existing paged numerical graph; B>1 gathers padded attention inputs while retaining separate persistent pools. Gemma12 full sampled logits and HTTP choices match the ordinary same-B controls, including seeded sampling, logprobs and grammar. Both-machine model-free, SSD and typechecks pass. The native storage comparison fixes cohort membership and snapshot boundaries on both sides, covering small blocks and the default block boundary; otherwise cache publication changes the numerical prefill graph. The matched M4 source comparison is effectively flat, and shared paging improves concurrent throughput versus serial while retaining effectively equal B1 decode. Concurrent text can differ across batch geometries; the exact storage oracle compares the same geometry. Timing and limitations live in benchmarks.md; evidence is `reports/qwen38-closeout/composition-baseline/paged-rows/`. Subsequent C1–C5 work adds shared SSD blocks and optional direct bf16/affine paged attention; its measured results are recorded in benchmarks.md. TQ pages and paged speculation remain separate extensions.
1877
-
1878
- The sampler now exposes an optional stateless operation for compatible independent score rows. Ordinary decode and speculative verification share normalized-score argmax. Verification can sample a rectangle in one device operation and readback; acceptance still consumes only each request's valid positions. Grammar, processors, stochastic sampling, logprob capture and custom sampler functions retain their request-local operation. Same-shape numerical tests include rounding-created ties and large vocabularies; both-machine native/generated RAM/SSD and suites pass. Twelve matched M4 arms preserve ordinary throughput and improve lookup aggregate throughput; startup and trajectory limits are recorded in benchmarks.md. Lookup defaults remain unchanged. Evidence: `reports/qwen38-closeout/composition-baseline/independent-sampling/`.
1879
-
1880
-
1881
- The affine same-B oracle now covers full-attention and rotating mixed-precision
1882
- rows, including unequal lengths, window crossing, reordering and retirement.
1883
- Equal-length rotating storage uses the pinned OptiQ implementation. Padded
1884
- rotating cases compose it with stock mlx-lm batch positions and masks; they do
1885
- not substitute solo-row logits for the batch oracle. The test-only reference
1886
- and its limits are described in [benchmarks.md](../reference/benchmarks.md#same-batch-rotating-affine-oracle).
1887
- Existing delayed-conversion, provider and lifecycle tests remain separate evidence.
1888
-
1889
- ### Campaign checkout consolidation
1890
-
1891
- The PR incorporates the accepted implementations from the M1 and M4 campaign
1892
- checkouts, including ordinary resume, adapter-aware resume and GLM shared MTP
1893
- state. Older TQ, provider, cache and prefill candidates are superseded by the
1894
- integrated compositions above. Benchmark baselines and rejected wider-Trellis,
1895
- option-capture, state-only replay and cohort scheduling variants remain
1896
- experiment records; copying them over the selected implementation would undo
1897
- later work. The singleton `copyOf` to `contiguous` candidate remains unpromoted
1898
- because its attribution and startup result are unresolved.
1899
-
1900
- The 80 secondary Git worktrees and 99 campaign source copies were removed
1901
- after their source files and reports were verified in a content-addressed local archive under
1902
- `reports/pr-closeout/checkout-archive/`, with per-checkout manifests, SHA-256
1903
- objects and the recorded base commit. The PR retains the implementation and
1904
- findings; bulky raw experiment data stays local under the repository policy.
1905
-
1906
-
1907
- ### Scheduling reference algorithms and request observations
1908
-
1909
- The next scheduling baseline is an iteration token budget, following the
1910
- upstream implementations below. Review date: 2026-09-12. Read implementation
1911
- order as well as configuration docs; scheduling first does not necessarily
1912
- mean delivering output before the iteration's other GPU work finishes.
1913
-
1914
- | Reference | Algorithm and ownership | Application here |
1915
- |---|---|---|
1916
- | [vLLM V1 scheduler, `dff76bc`](https://github.com/vllm-project/vllm/blob/dff76bc3e8d702901e6dda5971f9506a6c1bc00f/vllm/v1/core/sched/scheduler.py#L562) | Tracks computed tokens against required tokens, including speculative positions. Allocates the iteration budget to running requests before waiting requests. Prefill chunks and decode tokens can enter the same execution batch. | Represent remaining work through the scheduling interface; methods supply valid token work and the backend owns batch packing and kernels. |
1917
- | [llama.cpp server, `3057bb6`](https://github.com/ggml-org/llama.cpp/blob/3057bb66c86c46d5781e50e85462a760ba7d1feb/tools/server/server-context.cpp#L2974) | Adds generating/drafting slots before filling the remaining batch capacity with compatible prompt tokens. Logical batch and physical microbatch capacities are distinct. HTTP workers own parsing, templates and streaming; one inference thread owns slots. | Preserve execution ownership while allowing request preparation and response transport to progress independently. A shared request count is insufficient; account for token work too. |
1918
- | [mlx-lm generator, `dcbcf78`](https://github.com/ml-explore/mlx-lm/blob/dcbcf786c0cf56f9a12fabe9468c887781431ae2/mlx_lm/generate.py#L1783) | Advances generation first, admits/splits prompt rows, then processes bounded prompt chunks. Prompt and generation batches remain separate. Responses return after prompt processing, whose cache evaluation synchronizes. The pinned oracle has the same order. | This is the closest numerical/backend reference, but its response timing is not evidence that output is immediately flushed. Compare its actual request timeline. |
1919
- | [LangChain streaming](https://docs.langchain.com/oss/python/langchain/streaming) | Routes model-provided text, reasoning and tool-call chunks through the agent application. | Reference for stream semantics; this layer does not select GPU prefill/decode work. |
1920
-
1921
- [vLLM's tuning guidance](https://docs.vllm.ai/en/latest/configuration/optimization/#chunked-prefill)
1922
- explicitly treats token budget as a latency/throughput tradeoff. Its GPU-specific
1923
- budget recommendations are not Apple Silicon defaults. Start with the established
1924
- algorithm, measure the same submitted work on M4, and retain deviations only
1925
- with recorded benefits and costs. No framework guarantees one universally best
1926
- chunk size or latency policy.
1927
-
1928
- The established backend exposes `advance()` and `advancePreparation()`
1929
- operations. `MlxPrefillRows` groups prompt rows, while ordinary and speculative
1930
- methods advance decoding rows separately. The existing preparation budget limits
1931
- cohort admission; it is not vLLM's combined per-iteration token budget. True mixed
1932
- execution therefore needs backend work as well as scheduling policy. Padding a
1933
- single decode token to a long prompt's width would defeat the intended saving.
1934
-
1935
- Phase 18 S1b adds bounded mixed work. The method retains request identity,
1936
- computed/required positions and candidate state. Scheduling allocates
1937
- bounded work to active requests and then queued requests; the backend executes
1938
- compatible selections. Sampling and cache ownership remain unchanged. Acceptance
1939
- covers lone requests, arrivals during prefill and decode, unequal lengths,
1940
- cancellation/retirement, speculative work and quantized state. Compare first
1941
- semantic output, inter-output gaps, total workload time, occupancy, padding and
1942
- memory alongside aggregate throughput. Same-B numerical contracts remain binding.
1943
-
1944
- The P2R observer now separates row preparation, forward, remaining cache
1945
- execution, maintenance, checkpoint capture, projection, completion and companion
1946
- work. Shared work IDs and process-local time origins allow cross-request
1947
- attribution without counting shared GPU work several times. Bounded initial
1948
- routing spans expose hidden control tokens. Measurement semantics and the report
1949
- command live in [server-config](../reference/server-config.md); measured results
1950
- live in [benchmarks](../reference/benchmarks.md#prefill-observation-and-scheduling-screen).
1951
- The simple active-before-prefill experiment remains an unadopted patch in the
1952
- local report directory; it is not the mixed execution design above.
1953
-
1954
- A second bounded screen submits shared prefill evaluation asynchronously and
1955
- polls MLX completion while allowing Bun to service I/O. MLX's
1956
- [`array::is_available`](https://github.com/ml-explore/mlx/blob/main/mlx/array.cpp)
1957
- checks the completion event, not merely graph submission; the pinned native
1958
- pack exports mlx-c's `_mlx_array_is_available`. This avoids holding the host
1959
- thread during the GPU wait, but changes when requests become visible to the
1960
- existing scheduler. Both M1 MiniCPM arm orders delay the first staggered
1961
- request substantially; aggregate results are inconsistent. The candidate is
1962
- removed. [Measurements](../reference/benchmarks.md#nonblocking-prefill-completion-screen)
1963
- retain the excluded patch. Host responsiveness and request scheduling must be
1964
- measured together before retaining a different completion boundary.
1965
-
1966
-
1967
- #### Mixed token execution
1968
-
1969
- The Lab Gemma and Qwen3.5/3.8 paths execute running decode and queued prompt work together.
1970
- `ExecutionGroup.mixedPreparation` reports running-token demand and the minimum
1971
- prompt work needed for progress. The scheduler reserves that demand and assigns
1972
- the remaining iteration budget to preparation. Existing cohort admission still
1973
- accounts for total prompt work. Removing that limit was measured and rejected:
1974
- on this M4 workload, larger cohorts lost throughput and delayed later first
1975
- outputs. Both variants execute the same bounded mixed-token model port.
1976
-
1977
- `MlxPreparationWork` carries a token allowance and forward operation into the
1978
- shared preparation driver. It limits actual tokens across rows while preserving
1979
- planned precision conversions and checkpoint endpoints. `runMixedTokenIteration`
1980
- collects the preparation and decode forward inputs. The model's `MixedTokenModel`
1981
- port receives independent `TokenGroup` inputs with their existing row geometry
1982
- and caches. Decode finishes sampling, publication and retirement before a
1983
- completed preparation can join the active rows. A restored or cancelled
1984
- preparation can finish without producing model work; an exhausted decode can
1985
- publish its pending token without another forward.
1986
-
1987
- Gemma reuses its existing attention and feed-forward layer interfaces. Attention
1988
- retains each group's RoPE positions, masks, SDPA shape and cache operations.
1989
- `mapPackedTokens` concatenates only real tokens for the feed-forward block and
1990
- restores each group's output shape afterwards. It adds no padding or host
1991
- readback. This is shared feed-forward execution, not a fully packed attention
1992
- kernel. The unpacked control keeps the same scheduler budget to isolate the
1993
- packing benefit. The original single-group model call remains unchanged.
1994
-
1995
- Packing can change GEMV/GEMM dispatch relative to solo generation. The numerical
1996
- contract therefore uses the pinned oracle with the same packed feed-forward
1997
- geometry and independent attention shapes. The real-model test passes on M4 for
1998
- Gemma e4b, 12B and 26B with bf16 and uniform affine KV4, continuation, and unequal
1999
- decode/prefill row counts. The
2000
- uniform-KV oracle explicitly uses stock quantized SDPA on both sides. Native
2001
- state tests also cover bounded preparation with immediate and delayed affine
2002
- and TurboQuant conversion. Failure, cancellation and retirement tests exercise
2003
- the shared execution group and the work coordinator.
2004
-
2005
- Qwen reuses its attention/DeltaNet and feed-forward blocks through the same
2006
- model port. Masks, convolution tails and recurrent state remain per group;
2007
- layer evaluation materializes both residual and cache outputs to bound prefill
2008
- memory. Each `TokenGroup` can supply a borrowed layer-capture callback and
2009
- request preservation of its matmul geometry. Speculative methods use those
2010
- options for target verification, including pending tokens and draft candidates,
2011
- and keep provider companion updates in their preparation/verification adapters.
2012
- The scheduler sees the method’s target-token demand, not its draft algorithm.
2013
- Hidden captures belong to each work item instead of a shared mutable model tap.
2014
-
2015
- The measured default decision is to retain mixed execution as opt-in. Gemma
2016
- first-output latency improves with an aggregate-throughput cost; Qwen MTP has
2017
- no first-output win and loses aggregate throughput, despite a shorter worst
2018
- streaming pause. This closes the bounded S1b comparison. Trace phase
2019
- `engine.mixed_forward` records the model-call construction span and real token
2020
- counts; GPU waits remain at the existing evaluation/readback boundaries. The
2021
- [measured comparison](../reference/benchmarks.md#mixed-prefill-and-decode-token-work)
2022
- records latency, throughput, streaming gaps and the unpacked control. Runtime
2023
- settings live in [server-config](../reference/server-config.md).
2024
-
2025
-
2026
- ### Grammar candidates through the shared verifier
2027
-
2028
- `GrammarController.proposeTokens` reads xgrammar's forced continuation without
2029
- advancing the matcher. It joins the existing WASM queue so concurrent requests
2030
- cannot overlap calls into the single WASM instance. A request-owned constraint
2031
- port supplies candidates to the same grouped verifier used by learned drafts
2032
- and prompt lookup. The sampler applies each request's mask and commits its
2033
- accepted tokens; scheduling only sees method work. Target graph binding no
2034
- longer requires a serial draft constructor.
2035
-
2036
- The constraint provider reuses prompt lookup's committed-history ownership and
2037
- checkpoint format. It never searches history and has no serial draft method.
2038
- Empty candidate lists use the verifier's existing one-token step. Candidate
2039
- rejection and row retirement keep target and companion state aligned through
2040
- the existing transaction and output interfaces.
2041
-
2042
- `MLX_BUN_GRAMMAR_JUMP=1` selects this method for eligible shared structured-output
2043
- requests without a configured drafter; `MLX_BUN_GRAMMAR_DRAFT_TOKENS` controls
2044
- depth separately from batching. Shared logprobs remain available because every
2045
- output is sampled. Explicit serial retains direct retokenized jump-forward.
2046
- That algorithm skips target sampling for forced spans; the shared proposal
2047
- algorithm verifies them. Both remain opt-in and must be compared separately.
2048
-
2049
- The focused MiniCPM and packed-Qwen checks cover nonmutating proposals, B1 and
2050
- concurrent requests, independent stopping/cancellation, affine/TurboQuant and
2051
- logprob delivery. Fixed bf16 MiniCPM outputs match ordinary decoding. Affine
2052
- MiniCPM can choose different valid tokenizations across verification widths;
2053
- repeated fixed-configuration outputs match. Same-geometry verifier/KV oracles
2054
- remain the numerical contract. Performance is recorded in benchmarks.md.