@mlx-bun/inference 0.0.0-stage → 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 (467) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1475 -2
  3. package/THIRD_PARTY_NOTICES.md +3404 -0
  4. package/dist/native/libmlx_bun_ane.dylib +0 -0
  5. package/dist/native/libmlx_bun_expert_io.dylib +0 -0
  6. package/dist/native/mlx-bun-frame-extract +0 -0
  7. package/examples/fit-model.ts +25 -0
  8. package/examples/qwen3-forward.ts +42 -0
  9. package/examples/qwen3-generate.ts +28 -0
  10. package/examples/trellis-expand.ts +23 -0
  11. package/package.json +129 -4
  12. package/src/adapters/available.ts +110 -0
  13. package/src/adapters/index.ts +3 -0
  14. package/src/adapters/manager.ts +316 -0
  15. package/src/artifacts/auxiliary-files.ts +45 -0
  16. package/src/artifacts/config-dialects.ts +94 -0
  17. package/src/artifacts/config.ts +341 -0
  18. package/src/artifacts/draft-vocabulary.ts +30 -0
  19. package/src/artifacts/expert-offload-build.ts +75 -0
  20. package/src/artifacts/expert-offload.ts +89 -0
  21. package/src/artifacts/experts/index.ts +3 -0
  22. package/src/artifacts/experts/io.ts +386 -0
  23. package/src/artifacts/experts/residency.ts +1134 -0
  24. package/src/artifacts/experts/usage.ts +415 -0
  25. package/src/artifacts/fingerprint.ts +44 -0
  26. package/src/artifacts/glm52/container.ts +305 -0
  27. package/src/artifacts/glm52/expert-layout.ts +249 -0
  28. package/src/artifacts/glm52/index.ts +7 -0
  29. package/src/artifacts/glm52/kernel-layout.ts +124 -0
  30. package/src/artifacts/glm52/layout.ts +152 -0
  31. package/src/artifacts/glm52/memory.ts +500 -0
  32. package/src/artifacts/glm52/resident-weights.ts +135 -0
  33. package/src/artifacts/glm52/weights.ts +503 -0
  34. package/src/artifacts/glm52-config.ts +262 -0
  35. package/src/artifacts/identity.ts +94 -0
  36. package/src/artifacts/index.ts +15 -0
  37. package/src/artifacts/mmap.ts +90 -0
  38. package/src/artifacts/safetensors-writer.ts +249 -0
  39. package/src/artifacts/safetensors.ts +164 -0
  40. package/src/artifacts/turboquant-fold.ts +42 -0
  41. package/src/artifacts/weight-audit.ts +43 -0
  42. package/src/artifacts/weights.ts +255 -0
  43. package/src/contracts/index.ts +2 -0
  44. package/src/contracts/mlx/attention.ts +8 -0
  45. package/src/contracts/mlx/cache.ts +510 -0
  46. package/src/contracts/mlx/checkpoint.ts +10 -0
  47. package/src/contracts/mlx/draft-checkpoint.ts +14 -0
  48. package/src/contracts/mlx/draft-projection.ts +27 -0
  49. package/src/contracts/mlx/draft-target.ts +84 -0
  50. package/src/contracts/mlx/drafter.ts +176 -0
  51. package/src/contracts/mlx/forward-work.ts +23 -0
  52. package/src/contracts/mlx/glm52-weights.ts +25 -0
  53. package/src/contracts/mlx/graph.ts +309 -0
  54. package/src/contracts/mlx/head.ts +6 -0
  55. package/src/contracts/mlx/index.ts +15 -0
  56. package/src/contracts/mlx/kv-codec.ts +9 -0
  57. package/src/contracts/mlx/media.ts +113 -0
  58. package/src/contracts/mlx/positions.ts +16 -0
  59. package/src/contracts/mlx/row-table.ts +2 -0
  60. package/src/contracts/mlx/trainable.ts +103 -0
  61. package/src/contracts/mlx/whisper.ts +96 -0
  62. package/src/contracts/portable/composition.ts +106 -0
  63. package/src/contracts/portable/denoising.ts +17 -0
  64. package/src/contracts/portable/execution.ts +79 -0
  65. package/src/contracts/portable/generation.ts +103 -0
  66. package/src/contracts/portable/graph.ts +80 -0
  67. package/src/contracts/portable/index.ts +15 -0
  68. package/src/contracts/portable/memory-plan.ts +26 -0
  69. package/src/contracts/portable/object-cache.ts +8 -0
  70. package/src/contracts/portable/prefill.ts +63 -0
  71. package/src/contracts/portable/prefix-cache.ts +33 -0
  72. package/src/contracts/portable/prompt.ts +2 -0
  73. package/src/contracts/portable/resources.ts +25 -0
  74. package/src/contracts/portable/rollback.ts +7 -0
  75. package/src/contracts/portable/sampling.ts +44 -0
  76. package/src/contracts/portable/scheduling.ts +54 -0
  77. package/src/contracts/portable/token-delivery.ts +4 -0
  78. package/src/contracts/portable/tsconfig.json +14 -0
  79. package/src/embeddings/index.ts +1 -0
  80. package/src/embeddings/text.ts +85 -0
  81. package/src/execution/admission.ts +59 -0
  82. package/src/execution/batch-group.ts +1240 -0
  83. package/src/execution/batch-types.ts +225 -0
  84. package/src/execution/cancellation.ts +38 -0
  85. package/src/execution/continuation-request.ts +45 -0
  86. package/src/execution/continuation-types.ts +10 -0
  87. package/src/execution/coordinator.ts +70 -0
  88. package/src/execution/denoising-group.ts +180 -0
  89. package/src/execution/engine.ts +56 -0
  90. package/src/execution/fill-group.ts +505 -0
  91. package/src/execution/fit.ts +184 -0
  92. package/src/execution/gateway-binding.ts +178 -0
  93. package/src/execution/grammar-group.ts +224 -0
  94. package/src/execution/index.ts +18 -0
  95. package/src/execution/methods.ts +75 -0
  96. package/src/execution/mixed-iteration.ts +96 -0
  97. package/src/execution/plan.ts +65 -0
  98. package/src/execution/prefill-cohort.ts +100 -0
  99. package/src/execution/prefill-rows.ts +263 -0
  100. package/src/execution/request-policy.ts +19 -0
  101. package/src/execution/row-sampling.ts +49 -0
  102. package/src/execution/scheduler.ts +92 -0
  103. package/src/execution/session.ts +247 -0
  104. package/src/execution/speculative-group.ts +797 -0
  105. package/src/execution/tasks.ts +44 -0
  106. package/src/generation/autoregressive.ts +840 -0
  107. package/src/generation/bindings/autoregressive.ts +104 -0
  108. package/src/generation/bindings/denoising.ts +60 -0
  109. package/src/generation/bindings/prefill-policy.ts +36 -0
  110. package/src/generation/checkpoint-identity.ts +60 -0
  111. package/src/generation/denoising.ts +85 -0
  112. package/src/generation/diffusion.ts +583 -0
  113. package/src/generation/fill/echo-index.ts +188 -0
  114. package/src/generation/fill/index.ts +3 -0
  115. package/src/generation/fill/proposal.ts +69 -0
  116. package/src/generation/fill/schema-rows.ts +506 -0
  117. package/src/generation/fill/session.ts +464 -0
  118. package/src/generation/fill/tool-boundary.ts +103 -0
  119. package/src/generation/fill-append.ts +58 -0
  120. package/src/generation/generate.ts +44 -0
  121. package/src/generation/grammar-step.ts +39 -0
  122. package/src/generation/index.ts +14 -0
  123. package/src/generation/phase-dispatch.ts +115 -0
  124. package/src/generation/prefill.ts +33 -0
  125. package/src/generation/result.ts +49 -0
  126. package/src/generation/scopes.ts +157 -0
  127. package/src/generation/speculative/acceptance.ts +41 -0
  128. package/src/generation/speculative/adaptive-draft-gate.ts +166 -0
  129. package/src/generation/speculative/bindings/assistant-rows.ts +102 -0
  130. package/src/generation/speculative/bindings/autoregressive-rows.ts +124 -0
  131. package/src/generation/speculative/bindings/binding.ts +106 -0
  132. package/src/generation/speculative/bindings/deepspec-rows.ts +33 -0
  133. package/src/generation/speculative/bindings/draft-target.ts +10 -0
  134. package/src/generation/speculative/bindings/glm52-mtp-rows.ts +128 -0
  135. package/src/generation/speculative/bindings/projected-context-rows.ts +78 -0
  136. package/src/generation/speculative/bindings/projected-draft-rows.ts +95 -0
  137. package/src/generation/speculative/bindings/qwen-mtp-rows.ts +336 -0
  138. package/src/generation/speculative/bindings/two-model-rows.ts +63 -0
  139. package/src/generation/speculative/cache-identity.ts +16 -0
  140. package/src/generation/speculative/draft-kind.ts +149 -0
  141. package/src/generation/speculative/draft-policy.ts +41 -0
  142. package/src/generation/speculative/draft-registry.ts +118 -0
  143. package/src/generation/speculative/draft-vocab.ts +110 -0
  144. package/src/generation/speculative/index.ts +17 -0
  145. package/src/generation/speculative/output.ts +49 -0
  146. package/src/generation/speculative/round.ts +307 -0
  147. package/src/generation/speculative/source.ts +144 -0
  148. package/src/generation/speculative/sources/assistant-source.ts +64 -0
  149. package/src/generation/speculative/sources/deepspec-source.ts +70 -0
  150. package/src/generation/speculative/sources/dflash-source.ts +84 -0
  151. package/src/generation/speculative/sources/dflash2-source.ts +86 -0
  152. package/src/generation/speculative/sources/glm52-mtp-source.ts +66 -0
  153. package/src/generation/speculative/sources/ngram-source.ts +165 -0
  154. package/src/generation/speculative/sources/qwen-mtp-source.ts +154 -0
  155. package/src/generation/speculative/sources/two-model.ts +75 -0
  156. package/src/generation/types.ts +194 -0
  157. package/src/index.ts +20 -0
  158. package/src/input/audio/audiotoolbox.ts +112 -0
  159. package/src/input/audio/decode.ts +133 -0
  160. package/src/input/audio/features.ts +202 -0
  161. package/src/input/audio/index.ts +6 -0
  162. package/src/input/audio/transcode.ts +67 -0
  163. package/src/input/audio/whisper-mel.ts +185 -0
  164. package/src/input/audio/whisper-tokenizer.ts +238 -0
  165. package/src/input/chat-template.ts +389 -0
  166. package/src/input/index.ts +6 -0
  167. package/src/input/media-fetch.ts +439 -0
  168. package/src/input/packed-mixed.ts +43 -0
  169. package/src/input/special-tokens.ts +75 -0
  170. package/src/input/token-groups.ts +49 -0
  171. package/src/input/tokenizer.ts +336 -0
  172. package/src/input/tool-call.ts +689 -0
  173. package/src/input/vision/index.ts +3 -0
  174. package/src/input/vision/preprocess.ts +247 -0
  175. package/src/input/vision/prompt.ts +398 -0
  176. package/src/input/vision/qwen3vl-preprocess.ts +409 -0
  177. package/src/input/vision/qwen3vl-prompt.ts +237 -0
  178. package/src/input/vision/video-frames.ts +100 -0
  179. package/src/kernels/ane/fill.ts +101 -0
  180. package/src/kernels/ane/linear.ts +416 -0
  181. package/src/kernels/attention/flash.ts +694 -0
  182. package/src/kernels/attention/kvq4-decode.ts +155 -0
  183. package/src/kernels/attention/masks.ts +40 -0
  184. package/src/kernels/attention/multi-query.ts +193 -0
  185. package/src/kernels/attention/paged.ts +130 -0
  186. package/src/kernels/delta/gated.ts +306 -0
  187. package/src/kernels/delta/index.ts +3 -0
  188. package/src/kernels/delta/silu-rms-heads.ts +69 -0
  189. package/src/kernels/delta/state.ts +74 -0
  190. package/src/kernels/gated-rms-norm.ts +64 -0
  191. package/src/kernels/glm52/layout.ts +37 -0
  192. package/src/kernels/glm52/streamed-experts.ts +360 -0
  193. package/src/kernels/logits.ts +38 -0
  194. package/src/kernels/quantization/affine-row.ts +154 -0
  195. package/src/kernels/quantization/affine-rows.ts +36 -0
  196. package/src/kernels/quantization/affine-split-mma.ts +144 -0
  197. package/src/kernels/quantization/affine3-mma.ts +117 -0
  198. package/src/kernels/quantization/affine3-rows.ts +103 -0
  199. package/src/kernels/quantization/affine4-mma.ts +113 -0
  200. package/src/kernels/quantization/affine4-rows.ts +102 -0
  201. package/src/kernels/residual-rms-norm.ts +65 -0
  202. package/src/kernels/sampling/index.ts +2 -0
  203. package/src/kernels/sampling/normalized-argmax.ts +116 -0
  204. package/src/kernels/sampling/token-bitmask.ts +52 -0
  205. package/src/kernels/softcap.ts +35 -0
  206. package/src/kernels/trellis/codebook.ts +123 -0
  207. package/src/kernels/trellis/down-codebook.ts +184 -0
  208. package/src/kernels/trellis/down-factored.ts +180 -0
  209. package/src/kernels/trellis/down-k3-interleaved-factored.ts +146 -0
  210. package/src/kernels/trellis/down-mma-wide.ts +195 -0
  211. package/src/kernels/trellis/down-mma.ts +145 -0
  212. package/src/kernels/trellis/expand.ts +42 -0
  213. package/src/kernels/trellis/gate-up-codebook-mma.ts +141 -0
  214. package/src/kernels/trellis/gate-up-codebook.ts +137 -0
  215. package/src/kernels/trellis/gate-up-factored.ts +197 -0
  216. package/src/kernels/trellis/gate-up-mma-wide.ts +147 -0
  217. package/src/kernels/trellis/gate-up-mma.ts +148 -0
  218. package/src/kernels/trellis/gate-up.ts +248 -0
  219. package/src/kernels/trellis/geometry.ts +22 -0
  220. package/src/kernels/trellis/index.ts +103 -0
  221. package/src/kernels/trellis/launch.ts +12 -0
  222. package/src/kernels/trellis/mixed-gate-up-factored.ts +119 -0
  223. package/src/kernels/trellis/mixed-gate-up.ts +146 -0
  224. package/src/kernels/trellis/qualified.ts +34 -0
  225. package/src/kernels/trellis/reduce.ts +150 -0
  226. package/src/kernels/trellis/scatter.ts +407 -0
  227. package/src/kernels/trellis/splitk-prefill.ts +152 -0
  228. package/src/kernels/trellis/tiled-prefill.ts +131 -0
  229. package/src/kernels/trellis/vector-expand.ts +58 -0
  230. package/src/kernels/trellis/wide-prefill.ts +89 -0
  231. package/src/kernels/turboquant/decode.ts +82 -0
  232. package/src/kernels/turboquant/index.ts +5 -0
  233. package/src/kernels/turboquant/inverse.ts +122 -0
  234. package/src/kernels/turboquant/ops.ts +860 -0
  235. package/src/kernels/turboquant/signs.ts +130 -0
  236. package/src/kernels/turboquant/tables.ts +124 -0
  237. package/src/layers/affine-verify-linear.ts +75 -0
  238. package/src/layers/ane-prefill-split.ts +283 -0
  239. package/src/layers/compiled-normalization.ts +41 -0
  240. package/src/layers/delta-heads.ts +23 -0
  241. package/src/layers/dense-embedding.ts +29 -0
  242. package/src/layers/dense-linear.ts +32 -0
  243. package/src/layers/dequantized-linear.ts +72 -0
  244. package/src/layers/folded-attention.ts +42 -0
  245. package/src/layers/geglu.ts +130 -0
  246. package/src/layers/helpers.ts +7 -0
  247. package/src/layers/index.ts +36 -0
  248. package/src/layers/kv4-decode-attention.ts +15 -0
  249. package/src/layers/layer-norm.ts +24 -0
  250. package/src/layers/loading.ts +92 -0
  251. package/src/layers/lora.ts +150 -0
  252. package/src/layers/normalization.ts +9 -0
  253. package/src/layers/prefix-layout.ts +33 -0
  254. package/src/layers/quantized-append-attention.ts +32 -0
  255. package/src/layers/quantized-attention.ts +353 -0
  256. package/src/layers/quantized-embedding.ts +39 -0
  257. package/src/layers/quantized-linear.ts +137 -0
  258. package/src/layers/quantized-switch-linear.ts +37 -0
  259. package/src/layers/qwen-append.ts +12 -0
  260. package/src/layers/qwen-mrope.ts +238 -0
  261. package/src/layers/rope-2d.ts +110 -0
  262. package/src/layers/rope.ts +389 -0
  263. package/src/layers/swiglu.ts +23 -0
  264. package/src/layers/trellis-codebook-down.ts +52 -0
  265. package/src/layers/trellis-codebook-gate-up.ts +42 -0
  266. package/src/layers/trellis-down.ts +117 -0
  267. package/src/layers/trellis-gate-up.ts +71 -0
  268. package/src/layers/trellis-linear.ts +211 -0
  269. package/src/layers/trellis-prompt.ts +102 -0
  270. package/src/layers/trellis-reduce-gate-up.ts +73 -0
  271. package/src/layers/trellis-scatter-down.ts +98 -0
  272. package/src/models/ane-install.ts +74 -0
  273. package/src/models/audio/conformer.ts +656 -0
  274. package/src/models/cache-composition.ts +44 -0
  275. package/src/models/capabilities.ts +89 -0
  276. package/src/models/chat-template.ts +10 -0
  277. package/src/models/composition.ts +107 -0
  278. package/src/models/diffusion-gemma/experts.ts +59 -0
  279. package/src/models/diffusion-gemma/family.ts +21 -0
  280. package/src/models/diffusion-gemma/model.ts +900 -0
  281. package/src/models/diffusion-gemma/vision.ts +549 -0
  282. package/src/models/drafter-loaders.ts +49 -0
  283. package/src/models/drafters.ts +39 -0
  284. package/src/models/factory.ts +351 -0
  285. package/src/models/families.ts +70 -0
  286. package/src/models/family.ts +125 -0
  287. package/src/models/gemma4/assistant-target.ts +46 -0
  288. package/src/models/gemma4/assistant.ts +327 -0
  289. package/src/models/gemma4/blocks.ts +755 -0
  290. package/src/models/gemma4/compiled-decode.ts +452 -0
  291. package/src/models/gemma4/family.ts +39 -0
  292. package/src/models/gemma4/gemma4-12b.ts +677 -0
  293. package/src/models/gemma4/gemma4-26b.ts +544 -0
  294. package/src/models/gemma4/gemma4-e4b.ts +599 -0
  295. package/src/models/gemma4/graphs.ts +147 -0
  296. package/src/models/gemma4/media.ts +88 -0
  297. package/src/models/gemma4/model.ts +860 -0
  298. package/src/models/gemma4/serving-blocks.ts +72 -0
  299. package/src/models/gemma4/trainable.ts +186 -0
  300. package/src/models/gemma4/written-blocks.ts +232 -0
  301. package/src/models/gemma4/written-forward.ts +159 -0
  302. package/src/models/gemma4/written-input.ts +29 -0
  303. package/src/models/glm52/atlas.ts +507 -0
  304. package/src/models/glm52/coupling.ts +388 -0
  305. package/src/models/glm52/dsa.ts +601 -0
  306. package/src/models/glm52/family.ts +20 -0
  307. package/src/models/glm52/mla.ts +812 -0
  308. package/src/models/glm52/model.ts +1127 -0
  309. package/src/models/glm52/moe.ts +223 -0
  310. package/src/models/glm52/mtp.ts +60 -0
  311. package/src/models/glm52/pilot.ts +537 -0
  312. package/src/models/glm52/reference.ts +445 -0
  313. package/src/models/glm52/residency.ts +557 -0
  314. package/src/models/glm52/streamed-experts.ts +447 -0
  315. package/src/models/graph.ts +59 -0
  316. package/src/models/implementation.ts +230 -0
  317. package/src/models/index.ts +15 -0
  318. package/src/models/media-input.ts +15 -0
  319. package/src/models/memory-plan.ts +39 -0
  320. package/src/models/minicpm5/family.ts +29 -0
  321. package/src/models/minicpm5/model.ts +394 -0
  322. package/src/models/minicpm5/trainable.ts +90 -0
  323. package/src/models/profile.ts +337 -0
  324. package/src/models/qwen/blocks.ts +658 -0
  325. package/src/models/qwen/checkpoint.ts +190 -0
  326. package/src/models/qwen/families.ts +57 -0
  327. package/src/models/qwen/media-input.ts +161 -0
  328. package/src/models/qwen/mtp.ts +101 -0
  329. package/src/models/qwen/qwen3-moe.ts +355 -0
  330. package/src/models/qwen/qwen3.ts +323 -0
  331. package/src/models/qwen/qwen38-27b-trellis-m1max.ts +1532 -0
  332. package/src/models/qwen/qwen38-27b-trellis-m4pro.ts +1092 -0
  333. package/src/models/qwen/qwen3_5.ts +433 -0
  334. package/src/models/qwen/written-layers.ts +285 -0
  335. package/src/models/row-table.ts +1 -0
  336. package/src/models/runtime.ts +73 -0
  337. package/src/models/speculative/deepspec-artifact.ts +10 -0
  338. package/src/models/speculative/deepspec.ts +744 -0
  339. package/src/models/speculative/dflash.ts +553 -0
  340. package/src/models/speculative/dflash2-artifact.ts +9 -0
  341. package/src/models/speculative/dflash2-operations.ts +1 -0
  342. package/src/models/speculative/dflash2.ts +576 -0
  343. package/src/models/speculative/loader.ts +29 -0
  344. package/src/models/support.ts +79 -0
  345. package/src/models/universal/archs.ts +365 -0
  346. package/src/models/universal/blocks.ts +479 -0
  347. package/src/models/universal/dense.ts +255 -0
  348. package/src/models/universal/family.ts +17 -0
  349. package/src/models/vision/qwen3vl.ts +566 -0
  350. package/src/models/vision/siglip.ts +520 -0
  351. package/src/models/vision/unified.ts +176 -0
  352. package/src/models/whisper/family.ts +19 -0
  353. package/src/models/whisper/fast.ts +734 -0
  354. package/src/models/whisper/model.ts +430 -0
  355. package/src/runtime/compiled-trace.ts +16 -0
  356. package/src/runtime/config.ts +77 -0
  357. package/src/runtime/expert-trace.ts +71 -0
  358. package/src/runtime/graph-forward.ts +65 -0
  359. package/src/runtime/integers.ts +6 -0
  360. package/src/runtime/native.ts +16 -0
  361. package/src/runtime/resources.ts +53 -0
  362. package/src/runtime/row-operations.ts +42 -0
  363. package/src/runtime/trace.ts +161 -0
  364. package/src/sampling/curve.ts +122 -0
  365. package/src/sampling/draft.ts +55 -0
  366. package/src/sampling/extras.ts +45 -0
  367. package/src/sampling/filters.ts +134 -0
  368. package/src/sampling/grammar.ts +630 -0
  369. package/src/sampling/hlg.ts +290 -0
  370. package/src/sampling/index.ts +9 -0
  371. package/src/sampling/processors.ts +142 -0
  372. package/src/sampling/sampler.ts +120 -0
  373. package/src/sampling/step.ts +343 -0
  374. package/src/sampling/types.ts +161 -0
  375. package/src/sampling/window-filters.ts +46 -0
  376. package/src/scoring/decisions.ts +96 -0
  377. package/src/scoring/full-sequence.ts +103 -0
  378. package/src/scoring/index.ts +9 -0
  379. package/src/scoring/kl.ts +29 -0
  380. package/src/scoring/perplexity.ts +165 -0
  381. package/src/scoring/response-logps.ts +65 -0
  382. package/src/state/affine-attention.ts +180 -0
  383. package/src/state/attachment.ts +68 -0
  384. package/src/state/attention-read.ts +215 -0
  385. package/src/state/batched-kv.ts +294 -0
  386. package/src/state/batched-mask.ts +445 -0
  387. package/src/state/batched-quant.ts +389 -0
  388. package/src/state/batched-quantized-kv.ts +209 -0
  389. package/src/state/batched-rotating-quant.ts +457 -0
  390. package/src/state/batched-rotating-state.ts +149 -0
  391. package/src/state/batched-rotating.ts +430 -0
  392. package/src/state/batched-row-storage.ts +179 -0
  393. package/src/state/batched-ssm.ts +54 -0
  394. package/src/state/batched-turboquant-kv.ts +144 -0
  395. package/src/state/bf16-first-kv.ts +663 -0
  396. package/src/state/cache-service.ts +592 -0
  397. package/src/state/capabilities.ts +65 -0
  398. package/src/state/checkpoint.ts +46 -0
  399. package/src/state/continuation-persistence.ts +126 -0
  400. package/src/state/decode-step-inputs.ts +31 -0
  401. package/src/state/decoded-kv-donor.ts +12 -0
  402. package/src/state/delayed-quantized-kv.ts +141 -0
  403. package/src/state/delayed-rotating-quantized-kv.ts +178 -0
  404. package/src/state/delayed-turboquant-kv.ts +123 -0
  405. package/src/state/durability.ts +214 -0
  406. package/src/state/encoder-cache.ts +26 -0
  407. package/src/state/full-kv-row-append.ts +33 -0
  408. package/src/state/full-kv-row-donor.ts +54 -0
  409. package/src/state/full-prefill-padding.ts +56 -0
  410. package/src/state/full-prefill-row.ts +112 -0
  411. package/src/state/full-transitioning-kv-rows.ts +70 -0
  412. package/src/state/glm52-cache.ts +783 -0
  413. package/src/state/grouped-heads-kv4-cache.ts +26 -0
  414. package/src/state/index.ts +38 -0
  415. package/src/state/kv-attention-view.ts +77 -0
  416. package/src/state/kv-budget.ts +14 -0
  417. package/src/state/kv-scheme.ts +315 -0
  418. package/src/state/kv-storage.ts +57 -0
  419. package/src/state/kv-tensor-rows.ts +212 -0
  420. package/src/state/kv.ts +324 -0
  421. package/src/state/kv4-head256-cache.ts +46 -0
  422. package/src/state/latent-legos.ts +895 -0
  423. package/src/state/layout.ts +277 -0
  424. package/src/state/leases.ts +26 -0
  425. package/src/state/paged/cache.ts +689 -0
  426. package/src/state/paged/codec.ts +36 -0
  427. package/src/state/paged/rows.ts +223 -0
  428. package/src/state/persistence-types.ts +173 -0
  429. package/src/state/persistence-worker.ts +83 -0
  430. package/src/state/persistence.ts +1036 -0
  431. package/src/state/persistence.worker.d.ts +3 -0
  432. package/src/state/persistence.worker.js +264 -0
  433. package/src/state/prefix-cache.ts +685 -0
  434. package/src/state/quantized-kv.ts +369 -0
  435. package/src/state/quantized-tensor.ts +26 -0
  436. package/src/state/request-policy.ts +48 -0
  437. package/src/state/retention-policy.ts +37 -0
  438. package/src/state/rollback.ts +131 -0
  439. package/src/state/rotating-kv-donor.ts +35 -0
  440. package/src/state/rotating-kv-layout.ts +341 -0
  441. package/src/state/rotating-kv.ts +529 -0
  442. package/src/state/rotating-quantized-kv.ts +616 -0
  443. package/src/state/rotating-row-transaction.ts +35 -0
  444. package/src/state/rows.ts +104 -0
  445. package/src/state/softcap-attention.ts +118 -0
  446. package/src/state/speculative/glm52-mtp-state.ts +38 -0
  447. package/src/state/speculative/qwen-mtp-state.ts +34 -0
  448. package/src/state/speculative-rotating-kv.ts +113 -0
  449. package/src/state/ssd-cache.ts +434 -0
  450. package/src/state/ssm-prefill-padding.ts +39 -0
  451. package/src/state/ssm.ts +422 -0
  452. package/src/state/target-layout.ts +7 -0
  453. package/src/state/tiered-prefix-cache.ts +45 -0
  454. package/src/state/training-cache.ts +300 -0
  455. package/src/state/transitioning-kv-rows.ts +267 -0
  456. package/src/state/turboquant-attention.ts +97 -0
  457. package/src/state/turboquant-codec.ts +90 -0
  458. package/src/state/turboquant-kv.ts +442 -0
  459. package/src/state/views.ts +26 -0
  460. package/src/transcription/index.ts +3 -0
  461. package/src/transcription/silero-vad.ts +379 -0
  462. package/src/transcription/whisper/decode.ts +773 -0
  463. package/src/transcription/whisper/format.ts +74 -0
  464. package/src/transcription/whisper/index.ts +4 -0
  465. package/src/transcription/whisper/timing.ts +274 -0
  466. package/src/transcription/whisper/transcribe.ts +394 -0
  467. package/src/transcription/whisper/types.ts +22 -0
@@ -0,0 +1,343 @@
1
+ // Sampling — port of mlx-lm's sample_utils.make_sampler /
2
+ // make_logits_processors (temperature, top-p, top-k, min-p, XTC,
3
+ // repetition/presence/frequency penalties, logit bias).
4
+ // All filtering happens on-device; only the chosen token id crosses to JS.
5
+ // Seeded: each step derives a fresh key from (seed, step) so runs are
6
+ // reproducible without sharing global RNG state. (mlx-lm draws XTC's
7
+ // uniform and the categorical from one global stream; we split the
8
+ // per-step key into (xtc, categorical) subkeys instead — deterministic,
9
+ // and the XTC-off path is unchanged.)
10
+
11
+ import { MlxArray } from "@mlx-bun/mlx/array";
12
+ import * as ops from "@mlx-bun/mlx/ops";
13
+ import { runtimeFlag } from "../runtime/config";
14
+ import { normalizedArgmax } from "../kernels/sampling/normalized-argmax";
15
+ import { disposeStepExtras } from "./extras";
16
+ import { applyTopKRows,applyTopPRows } from "./window-filters";
17
+ import { logitsHistoryLimit,makeLogitsProcessors } from "./processors";
18
+ import { makeSampler,stepKey,toLogprobs } from "./sampler";
19
+ import { DeviceStepSampler,DeviceStepSamplerConfig,NumberStepSampler,NumberStepSamplerConfig,StepExtras,StepSample,StepSamplerOptions } from "./types";
20
+
21
+ /** Whether scores can use stateless greedy sampling across independent rows. */
22
+ export function isPlainGreedy(
23
+ opts: StepSamplerOptions,
24
+ hasProcessors = makeLogitsProcessors(opts).length > 0,
25
+ ): boolean {
26
+ return (opts.temperature ?? 0) === 0 && !opts.curve && !hasProcessors && !opts.grammar;
27
+ }
28
+
29
+ export function makeStepSampler(
30
+ options: StepSamplerOptions,
31
+ config: DeviceStepSamplerConfig,
32
+ ): DeviceStepSampler;
33
+ export function makeStepSampler(
34
+ options: StepSamplerOptions,
35
+ config: NumberStepSamplerConfig,
36
+ ): NumberStepSampler;
37
+ export function makeStepSampler(
38
+ options: StepSamplerOptions,
39
+ config: DeviceStepSamplerConfig | NumberStepSamplerConfig,
40
+ ): DeviceStepSampler | NumberStepSampler {
41
+ const sampler = config.sampler ?? makeSampler(options);
42
+ const processors = makeLogitsProcessors(options);
43
+ const grammar = options.grammar ?? null;
44
+ const captureSelected = config.captureSelectedLogprob === true;
45
+ const captureTop = Math.max(0, config.captureTopLogprobs ?? 0);
46
+ const capture = captureSelected || captureTop > 0;
47
+ const greedyWithoutMetadata = (options.temperature ?? 0) === 0 && !options.curve && !capture && !config.sampler;
48
+ const historyLimit = logitsHistoryLimit(options);
49
+ let history: MlxArray | null = null;
50
+
51
+ const seedHistory = (tokens: readonly number[]): void => {
52
+ if (historyLimit === 0) return;
53
+ if (tokens.length === 0) {
54
+ history?.dispose();
55
+ history = null;
56
+ return;
57
+ }
58
+ const recent = tokens.slice(Math.max(0, tokens.length - historyLimit));
59
+ const next = ops.fromInt32(recent, [recent.length]);
60
+ const previous = history;
61
+ history = next;
62
+ previous?.dispose();
63
+ };
64
+
65
+ // Takes ownership of next. Trim before concatenation so the appended array
66
+ // never exceeds the largest active finite window.
67
+ const appendHistory = (next: MlxArray): void => {
68
+ const previous = history;
69
+ if (!previous || next.size >= historyLimit) {
70
+ history = next;
71
+ previous?.dispose();
72
+ return;
73
+ }
74
+ const keep = Math.min(previous.size, historyLimit - next.size);
75
+ let tail: MlxArray | null = null;
76
+ try {
77
+ if (keep < previous.size) tail = previous.slice([previous.size - keep], [previous.size]);
78
+ history = ops.concatAxis([tail ?? previous, next], 0);
79
+ previous.dispose();
80
+ } finally {
81
+ tail?.dispose();
82
+ next.dispose();
83
+ }
84
+ };
85
+
86
+ const commitDevice = (token: MlxArray): void => {
87
+ if (historyLimit === 0) return;
88
+ appendHistory(ops.reshape(token, [1]));
89
+ };
90
+
91
+ const commitNumbers = (tokens: readonly number[]): void => {
92
+ if (tokens.length === 0 || historyLimit === 0) return;
93
+ const recent = tokens.slice(Math.max(0, tokens.length - historyLimit));
94
+ appendHistory(ops.fromInt32(recent, [recent.length]));
95
+ };
96
+
97
+ // Borrows `scores` [1, V]; returns them processed over `tokens` (the history the processors read), owned unless
98
+ // no processor replaced them.
99
+ const applyProcessors = (tokens: MlxArray | null, scores: MlxArray): MlxArray => {
100
+ let current = scores;
101
+ try {
102
+ for (const processor of processors) {
103
+ const next = processor(tokens, current);
104
+ if (current !== scores && next !== current) current.dispose();
105
+ current = next;
106
+ }
107
+ return current;
108
+ } catch (error) {
109
+ if (current !== scores) current.dispose();
110
+ throw error;
111
+ }
112
+ };
113
+
114
+ const sampleDevice = (input: MlxArray, step: number): StepSample<MlxArray> => {
115
+ const vocab = input.shape[input.shape.length - 1]!;
116
+ let current = input.shape.length === 2 ? input : ops.reshape(input, [1, vocab]);
117
+ let ownsCurrent = current !== input;
118
+ let logprobs: MlxArray | null = null;
119
+ let token: MlxArray | null = null;
120
+ let extras: StepExtras | null = null;
121
+ try {
122
+ if (processors.length) {
123
+ const next = applyProcessors(history, current);
124
+ if (ownsCurrent && next !== current) current.dispose();
125
+ current = next;
126
+ ownsCurrent = current !== input;
127
+ }
128
+ if (grammar && !grammar.isTerminated) {
129
+ const next = grammar.applyMask(current);
130
+ if (ownsCurrent && next !== current) current.dispose();
131
+ current = next;
132
+ ownsCurrent = current !== input;
133
+ }
134
+ if (greedyWithoutMetadata)
135
+ return { token: normalizedArgmax(current), extras: null };
136
+ logprobs = toLogprobs(current);
137
+ if (ownsCurrent) current.dispose();
138
+ current = input;
139
+ ownsCurrent = false;
140
+ token = sampler(logprobs, step);
141
+
142
+ if (capture) {
143
+ extras = { sel: null, topIdx: null, topVals: null };
144
+ if (captureSelected) {
145
+ const idx = ops.reshape(token, [1, 1]);
146
+ try {
147
+ extras.sel = ops.takeAlongAxis(logprobs, idx, -1);
148
+ } finally {
149
+ idx.dispose();
150
+ }
151
+ }
152
+ if (captureTop > 0) {
153
+ const k = Math.min(captureTop, vocab);
154
+ let neg: MlxArray | null = null;
155
+ let partition: MlxArray | null = null;
156
+ let view: MlxArray | null = null;
157
+ try {
158
+ neg = ops.neg(logprobs);
159
+ partition = ops.argpartitionAxis(neg, k - 1, -1);
160
+ view = partition.slice([0, 0], [1, k]);
161
+ extras.topIdx = ops.contiguous(view);
162
+ extras.topVals = ops.takeAlongAxis(logprobs, extras.topIdx, -1);
163
+ } finally {
164
+ neg?.dispose();
165
+ partition?.dispose();
166
+ view?.dispose();
167
+ }
168
+ }
169
+ }
170
+
171
+ logprobs.dispose();
172
+ logprobs = null;
173
+ const result = { token, extras };
174
+ token = null;
175
+ extras = null;
176
+ return result;
177
+ } finally {
178
+ if (ownsCurrent) current.dispose();
179
+ logprobs?.dispose();
180
+ token?.dispose();
181
+ disposeStepExtras(extras);
182
+ }
183
+ };
184
+
185
+ if (config.initialHistory) seedHistory(config.initialHistory);
186
+
187
+ // Per-position sampling for a verify window: with no grammar or capture, position p's draw depends only on its
188
+ // scores, its step key and its processor history, which is this request's history extended by the window's inputs
189
+ // up to p (the drafts the walk accepted before reaching p). Every position of the window can then sit in ONE device
190
+ // graph and read back together. Row p reproduces sample(scores[p], steps[p]) exactly, processors included: they run
191
+ // per position on its own [1, V] row over that history, as sample() runs them.
192
+ // The plain filter chain (top-p -> top-k -> temperature -> categorical) can run
193
+ // its filters once over the whole window. Other samplers keep per-row calls.
194
+ const windowFilters = !config.sampler && !options.curve && options.hlg?.enabled !== true &&
195
+ (options.minP ?? 0) === 0 && (options.xtcProbability ?? 0) === 0 && (options.temperature ?? 0) > 0 &&
196
+ runtimeFlag("MLX_BUN_SAMPLER_WINDOW_FILTERS", true)
197
+ ? { temperature: options.temperature!, topP: options.topP ?? 0, topK: options.topK ?? 0, seed: options.seed ?? 0 }
198
+ : null;
199
+ const positional = !grammar && !capture
200
+ ? {
201
+ sample(scores: MlxArray, steps: readonly number[], inputs?: MlxArray): MlxArray {
202
+ if (scores.shape.length !== 2 || scores.shape[0] !== steps.length)
203
+ throw new Error(`positional sampling expects [${steps.length}, V] scores, got [${scores.shape.join(", ")}]`);
204
+ if (historyLimit > 0 && inputs?.size !== steps.length)
205
+ throw new Error(`positional sampling with processor history needs the window's ${steps.length} inputs`);
206
+ const vocab = scores.shape[1]!;
207
+ const tokens: MlxArray[] = [];
208
+ const processed: MlxArray[] = [];
209
+ let packed: MlxArray | null = null, stacked: MlxArray | null = null;
210
+ try {
211
+ // Each position's scores after its processors, over the history sample() would hold there.
212
+ const rowScores = (position: number): MlxArray => {
213
+ const row = scores.slice([position, 0], [position + 1, vocab]);
214
+ if (!processors.length) return row;
215
+ let extended: MlxArray | null = null;
216
+ try {
217
+ if (historyLimit > 0 && position > 0) {
218
+ using drafts = inputs!.slice([1], [position + 1]);
219
+ extended = history ? ops.concatAxis([history, drafts], 0) : ops.contiguous(drafts);
220
+ }
221
+ const next = applyProcessors(extended ?? history, row);
222
+ if (next !== row) row.dispose();
223
+ return next;
224
+ } catch (error) { row.dispose(); throw error; }
225
+ finally { extended?.dispose(); }
226
+ };
227
+ if (windowFilters && steps.length > 1) {
228
+ // One filter chain over [W, V] (bit-identical per row to the [1, V]
229
+ // chain, packages/inference/tests/sampling/window.test.ts), then each row's own
230
+ // keyed draw on its [1, V] slice: that shape keeps MLX's inverse-CDF
231
+ // categorical, which the draft's coupled draw relies on.
232
+ if (processors.length) {
233
+ for (let position = 0; position < steps.length; position++) processed.push(rowScores(position));
234
+ stacked = ops.concatAxis(processed, 0);
235
+ }
236
+ using logprobs = toLogprobs(stacked ?? scores);
237
+ let current = logprobs, owned: MlxArray | null = null;
238
+ try {
239
+ if (windowFilters.topP > 0 && windowFilters.topP < 1) { owned = applyTopPRows(current, windowFilters.topP); current = owned; }
240
+ if (windowFilters.topK > 0) {
241
+ const next = applyTopKRows(current, windowFilters.topK);
242
+ owned?.dispose(); owned = next; current = next;
243
+ }
244
+ for (const [position, step] of steps.entries()) {
245
+ using row = current.slice([position, 0], [position + 1, vocab]);
246
+ using scaled = ops.mulScalar(row, 1 / windowFilters.temperature);
247
+ using key = stepKey(windowFilters.seed, step);
248
+ tokens.push(ops.randomCategorical(scaled, key));
249
+ }
250
+ } finally { owned?.dispose(); }
251
+ } else
252
+ for (const [position, step] of steps.entries()) {
253
+ using row = rowScores(position);
254
+ if (greedyWithoutMetadata) { tokens.push(normalizedArgmax(row)); continue; }
255
+ using logprobs = toLogprobs(row);
256
+ tokens.push(sampler(logprobs, step));
257
+ }
258
+ if (tokens.length === 1) return tokens.pop()!;
259
+ packed = ops.concatAxis(tokens, 0);
260
+ const result = packed;
261
+ packed = null;
262
+ return result;
263
+ } finally {
264
+ packed?.dispose();
265
+ stacked?.dispose();
266
+ for (const array of [...tokens, ...processed]) array.dispose();
267
+ }
268
+ },
269
+ commit(tokens: readonly number[]): void {
270
+ if (config.historyUpdate === "after-sample") commitNumbers(tokens);
271
+ },
272
+ }
273
+ : undefined;
274
+
275
+ const unread = !grammar && !capture
276
+ ? {
277
+ sample(scores: MlxArray, step: number): MlxArray {
278
+ const { token } = sampleDevice(scores, step);
279
+ if (config.historyUpdate === "after-sample") commitDevice(token);
280
+ return token;
281
+ },
282
+ }
283
+ : undefined;
284
+
285
+ const common = {
286
+ unread,
287
+ independent: isPlainGreedy(options, processors.length > 0) && !capture && !config.sampler
288
+ ? independentGreedySampling : undefined,
289
+ positional,
290
+ isPlainGreedy: isPlainGreedy(options, processors.length > 0),
291
+ capturesLogprobs: capture,
292
+ needsHistory: historyLimit > 0,
293
+ seedHistory,
294
+ commitDevice,
295
+ commitNumbers,
296
+ dispose: () => {
297
+ history?.dispose();
298
+ history = null;
299
+ },
300
+ };
301
+
302
+ if (config.tokenRepresentation === "device") {
303
+ return {
304
+ ...common,
305
+ sample(logits, step) {
306
+ const result = sampleDevice(logits, step);
307
+ if (config.historyUpdate === "after-sample") commitDevice(result.token);
308
+ return result;
309
+ },
310
+ };
311
+ }
312
+
313
+ return {
314
+ ...common,
315
+ async sample(logits, step) {
316
+ if (grammar && !grammar.isTerminated) await grammar.ready();
317
+ const result = sampleDevice(logits, step);
318
+ let tokenArray: MlxArray | null = result.token;
319
+ try {
320
+ // Read the sampler's integer result directly. Adding an astype here
321
+ // would append work behind the next dispatched decode step.
322
+ const token = ops.itemUint32(tokenArray);
323
+ tokenArray.dispose();
324
+ tokenArray = null;
325
+ if (config.acceptGrammar && grammar && !config.eosTokenIds?.includes(token))
326
+ grammar.accept(token);
327
+ if (config.historyUpdate === "after-sample") commitNumbers([token]);
328
+ return { token, extras: result.extras };
329
+ } catch (error) {
330
+ disposeStepExtras(result.extras);
331
+ throw error;
332
+ } finally {
333
+ tokenArray?.dispose();
334
+ }
335
+ },
336
+ };
337
+ }
338
+
339
+ /** Stateless sampling across rows or verification positions. Preserve the
340
+ * normalized-score argmax, including rounding-created ties. */
341
+ export const independentGreedySampling = Object.freeze({
342
+ sample: normalizedArgmax,
343
+ });
@@ -0,0 +1,161 @@
1
+ // Sampling — port of mlx-lm's sample_utils.make_sampler /
2
+ // make_logits_processors (temperature, top-p, top-k, min-p, XTC,
3
+ // repetition/presence/frequency penalties, logit bias).
4
+ // All filtering happens on-device; only the chosen token id crosses to JS.
5
+ // Seeded: each step derives a fresh key from (seed, step) so runs are
6
+ // reproducible without sharing global RNG state. (mlx-lm draws XTC's
7
+ // uniform and the categorical from one global stream; we split the
8
+ // per-step key into (xtc, categorical) subkeys instead — deterministic,
9
+ // and the XTC-off path is unchanged.)
10
+
11
+ import { MlxArray } from "@mlx-bun/mlx/array";
12
+ import type { SampledToken,SamplingSession } from "../contracts/portable/sampling";
13
+ import { type CurveParams } from "./curve";
14
+
15
+ /** Resolved HLG sampling config (the user-facing knobs). The mid gain is NOT
16
+ * here — it folds from temperature (m = 1/T) when the sampler is built. */
17
+ export interface HlgConfig {
18
+ enabled: boolean;
19
+ width: number;
20
+ shoulder: number;
21
+ toe: number;
22
+ pivot?: "top" | "entropy" | "median";
23
+ pivotOffset: number;
24
+ /** Use the LITERAL HLG OETF (gamma below 1/12, log above) instead of the
25
+ * parametric toe/mid/shoulder curve. Ignores gain/width/shoulder/toe/pivot. */
26
+ oetf?: boolean;
27
+ /** Use the LITERAL HLG EOTF (inverse OETF → OOTF gamma) — the decode/display
28
+ * direction (suppresses the tail). Ignores the parametric knobs. */
29
+ eotf?: boolean;
30
+ /** EOTF only: nominal peak display luminance L_W (cd/m²) → OOTF gamma.
31
+ * Default 1000 (γ = 1.2). Lower = flatter (more diverse), higher = sharper. */
32
+ lw?: number;
33
+ /** Use the FULL HLG chain: OETF signal shape → OOTF scaling (applyHlgPipeline).
34
+ * γ from `lw`. Ignores the parametric knobs. */
35
+ pipeline?: boolean;
36
+ /** Pipeline only: α = max brightness — the top logit the OOTF scales everything
37
+ * under ("the most confidence we want"). Default 5. For the shaper, reused as
38
+ * `out_scale` (default 12). */
39
+ maxBrightness?: number;
40
+ /** Use the user-specified HLGShaper (windowed anchor → piecewise OETF with a
41
+ * cubic suppress-toe → OOTF → ×out_scale). `lw`→L_W, `maxBrightness`→out_scale. */
42
+ shaper?: boolean;
43
+ /** Shaper only: W = nats of headroom below the top logit that span the curve
44
+ * (`x = clamp((ℓ−ℓmax)/W + 1, 0, 1)`). Default 10. */
45
+ window?: number;
46
+ /** Shaper only: s_m — mid sharpness (how hard above-median tokens are elevated). */
47
+ sM?: number;
48
+ /** Shaper only: A — shoulder compression (how hard top-end confidence is tamed). */
49
+ shoulderA?: number;
50
+ /** Shaper only: target top-to-reference logit gap (out_scale auto-derives from this
51
+ * so it's decoupled from W). Default 15. */
52
+ targetGap?: number;
53
+ /** Shaper only: reference token nats below the top for the auto out_scale. Default 4. */
54
+ refGap?: number;
55
+ /** Shaper only: curve geometry (the pivots / toe / mid power). Default to the
56
+ * HLGShaper constants (xM 0.55, yM 0.5, xFloor 0.2, yFloor 0.18, p 2.0) when unset. */
57
+ xM?: number; yM?: number; xFloor?: number; yFloor?: number; p?: number;
58
+ /** Optional explicit mid gain m. Default: folds from temperature (m = 1/T),
59
+ * so temperature stays the contrast knob. An explicit value decouples mid
60
+ * contrast from temperature — needed to probe mid-boost while holding the
61
+ * model's recommended temperature fixed. */
62
+ gain?: number;
63
+ }
64
+
65
+ export interface SamplerOptions {
66
+ temperature?: number;
67
+ topP?: number;
68
+ topK?: number;
69
+ /** min-p: keep tokens whose probability ≥ minP · p(top token). mlx-lm's
70
+ * `min_p`. Applied after top-p, before XTC/top-k (reference chain order). */
71
+ minP?: number;
72
+ /** Minimum number of tokens min-p may never filter out. Default 1. */
73
+ minTokensToKeep?: number;
74
+ /** XTC ("exclude top choices"): with this probability per step, drop every
75
+ * token above the threshold EXCEPT the least likely of them. */
76
+ xtcProbability?: number;
77
+ /** Probability a token must exceed to be an XTC removal candidate. [0, 0.5]. */
78
+ xtcThreshold?: number;
79
+ /** Token ids XTC never removes (mlx-lm's server passes EOS + "\n"). */
80
+ xtcSpecialTokens?: number[];
81
+ seed?: number;
82
+ /** HLG tone-curve sampling. When enabled, replaces temperature's flat slope
83
+ * with the piecewise curve (temperature becomes the mid gain). Off/undefined
84
+ * ⇒ the plain temperature path, unchanged. `02d723a:docs/archive/hlg-sampling.md`. */
85
+ hlg?: HlgConfig;
86
+ /** v2 log-prob transfer-curve sampler. When set, the drawn monotone curve
87
+ * REPLACES temperature+softmax entirely (stochastic, seeded) — see
88
+ * packages/inference/src/sampling/curve.ts. Identity curve ≡ temperature 1. */
89
+ curve?: CurveParams;
90
+ }
91
+
92
+ export interface LogitsProcessorOptions {
93
+ /** Additive per-token-id logit bias, applied before any penalty
94
+ * (mlx-lm's `logit_bias`: {tokenId: bias}). */
95
+ logitBias?: Record<number, number>;
96
+ repetitionPenalty?: number;
97
+ repetitionContextSize?: number;
98
+ /** OpenAI-style presence penalty: subtracted ONCE from a token's logit if it
99
+ * occurred at all in the recent window. May be negative. */
100
+ presencePenalty?: number;
101
+ presenceContextSize?: number;
102
+ /** OpenAI-style frequency penalty: subtracted once PER OCCURRENCE of the
103
+ * token in the recent window. May be negative. */
104
+ frequencyPenalty?: number;
105
+ frequencyContextSize?: number;
106
+ }
107
+
108
+ /** logprobs [1, V] → sampled token array (uint32, shape [1]). */
109
+ export type Sampler = (logprobs: MlxArray, step: number) => MlxArray;
110
+
111
+ /** (deviceTokens [n] | null, logits [1, V]) → logits [1, V]. */
112
+ export type LogitsProcessor = (tokens: MlxArray | null, logits: MlxArray) => MlxArray;
113
+
114
+ /** Optional arrays captured from the same post-processor logprobs used to
115
+ * sample a token. The caller controls readback timing and owns disposal. */
116
+ export interface StepExtras {
117
+ sel: MlxArray | null;
118
+ topIdx: MlxArray | null;
119
+ topVals: MlxArray | null;
120
+ }
121
+
122
+ export type StepSample<T> = SampledToken<T, StepExtras>;
123
+
124
+ interface StepGrammar {
125
+ readonly isTerminated: boolean;
126
+ ready(): Promise<void>;
127
+ applyMask(logits: MlxArray): MlxArray;
128
+ accept(token: number): void;
129
+ }
130
+
131
+ export interface StepSamplerOptions extends SamplerOptions, LogitsProcessorOptions {
132
+ grammar?: StepGrammar | null;
133
+ }
134
+
135
+ interface StepSamplerConfigBase {
136
+ /** The grammar owner already awaited ready(), or this sampler must do it. */
137
+ grammarWait: "external" | "before-sample";
138
+ /** Update processor history in sample(), or let the decode loop commit later. */
139
+ historyUpdate: "after-sample" | "manual";
140
+ initialHistory?: readonly number[];
141
+ captureSelectedLogprob?: boolean;
142
+ captureTopLogprobs?: number;
143
+ /** Eager-number mode can advance grammar after sampling. */
144
+ acceptGrammar?: boolean;
145
+ eosTokenIds?: readonly number[];
146
+ /** Reuse a sampler already shared with a speculative draft source. */
147
+ sampler?: Sampler;
148
+ }
149
+
150
+ export interface DeviceStepSamplerConfig extends StepSamplerConfigBase {
151
+ tokenRepresentation: "device";
152
+ grammarWait: "external";
153
+ }
154
+
155
+ export interface NumberStepSamplerConfig extends StepSamplerConfigBase {
156
+ tokenRepresentation: "number";
157
+ grammarWait: "before-sample";
158
+ }
159
+
160
+ export type DeviceStepSampler = SamplingSession<MlxArray, MlxArray, StepSample<MlxArray>>;
161
+ export type NumberStepSampler = SamplingSession<MlxArray, MlxArray, Promise<StepSample<number>>>;
@@ -0,0 +1,46 @@
1
+ // Row-batched top-p / top-k filtering for a verify window.
2
+ //
3
+ // The accept walk samples every position of a speculative window. Each position
4
+ // runs the same filter chain over the full vocabulary: exp, argsort, gather,
5
+ // cumsum, inverse permutation, compare (top-p), then argpartition and a scatter
6
+ // (top-k). Done per position that is W independent chains of full-vocabulary
7
+ // kernels. Every one of those ops is row-wise along the last axis, so the chain
8
+ // over [W, V] computes each row with the same arithmetic as the [1, V] chain.
9
+ // Only the FILTERS batch: each row is still drawn separately with its own step
10
+ // key on a [1, V] slice, because MLX's categorical takes its inverse-CDF path
11
+ // only for that shape, and the draft is coupled to exactly that draw.
12
+ import { MlxArray } from "@mlx-bun/mlx/array";
13
+ import * as ops from "@mlx-bun/mlx/ops";
14
+
15
+ function negInfLike(a: MlxArray): MlxArray {
16
+ return ops.scalarLike(-Infinity, a);
17
+ }
18
+
19
+ /** apply_top_p over rows: logprobs [W, V] -> logprobs with the tail at -inf. */
20
+ export function applyTopPRows(lp: MlxArray, topP: number): MlxArray {
21
+ const V = lp.shape[1]!;
22
+ using probs = ops.exp(lp);
23
+ using sortedIdx = ops.argsortAxis(lp, -1);
24
+ using sortedProbs = ops.takeAlongAxis(probs, sortedIdx, -1);
25
+ using cum = ops.cumsum(sortedProbs, -1);
26
+ using zerosIdx = ops.zeros(sortedIdx.shape, sortedIdx.dtype);
27
+ using ar = ops.arange(0, V, 1, sortedIdx.dtype);
28
+ using arRow = ops.reshape(ar, [1, V]);
29
+ using arRows = ops.add(zerosIdx, arRow); // integer broadcast: exact
30
+ using inverse = ops.putAlongAxis(zerosIdx, sortedIdx, arRows, -1);
31
+ using cumOrig = ops.takeAlongAxis(cum, inverse, -1);
32
+ using threshold = ops.scalarLike(1 - topP, cumOrig);
33
+ using keep = ops.less(threshold, cumOrig);
34
+ using ninf = negInfLike(lp);
35
+ return ops.where(keep, lp, ninf);
36
+ }
37
+
38
+ /** apply_top_k over rows: all but each row's k highest logprobs -> -inf. */
39
+ export function applyTopKRows(lp: MlxArray, topK: number): MlxArray {
40
+ const [W, V] = lp.shape as [number, number];
41
+ using negLp = ops.neg(lp);
42
+ using part = ops.argpartitionAxis(negLp, topK - 1, -1);
43
+ using maskIdx = part.slice([0, topK], [W, V]);
44
+ using ninf = negInfLike(lp);
45
+ return ops.putAlongAxis(lp, maskIdx, ninf, -1);
46
+ }
@@ -0,0 +1,96 @@
1
+ import type { MlxArray } from "@mlx-bun/mlx/array";
2
+ import * as ops from "@mlx-bun/mlx/ops";
3
+ import type { Cache } from "../contracts/mlx/cache";
4
+ import type { MlxHeadForward, MlxSequenceForward } from "../contracts/mlx/graph";
5
+ import type { MlxPromptInput } from "../contracts/mlx/media";
6
+ import { logitsFromHiddenPadM, sequenceLogProbabilities } from "./response-logps";
7
+
8
+ /** The operations a closed-answer decision consumes. The graph and its weights
9
+ * are borrowed; each call owns fresh state. No sampling or generation is required. */
10
+ export interface DecisionScoringGraph {
11
+ makeCache(): Cache[];
12
+ forward: MlxSequenceForward;
13
+ logits: MlxHeadForward;
14
+ }
15
+
16
+ /** Teacher-forced conditional likelihoods for complete supplied answers.
17
+ * Each forward reads [prompt; answer[:-1]]. Only hidden rows that predict answer
18
+ * tokens reach the head: prompt targets are masked out. Average log p(token | prefix)
19
+ * over each answer (negative mean NLL), then softmax across candidate averages. No vocabulary restriction
20
+ * changes a token's likelihood. Include a terminator in each answer to score
21
+ * complete, mutually exclusive responses (especially when one prefixes another).
22
+ *
23
+ * Inputs and graph are borrowed; every candidate gets fresh caches, released with
24
+ * its intermediates on success, error or cancellation. The caller serializes
25
+ * shared graph access. This is inference, with no training or reference model.
26
+ * The average includes the terminator and excludes every prompt token. */
27
+ export async function scoreDecisionAnswers(graph: DecisionScoringGraph, prompt: readonly number[],
28
+ candidates: readonly (readonly number[])[], options: { signal?: AbortSignal; input?: MlxPromptInput;
29
+ /** Run one candidate's owned work at a scheduler boundary. Direct library
30
+ * callers may omit this when they already own exclusive graph access. */
31
+ execute?: <T>(work: () => Promise<T>) => Promise<T>;
32
+ } = {}): Promise<{
33
+ probabilities: number[]; meanLogLikelihoods: number[];
34
+ }> {
35
+ options.signal?.throwIfAborted();
36
+ if (!prompt.length) throw new RangeError("a decision prompt must contain tokens");
37
+ if (!candidates.length || candidates.some(answer => !answer.length || answer.some(id => !Number.isSafeInteger(id) || id < 0)))
38
+ throw new RangeError("a decision requires nonempty candidate token sequences");
39
+ const meanLogLikelihoods: number[] = [];
40
+ for (const answer of candidates) {
41
+ options.signal?.throwIfAborted();
42
+ const score = await (options.execute ?? (work => work()))(async () => {
43
+ options.signal?.throwIfAborted();
44
+ const caches = graph.makeCache();
45
+ try {
46
+ const tokens = [...prompt, ...answer.slice(0, -1)];
47
+ using ids = ops.fromInt32(tokens, [1, tokens.length]);
48
+ using hidden = options.input ? mediaResponseHidden(options.input, prompt, answer, caches, options.signal)
49
+ : graph.forward(ids, caches, tokens.length);
50
+ const [batch, length, width] = hidden.shape;
51
+ if (batch !== 1 || length !== (options.input ? answer.length : tokens.length) || !width)
52
+ throw new Error("decision forward returned an invalid hidden shape");
53
+ // Prediction at prompt.length - 1 targets answer[0], not prompt's last token.
54
+ using response = hidden.slice([0, options.input ? 0 : prompt.length - 1, 0], [1, length, width]);
55
+ using logits = logitsFromHiddenPadM({ logitsFromHidden: graph.logits }, response);
56
+ const vocab = logits.shape.at(-1)!;
57
+ if (answer.some(id => id >= vocab)) throw new RangeError("decision token id is outside the graph vocabulary");
58
+ // The DPO reduction sees only supervised rows; equivalent to a zero mask
59
+ // on all earlier prompt positions, without projecting them to vocabulary.
60
+ using logp = sequenceLogProbabilities(logits, [[prompt.at(-1)!, ...answer]], [[0, ...answer.map(() => 1)]]);
61
+ const score = logp.toFloat32()[0]! / answer.length;
62
+ options.signal?.throwIfAborted();
63
+ if (!Number.isFinite(score)) throw new Error("decision answer has a nonfinite log likelihood");
64
+ return score;
65
+ } finally {
66
+ const errors: unknown[] = [];
67
+ for (const cache of caches) { try { cache.dispose(); } catch (error) { errors.push(error); } }
68
+ if (errors.length) throw new AggregateError(errors, "decision cache cleanup failed");
69
+ }
70
+ });
71
+ meanLogLikelihoods.push(score);
72
+ await new Promise<void>(resolve => setImmediate(resolve));
73
+ }
74
+ options.signal?.throwIfAborted();
75
+ const maximum = Math.max(...meanLogLikelihoods), weights = meanLogLikelihoods.map(value => Math.exp(value - maximum));
76
+ const total = weights.reduce((sum, value) => sum + value, 0);
77
+ return { meanLogLikelihoods, probabilities: weights.map(value => value / total) };
78
+ }
79
+
80
+ /** Media ports expose the prompt tip and position-aware continuation. Feed
81
+ * supplied answer tokens through those operations, collecting their prediction
82
+ * rows; no sampling, generated ids, or generation state is involved. */
83
+ function mediaResponseHidden(input: MlxPromptInput, prompt: readonly number[], answer: readonly number[],
84
+ caches: Cache[], signal?: AbortSignal): MlxArray {
85
+ const rows: MlxArray[] = [];
86
+ try {
87
+ using ids = ops.fromInt32([...prompt], [1, prompt.length]);
88
+ rows.push(input.prefill(ids, caches, 0, "tail", prompt.length));
89
+ for (let i = 0; i < answer.length - 1; i++) {
90
+ signal?.throwIfAborted();
91
+ using token = ops.fromInt32([answer[i]!], [1, 1]);
92
+ rows.push(input.prefill(token, caches, prompt.length + i, "decode", 1));
93
+ }
94
+ return ops.concatAxis(rows, 1);
95
+ } finally { for (const row of rows) row.dispose(); }
96
+ }