@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,80 @@
1
+ /** Describes an implementation's ABI, not a claim of oracle parity or speed. */
2
+ export interface GraphDescriptor {
3
+ readonly id: string;
4
+ readonly backend: string;
5
+ readonly graphAbi: string;
6
+ readonly stateAbi: string;
7
+ readonly artifact: string;
8
+ }
9
+
10
+ /** How prepared media reaches the graph.
11
+ * - `embeddings`: spliced prompt embeddings (image/audio soft tokens, optional
12
+ * bidirectional and multimodal masks) enter the ordinary forward.
13
+ * - `embeddings+positions`: spliced embeddings with request-owned multi-axis
14
+ * rotary positions and a decode delta.
15
+ * - `pixels`: raw image pixels are the denoising method's own prefill input. */
16
+ export type GraphMediaInput = "embeddings" | "embeddings+positions" | "pixels";
17
+
18
+ /** Portable facts about a bound graph. Composition binds the
19
+ * operations these facts require and refuses what is genuinely missing;
20
+ * scheduling reads only these declarations plus request and dynamic state
21
+ * (batch membership, cache contents, cancellation), never a model's class or
22
+ * name. A declaration that promises an operation is checked where it is bound. */
23
+ export interface GraphCapabilities {
24
+ /** The method the graph's forward serves. Token-level methods (speculation,
25
+ * grammar proposals, fill) bind only to `autoregressive`. */
26
+ readonly method: "autoregressive" | "denoising";
27
+ /** Prepared media the graph accepts, or null. Requests carrying prepared
28
+ * embeddings need a `bindMediaInput` operation; `video` says video frames
29
+ * are accepted input. */
30
+ readonly media: { readonly input: GraphMediaInput; readonly video: boolean } | null;
31
+ /** The graph owns a compiled decode step. The composition keeps it on only
32
+ * where its storage has a compiled form (`Composition.compiledDecode`). */
33
+ readonly compiledDecode: boolean;
34
+ /** Attention selects a sparse subset of positions (surfaced to clients). */
35
+ readonly sparseAttention: boolean;
36
+ /** Derived from the bound graph's pooled text-embedding operation. */
37
+ readonly embeddings: boolean;
38
+ /** Hidden-layer outputs can be tapped for drafts that read them. */
39
+ readonly hiddenLayerTaps: boolean;
40
+ /** A draft head the graph's own checkpoint carries and has loaded, or null.
41
+ * `kind` names the native draft provider that binds to this graph; the
42
+ * provider registry resolves it, so no consumer names the graph or its
43
+ * provider. `numDraftTokens` is the head's round width. */
44
+ readonly nativeDraft: { readonly kind: string; readonly numDraftTokens: number } | null;
45
+ /** How prefill is sized for this graph. */
46
+ readonly prefill: {
47
+ /** Long prompts prefill in smaller chunks so the attention score matrices
48
+ * a fallback (unfused) attention materializes stay within a fixed
49
+ * workspace. It changes work size, never admission. */
50
+ readonly boundedWorkspace: boolean;
51
+ };
52
+ /** What the graph's target verification qualifies beyond ordinary rounds.
53
+ * Each is a property of the graph's verifier, independent of the draft that
54
+ * supplies proposals and of how the composition's caches store keys and
55
+ * values (every cache lego holds the speculative transaction). */
56
+ readonly speculation: {
57
+ /** Verification under mounted adapters. */
58
+ readonly adapters: boolean;
59
+ /** Verification of requests that return logprobs. */
60
+ readonly logprobs: boolean;
61
+ /** Verification of externally supplied tokens (echo of a known continuation). */
62
+ readonly externalTokens: boolean;
63
+ /** Verified grammar-constraint proposals. */
64
+ readonly grammarProposals: boolean;
65
+ };
66
+ }
67
+
68
+ /** Facts supplied by a graph at construction. Pooled embedding support is the
69
+ * presence of its operation; only the bound portable description carries the
70
+ * derived boolean. `never` also rejects separately typed declaration objects
71
+ * that try to supply that flag. */
72
+ export type GraphDeclaration = Omit<GraphCapabilities, "embeddings"> & {
73
+ readonly embeddings?: never;
74
+ };
75
+
76
+ /** A fused InferenceMethod can bypass this graph interface entirely. It need
77
+ * only satisfy the method's output, cancellation, and ownership contracts. */
78
+ export interface GraphFactory<Artifact, Binding, Graph> {
79
+ open(artifact: Artifact, binding: Binding): Promise<{ graph: Graph; close(): Promise<void> }>;
80
+ }
@@ -0,0 +1,15 @@
1
+ export * from "./composition";
2
+ export * from "./denoising";
3
+ export * from "./execution";
4
+ export * from "./generation";
5
+ export * from "./graph";
6
+ export * from "./memory-plan";
7
+ export * from "./object-cache";
8
+ export * from "./prefill";
9
+ export * from "./prefix-cache";
10
+ export * from "./prompt";
11
+ export * from "./resources";
12
+ export * from "./rollback";
13
+ export * from "./sampling";
14
+ export * from "./scheduling";
15
+ export * from "./token-delivery";
@@ -0,0 +1,26 @@
1
+ /** Common admission contract for every runtime. Representation-specific
2
+ * planners may expose richer fields, but serving consumes only this shape. */
3
+ export interface MemoryPlan {
4
+ readonly schemaVersion: 1;
5
+ /** Names the accounting the plan comes from; informational, never branched on. */
6
+ readonly strategy: string;
7
+ readonly fits: boolean;
8
+ readonly contextTokens: number;
9
+ readonly maxSafeContext: number;
10
+ readonly weightsBytes: number;
11
+ readonly kvBytes: number;
12
+ readonly transientBytes: number;
13
+ readonly reserveBytes: number;
14
+ readonly totalBytes: number;
15
+ readonly usableBytes: number;
16
+ readonly predictedDecodeTps: number | null;
17
+ /** mlx allocator-cache limit, distinct from the process admission limit. */
18
+ readonly allocatorLimitBytes?: number;
19
+ /** Generated-token allowance reserved within `contextTokens`; absent when the plan reserves none. */
20
+ readonly maxGenerationTokens?: number;
21
+ /** Parts of `weightsBytes` held in bounded streaming tiers instead of resident
22
+ * memory, by display label. Resident weights are `weightsBytes` minus their sum. */
23
+ readonly streamedWeights?: Readonly<Record<string, number>>;
24
+ /** RAM the plan leaves to the rest of the machine. */
25
+ readonly machineHeadroomBytes?: number;
26
+ }
@@ -0,0 +1,8 @@
1
+ /** Exact reusable work, independent of token-prefix matching. The same cache
2
+ * owner chooses residency and persistence for objects and conversation state. */
3
+ export interface ObjectCache<Value> {
4
+ /** Borrow immutable data through an owned lease. A miss permits recompute. */
5
+ take(key: string): Promise<{ value: Value; dispose(): void } | null>;
6
+ /** Successful return transfers ownership, including immediate eviction. */
7
+ put(key: string, value: Value): void;
8
+ }
@@ -0,0 +1,63 @@
1
+ /** Request-level chunk policy. Execution consumes the selected size; model
2
+ * backends can account for their temporary workspace without changing admission. */
3
+ export interface PrefillPolicy {
4
+ chunkSize(promptTokens: number): number;
5
+ }
6
+
7
+ export interface PrefillPosition {
8
+ readonly length: number;
9
+ readonly position: number;
10
+ readonly chunkSize: number;
11
+ readonly tailSplit: boolean;
12
+ readonly snapshotAt?: number | null;
13
+ }
14
+
15
+ /** The phase a prompt step runs in, named by the planner that cut it: a whole
16
+ * planned `chunk` (exactly the step's chunk size), a `tail` (any other number
17
+ * of positions above one: what is left after the whole chunks, or a piece a
18
+ * snapshot boundary cuts short), or `decode`, a single position, which appends
19
+ * through the one-position read. */
20
+ export type PrefillPhase = "chunk" | "tail" | "decode";
21
+
22
+ export interface PrefillStep {
23
+ readonly start: number;
24
+ readonly end: number;
25
+ readonly kind: "drain" | "final";
26
+ readonly snapshot: boolean;
27
+ /** Batch admission may finish a short last drain and token zero in one tick.
28
+ * Otherwise execution yields between every drain and the next forward. */
29
+ readonly batchYield: boolean;
30
+ readonly phase: PrefillPhase;
31
+ }
32
+
33
+ /** The phase of a step the planner made `whole` (a full chunk) or not, from
34
+ * `start` to `end`. */
35
+ export function prefillPhase(start: number, end: number, whole: boolean): PrefillPhase {
36
+ return end - start === 1 ? "decode" : whole ? "chunk" : "tail";
37
+ }
38
+
39
+ /** The prefill program a row follows, alone or in a batch. No tensors, runtime policy reads,
40
+ * or sampling. Advance position only after executing the returned step. */
41
+ export function nextPrefillStep(input: PrefillPosition): PrefillStep {
42
+ const { length, position, chunkSize, tailSplit, snapshotAt } = input;
43
+ if (!Number.isSafeInteger(length) || !Number.isSafeInteger(position) ||
44
+ !Number.isSafeInteger(chunkSize) || chunkSize < 1 || position < 0 || position >= length)
45
+ throw new Error("invalid prefill position or chunk size");
46
+ if (snapshotAt != null && (!Number.isSafeInteger(snapshotAt) || snapshotAt < 0 || snapshotAt > length))
47
+ throw new Error("invalid prefill snapshot boundary");
48
+ if (snapshotAt != null && snapshotAt > position && snapshotAt < length) {
49
+ const whole = position + chunkSize <= snapshotAt;
50
+ const end = whole ? position + chunkSize : snapshotAt;
51
+ return { start: position, end, kind: "drain", snapshot: end === snapshotAt, batchYield: true,
52
+ phase: prefillPhase(position, end, whole) };
53
+ }
54
+ const remaining = length - position;
55
+ if (remaining > (tailSplit ? 1 : chunkSize)) {
56
+ const whole = !tailSplit || chunkSize <= remaining - 1;
57
+ const end = position + (whole ? chunkSize : remaining - 1);
58
+ return { start: position, end, kind: "drain", snapshot: false,
59
+ batchYield: !tailSplit || remaining - 1 > chunkSize, phase: prefillPhase(position, end, whole) };
60
+ }
61
+ return { start: position, end: length, kind: "final", snapshot: false, batchYield: false,
62
+ phase: prefillPhase(position, length, remaining === chunkSize) };
63
+ }
@@ -0,0 +1,33 @@
1
+ /** A reusable prefix and the backend/method state that covers its tokens.
2
+ * The caller owns a returned hit. After releasing or returning its state,
3
+ * it must release any retained backing through retain. */
4
+ export interface PrefixCacheHit<State, Attachment = never> {
5
+ tokens: number[];
6
+ caches: State;
7
+ attachments?: Attachment;
8
+ retain?: () => void;
9
+ }
10
+
11
+ /** Storage is independent of request scheduling and the method producing
12
+ * state. Both prefill and decode can publish processed-token prefixes.
13
+ * State is opaque here; its backend/method defines layout and alignment.
14
+ * Implementations own prefix selection, retention and storage tiers. */
15
+ export interface PrefixCache<State, Attachment = never> {
16
+ /** Give optional retained state back to the allocator at an execution
17
+ * boundary. This never rejects or changes the active request. */
18
+ reclaim?(): void;
19
+ /** Drop session affinity without deleting reusable checkpoints. */
20
+ closeSession?(sessionId: string): void;
21
+ /** Prepare a reusable prefix before execution. The returned release ends
22
+ * request interest; cache residency and IO remain implementation-owned. */
23
+ prefetch?(prompt: number[], ns?: string, sessionId?: string): Promise<() => void>;
24
+ /** Return owned state for a usable session continuation, otherwise the
25
+ * longest usable prefix, or null on a miss. Session identity is an affinity
26
+ * hint and never changes the content or numerical namespace.
27
+ * Namespace distinguishes incompatible artifacts, methods and adapters. */
28
+ take(prompt: number[], ns?: string, sessionId?: string): PrefixCacheHit<State, Attachment> | null;
29
+ /** Transfer state and its optional backing release to storage. If this
30
+ * throws, ownership stays with the caller; successful return transfers
31
+ * ownership even when storage immediately spills or disposes the entry. */
32
+ put(tokens: number[], caches: State, ns?: string, retain?: () => void, attachments?: Attachment, sessionId?: string): void;
33
+ }
@@ -0,0 +1,2 @@
1
+ /** Execute native prompt preparation under caller-owned resource coordination. */
2
+ export type PromptNativeWork = <T>(work: () => Promise<T>) => Promise<T>;
@@ -0,0 +1,25 @@
1
+ /** A single owner. Borrowing does not extend lifetime; transfer invalidates
2
+ * the old owner. close is idempotent, including when release throws. */
3
+ export interface ResourceOwner<T> {
4
+ borrow(): T;
5
+ transfer(): T;
6
+ close(): void;
7
+ }
8
+
9
+ /** State views may own temporary handles or borrow live state. Consumers
10
+ * always close the lease; only the provider knows which handles to release. */
11
+ export interface StateView<T> {
12
+ borrow(): readonly T[];
13
+ close(): void;
14
+ }
15
+
16
+ export interface DisposableResource {
17
+ dispose(): void;
18
+ }
19
+
20
+ /** A prepared replacement for live state. Commit once between method rounds;
21
+ * it publishes already-built state without graph work or validation. Dispose
22
+ * releases the unpublished replacement, or the superseded state after commit. */
23
+ export interface PreparedStateChange extends DisposableResource {
24
+ commit(): void;
25
+ }
@@ -0,0 +1,7 @@
1
+ /** A complete transaction over every participating target state layer.
2
+ * A failed begin/resolve invalidates the run; its state must be discarded. */
3
+ export interface SpeculativeTransaction<Acceptance = number> {
4
+ canBegin(drafts: number): boolean;
5
+ begin(drafts: number): void;
6
+ resolve(accepted: Acceptance): void;
7
+ }
@@ -0,0 +1,44 @@
1
+ /** Sampling owns score processing, token selection, and request-local history.
2
+ * Score/token representations and sync/device execution belong to the backend;
3
+ * this contract has no scheduling, cache, or tensor-runtime dependency. */
4
+ export interface SampledToken<Token, Metadata> {
5
+ token: Token;
6
+ extras: Metadata | null;
7
+ }
8
+
9
+ export interface SamplingSession<Scores, DeviceToken, Result> {
10
+ /** Optional stateless operation shared across compatible rows and positions.
11
+ * It borrows scores and returns owned device tokens, without advancing history. */
12
+ readonly independent?: { sample(scores: Scores): DeviceToken };
13
+ /** Optional per-position operation for ONE request whose selection depends on
14
+ * no grammar or probability capture: position p's draw depends only on its
15
+ * scores, its step and its processor history. Borrows [N,V] scores and returns
16
+ * owned device tokens [N] for the given steps, without advancing history.
17
+ * A request with processor history (`needsHistory`) also passes the window's
18
+ * inputs [N] (borrowed: the pending token, then the drafts): position p reads
19
+ * its history extended by inputs[1..p], the history sample() would hold there
20
+ * had every earlier position drawn its draft. Draws are those sample() would
21
+ * make at each step: seeded sampling keys by (seed, step), never by a shared
22
+ * stream, so unused positions cost nothing. `commit` takes the tokens a walk
23
+ * over the window used, in order, and advances history as sample() would have
24
+ * advanced it drawing each. */
25
+ readonly positional?: {
26
+ sample(scores: Scores, steps: readonly number[], inputs?: DeviceToken): DeviceToken;
27
+ commit(tokens: readonly number[]): void;
28
+ };
29
+ /** Optional draw left on the device for a request whose selection depends on
30
+ * its processor history but on no grammar or probability capture: borrows
31
+ * [1,V] scores and returns the owned device token sample() would draw at
32
+ * `step`, committing it to history unread when sample() would commit it. */
33
+ readonly unread?: { sample(scores: Scores, step: number): DeviceToken };
34
+ readonly isPlainGreedy: boolean;
35
+ readonly capturesLogprobs: boolean;
36
+ readonly needsHistory: boolean;
37
+ /** Borrows scores; transfers ownership of the result to the caller. */
38
+ sample(logits: Scores, step: number): Result;
39
+ seedHistory(tokens: readonly number[]): void;
40
+ /** Commit accepted tokens when configured for manual history updates. */
41
+ commitDevice(token: DeviceToken): void;
42
+ commitNumbers(tokens: readonly number[]): void;
43
+ dispose(): void;
44
+ }
@@ -0,0 +1,54 @@
1
+ /** Backend-owned context shared by compatible requests. A key identifies
2
+ * equivalent execution state; enter returns its scoped release. Scheduling
3
+ * observes group readiness without inspecting this context. */
4
+ export interface ExecutionContext {
5
+ readonly key: string;
6
+ enter(): () => void;
7
+ }
8
+
9
+ /** A backend execution group exposes bounded work units and readiness. Queue
10
+ * payloads, tensors, cache merging and sampling remain inside its adapter. */
11
+ export interface ExecutionGroup {
12
+ /** Native preparation may borrow the execution lease between work units.
13
+ * Payloads and task failures belong to the backend's queue. */
14
+ readonly pendingTasks?: number;
15
+ advanceTask?(): Promise<void>;
16
+ readonly active: number;
17
+ readonly queued: number;
18
+ readonly preparing: boolean;
19
+ readonly preparingRows?: number;
20
+ /** The preparation owner can accept another row before its next work unit. */
21
+ readonly canPrepareMore?: boolean;
22
+ /** Work estimates affect grouping only. A request larger than this budget
23
+ * still runs alone; it is never rejected or sent to another executor. */
24
+ readonly preparingTokens?: number;
25
+ readonly nextPreparationTokens?: number;
26
+ readonly maxPreparationTokens?: number;
27
+ /** The latest preparation published output that can be flushed by yielding. */
28
+ readonly preparationPublishedOutput?: boolean;
29
+ readonly maxActive: number;
30
+ readonly admissionHeld: boolean;
31
+ readonly closed: boolean;
32
+ pruneCancelled(): void;
33
+ /** May reject/remove an impossible queued request before returning false. */
34
+ admitNext(): boolean;
35
+ /** Read-only budget check used to preserve admission before decode. */
36
+ canBurst(): boolean;
37
+ /** Method-owned token demand for a compatible mixed iteration. Absent when
38
+ * the backend has no packed implementation for the current work. */
39
+ readonly mixedPreparation?: { readonly runningTokens: number; readonly minimumPreparationTokens: number };
40
+ readonly maxIterationTokens?: number;
41
+ advanceMixed?(tokenBudget: number): Promise<void>;
42
+ advancePreparation(): Promise<void>;
43
+ advance(): Promise<void>;
44
+ failActive(error: unknown): void;
45
+ failAll(error: unknown): void;
46
+ reserveResidency(): () => void;
47
+ acquireExecution?(): Promise<() => void>;
48
+ waitForWork(): Promise<void>;
49
+ }
50
+
51
+ export interface SchedulingClock {
52
+ now(): number;
53
+ yield(): Promise<void>;
54
+ }
@@ -0,0 +1,4 @@
1
+ import type { TokenLogprobs } from "./generation";
2
+
3
+ /** Returning false stops delivery after the current token. */
4
+ export type OnToken = (token: number, logprobs?: TokenLogprobs) => void | boolean | Promise<void | boolean>;
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ES2022"],
4
+ "target": "ES2022",
5
+ "module": "Preserve",
6
+ "moduleResolution": "bundler",
7
+ "types": [],
8
+ "strict": true,
9
+ "skipLibCheck": false,
10
+ "noUncheckedIndexedAccess": true,
11
+ "noEmit": true
12
+ },
13
+ "include": ["./*.ts"]
14
+ }
@@ -0,0 +1 @@
1
+ export * from "./text";
@@ -0,0 +1,85 @@
1
+ // Text-embedding entry point. Wraps a graph's `embedPooled` (last-token hidden,
2
+ // L2-normalized vector) with the input convention its profile declares, so the
3
+ // CLI (`mlx-bun embed`), the server (`/v1/embeddings`), and in-repo experiments
4
+ // all produce the SAME vectors — the ones verified bit-exact vs mlx-lm and main in
5
+ // tests/parity/embedding-parity.test.ts (opt-in) for the Qwen3-Embedding backbone.
6
+
7
+ import type { MlxArray } from "@mlx-bun/mlx/array";
8
+ import { declaredGraph } from "../models/capabilities";
9
+ import type { LoadedTokenizer } from "../input/tokenizer";
10
+ import * as ops from "@mlx-bun/mlx/ops";
11
+
12
+ /** The operation behind a graph's `embeddings` capability. */
13
+ export interface EmbeddingGraph { embedPooled(ids: MlxArray): MlxArray }
14
+
15
+ /** A graph declares the pooled-embedding path. */
16
+ export function isEmbeddingModel(model: object): model is EmbeddingGraph {
17
+ return typeof declaredGraph(model).embedPooled === "function";
18
+ }
19
+
20
+ /** The token id of the profile's declared pooling terminator in this tokenizer. */
21
+ export function embeddingTerminatorId(tok: Pick<LoadedTokenizer, "encode">, terminator: string): number {
22
+ const ids = tok.encode(terminator, false);
23
+ if (ids.length !== 1) throw new Error(`embedding terminator ${JSON.stringify(terminator)} is not a single token in this tokenizer`);
24
+ return ids[0]!;
25
+ }
26
+
27
+ /** Qwen3-Embedding query format: a task instruction steers WHICH similarity axis
28
+ * the geometry reflects ("represent this for topic routing" vs "...for
29
+ * sentiment"). Documents are embedded raw; only queries get an instruction. */
30
+ export function withInstruction(text: string, instruction?: string): string {
31
+ return instruction ? `Instruct: ${instruction}\nQuery:${text}` : text;
32
+ }
33
+
34
+ /** Tripwire: counts embedOne invocations (embedMany accrues via embedOne) so
35
+ * callers can assert the pipeline embeds exactly as many times as expected. */
36
+ let embedCallCount = 0;
37
+
38
+ /** Reset the embed tripwire counter to 0. */
39
+ export function resetEmbedCounter(): void {
40
+ embedCallCount = 0;
41
+ }
42
+
43
+ /** Read the embed tripwire counter (total embedOne calls since last reset). */
44
+ export function getEmbedCounter(): number {
45
+ return embedCallCount;
46
+ }
47
+
48
+ export interface EmbedResult {
49
+ /** L2-normalized embedding (length = model hidden size). */
50
+ vector: Float32Array;
51
+ /** Token count of the embedded input (incl. the pooling token). */
52
+ tokens: number;
53
+ }
54
+
55
+ /** Embed one text. `terminator` is the pooling token id from
56
+ * `embeddingTerminatorId`; `instruction` (optional) applies the query format. */
57
+ export function embedOne(
58
+ model: EmbeddingGraph,
59
+ tok: LoadedTokenizer,
60
+ terminator: number,
61
+ text: string,
62
+ instruction?: string,
63
+ ): EmbedResult {
64
+ embedCallCount++;
65
+ // addSpecialTokens=false matches the bit-exact parity path (no BOS;
66
+ // we append the pooling token ourselves).
67
+ const ids = [...tok.encode(withInstruction(text, instruction), false), terminator];
68
+ const idArr = ops.fromInt32(ids, [1, ids.length]);
69
+ const vec = model.embedPooled(idArr);
70
+ idArr.dispose();
71
+ const out = vec.toFloat32();
72
+ vec.dispose();
73
+ return { vector: out, tokens: ids.length };
74
+ }
75
+
76
+ /** Embed many texts (one forward each — the runtime is single-sequence). */
77
+ export function embedMany(
78
+ model: EmbeddingGraph,
79
+ tok: LoadedTokenizer,
80
+ terminator: number,
81
+ texts: string[],
82
+ instruction?: string,
83
+ ): EmbedResult[] {
84
+ return texts.map((t) => embedOne(model, tok, terminator, t, instruction));
85
+ }
@@ -0,0 +1,59 @@
1
+ import type { Cancellation } from "../contracts/portable/generation";
2
+ import type { DisposableResource } from "../contracts/portable/resources";
3
+ import { throwIfCancelled } from "./cancellation";
4
+
5
+ export class AdmissionRejected extends Error {
6
+ constructor() { super("admission queue is full"); this.name = "AdmissionRejected"; }
7
+ }
8
+
9
+ /** FIFO reservations. A cancelled waiter releases no active owner's capacity. */
10
+ export class AdmissionPool {
11
+ #active = 0;
12
+ #closed = false;
13
+ #waiting: Array<{ resolve(lease: DisposableResource): void; reject(error: unknown): void; unsubscribe(): void }> = [];
14
+ constructor(readonly capacity: number, readonly maxQueued = 64) {
15
+ if (!Number.isSafeInteger(capacity) || capacity < 1 || !Number.isSafeInteger(maxQueued) || maxQueued < 0)
16
+ throw new Error("invalid admission capacity");
17
+ }
18
+ get active(): number { return this.#active; }
19
+ get queued(): number { return this.#waiting.length; }
20
+
21
+ acquire(cancellation?: Cancellation): Promise<DisposableResource> {
22
+ if (this.#closed) return Promise.reject(new Error("admission is closed"));
23
+ try { if (cancellation) throwIfCancelled(cancellation); }
24
+ catch (error) { return Promise.reject(error); }
25
+ if (this.#active < this.capacity) return Promise.resolve(this.#lease());
26
+ if (this.#waiting.length >= this.maxQueued) return Promise.reject(new AdmissionRejected());
27
+ return new Promise((resolve, reject) => {
28
+ const waiter = { resolve, reject, unsubscribe() {} };
29
+ this.#waiting.push(waiter);
30
+ waiter.unsubscribe = cancellation?.subscribe((reason) => {
31
+ const index = this.#waiting.indexOf(waiter);
32
+ if (index < 0) return;
33
+ this.#waiting.splice(index, 1);
34
+ waiter.unsubscribe();
35
+ reject(new Error(`admission cancelled: ${reason}`));
36
+ }) ?? (() => {});
37
+ if (!this.#waiting.includes(waiter)) waiter.unsubscribe();
38
+ });
39
+ }
40
+
41
+ #lease(): DisposableResource {
42
+ this.#active++;
43
+ let released = false;
44
+ return { dispose: () => {
45
+ if (released) return;
46
+ released = true;
47
+ this.#active--;
48
+ const next = this.#waiting.shift();
49
+ if (next) { next.unsubscribe(); next.resolve(this.#lease()); }
50
+ } };
51
+ }
52
+
53
+ close(): void {
54
+ this.#closed = true;
55
+ for (const waiter of this.#waiting.splice(0)) {
56
+ waiter.unsubscribe(); waiter.reject(new Error("admission is closed"));
57
+ }
58
+ }
59
+ }