mlx-bun 0.0.13 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (427) hide show
  1. package/README.md +71 -578
  2. package/bin/mlx-bun.mjs +1 -1
  3. package/docs/README.md +91 -0
  4. package/docs/{design → archive}/hlg-sampling.md +3 -3
  5. package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
  6. package/docs/{design → archive/investigations}/colibri-glm52-port.md +1 -1
  7. package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
  8. package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
  9. package/docs/{investigations → archive/investigations}/hlg-sampling-investigation.md +1 -1
  10. package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
  11. package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
  12. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +92 -0
  13. package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
  14. package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
  15. package/docs/{planning → archive/planning}/chat-ui-vision.md +1 -1
  16. package/docs/{planning → archive/planning}/chunk-task-roadmap.md +1 -1
  17. package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +4 -4
  18. package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +1 -1
  19. package/docs/{planning → archive/planning}/release-notes-v0.0.9.md +1 -1
  20. package/docs/archive/planning/release-notes-v0.2.0.md +49 -0
  21. package/docs/{planning → archive/planning}/website-readme-pass-plan.md +1 -1
  22. package/docs/design/batching.md +569 -0
  23. package/docs/design/decode-speed-program.md +3178 -169
  24. package/docs/design/dreaming-nightly-pipeline.md +675 -282
  25. package/docs/design/generic-model-support.md +579 -334
  26. package/docs/design/kv-cache.md +680 -0
  27. package/docs/design/orpo-training.md +673 -1239
  28. package/docs/design/speculative-decoding.md +1627 -0
  29. package/docs/design/turboquant.md +2102 -0
  30. package/docs/design/unified-engine-frontier-plan.md +1831 -727
  31. package/docs/design/web-chat-redesign.md +666 -1083
  32. package/docs/planning/PRODUCT_ROADMAP.md +34 -1
  33. package/docs/planning/release-notes-v0.3.0.md +35 -0
  34. package/docs/planning/video-series.md +421 -0
  35. package/docs/reference/benchmarks.md +1162 -0
  36. package/docs/reference/cli.md +395 -149
  37. package/docs/reference/distribution.md +321 -181
  38. package/docs/reference/environment.md +348 -0
  39. package/docs/reference/glossary.md +39 -0
  40. package/docs/reference/library-api.md +580 -142
  41. package/docs/reference/memory.md +227 -64
  42. package/docs/reference/models.md +137 -6
  43. package/docs/reference/server-api.md +938 -422
  44. package/docs/reference/server-config.md +652 -320
  45. package/docs/reference/training.md +393 -254
  46. package/docs/reference/troubleshooting.md +166 -0
  47. package/package.json +10 -5
  48. package/src/admission-plan.ts +18 -0
  49. package/src/anthropic.ts +92 -3
  50. package/src/audio/decode.ts +2 -2
  51. package/src/audio/features.ts +2 -2
  52. package/src/backends/mlx/autoregressive.ts +108 -0
  53. package/src/backends/mlx/batch-group.ts +1646 -0
  54. package/src/backends/mlx/diffusion.ts +39 -0
  55. package/src/backends/mlx/draft-target.ts +47 -0
  56. package/src/backends/mlx/evaluation.ts +105 -0
  57. package/src/backends/mlx/fill-append.ts +45 -0
  58. package/src/backends/mlx/gateway-binding.ts +71 -0
  59. package/src/backends/mlx/graph.ts +38 -0
  60. package/src/backends/mlx/memory-client.ts +189 -0
  61. package/src/backends/mlx/memory-guard.ts +40 -0
  62. package/src/backends/mlx/methods.ts +90 -0
  63. package/src/backends/mlx/model-prompt.ts +164 -0
  64. package/src/backends/mlx/model-serving.ts +79 -0
  65. package/src/backends/mlx/prefill.ts +33 -0
  66. package/src/backends/mlx/request-policy.ts +20 -0
  67. package/src/backends/mlx/rollback.ts +51 -0
  68. package/src/backends/mlx/serial-executor.ts +273 -0
  69. package/src/backends/mlx/speculative.ts +114 -0
  70. package/src/backends/mlx/state-views.ts +26 -0
  71. package/src/backends/mlx/text-engine.ts +73 -0
  72. package/src/cli.ts +189 -61
  73. package/src/client.ts +66 -0
  74. package/src/config.ts +21 -4
  75. package/src/contracts/completion.ts +16 -0
  76. package/src/contracts/execution.ts +49 -0
  77. package/src/contracts/generation.ts +103 -0
  78. package/src/contracts/host.ts +7 -0
  79. package/src/contracts/jobs.ts +66 -0
  80. package/src/contracts/pi-provider.ts +30 -0
  81. package/src/contracts/pi.ts +259 -0
  82. package/src/contracts/resources.ts +18 -0
  83. package/src/contracts/scheduling.ts +27 -0
  84. package/src/contracts/tsconfig.json +14 -0
  85. package/src/diffusion/diffusion-generate.ts +218 -153
  86. package/src/embed.ts +1 -1
  87. package/src/engine/admission.ts +59 -0
  88. package/src/engine/cancellation.ts +38 -0
  89. package/src/engine/engine.ts +56 -0
  90. package/src/engine/execution-coordinator.ts +70 -0
  91. package/src/engine/execution-plan.ts +51 -0
  92. package/src/engine/resources.ts +42 -0
  93. package/src/engine/scheduler.ts +67 -0
  94. package/src/engine/session.ts +247 -0
  95. package/src/eval/README.md +44 -1
  96. package/src/eval/kl.ts +263 -23
  97. package/src/eval/reference-logits.ts +207 -0
  98. package/src/eval/runner.ts +10 -79
  99. package/src/eval/tasks/bfcl.ts +2 -1
  100. package/src/eval/tasks/gsm8k.ts +2 -1
  101. package/src/eval/tasks/hashhop.ts +2 -1
  102. package/src/eval/tasks/humaneval.ts +5 -4
  103. package/src/eval/tasks/ifeval.ts +2 -1
  104. package/src/eval/tasks/mmlu.ts +2 -1
  105. package/src/expert-io.ts +3 -1
  106. package/src/expert-offload.ts +3 -1
  107. package/src/fill/echo-index.ts +188 -0
  108. package/src/fill/fill-session.ts +452 -0
  109. package/src/fill/proposal.ts +68 -0
  110. package/src/fill/schema-rows.ts +506 -0
  111. package/src/fill/tool-boundary.ts +103 -0
  112. package/src/fit.ts +24 -43
  113. package/src/generate.ts +740 -337
  114. package/src/grammar.ts +14 -8
  115. package/src/harness-pi.ts +15 -34
  116. package/src/hub-rest.ts +4 -4
  117. package/src/index.ts +27 -0
  118. package/src/inference/denoising.ts +17 -0
  119. package/src/inference/graph.ts +33 -0
  120. package/src/inference/prefill.ts +39 -0
  121. package/src/inference/rollback.ts +7 -0
  122. package/src/jobs/db.ts +4 -0
  123. package/src/jobs/index.ts +2 -0
  124. package/src/jobs/job-entry.ts +15 -14
  125. package/src/jobs/runner.ts +98 -13
  126. package/src/jobs/task-client.ts +19 -0
  127. package/src/jobs/types.ts +2 -58
  128. package/src/kv-scheme.ts +199 -0
  129. package/src/kv-store.ts +459 -234
  130. package/src/lab/README.md +18 -0
  131. package/src/{assets → lab/curve}/curve-designer.html +2 -2
  132. package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
  133. package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +7 -5
  134. package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
  135. package/src/library.ts +40 -0
  136. package/src/lora.ts +18 -0
  137. package/src/media-fetch.ts +20 -4
  138. package/src/memory/article.ts +1 -1
  139. package/src/memory/chunk.ts +1 -1
  140. package/src/memory/model.ts +32 -170
  141. package/src/memory/prompts.ts +1 -1
  142. package/src/memory/rest.ts +1 -1
  143. package/src/memory/schedule.ts +1 -1
  144. package/src/memory/stages.ts +4 -1
  145. package/src/memory/vault.ts +3 -4
  146. package/src/mlx/array.ts +9 -10
  147. package/src/mlx/compile.ts +12 -2
  148. package/src/mlx/ffi.ts +66 -22
  149. package/src/mlx/materialize.ts +23 -0
  150. package/src/mlx/metal-kernel.ts +10 -11
  151. package/src/mlx/ops.ts +64 -19
  152. package/src/mlx/quantized-rows.ts +36 -0
  153. package/src/mlx/token-bitmask.ts +52 -0
  154. package/src/mlx/turboquant-kv-decode.ts +82 -0
  155. package/src/mlx/turboquant-kv-inverse.ts +122 -0
  156. package/src/mlx/turboquant-ops.ts +33 -3
  157. package/src/mlx/turboquant-tables.ts +2 -2
  158. package/src/model/batched-mask.ts +9 -3
  159. package/src/model/batched-quant.ts +2 -2
  160. package/src/model/batched-rotating-quant.ts +45 -99
  161. package/src/model/batched-rotating-state.ts +73 -0
  162. package/src/model/batched-rotating.ts +57 -106
  163. package/src/model/batched-row-storage.ts +127 -0
  164. package/src/model/compiled-decode.ts +4 -3
  165. package/src/model/diffusion-gemma.ts +8 -8
  166. package/src/model/factory.ts +87 -49
  167. package/src/model/fingerprint.ts +1 -1
  168. package/src/model/flash-attention.ts +3 -3
  169. package/src/model/gemma4-base.ts +187 -17
  170. package/src/model/gemma4.ts +6 -6
  171. package/src/model/generated/gemma4-12b.ts +52 -148
  172. package/src/model/generated/gemma4-26b.ts +34 -94
  173. package/src/model/generated/gemma4-e4b.ts +28 -76
  174. package/src/model/glm52-cache.ts +19 -9
  175. package/src/model/glm52-dsa.ts +13 -9
  176. package/src/model/glm52-memory.ts +28 -1
  177. package/src/model/implementation.ts +69 -0
  178. package/src/model/minicpm5.ts +7 -8
  179. package/src/model/profile.ts +443 -0
  180. package/src/model/qwen-append.ts +12 -0
  181. package/src/model/qwen-conv.ts +52 -0
  182. package/src/model/qwen3-delta.ts +98 -2
  183. package/src/model/qwen3-mrope.ts +252 -0
  184. package/src/model/qwen3_5-checkpoint.ts +164 -0
  185. package/src/model/qwen3_5.ts +336 -60
  186. package/src/model/support.ts +1 -1
  187. package/src/model/trellis-balanced-scatter.ts +51 -0
  188. package/src/model/trellis-linear.ts +672 -0
  189. package/src/model/trellis-shared-m.ts +151 -0
  190. package/src/model/trellis-shared-scatter.ts +144 -0
  191. package/src/model/trellis-splitk-prefill.ts +158 -0
  192. package/src/model/trellis-tiled-prefill.ts +136 -0
  193. package/src/model/trellis-vector-expand.ts +62 -0
  194. package/src/model/trellis-wide-prefill.ts +94 -0
  195. package/src/model/universal/dense.ts +1 -1
  196. package/src/model/universal/modules.ts +8 -1
  197. package/src/native/frame_extract.swift +140 -0
  198. package/src/native-pack.ts +15 -7
  199. package/src/pi-provider.ts +6 -64
  200. package/src/pi-web.ts +14 -320
  201. package/src/prompt-cache.ts +97 -54
  202. package/src/quantize/allocator.ts +1 -1
  203. package/src/quantize/atomic-output.ts +38 -0
  204. package/src/quantize/calibration.ts +2 -1
  205. package/src/quantize/config-writer.ts +16 -3
  206. package/src/quantize/index.ts +28 -1
  207. package/src/quantize/job.ts +11 -0
  208. package/src/quantize/quantizer.ts +265 -100
  209. package/src/quantize/rotate.ts +603 -0
  210. package/src/quantize/safetensors-writer.ts +79 -1
  211. package/src/quantize/sensitivity.ts +41 -59
  212. package/src/quantize/trellis.ts +586 -0
  213. package/src/quantize/weight-transform.ts +292 -0
  214. package/src/responses.ts +130 -3
  215. package/src/runtime-config.ts +81 -0
  216. package/src/sampler.ts +276 -4
  217. package/src/serve/admin-routes.ts +184 -0
  218. package/src/serve/aux-routes.ts +132 -0
  219. package/src/serve/batch-scheduler.ts +8 -1558
  220. package/src/serve/chat-request.ts +391 -0
  221. package/src/serve/chat-stage.ts +195 -0
  222. package/src/serve/checkpoint-identity.ts +64 -0
  223. package/src/serve/completion-executor.ts +354 -0
  224. package/src/serve/completion-sink.ts +157 -0
  225. package/src/serve/discovery-routes.ts +204 -0
  226. package/src/serve/generation-gateway.ts +211 -243
  227. package/src/serve/http.ts +159 -0
  228. package/src/serve/inference-request.ts +79 -0
  229. package/src/serve/isolate.ts +297 -105
  230. package/src/serve/kv-budget.ts +14 -0
  231. package/src/serve/lab-routes.ts +341 -0
  232. package/src/serve/lane-registry.ts +2 -1
  233. package/src/serve/model-admin-routes.ts +155 -0
  234. package/src/serve/model-binding.ts +43 -0
  235. package/src/serve/model-host.ts +512 -0
  236. package/src/serve/openai-wire.ts +103 -0
  237. package/src/serve/parent-application.ts +56 -0
  238. package/src/serve/pipeline.ts +33 -0
  239. package/src/serve/preparation.ts +33 -0
  240. package/src/serve/prompt-response-trace.ts +147 -0
  241. package/src/serve/request-plan.ts +162 -0
  242. package/src/serve/request-prep.ts +346 -0
  243. package/src/serve/response-lease.ts +24 -0
  244. package/src/serve/responses-client.ts +69 -0
  245. package/src/serve/session-completion-engine.ts +110 -0
  246. package/src/serve/session-search.ts +1 -1
  247. package/src/serve/static-routes.ts +101 -0
  248. package/src/serve/text-completion-stage.ts +104 -0
  249. package/src/serve/token-streams.ts +519 -0
  250. package/src/server.ts +523 -3551
  251. package/src/spec/assistant-source.ts +10 -44
  252. package/src/spec/deepspec-source.ts +6 -6
  253. package/src/spec/dflash-source.ts +9 -8
  254. package/src/spec/dspark/ab-stats.ts +3 -3
  255. package/src/spec/dspark/calibration.ts +2 -2
  256. package/src/spec/dspark/deepspec-module.ts +2 -2
  257. package/src/spec/dspark/generate-dflash.ts +2 -2
  258. package/src/spec/dspark/generate.ts +2 -2
  259. package/src/spec/dspark/loader.ts +1 -1
  260. package/src/spec/dspark/module-dflash.ts +5 -4
  261. package/src/spec/dspark/module.ts +3 -3
  262. package/src/spec/dspark/quantize-drafter.ts +1 -1
  263. package/src/spec/generate.ts +1 -1
  264. package/src/spec/glm52-mtp-source.ts +9 -1
  265. package/src/spec/ngram-source.ts +1 -1
  266. package/src/spec/prefix-state.ts +45 -0
  267. package/src/spec/qwen-mtp-source.ts +528 -0
  268. package/src/spec/serve-loop.ts +179 -213
  269. package/src/spec/source.ts +42 -10
  270. package/src/spec/two-model.ts +1 -1
  271. package/src/ssd-cache.ts +95 -24
  272. package/src/ssd-durability.ts +207 -0
  273. package/src/tool-call.ts +51 -14
  274. package/src/train/flash-cce.ts +14 -13
  275. package/src/train/forward.ts +38 -1
  276. package/src/train/loss.ts +3 -2
  277. package/src/train/prefix-shared.ts +5 -1
  278. package/src/train/segmented.ts +9 -6
  279. package/src/train/trainer.ts +9 -8
  280. package/src/vision/preprocess.ts +1 -1
  281. package/src/vision/prompt.ts +48 -2
  282. package/src/vision/qwen3vl-preprocess.ts +409 -0
  283. package/src/vision/qwen3vl-prompt.ts +196 -0
  284. package/src/vision/qwen3vl-tower.ts +555 -0
  285. package/src/vision/video-frames.ts +148 -0
  286. package/src/web/app.html +1 -1
  287. package/src/web/app.js +15 -6
  288. package/src/web/src/adapters-panel.ts +3 -3
  289. package/src/web/src/assistant.ts +2 -36
  290. package/src/web/src/chat.ts +16 -9
  291. package/src/web/src/composer.ts +4 -4
  292. package/src/web/src/hub.ts +1 -1
  293. package/src/web/src/markdown.ts +0 -0
  294. package/src/web/src/memory-panel.ts +1 -1
  295. package/src/web/src/model-picker.ts +1 -1
  296. package/src/web/src/palette.ts +1 -1
  297. package/src/web/src/protocol.ts +3 -39
  298. package/src/web/src/sessions.ts +1 -1
  299. package/src/web/src/ui-catalog.ts +4 -18
  300. package/src/web-assets.ts +47 -0
  301. package/src/weights.ts +134 -33
  302. package/docs/archive/mlx-bun-lab-report.html +0 -275
  303. package/docs/archive/wwdc-mlx-bun.html +0 -848
  304. package/docs/design/adapters-end-to-end.md +0 -205
  305. package/docs/design/batching-perf-path.md +0 -255
  306. package/docs/design/batching-v2-plan.md +0 -529
  307. package/docs/design/bucketing-stage.md +0 -197
  308. package/docs/design/compat-cli-surface-design.md +0 -230
  309. package/docs/design/docs-reorg-plan.md +0 -165
  310. package/docs/design/dspark-serving-program.md +0 -371
  311. package/docs/design/dspark-speculative-decoding.md +0 -268
  312. package/docs/design/grammar-spec-batching-integration.md +0 -301
  313. package/docs/design/memory-inference-path.md +0 -422
  314. package/docs/design/memory-synthesis.md +0 -287
  315. package/docs/design/memory-system.md +0 -237
  316. package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
  317. package/docs/design/omlx-adoption-map.md +0 -98
  318. package/docs/design/orpo-dynamic-lambda.md +0 -220
  319. package/docs/design/orpo-future-enhancements.md +0 -340
  320. package/docs/design/paged-kv-cache.md +0 -214
  321. package/docs/design/parallel-slots.md +0 -567
  322. package/docs/design/parity-tier-dag.md +0 -161
  323. package/docs/design/repo-cleanup-plan.md +0 -189
  324. package/docs/design/runtime-isolation.md +0 -263
  325. package/docs/design/segmented-backward-training.md +0 -543
  326. package/docs/design/spec-decode-larger-targets.md +0 -229
  327. package/docs/design/ssd-kv-cold-tier.md +0 -288
  328. package/docs/design/structured-output.md +0 -484
  329. package/docs/design/tauri-desktop-app.md +0 -127
  330. package/docs/design/the-dreaming-master-plan.md +0 -445
  331. package/docs/design/turboquant-kv.md +0 -235
  332. package/docs/design/w4a16-compute-precision-spike.md +0 -232
  333. package/docs/design/web-chat-beat-matrix.md +0 -315
  334. package/docs/design/write-pipeline-entity.md +0 -468
  335. package/docs/investigations/curve-runs/bisector-route.json +0 -98
  336. package/docs/investigations/curve-runs/bisector-trace.json +0 -1393
  337. package/docs/investigations/curve-runs/curve-terrain.json +0 -1179
  338. package/docs/investigations/curve-runs/curve-terrain.svg +0 -42
  339. package/docs/investigations/hlg-ab.html +0 -34
  340. package/docs/investigations/hlg-explorer.html +0 -238
  341. package/docs/investigations/hlg-figs/cliff.svg +0 -1
  342. package/docs/investigations/hlg-figs/cross.svg +0 -1
  343. package/docs/investigations/hlg-figs/edges.svg +0 -1
  344. package/docs/investigations/hlg-figs/k20-grids.svg +0 -1
  345. package/docs/investigations/hlg-figs/k20-peaks.svg +0 -1
  346. package/docs/investigations/hlg-figs/lw-curve.svg +0 -1
  347. package/docs/investigations/hlg-figs/terrain.svg +0 -1
  348. package/docs/investigations/hlg-report.html +0 -262
  349. package/docs/investigations/hlg-runs/hlg-ab.json +0 -149
  350. package/docs/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
  351. package/docs/investigations/hlg-runs/hlg-blind.json +0 -107
  352. package/docs/investigations/hlg-runs/hlg-cliff.json +0 -197
  353. package/docs/investigations/hlg-runs/hlg-cross.json +0 -505
  354. package/docs/investigations/hlg-runs/hlg-edge.json +0 -388
  355. package/docs/investigations/hlg-runs/hlg-eval1.json +0 -25
  356. package/docs/investigations/hlg-runs/hlg-frontier.json +0 -86
  357. package/docs/investigations/hlg-runs/hlg-grid.json +0 -242
  358. package/docs/investigations/hlg-runs/hlg-grid2.json +0 -343
  359. package/docs/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
  360. package/docs/investigations/hlg-runs/hlg-map.json +0 -383
  361. package/docs/investigations/hlg-runs/run0-original.log +0 -159
  362. package/docs/investigations/hlg-runs/runA-recommended.log +0 -131
  363. package/docs/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
  364. package/docs/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
  365. package/docs/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
  366. package/docs/investigations/hlg-runs/runAD-ab-samples.log +0 -8
  367. package/docs/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
  368. package/docs/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
  369. package/docs/investigations/hlg-runs/runAG-eval-config2.log +0 -16
  370. package/docs/investigations/hlg-runs/runB-gain-variants.log +0 -192
  371. package/docs/investigations/hlg-runs/runC-pivots.log +0 -162
  372. package/docs/investigations/hlg-runs/runD-oetf.log +0 -132
  373. package/docs/investigations/hlg-runs/runE-eotf.log +0 -162
  374. package/docs/investigations/hlg-runs/runF-shaper-working.log +0 -86
  375. package/docs/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
  376. package/docs/investigations/hlg-runs/runH-wsweep.log +0 -176
  377. package/docs/investigations/hlg-runs/runI-asweep.log +0 -176
  378. package/docs/investigations/hlg-runs/runJ-knobmap.log +0 -49
  379. package/docs/investigations/hlg-runs/runK-frontier.log +0 -19
  380. package/docs/investigations/hlg-runs/runL-widemap.log +0 -65
  381. package/docs/investigations/hlg-runs/runM-frontier-emb.log +0 -15
  382. package/docs/investigations/hlg-runs/runN-verify.log +0 -116
  383. package/docs/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
  384. package/docs/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
  385. package/docs/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
  386. package/docs/investigations/hlg-runs/runR-blind-transcript.log +0 -79
  387. package/docs/investigations/hlg-runs/runS-cross-coherence.log +0 -33
  388. package/docs/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
  389. package/docs/investigations/hlg-runs/runU-cross-12b.log +0 -33
  390. package/docs/investigations/hlg-runs/runV-lw-e4b.log +0 -22
  391. package/docs/investigations/hlg-runs/runW-lw-12b.log +0 -22
  392. package/docs/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
  393. package/docs/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
  394. package/docs/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
  395. package/docs/planning/IDEAS.md +0 -69
  396. package/docs/reference/embedding.md +0 -197
  397. package/docs/reference/features-matrix.md +0 -100
  398. package/docs/reference/orpo-quickstart.md +0 -135
  399. package/src/flags.ts +0 -13
  400. /package/docs/{investigations → archive/investigations}/chunk-finetune-experiment.md +0 -0
  401. /package/docs/{investigations → archive/investigations}/colibri-oracle-pin.md +0 -0
  402. /package/docs/{investigations → archive/investigations}/curve-bisector-routing.md +0 -0
  403. /package/docs/{investigations → archive/investigations}/decode-roofline-lookagain.md +0 -0
  404. /package/docs/{investigations → archive/investigations}/dspark-handoff.md +0 -0
  405. /package/docs/{investigations → archive/investigations}/expert-offload-single-user-moe.md +0 -0
  406. /package/docs/{investigations → archive/investigations}/indexshare-performance-spike.md +0 -0
  407. /package/docs/{investigations → archive/investigations}/kernel-perf-review-2026-07.md +0 -0
  408. /package/docs/{investigations → archive/investigations}/lab-build-journal.md +0 -0
  409. /package/docs/{investigations → archive/investigations}/openwiki-evaluation.md +0 -0
  410. /package/docs/{investigations → archive/investigations}/orpo-base-uf-experiment-and-directions.md +0 -0
  411. /package/docs/{investigations → archive/investigations}/orpo-flash-cce-pin-leak.md +0 -0
  412. /package/docs/{investigations → archive/investigations}/orpo-uf-testing-handoff.md +0 -0
  413. /package/docs/{investigations → archive/investigations}/pi-builtin-investigation.md +0 -0
  414. /package/docs/{investigations → archive/investigations}/segmented-backward-handoff.md +0 -0
  415. /package/docs/{investigations → archive/investigations}/starter-model-port-handoff.md +0 -0
  416. /package/docs/{investigations → archive/investigations}/steel-flash-cce-handoff.md +0 -0
  417. /package/docs/{investigations → archive/investigations}/trainer-validation-experiment.md +0 -0
  418. /package/docs/{planning → archive/planning}/chat-ui-vision-memory-first.md +0 -0
  419. /package/docs/{planning → archive/planning}/journal.md +0 -0
  420. /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
  421. /package/docs/{planning → archive/planning}/release-notes-v0.0.10.md +0 -0
  422. /package/docs/{planning → archive/planning}/release-notes-v0.0.11.md +0 -0
  423. /package/docs/{planning → archive/planning}/release-notes-v0.0.12.md +0 -0
  424. /package/docs/{planning → archive/planning}/release-notes-v0.0.13.md +0 -0
  425. /package/docs/{planning → archive/planning}/web-ui-pass-plan.md +0 -0
  426. /package/{docs/investigations → src/serve/assets}/curve-terrain.html +0 -0
  427. /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
@@ -20,6 +20,7 @@ import { MlxArray } from "../mlx/array";
20
20
  import { Dtype } from "../mlx/ffi";
21
21
  import * as ops from "../mlx/ops";
22
22
  import { createCausalMask, type Cache, type Mask } from "../model/gemma4-base";
23
+ import { SSMCache } from "../model/qwen3-delta";
23
24
  import type { RuntimeModel } from "../model/factory";
24
25
 
25
26
  /** Stateless cache for the training forward. Training is always a single
@@ -30,6 +31,8 @@ import type { RuntimeModel } from "../model/factory";
30
31
  * backward recompute, and a stateful (appending) cache would corrupt on the
31
32
  * second run. */
32
33
  export class TrainingCache implements Cache {
34
+ /** Training-only adapter; never admitted, merged, or persisted. */
35
+ signature(): string { return "train:training"; }
33
36
  offset = 0;
34
37
  updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
35
38
  // Return fresh views: the caller disposes its input k/v right after (its
@@ -57,6 +60,37 @@ export class TrainingCache implements Cache {
57
60
  }
58
61
  }
59
62
 
63
+ /** Stateless DeltaNet stand-in (same re-runnability contract as
64
+ * TrainingCache): state WRITES are discarded immediately, so a gradient-
65
+ * checkpoint recompute re-enters with the identical null-state conditions;
66
+ * reads always yield null (zeros inside the kernel) — a full-sequence
67
+ * forward from t=0, exactly the training/perplexity semantics. Before this
68
+ * existed, qwen3_5 routed its DeltaNet layers into plain TrainingCache and
69
+ * threw on the missing conv/recurrent/advance surface (the recorded
70
+ * "mlx-bun perplexity cannot score qwen3_5" gap). */
71
+ export class TrainingSSMCache implements Cache {
72
+ /** Training-only adapter; never admitted, merged, or persisted. */
73
+ signature(): string { return "train:training-ssm"; }
74
+ offset = 0;
75
+ specRound: null = null;
76
+ get conv(): MlxArray | null { return null; }
77
+ set conv(v: MlxArray | null) { v?.dispose(); }
78
+ get recurrent(): MlxArray | null { return null; }
79
+ set recurrent(v: MlxArray | null) { v?.dispose(); }
80
+ advance(_n: number): void { /* offset pinned at 0 */ }
81
+ rowOffset(_i: number): number { return 0; }
82
+ updateAndFetch(): [MlxArray, MlxArray] {
83
+ throw new Error("TrainingSSMCache: DeltaNet layers do not use the KV path");
84
+ }
85
+ makeMask(_N: number, _w: number | null): Mask {
86
+ return { mode: "", arr: null }; // ssm_mask is None at B=1 (SSMCache parity)
87
+ }
88
+ state(): MlxArray[] { return []; }
89
+ isTrimmable(): boolean { return true; }
90
+ trim(_n: number): void { /* stateless */ }
91
+ dispose(): void { /* owns no arrays */ }
92
+ }
93
+
60
94
  /** Run a full-sequence forward for training.
61
95
  * @param ids int32 array [B, L].
62
96
  * @returns logits [B, L, V] (caller owns; dispose when done).
@@ -90,7 +124,8 @@ export function trainForwardHidden(
90
124
  // B=1 / no padding: stateless offset-0 caches (one per donor layer), so the
91
125
  // forward is pure and safe to recompute under gradient checkpointing.
92
126
  const probe = model.makeCache();
93
- const cache: Cache[] = probe.map(() => new TrainingCache());
127
+ const cache: Cache[] = probe.map((c) =>
128
+ c instanceof SSMCache ? new TrainingSSMCache() : new TrainingCache());
94
129
  for (const c of probe) c.dispose();
95
130
  try {
96
131
  return model.forwardHidden(ids, cache); // [B, L, hidden]
@@ -161,6 +196,8 @@ export function buildBatchedPadMask(
161
196
  * forwardLayers only disposes the mask it pulled from makeMask, so makeMask
162
197
  * hands back a fresh view each call and keeps the canonical copy itself. */
163
198
  class BatchedMaskCache implements Cache {
199
+ /** Training-only adapter; never admitted, merged, or persisted. */
200
+ signature(): string { return "train:batched-mask"; }
164
201
  constructor(
165
202
  private readonly inner: Cache,
166
203
  private readonly window: number | null,
package/src/train/loss.ts CHANGED
@@ -27,6 +27,7 @@ import { logitSoftcap } from "../model/gemma4-base";
27
27
  import { trainForward, trainForwardHidden } from "./forward";
28
28
  import { setLoraScale, type TrainableLora } from "./lora-params";
29
29
  import { flashCceForward, flashCceBackward, type FlashCceHead } from "./flash-cce";
30
+ import { runtimeValue } from "../runtime-config";
30
31
 
31
32
  /** The quantized LM-head weights + final-logit softcap, accessed uniformly across
32
33
  * models for the fused linear-CE head: Gemma is tied (`embed.asLinear`, softcap
@@ -1189,7 +1190,7 @@ export function fusedRespLogpMean(
1189
1190
  // [chunk,vocabBlock]. Otherwise the whole-vocab analytic head below.
1190
1191
  const vb = vocabBlock > 0 && vocabBlock < V ? vocabBlock : 0;
1191
1192
  // Auto-dispatch by M (kernel backlog #3; measured 2026-07-02 on the M1 Max,
1192
- // scripts/experiments/head-dispatch-sweep.ts): the fused QM head is FASTER
1193
+ // head-dispatch-sweep.ts (deleted 2026-08-23; git history)): the fused QM head is FASTER
1193
1194
  // at short M (e4b M=64 1.6×, M=512 1.13×; CPM5 faster at EVERY M) — the
1194
1195
  // flash head's win is residency, plus time at long M on e4b (M≥2048 after
1195
1196
  // the 2026-07-02 filter/blockMax flip; 8K: 10.7 vs 13.2 s). So a
@@ -1197,7 +1198,7 @@ export function fusedRespLogpMean(
1197
1198
  // e4b time-tie point where flash's memory win takes over) takes the EXACT
1198
1199
  // fused head instead — short rows get the faster exact head for free.
1199
1200
  // MLX_BUN_FLASH_MIN_M=0 always honors flash (the pre-dispatch behavior).
1200
- const flashMinM = Number(process.env.MLX_BUN_FLASH_MIN_M ?? "1024");
1201
+ const flashMinM = Number(runtimeValue("MLX_BUN_FLASH_MIN_M") ?? "1024");
1201
1202
  const useFlash = flash && (flashMinM <= 0 || M >= flashMinM);
1202
1203
  let op: CustomVjp;
1203
1204
  if (useFlash) {
@@ -11,7 +11,7 @@
11
11
  // prompt dominates (our chunk/document fine-tunes), ~0 when the response does.
12
12
  //
13
13
  // Bit-exact with the two-forward path (validated in
14
- // scripts/experiments/prefix-shared-parity.ts): each predicted token uses a hidden
14
+ // prefix-shared-parity.ts (deleted 2026-08-23; git history)): each predicted token uses a hidden
15
15
  // conditioned on exactly the same context + RoPE positions —
16
16
  // chosen[k] from H[P-1+k] (k=0..Rc-1; H[P-1] = prompt's last)
17
17
  // rejected[k] from H[P-1] (k=0) / H[P+Rc+k-1] (k≥1)
@@ -62,6 +62,8 @@ export function blockSparseMask(P: number, Rc: number, Rr: number): MlxArray {
62
62
  * TrainingCache) but `makeMask` returns the block-sparse mask. One per layer;
63
63
  * MiniCPM5's runLayerRange calls makeMask once and disposes the returned arr. */
64
64
  export class PrefixSharedCache implements Cache {
65
+ /** Training-only adapter; never admitted, merged, or persisted. */
66
+ signature(): string { return "train:prefix-shared"; }
65
67
  offset = 0;
66
68
  constructor(
67
69
  private readonly P: number, private readonly Rc: number, private readonly Rr: number,
@@ -371,6 +373,8 @@ export function blockSparsePrefixMaskGemma(P: number, Rc: number, Rr: number, wi
371
373
  * type serves both (the window arg selects the sliding term). One per DONOR
372
374
  * layer (sharers reuse donors' fetched KV). */
373
375
  class Gemma4PrefixSharedCache implements Cache {
376
+ /** Training-only adapter; never admitted, merged, or persisted. */
377
+ signature(): string { return "train:gemma4prefix-shared"; }
374
378
  offset = 0;
375
379
  constructor(private readonly P: number, private readonly Rc: number, private readonly Rr: number) {}
376
380
  updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
@@ -2,7 +2,7 @@
2
2
  // segment-by-segment so only ONE segment's activations are ever live, instead
3
3
  // of holding every layer's recompute activations at once (the spike that makes
4
4
  // naive gradient checkpointing crash at long context). Design + proof:
5
- // docs/design/segmented-backward-training.md (mechanism validated on a toy in
5
+ // docs/design/orpo-training.md (mechanism validated on a toy in
6
6
  // scripts/ckpt-mem-test.ts, SEG mode: 0.000% grad error, peak below per-layer
7
7
  // checkpointing; bit-exact on MiniCPM5 under flash attention).
8
8
  //
@@ -24,7 +24,7 @@
24
24
  // `value_and_grad(sum(stop_grad(dh) (.) segment_forward(...)))` is numerically
25
25
  // identical but LEAKS ~one activation buffer per segment per step at the mlx
26
26
  // level (measured; not GC/cache/synchronize/reuse reclaimable — see
27
- // docs/design/segmented-backward-training.md §9.4). mlx_vjp takes the cotangent
27
+ // docs/design/orpo-training.md §9.4). mlx_vjp takes the cotangent
28
28
  // directly, needs no surrogate, and does not leak.
29
29
  //
30
30
  // IMPORTANT lifetime fact: mlx `eval` does NOT detach — an eval'd array retains
@@ -38,6 +38,7 @@ import { MlxArray } from "../mlx/array";
38
38
  import * as ops from "../mlx/ops";
39
39
  import { peakMemory } from "../mlx/ffi";
40
40
  import { Vjp } from "../mlx/autograd";
41
+ import { runtimeValue } from "../runtime-config";
41
42
 
42
43
  // MLX_BUN_SEG_MEM_LOG=1: log the within-step peak after each phase of the
43
44
  // segmented step (forward+boundaries / head vjp / each segment backward).
@@ -46,7 +47,7 @@ import { Vjp } from "../mlx/autograd";
46
47
  // (This is how the backlog-#2 refutation below was measured. An inter-segment
47
48
  // clearCache was also tried and had NO effect — the watermark is live memory,
48
49
  // not allocator cache retention.)
49
- const SEG_MEM = process.env.MLX_BUN_SEG_MEM_LOG === "1";
50
+ const SEG_MEM = runtimeValue("MLX_BUN_SEG_MEM_LOG") === "1";
50
51
  const segMem = (label: string): void => {
51
52
  if (SEG_MEM) console.log(` [seg-mem] ${label}: peak=${(peakMemory() / 1e9).toFixed(2)} GB`);
52
53
  };
@@ -68,7 +69,7 @@ import { prefixSharedCaches, prefixGatherIdx, branchLogpMeanGathered, blockSpars
68
69
  // memory at the TOP level but not when nested in the segmented mlx_vjp (its
69
70
  // per-chunk graph isn't freed incrementally there); Checkpoint does. See
70
71
  // docs/design/orpo-training.md.
71
- const SEG_HEAD = process.env.MLX_BUN_SEG_HEAD ?? "checkpoint";
72
+ const SEG_HEAD = runtimeValue("MLX_BUN_SEG_HEAD") ?? "checkpoint";
72
73
  function boundedHeadFromHidden(
73
74
  model: MiniCPM5Model | Gemma4Model, h: MlxArray, ids: number[], mask: number[],
74
75
  chunkSize: number, sink: Array<{ dispose(): void }>, span?: HeadSpan,
@@ -82,7 +83,7 @@ import { rowLength, type SftBatch, type DpoBatch } from "./dataset";
82
83
 
83
84
  // Token-chunk size for the bounded SFT head (the ORPO paths take theirs from
84
85
  // cfg.orpoChunkSize; SFT has no per-run knob, 512 matches the ORPO default).
85
- const SFT_HEAD_CHUNK = Number(process.env.MLX_BUN_SEG_HEAD_CHUNK ?? "512");
86
+ const SFT_HEAD_CHUNK = Number(runtimeValue("MLX_BUN_SEG_HEAD_CHUNK") ?? "512");
86
87
 
87
88
  /** [M,V]-bounded response-only CE from post-finalNorm hidden — the SFT
88
89
  * segmented head (kernel-review backlog #8). Same span arithmetic and
@@ -129,7 +130,7 @@ export function planSegmentsBySize(nLayers: number, segSize: number): [number, n
129
130
  // segment_size 1 measures 14.59 GB @8K (+3% step time) vs 17.5 @seg2. Below
130
131
  // that needs the [M,V]-bounded SFT head (backlog #8, ~3 GB head-vjp spike)
131
132
  // and/or an O(L)-memory attention backward. Evidence:
132
- // scripts/experiments/seg-isolation-smoke.ts + MLX_BUN_SEG_MEM_LOG=1 probes.
133
+ // seg-isolation-smoke.ts (deleted 2026-08-23; git history) + MLX_BUN_SEG_MEM_LOG=1 probes.
133
134
 
134
135
  /** Detach `a` into a graph-free LEAF holding its exact (evaluated) bytes, then
135
136
  * dispose `a`. mlx retains the upstream graph after `eval`, so a segment
@@ -1516,6 +1517,8 @@ export class SegmentedBackwardOrpoGemma4 {
1516
1517
  * and null for full layers; the window arg selects the sliding term. One per DONOR
1517
1518
  * layer (sharers reuse donors' fetched KV inside runLayerRange). */
1518
1519
  class Gemma4PrefixSharedSegCache implements Cache {
1520
+ /** Training-only adapter; never admitted, merged, or persisted. */
1521
+ signature(): string { return "train:gemma4-prefix-shared-seg"; }
1519
1522
  offset = 0;
1520
1523
  constructor(private readonly P: number, private readonly Rc: number, private readonly Rr: number) {}
1521
1524
  updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
@@ -10,8 +10,9 @@
10
10
 
11
11
  import { MlxArray, pinnedBufferCount } from "../mlx/array";
12
12
  import { clearCache, peakMemory, activeMemory, cacheMemory, resetPeakMemory } from "../mlx/ffi";
13
+ import { runtimeValue } from "../runtime-config";
13
14
 
14
- const MEM_LOG = process.env.MLX_BUN_MEM_LOG === "1";
15
+ const MEM_LOG = runtimeValue("MLX_BUN_MEM_LOG") === "1";
15
16
  const gbStr = (b: number) => `${(b / 1e9).toFixed(2)}GB`;
16
17
  import { ValueAndGrad } from "../mlx/autograd";
17
18
  import * as ops from "../mlx/ops";
@@ -81,7 +82,7 @@ export interface TrainConfig {
81
82
  * beats per-layer checkpointing at long context, where naive checkpointing
82
83
  * holds every layer's recompute activations at once and crashes. B=1 SFT
83
84
  * only (the responseOnlyCe path); mutually exclusive with gradCheckpoint.
84
- * Numerically identical to off. See docs/design/segmented-backward-training.md. */
85
+ * Numerically identical to off. See docs/design/orpo-training.md. */
85
86
  segmentSize: number;
86
87
  /** Keep ALL eval-step checkpoints (full mountable adapters under
87
88
  * checkpoints/step-<NNNNN>-val<loss>/) + write metrics.json. Off by default
@@ -439,12 +440,12 @@ export async function trainLora(
439
440
 
440
441
  // ---------------------------------------------------------------------------
441
442
  // Segment planning: uniform planSegmentsBySize only. A full-attention
442
- // ISOLATION planner (kernel-review backlog #2 / segmented-backward-training.md
443
+ // ISOLATION planner (kernel-review backlog #2 / docs/design/orpo-training.md
443
444
  // §5) was built and A/B-measured 2026-07-02: ZERO peak win — mlx's sdpa
444
445
  // backward is O(L²) for EVERY layer (~3.5 GB/layer @8K e4b), sliding included,
445
446
  // so the worst segment is set by layer count alone and segment_size is the
446
447
  // whole knob (seg1 = 14.59 GB @8K, +3% step time — fits the 24 GB M4 Pro).
447
- // Evidence: scripts/experiments/seg-isolation-smoke.ts; note in segmented.ts.
448
+ // Evidence: seg-isolation-smoke.ts (deleted 2026-08-23; git history); note in segmented.ts.
448
449
  // ---------------------------------------------------------------------------
449
450
 
450
451
  // ---------------------------------------------------------------------------
@@ -472,7 +473,7 @@ async function sftLoop(
472
473
  // Segmented backward: stream the SFT backward segment-by-segment (only one
473
474
  // segment's activations live at a time). Phase A is MiniCPM5 SFT B=1 only;
474
475
  // it replaces the single value_and_grad below and is mutually exclusive with
475
- // gradient checkpointing (see docs/design/segmented-backward-training.md).
476
+ // gradient checkpointing (see docs/design/orpo-training.md).
476
477
  const useSegmented = cfg.segmentSize > 0;
477
478
  let segmented: SegmentedBackward | SegmentedBackwardGemma4 | null = null;
478
479
  if (useSegmented) {
@@ -531,19 +532,19 @@ async function sftLoop(
531
532
  // hand-rolled flash kernel is an opt-in (MLX_BUN_TRAIN_ATTN=flash) O(L)-memory
532
533
  // path for memory-bound long context — now correct (its dK-transpose and
533
534
  // dQ-causal-barrier bugs were fixed; FD-validated in
534
- // scripts/experiments/flash-fd-check.ts), though ~30× slower than ops.sdpa.
535
+ // flash-fd-check.ts (deleted 2026-08-23; git history)), though ~30× slower than ops.sdpa.
535
536
  // Cleared in finally.
536
537
  //
537
538
  // GEMMA GUARD (same convention as the launcher's env sanitization in
538
539
  // cli.ts / job.ts, but enforced at the trainer): e4b on this path SIGTRAPed
539
540
  // (uncatchable native crash) at multi-K sequence lengths (>=2K, reproduced in
540
- // scripts/experiments/segmented-grad-test-e4b.ts; docs/reference/training.md)
541
+ // segmented-grad-test-e4b.ts (deleted 2026-08-23; git history); docs/reference/training.md)
541
542
  // and has NOT been re-validated at that scale since the two kernel fixes —
542
543
  // the regression tests stop at T<=256. cli.ts even defaults e4b seq to 8192,
543
544
  // so a stale `export MLX_BUN_TRAIN_ATTN=flash` from a MiniCPM5 experiment
544
545
  // would otherwise ride silently into a crash mid-run. Refuse it for Gemma
545
546
  // until the >=2K re-validation lands; MiniCPM5 stays allowed.
546
- if (process.env.MLX_BUN_TRAIN_ATTN === "flash") {
547
+ if (runtimeValue("MLX_BUN_TRAIN_ATTN") === "flash") {
547
548
  if (model instanceof Gemma4Model)
548
549
  throw new Error(
549
550
  "MLX_BUN_TRAIN_ATTN=flash is disabled for Gemma models: e4b SIGTRAPs on this " +
@@ -6,7 +6,7 @@
6
6
  // 1/255 → patchify into 48×48 patch vectors with 2D grid positions →
7
7
  // pad to the 280 soft-token budget.
8
8
  //
9
- // Decode strategy (Bun ≥1.3.14): PNG goes straight through fast-png
9
+ // Decode strategy (Bun ≥1.4.0): PNG goes straight through fast-png
10
10
  // (exact, lossless — the parity-golden path). Everything else (JPEG,
11
11
  // HEIC, AVIF, WebP, TIFF, GIF, BMP) is transcoded to lossless PNG by
12
12
  // Bun.Image — native OS codecs (ImageIO on macOS), EXIF auto-orient,
@@ -8,7 +8,7 @@
8
8
  // n = min(ceil(duration_ms/40), 750) (processing_gemma4.py) — embed the full
9
9
  // sequence, then overwrite the soft-token rows with the towers' features.
10
10
  //
11
- // Attention semantics (audio-input-plan.md §3.3, resolved vs the oracle):
11
+ // Attention semantics (docs/design/generic-model-support.md §3.3, resolved vs the oracle):
12
12
  // - image runs attend bidirectionally among themselves (bidirMask), BUT
13
13
  // any audio token in the prompt disables the vision overlay entirely —
14
14
  // mixed image+audio prompts run fully causal (bidirMask = null);
@@ -26,7 +26,7 @@ import type { LoadedTokenizer } from "../tokenizer";
26
26
  import type { AudioTower } from "../audio/conformer";
27
27
  import { audioSoftTokenCount, decodeAudio } from "../audio/decode";
28
28
  import { extractMelFeatures } from "../audio/features";
29
- import { fetchMediaBytes } from "../media-fetch";
29
+ import { fetchMediaBytes, videoMediaFetchPolicy } from "../media-fetch";
30
30
 
31
31
  /** Common contract for both vision towers (encoder-free gemma4_unified in
32
32
  * ./embedder.ts and the SigLIP encoder in ./siglip.ts): preprocess image
@@ -117,6 +117,52 @@ export async function extractImages(
117
117
  return { messages: out, images };
118
118
  }
119
119
 
120
+ /** Extract video bytes from content parts, rewriting to the template's
121
+ * {type:"video"} form. Accepts {type:"video_url", video_url:{url}} (data:
122
+ * or http(s), same SSRF guard as images with the larger video body cap)
123
+ * and {type:"video"} with base64 `data`. Decode to frames happens later
124
+ * (vision/video-frames.ts — the AVFoundation sidecar). */
125
+ export async function extractVideos(
126
+ messages: ChatMessage[],
127
+ ): Promise<{ messages: ChatMessage[]; videos: Uint8Array[] }> {
128
+ const videos: Uint8Array[] = [];
129
+ const out: ChatMessage[] = [];
130
+ for (const m of messages) {
131
+ if (!Array.isArray(m.content)) {
132
+ out.push(m);
133
+ continue;
134
+ }
135
+ const parts: Array<Record<string, unknown>> = [];
136
+ for (const part of m.content) {
137
+ if (part.type === "video_url") {
138
+ const url = (part.video_url as { url: string } | undefined)?.url
139
+ ?? (part.video_url as unknown as string);
140
+ if (typeof url !== "string") throw new Error("video_url part missing url");
141
+ videos.push(await fetchMediaBytes(url, "video"));
142
+ parts.push({ type: "video" });
143
+ } else if (part.type === "video") {
144
+ if (typeof part.data === "string") {
145
+ // Same cap as fetched/data:-URL video bodies — the inline base64
146
+ // form must not be the uncapped route.
147
+ const cap = videoMediaFetchPolicy().maxBytes;
148
+ if (part.data.length * 0.75 > cap)
149
+ throw new Error(
150
+ `video part exceeds the ${Math.round(cap / 1024 / 1024)} MB cap`,
151
+ );
152
+ videos.push(Uint8Array.from(Buffer.from(part.data, "base64")));
153
+ parts.push({ type: "video" });
154
+ } else {
155
+ throw new Error("video part requires base64 `data`");
156
+ }
157
+ } else {
158
+ parts.push(part);
159
+ }
160
+ }
161
+ out.push({ ...m, content: parts });
162
+ }
163
+ return { messages: out, videos };
164
+ }
165
+
120
166
  /** Extract audio bytes from OpenAI-style content parts, rewriting the parts
121
167
  * to the template's {type:"audio"} form. Accepts the OpenAI-canonical
122
168
  * {type:"input_audio", input_audio:{data:<b64>, format:...}} plus optiq's