@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,316 @@
1
+ // Hot-swap mounted LoRA adapters — port of optiq/adapters/{mount,
2
+ // registry,resolver}.py (serving side; lora/apply.py is the training-
3
+ // side rank logic). Mount N adapters on one quantized base, select per
4
+ // request by id, never reload the base.
5
+ //
6
+ // Deviations from the reference, both deliberate:
7
+ // - No ContextVar/serve-pin: our generation queue is serialized, so the
8
+ // active adapter is a plain field (LoraState) set by generate().
9
+ // - Residual composition is mlx-lm LoRALinear / optiq apply.py
10
+ // (`y + (scale·z).astype(x.dtype)`), not mount.py's uncast f32 add —
11
+ // the cast form is what the adapters were trained behind.
12
+
13
+ import { MlxArray,cpuStream } from "@mlx-bun/mlx/array";
14
+ import { C } from "@mlx-bun/mlx/ffi";
15
+ import { transposeAxes } from "@mlx-bun/mlx/ops";
16
+ import { ptr,read } from "bun:ffi";
17
+ import { createHash } from "node:crypto";
18
+ import { existsSync,statSync } from "node:fs";
19
+ import { resolve } from "node:path";
20
+ import { SafetensorsFile } from "../artifacts/safetensors";
21
+ import type { LoraState,LoraWeights } from "../layers/lora";
22
+ import { adapterWeightsFile,readAdapterScale } from "./available";
23
+
24
+ /** The adapter manager needs named mount points, not a concrete model family. */
25
+ export interface LoraTarget {
26
+ readonly inFeatures: number;
27
+ readonly outFeatures: number;
28
+ adapters?: Map<string, LoraWeights> | null;
29
+ loraState?: LoraState | null;
30
+ }
31
+ export interface AdapterTarget {
32
+ readonly prefixBase: string;
33
+ readonly loraState: LoraState;
34
+ loraTargets(): ReadonlyMap<string, LoraTarget>;
35
+ }
36
+
37
+ const cstr = (s: string) => Buffer.from(s + "\0", "utf8");
38
+
39
+ export interface AdapterInfo {
40
+ id: string;
41
+ path: string;
42
+ rank: number | null;
43
+ scale: number;
44
+ sizeBytes: number;
45
+ mountedLayers: number;
46
+ /** Adapter tensors for modules outside the 7 target suffixes
47
+ * (per-layer-input projections etc.) — skipped, like the reference. */
48
+ skippedTensors: number;
49
+ /** Actual resident bytes of this adapter's mounted lora_a/lora_b arrays
50
+ * (sum of MlxArray.nbytes — the real RAM cost while mounted, not the
51
+ * on-disk safetensors size). Web chat's adapter routing table shows this
52
+ * per loaded adapter instead of guessing
53
+ * from file size. */
54
+ ramBytes: number;
55
+ }
56
+
57
+ /** Read every tensor from an adapter safetensors file (small, f32) into
58
+ * materialized mlx arrays. Tensor names come from our header parser;
59
+ * arrays from mlx's native loader via the proven map-get pattern
60
+ * (weights.ts). The map and header mmap are freed before returning. */
61
+ export function loadAdapterTensors(file: string): Map<string, MlxArray> {
62
+ const sf = SafetensorsFile.open(file);
63
+ const names = [...sf.tensors.keys()];
64
+ sf.mmap.unmap();
65
+
66
+ const arrMapSlot = new BigUint64Array([C.mlx_map_string_to_array_new()]);
67
+ const metaMapSlot = new BigUint64Array([C.mlx_map_string_to_string_new()]);
68
+ const arrMapPtr = ptr(arrMapSlot);
69
+ const metaMapPtr = ptr(metaMapSlot);
70
+ const status = C.mlx_load_safetensors(arrMapPtr, metaMapPtr, ptr(cstr(file)), cpuStream);
71
+ C.mlx_map_string_to_string_free(read.u64(metaMapPtr, 0));
72
+ const mapHandle = read.u64(arrMapPtr, 0);
73
+ if (status !== 0) {
74
+ C.mlx_map_string_to_array_free(mapHandle);
75
+ throw new Error(`mlx_load_safetensors(${file}) failed`);
76
+ }
77
+ const out = new Map<string, MlxArray>();
78
+ try {
79
+ for (const name of names) {
80
+ const slot = new BigUint64Array([C.mlx_array_new()]);
81
+ const slotPtr = ptr(slot);
82
+ if (C.mlx_map_string_to_array_get(slotPtr, mapHandle, ptr(cstr(name))) !== 0)
83
+ throw new Error(`adapter tensor ${name} missing from native map`);
84
+ const arr = new MlxArray(read.u64(slotPtr, 0));
85
+ arr.eval(); // materialize — the map (and its Load refs) is freed below
86
+ out.set(name, arr);
87
+ }
88
+ } catch (e) {
89
+ for (const a of out.values()) a.dispose();
90
+ C.mlx_map_string_to_array_free(mapHandle);
91
+ throw e;
92
+ }
93
+ C.mlx_map_string_to_array_free(mapHandle);
94
+ return out;
95
+ }
96
+
97
+ /** Parse an adapter selection spec: "sft" | "sft+dpo" | "sft,dpo"
98
+ * (reference mount.py stacking syntax — residuals sum in order). */
99
+ export function parseAdapterSpec(spec: string): string[] {
100
+ const sep = spec.includes("+") ? "+" : ",";
101
+ return spec.split(sep).map((s) => s.trim()).filter(Boolean);
102
+ }
103
+
104
+ export class AdapterManager {
105
+ readonly #model: AdapterTarget;
106
+ readonly #mounted = new Map<string, AdapterInfo>();
107
+ /** Owned adapter arrays per id (disposed on unmount). */
108
+ readonly #arrays = new Map<string, MlxArray[]>();
109
+ readonly #revisions = new Map<string, string>();
110
+
111
+ constructor(model: AdapterTarget) {
112
+ this.#model = model;
113
+ }
114
+
115
+ /** Mount an adapter directory under `id`. Validates layer-name and
116
+ * rank/shape compatibility against the base BEFORE any weights are
117
+ * attached — a bad adapter fails here, never at request time
118
+ * (reference: resolver.py + mount.py add_adapter checks). */
119
+ async mount(id: string, dir: string): Promise<AdapterInfo> {
120
+ if (this.#mounted.has(id)) return this.#mounted.get(id)!;
121
+ if (id.includes("+") || id.includes(","))
122
+ throw new Error(`adapter id ${JSON.stringify(id)} may not contain '+' or ','`);
123
+ const path = resolve(dir);
124
+ if (!existsSync(path)) throw new Error(`adapter dir not found: ${path}`);
125
+
126
+ const weightsFile = adapterWeightsFile(path);
127
+ const { scale, rank: configRank, rsLora } = await readAdapterScale(path);
128
+ // Hash once at mount, before native allocation. Reusing an adapter name
129
+ // for different weights or scale must never reuse its prefix/checkpoint.
130
+ const digest = createHash("sha256").update(JSON.stringify({ scale, rank: configRank, rsLora }));
131
+ for await (const chunk of Bun.file(weightsFile).stream()) digest.update(chunk);
132
+ const revision = digest.digest("hex");
133
+ const tensors = loadAdapterTensors(weightsFile);
134
+ const targets = this.#model.loraTargets();
135
+
136
+ // Group adapter tensors into (modulePath → {a, b}), probing both the
137
+ // pure-LLM and VLM-wrapped prefixes like the reference _find_weight_pair.
138
+ const pairs = new Map<string, {
139
+ a?: MlxArray;
140
+ b?: MlxArray;
141
+ aIsPeft?: boolean;
142
+ bIsPeft?: boolean;
143
+ }>();
144
+ let skipped = 0;
145
+ const ourPrefix = `${this.#model.prefixBase}.layers.`;
146
+ const altPrefix = ourPrefix.startsWith("language_model.")
147
+ ? ourPrefix.slice("language_model.".length)
148
+ : `language_model.${ourPrefix}`;
149
+ for (const [name, arr] of tensors) {
150
+ const m = name.match(/^(.*)\.(lora_a|lora_A|lora_b|lora_B)(\.weight)?$/);
151
+ if (!m) { skipped++; arr.dispose(); continue; }
152
+ let modulePath = m[1]!;
153
+ // Standard PEFT state dicts wrap the model path in
154
+ // `base_model.model.`. The remainder is the underlying HF module path
155
+ // (`model.layers...` or `language_model.model.layers...`).
156
+ if (modulePath.startsWith("base_model.model."))
157
+ modulePath = modulePath.slice("base_model.model.".length);
158
+ if (modulePath.startsWith(altPrefix))
159
+ modulePath = ourPrefix + modulePath.slice(altPrefix.length);
160
+ if (!targets.has(modulePath)) { skipped++; arr.dispose(); continue; }
161
+ const slot = pairs.get(modulePath) ?? {};
162
+ const peftName = m[3] === ".weight" || m[2] === "lora_A" || m[2] === "lora_B";
163
+ if (m[2]!.toLowerCase() === "lora_a") {
164
+ slot.a = arr;
165
+ slot.aIsPeft = peftName;
166
+ } else {
167
+ slot.b = arr;
168
+ slot.bIsPeft = peftName;
169
+ }
170
+ pairs.set(modulePath, slot);
171
+ }
172
+
173
+ // Validate every pair against the base linear's dims before mounting
174
+ // anything (all-or-nothing: a bad adapter must not half-mount).
175
+ const validated: { linear: LoraTarget; lw: LoraWeights }[] = [];
176
+ const dispose = () => {
177
+ for (const { a, b } of pairs.values()) { a?.dispose(); b?.dispose(); }
178
+ };
179
+ try {
180
+ for (const [modulePath, pair] of pairs) {
181
+ const { a, b } = pair;
182
+ if (!a || !b)
183
+ throw new Error(`${modulePath}: adapter has only one of lora_a/lora_b`);
184
+ const linear = targets.get(modulePath)!;
185
+ if (a.shape.length !== 2 || b.shape.length !== 2)
186
+ throw new Error(
187
+ `${modulePath}: LoRA tensors must be rank-2; got ` +
188
+ `lora_a [${a.shape}] / lora_b [${b.shape}]`,
189
+ );
190
+
191
+ const internalLayout =
192
+ a.shape[0] === linear.inFeatures &&
193
+ b.shape[0] === a.shape[1] &&
194
+ b.shape[1] === linear.outFeatures;
195
+ const peftLayout =
196
+ a.shape[1] === linear.inFeatures &&
197
+ b.shape[1] === a.shape[0] &&
198
+ b.shape[0] === linear.outFeatures;
199
+ if (!internalLayout && !peftLayout)
200
+ throw new Error(
201
+ `${modulePath}: shape mismatch — lora_a [${a.shape}] / lora_b [${b.shape}] ` +
202
+ `vs base [in ${linear.inFeatures}, out ${linear.outFeatures}]. Expected ` +
203
+ `mlx-lm [in, rank] + [rank, out] or PEFT [rank, in] + [out, rank].`,
204
+ );
205
+
206
+ // Full-rank square adapters satisfy both shape predicates. In that
207
+ // ambiguous case the PEFT spelling is authoritative; otherwise the
208
+ // unique compatible layout decides.
209
+ const usePeftLayout =
210
+ peftLayout && (!internalLayout || (pair.aIsPeft === true && pair.bIsPeft === true));
211
+ let mountedA = a;
212
+ let mountedB = b;
213
+ if (usePeftLayout) {
214
+ let transposedA: MlxArray | null = null;
215
+ let transposedB: MlxArray | null = null;
216
+ try {
217
+ transposedA = transposeAxes(a, [1, 0]);
218
+ transposedB = transposeAxes(b, [1, 0]);
219
+ } catch (e) {
220
+ transposedA?.dispose();
221
+ transposedB?.dispose();
222
+ throw e;
223
+ }
224
+ mountedA = transposedA;
225
+ mountedB = transposedB;
226
+ pair.a = mountedA;
227
+ pair.b = mountedB;
228
+ a.dispose();
229
+ b.dispose();
230
+ }
231
+ const rank = mountedA.shape[1]!;
232
+ // rsLoRA: effective per-layer scale is α/√rank (matches training).
233
+ const effScale = rsLora ? scale / Math.sqrt(rank) : scale;
234
+ validated.push({ linear, lw: { a: mountedA, b: mountedB, scale: effScale, rank } });
235
+ }
236
+ if (validated.length === 0)
237
+ throw new Error(
238
+ `failed to mount adapter ${JSON.stringify(id)}: no tensors match the ` +
239
+ `target modules (q/k/v/o/gate/up/down_proj). Check that the adapter ` +
240
+ `was trained for this base model.`,
241
+ );
242
+ } catch (e) {
243
+ dispose();
244
+ throw e;
245
+ }
246
+
247
+ for (const { linear, lw } of validated) {
248
+ (linear.adapters ??= new Map()).set(id, lw);
249
+ linear.loraState = this.#model.loraState;
250
+ }
251
+
252
+ const ramBytes = validated.reduce((sum, { lw }) => sum + lw.a.nbytes + lw.b.nbytes, 0);
253
+ const info: AdapterInfo = {
254
+ id,
255
+ path,
256
+ rank: configRank,
257
+ scale,
258
+ sizeBytes: statSync(weightsFile).size,
259
+ mountedLayers: validated.length,
260
+ skippedTensors: skipped,
261
+ ramBytes,
262
+ };
263
+ this.#mounted.set(id, info);
264
+ this.#revisions.set(id, revision);
265
+ this.#arrays.set(id, validated.flatMap(({ lw }) => [lw.a, lw.b]));
266
+ this.#model.loraState.refreshBindings();
267
+ return info;
268
+ }
269
+
270
+ /** Remove `id` from every linear (frees its arrays). The mount points
271
+ * stay in place for other adapters, like the reference. */
272
+ unmount(id: string): number {
273
+ if (!this.#mounted.delete(id)) return 0;
274
+ this.#revisions.delete(id);
275
+ let removed = 0;
276
+ for (const linear of this.#model.loraTargets().values()) {
277
+ if (linear.adapters?.delete(id)) removed++;
278
+ }
279
+ for (const a of this.#arrays.get(id) ?? []) a.dispose();
280
+ this.#arrays.delete(id);
281
+ if (this.#model.loraState.active.includes(id))
282
+ this.#model.loraState.active = this.#model.loraState.active.filter((x) => x !== id);
283
+ return removed;
284
+ }
285
+
286
+ /** Ordered, content-addressed namespace for the actual mounted composition. */
287
+ cacheNamespace(ids: readonly string[]): string {
288
+ return ids.map((id) => {
289
+ const revision = this.#revisions.get(id);
290
+ if (!revision) throw new Error(`adapter ${id} is no longer mounted`);
291
+ return `${id}@${revision}`;
292
+ }).join("+");
293
+ }
294
+
295
+ list(): AdapterInfo[] {
296
+ return [...this.#mounted.values()];
297
+ }
298
+
299
+ get(id: string): AdapterInfo | undefined {
300
+ return this.#mounted.get(id);
301
+ }
302
+
303
+ /** Resolve a request's adapter spec to validated ids ([] for none).
304
+ * Unknown ids are an error — selection must fail loudly, not no-op. */
305
+ resolveSpec(spec: string | null | undefined): string[] {
306
+ if (!spec || spec.toLowerCase() === "none") return [];
307
+ const ids = parseAdapterSpec(spec);
308
+ for (const id of ids)
309
+ if (!this.#mounted.has(id))
310
+ throw new Error(
311
+ `unknown adapter ${JSON.stringify(id)} — mounted: ` +
312
+ `${[...this.#mounted.keys()].join(", ") || "(none)"}`,
313
+ );
314
+ return ids;
315
+ }
316
+ }
@@ -0,0 +1,45 @@
1
+ // Auxiliary checkpoint files: the non-weight files (tokenizer, chat template,
2
+ // generation config, …) a written model directory needs to stay loadable.
3
+ // Checkpoint producers (quantization, adapter fusion) copy them through verbatim.
4
+
5
+ import { existsSync } from "node:fs";
6
+ import { join } from "node:path";
7
+
8
+ /** Aux files copied through verbatim from the source dir when present. */
9
+ const AUX_FILES = [
10
+ "tokenizer.json",
11
+ "tokenizer_config.json",
12
+ "tokenizer.model",
13
+ "spiece.model",
14
+ "chat_template.jinja",
15
+ "generation_config.json",
16
+ "special_tokens_map.json",
17
+ "added_tokens.json",
18
+ "vocab.json",
19
+ "merges.txt",
20
+ "README.md",
21
+ "kv_config.json",
22
+ ];
23
+
24
+ /** Copy known aux files (and any `*.model`) from src → out when present. */
25
+ export async function copyAuxFiles(srcDir: string, outDir: string): Promise<void> {
26
+ const seen = new Set<string>();
27
+ for (const f of AUX_FILES) {
28
+ const src = join(srcDir, f);
29
+ if (existsSync(src)) {
30
+ await Bun.write(join(outDir, f), Bun.file(src));
31
+ seen.add(f);
32
+ }
33
+ }
34
+ // Sweep any additional *.model tokenizer sidecars not in the known list.
35
+ try {
36
+ const { readdirSync } = await import("node:fs");
37
+ for (const f of readdirSync(srcDir)) {
38
+ if (f.endsWith(".model") && !seen.has(f)) {
39
+ await Bun.write(join(outDir, f), Bun.file(join(srcDir, f)));
40
+ }
41
+ }
42
+ } catch {
43
+ // src dir unreadable — aux copy is best-effort.
44
+ }
45
+ }
@@ -0,0 +1,94 @@
1
+ // How a `config.json` differs from the plain Hugging Face layout, by `model_type`.
2
+ // A dialect is a reading rule for the artifact, not a statement about a model
3
+ // family (`models/families.ts` owns those): `loadModelConfig` applies exactly one
4
+ // and branches on its fields, never on a model type.
5
+
6
+ /** How `rope_parameters` is spelled. `map`: a per-attention-type map, absent means none.
7
+ * `theta`: as `map`, and a flat `rope_theta` stands in for a missing map.
8
+ * `flat`: one flat dict (`rope_theta`, `partial_rotary_factor`) for full attention. */
9
+ export type RopeLayout = "map" | "theta" | "flat";
10
+
11
+ export interface ConfigDialect {
12
+ /** Text-config fields the config omits: the architecture's own defaults. Fields present still win. */
13
+ readonly textDefaults?: () => Record<string, any>;
14
+ /** Geometry and stop tokens come from a converted container's own parser. */
15
+ readonly container?: "colibri";
16
+ readonly rope: RopeLayout;
17
+ /** `layer_types` when the config declares none: every layer full attention. */
18
+ readonly fullAttentionByDefault?: true;
19
+ /** Refuse a value this loader has not verified rather than mis-read it. */
20
+ readonly validate?: (text: Record<string, any>) => void;
21
+ }
22
+
23
+ /** DiffusionGemma's config.json ships only token ids + canvas_length + the
24
+ * quant map — the architecture dims live in optiq's `config.py` TextConfig
25
+ * defaults (and layer_types / rope_parameters are computed in __post_init__).
26
+ * We reproduce those defaults in snake_case so the generic parser below picks
27
+ * them up; any field actually present in config.json still overrides. Source:
28
+ * optiq/vlm/_mlxvlm/models/diffusion_gemma/config.py. */
29
+ function diffusionGemmaRawDefaults(): Record<string, any> {
30
+ const numLayers = 30;
31
+ const pattern = ["sliding_attention", "sliding_attention", "sliding_attention",
32
+ "sliding_attention", "sliding_attention", "full_attention"];
33
+ const layer_types = Array.from({ length: numLayers }, (_, i) => pattern[i % pattern.length]);
34
+ layer_types[numLayers - 1] = "full_attention"; // last forced full
35
+ return {
36
+ hidden_size: 2816,
37
+ num_hidden_layers: numLayers,
38
+ num_attention_heads: 16,
39
+ num_key_value_heads: 8,
40
+ num_global_key_value_heads: 2,
41
+ head_dim: 256,
42
+ global_head_dim: 512,
43
+ intermediate_size: 2112,
44
+ moe_intermediate_size: 704,
45
+ hidden_activation: "gelu_pytorch_tanh",
46
+ rms_norm_eps: 1e-6,
47
+ vocab_size: 262144,
48
+ max_position_embeddings: 262144,
49
+ sliding_window: 1024,
50
+ layer_types,
51
+ enable_moe_block: true,
52
+ num_experts: 128,
53
+ top_k_experts: 8,
54
+ final_logit_softcapping: 30.0,
55
+ tie_word_embeddings: true,
56
+ bos_token_id: 2,
57
+ eos_token_id: 1,
58
+ rope_parameters: {
59
+ sliding_attention: { rope_type: "default", rope_theta: 10000.0 },
60
+ full_attention: { rope_type: "proportional", partial_rotary_factor: 0.25, rope_theta: 1000000.0 },
61
+ },
62
+ };
63
+ }
64
+
65
+ /** Qwen3.8 adds output_gate_type ("swish"), which every implementation
66
+ * (transformers ground truth, mlx-lm pinned + main) currently ignores —
67
+ * the attention output gate is hardcoded o_proj(out·σ(gate)). Only accept
68
+ * values verified to mean that; an unknown value must fail at load, not
69
+ * silently mis-gate a future checkpoint where the field starts mattering. */
70
+ function verifyOutputGate(text: Record<string, any>): void {
71
+ if (text.output_gate_type != null && text.output_gate_type !== "swish")
72
+ throw new Error(
73
+ `qwen3_5: unverified output_gate_type "${text.output_gate_type}" (only "swish" ` +
74
+ `is verified to mean the standard sigmoid output gate)`,
75
+ );
76
+ }
77
+
78
+ const PLAIN: ConfigDialect = Object.freeze({ rope: "map" });
79
+ const FLAT_THETA: ConfigDialect = Object.freeze({ rope: "theta" });
80
+
81
+ /** The reading rule for a config's `model_type`. Anything not listed reads as the plain layout. */
82
+ export function dialectFor(modelType: unknown): ConfigDialect {
83
+ if (modelType === "diffusion_gemma") return { rope: "map", textDefaults: diffusionGemmaRawDefaults };
84
+ if (modelType === "glm_moe_dsa") return { rope: "map", container: "colibri" };
85
+ // Qwen3.5 rope_parameters is a flat dict ({type, rope_theta, mrope_section,
86
+ // partial_rotary_factor}), not the gemma per-attention-type map — and
87
+ // type "default" means plain partial nn.RoPE (mrope_section ignored for text).
88
+ if (typeof modelType === "string" && modelType.startsWith("qwen3_5")) return { rope: "flat", validate: verifyOutputGate };
89
+ // Llama-style flat configs: a scalar rope_theta and no rope_parameters map.
90
+ if (modelType === "llama") return { rope: "theta", fullAttentionByDefault: true };
91
+ // Plain Qwen3 (Qwen3ForCausalLM, e.g. Qwen3-Embedding): handled like llama for rope.
92
+ if (modelType === "qwen3") return FLAT_THETA;
93
+ return PLAIN;
94
+ }