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
@@ -0,0 +1,346 @@
1
+ // Per-request preparation shared by every completion surface (chat,
2
+ // raw text, /signal, /generate): request → GenerateOptions with the
3
+ // server/model defaults folded in, chat-template rendering + prompt ids,
4
+ // the stable prompt-cache boundary probe, grammar compilation, and the
5
+ // tool-call stream router. Resolved ONCE per server (createRequestPrep) and
6
+ // captured by the route handlers. Extracted from src/server.ts (repo-taming
7
+ // Phase 4).
8
+ import type { ToolDefinition } from "../chat-template";
9
+ import {
10
+ fillEchoConfig,
11
+ fillMaxSpan,
12
+ FillSession,
13
+ resolveFillMode,
14
+ } from "../fill/fill-session";
15
+ import { compileStrictFillRows, type StrictRowPlan } from "../fill/schema-rows";
16
+ import type { GenerateOptions } from "../generate";
17
+ import { compileGrammarRequest, grammarEnabled, type GrammarRequest } from "../grammar";
18
+ import type { KvSchemeOptions } from "../kv-scheme";
19
+ import { isMiniCPM5Config } from "../model/support";
20
+ import type { HlgConfig } from "../sampler";
21
+ import {
22
+ nextDefaultSeed,
23
+ normalizeMessages,
24
+ parseLogitBias,
25
+ promptEndsInOpenThink,
26
+ resolveHlg,
27
+ type ChatRequestParams,
28
+ } from "./chat-request";
29
+ import type { ServingContext } from "./model-host";
30
+ import { selectToolStreamMode, ToolAwareStream, type ToolStreamMode } from "./token-streams";
31
+
32
+ /** The ServerOptions fields request preparation reads (server-wide
33
+ * defaults a request field overrides). */
34
+ export interface RequestPrepOptions {
35
+ defaultThinking?: boolean;
36
+ defaultTemperature?: number;
37
+ defaultTopP?: number;
38
+ defaultTopK?: number;
39
+ hlg?: HlgConfig;
40
+ pagedKv?: { blockSize?: number };
41
+ }
42
+
43
+ export function createRequestPrep(input: {
44
+ ctx: ServingContext;
45
+ serverOptions: RequestPrepOptions;
46
+ /** The server-wide KV scheme (resolveKvScheme(...).generationOptions),
47
+ * spread into every request's GenerateOptions. */
48
+ kvScheme: KvSchemeOptions;
49
+ /** `--max-tokens` / the GLM memory plan's generation cap; undefined = the
50
+ * surface's own default (chat 65 536, raw completion 512). */
51
+ defaultGeneratedTokens: number | undefined;
52
+ }) {
53
+ const { ctx, serverOptions, kvScheme, defaultGeneratedTokens } = input;
54
+
55
+ // XTC never removes EOS or the newline token: mlx_lm.server passes
56
+ // [tokenizer.eos_token_id, tokenizer.encode("\n")] as xtc_special_tokens.
57
+ // We pass the flat equivalent — ALL configured EOS ids plus the bare newline
58
+ // id(s), encoded WITHOUT special tokens (mlx-lm's encode("\n") can drag a
59
+ // BOS along on some tokenizers; protecting BOS from XTC is a no-op, so the
60
+ // flat/no-BOS form is behaviorally identical and cleaner).
61
+ const xtcSpecialTokens = [
62
+ ...ctx.model.config.eosTokenIds,
63
+ ...ctx.tokenizer.encode("\n", false),
64
+ ];
65
+
66
+ // Effective enable_thinking for a request, with the same precedence the chat
67
+ // template uses (extracted so sampling and template rendering can't disagree):
68
+ // explicit chat_template_kwargs.enable_thinking → reasoning_effort ("none" =
69
+ // off) → server --thinking default → model default (MiniCPM5 → off). undefined
70
+ // means "not a switchable-thinking model / leave the template default".
71
+ const resolveEnableThinking = (req: ChatRequestParams): boolean | undefined => {
72
+ const explicit = req.chat_template_kwargs?.enable_thinking;
73
+ if (typeof explicit === "boolean") return explicit;
74
+ const effort = req.reasoning_effort;
75
+ if (effort !== undefined) return effort !== "none";
76
+ if (serverOptions.defaultThinking !== undefined) return serverOptions.defaultThinking;
77
+ return isMiniCPM5Config(ctx.model.config) ? false : undefined;
78
+ };
79
+
80
+ const toOptions = (req: ChatRequestParams): GenerateOptions & { stopSequences: string[] } => {
81
+ // Sampling follows the thinking state — which the web UI's thinking button
82
+ // drives via enable_thinking. Model authors publish a SINGLE
83
+ // generation_config temperature (the think-mode value) but recommend a
84
+ // cooler one for direct, no-think replies (MiniCPM5 card: 0.9 think / 0.7
85
+ // no-think, top_p 0.95 for both). So with no explicit temperature set, a
86
+ // no-think turn runs at most NO_THINK_TEMPERATURE while a think turn keeps
87
+ // the model's hotter configured default. An explicit request/CLI
88
+ // temperature always wins. top_p is unchanged by mode.
89
+ const NO_THINK_TEMPERATURE = 0.7;
90
+ const genTemp = ctx.genDefaults.temperature ?? 0.7;
91
+ const defaultTemp = resolveEnableThinking(req) === false
92
+ ? Math.min(genTemp, NO_THINK_TEMPERATURE)
93
+ : genTemp;
94
+ return {
95
+ // No invented cap: unset means "generate until EOS or the admitted
96
+ // context is exhausted" — admission (planRequest) is the real limit
97
+ // and clamps to maxSafeContext − promptLen. Only an explicit request
98
+ // max_tokens or the operator's --max-tokens narrows it further.
99
+ maxTokens: req.max_completion_tokens ?? req.max_tokens ??
100
+ defaultGeneratedTokens,
101
+ temperature: req.temperature ?? serverOptions.defaultTemperature ?? defaultTemp,
102
+ topP: req.top_p ?? serverOptions.defaultTopP ?? ctx.genDefaults.topP ?? 0,
103
+ topK: req.top_k ?? serverOptions.defaultTopK ?? ctx.genDefaults.topK ?? 0,
104
+ seed: req.seed ?? nextDefaultSeed(),
105
+ repetitionPenalty: req.repetition_penalty ?? ctx.genDefaults.repetitionPenalty,
106
+ // mlx_lm.server sampling extensions (defaults mirror server.py: min_p /
107
+ // xtc off, context windows 20). parseLogitBias throws on non-numeric
108
+ // input — callers surface it as a 400 (mlx-lm's coercion error).
109
+ minP: req.min_p ?? 0,
110
+ xtcProbability: req.xtc_probability ?? 0,
111
+ xtcThreshold: req.xtc_threshold ?? 0,
112
+ ...(req.xtc_probability ? { xtcSpecialTokens } : {}),
113
+ logitBias: parseLogitBias(req.logit_bias),
114
+ repetitionContextSize: req.repetition_context_size ?? 20,
115
+ presencePenalty: req.presence_penalty ?? 0,
116
+ presenceContextSize: req.presence_context_size ?? 20,
117
+ frequencyPenalty: req.frequency_penalty ?? 0,
118
+ frequencyContextSize: req.frequency_context_size ?? 20,
119
+ hlg: resolveHlg(req.hlg, serverOptions.hlg),
120
+ // generate() yields token ids; stop sequences match on decoded text
121
+ // (they can span token boundaries), so the StopMatcher sits at the
122
+ // decode layer below and halts the loop via onToken → false.
123
+ stopSequences: (typeof req.stop === "string" ? [req.stop] : req.stop ?? [])
124
+ .filter((s) => typeof s === "string" && s.length > 0),
125
+ ...kvScheme,
126
+ // Paged KV is a server-wide mode like the kv scheme above (v1:
127
+ // serial bf16 gemma4 — the createServer refusals hold the invariants).
128
+ ...(serverOptions.pagedKv ? { pagedKv: serverOptions.pagedKv } : {}),
129
+ };
130
+ };
131
+
132
+ /** Compile a grammar controller from the request's structured-output fields
133
+ * (response_format / guided_* / structured_outputs). Returns null when no
134
+ * constraint is requested OR when compile failed (degrade path). On degrade,
135
+ * `degradeHint` carries a human description for the system-prompt injection
136
+ * + Warning header (oMLX parity — never 500). Honors MLX_BUN_GRAMMAR=0. */
137
+ const compileGrammarForRequest = async (
138
+ req: ChatRequestParams,
139
+ ): Promise<{ controller: import("../grammar").GrammarController | null; degradeHint: string | null }> => {
140
+ if (!grammarEnabled()) {
141
+ return {
142
+ controller: null,
143
+ degradeHint: "grammar compilation disabled by MLX_BUN_GRAMMAR=0",
144
+ };
145
+ }
146
+ const r = await compileGrammarRequest(
147
+ req as GrammarRequest,
148
+ ctx.tokenizer,
149
+ ctx.model.config.text.vocabSize,
150
+ );
151
+ if (!r) return { controller: null, degradeHint: null };
152
+ return { controller: r.controller, degradeHint: r.degradeHint };
153
+ };
154
+
155
+ // Map OpenAI reasoning_effort levels onto the Qwen3.8 template's supported
156
+ // set (xhigh|medium|low — the template raises on anything else). "none"
157
+ // means thinking off (handled by resolveEnableThinking), so no depth is
158
+ // passed. Only consumed for templates with readsReasoningEffort.
159
+ const qwenReasoningEffort = (
160
+ effort: ChatRequestParams["reasoning_effort"],
161
+ ): "xhigh" | "medium" | "low" | undefined => {
162
+ switch (effort) {
163
+ case "minimal":
164
+ case "low":
165
+ return "low";
166
+ case "medium":
167
+ return "medium";
168
+ case "high":
169
+ case "xhigh":
170
+ return "xhigh";
171
+ default:
172
+ return undefined; // "none" / unset → template default (xhigh)
173
+ }
174
+ };
175
+
176
+ const templateOptionsFor = (req: ChatRequestParams, tools: ToolDefinition[] | null) => {
177
+ // enableThinking resolution (and its precedence) lives in
178
+ // resolveEnableThinking so template rendering and sampling stay in sync.
179
+ return {
180
+ tools,
181
+ enableThinking: resolveEnableThinking(req),
182
+ reasoningEffort: qwenReasoningEffort(req.reasoning_effort),
183
+ preserveThinking: req.chat_template_kwargs?.preserve_thinking,
184
+ };
185
+ };
186
+
187
+ const promptIdsFor = (
188
+ req: ChatRequestParams,
189
+ tools: ToolDefinition[] | null,
190
+ ): { ids: number[]; startInThinking: boolean } => {
191
+ const opts = templateOptionsFor(req, tools);
192
+ const rendered = ctx.template.render(normalizeMessages(req.messages), opts);
193
+ const ids = ctx.tokenizer.encode(rendered);
194
+ // template includes <bos>; tokenizer post-processor also prepends one
195
+ const trimmed = ids[0] === ids[1] && ids[0] === ctx.tokenizer.bosTokenId ? ids.slice(1) : ids;
196
+ return { ids: trimmed, startInThinking: promptEndsInOpenThink(rendered) };
197
+ };
198
+
199
+ /** STABLE cache boundary: the prompt's tail is a generation primer (e.g.
200
+ * 12B's `<|channel>thought` tokens) that the NEXT turn's re-render does
201
+ * not contain — a cache entry ending there can never prefix-match again
202
+ * once the rings wrap. Probe by rendering this conversation as if a
203
+ * reply existed; the common prefix is what every future turn preserves.
204
+ * The probe is EXPENSIVE (a second full render + encode — ~150 ms at
205
+ * 16k with our tokenizer; 30-50% of the 12B ctx-repeat TTFT,
206
+ * 2026-07-06b), but the PRIMER LENGTH it measures is constant per
207
+ * template mode: the divergence sits after a special-token turn
208
+ * delimiter, and special tokens break BPE merges, so message content
209
+ * can't shift it. So the full probe runs ONCE per mode and later
210
+ * requests pay a subtraction. (A wrong boundary is a QUALITY knob, not
211
+ * a correctness one — any prefix ≤ len-1 is a valid snapshot point.) */
212
+ const primerLenByMode = new Map<string, number>();
213
+ const stableLenFor = (
214
+ req: ChatRequestParams,
215
+ tools: ToolDefinition[] | null,
216
+ trimmed: number[],
217
+ ): number => {
218
+ const opts = templateOptionsFor(req, tools);
219
+ const mode = `${opts.enableThinking}|${!!tools?.length}|${opts.reasoningEffort ?? ""}|${opts.preserveThinking ?? ""}`;
220
+ const primer = primerLenByMode.get(mode);
221
+ if (primer !== undefined) return Math.max(0, trimmed.length - primer);
222
+ let stableLen = trimmed.length;
223
+ try {
224
+ const probe = ctx.tokenizer.encode(
225
+ ctx.template.render(
226
+ [...normalizeMessages(req.messages), { role: "assistant", content: "x" }],
227
+ opts,
228
+ ),
229
+ );
230
+ const probeTrimmed =
231
+ probe[0] === probe[1] && probe[0] === ctx.tokenizer.bosTokenId ? probe.slice(1) : probe;
232
+ let i = 0;
233
+ const n = Math.min(trimmed.length, probeTrimmed.length);
234
+ while (i < n && trimmed[i] === probeTrimmed[i]) i++;
235
+ stableLen = i;
236
+ // Memoize the mode's primer length (sanity-capped: a "primer" longer
237
+ // than 64 tokens means the probe diverged for content reasons —
238
+ // don't generalize that).
239
+ const p = trimmed.length - stableLen;
240
+ if (p >= 0 && p <= 64) primerLenByMode.set(mode, p);
241
+ } catch { /* probe is best-effort; full boundary is the fallback */ }
242
+ return stableLen;
243
+ };
244
+
245
+ const toolStreamMode = (tools: ToolDefinition[] | null): ToolStreamMode =>
246
+ // Gemma-4 keeps the token-level sentinel path; every other family
247
+ // (MiniCPM5, Qwen3/3.5, Tier-0 generics) emits tool calls as DECODED TEXT
248
+ // — see selectToolStreamMode for why sentinel ids must stay family-gated.
249
+ selectToolStreamMode(ctx.model.config.modelType, !!tools?.length);
250
+
251
+ const toolRouter = (
252
+ tools: ToolDefinition[] | null,
253
+ onParseFailure?: () => void,
254
+ ): ToolAwareStream =>
255
+ new ToolAwareStream(ctx.tokenizer, toolStreamMode(tools), tools, onParseFailure);
256
+
257
+ /** Strict fill rows are a function of (template mode, tool signature) only —
258
+ * they are token spans of the assistant turn, which opens with a special
259
+ * token, so message content cannot shift them (the same argument that lets
260
+ * stableLenFor memoize its primer length). Compiling costs several template
261
+ * renders + encodes, so it happens ONCE per mode. */
262
+ const fillRowsByMode = new Map<string, StrictRowPlan>();
263
+ const toolsSignature = (tools: ToolDefinition[]): string =>
264
+ JSON.stringify(tools.map((t) => [
265
+ t.function?.name,
266
+ Object.keys((t.function?.parameters?.properties ?? {}) as Record<string, unknown>),
267
+ (t.function?.parameters?.required as unknown) ?? null,
268
+ (t.function?.parameters?.additionalProperties as unknown) ?? null,
269
+ ]));
270
+
271
+ /** Token fast-forwarding (docs/design/speculative-decoding.md "Token
272
+ * fast-forwarding"): compile this request's determined-span table, or null
273
+ * when the feature is off or the request's shape refuses it.
274
+ *
275
+ * Refusals owned here (body-level); the ones only ChatStage can see —
276
+ * a compiled grammar, media prompts, a mounted draft model, a quantized KV
277
+ * scheme — are applied there. Continuous (batch) placement needs no
278
+ * refusal: generate() is the only fill site, so a batched request simply
279
+ * does not fill.
280
+ *
281
+ * MLX_BUN_FILL=echo additionally arms the echo index (K3c, Lab tier): the
282
+ * strict rows keep policy "assert", and copied spans ride the same
283
+ * mechanism under policy "verify". */
284
+ const fillPlanFor = (
285
+ req: ChatRequestParams,
286
+ tools: ToolDefinition[] | null,
287
+ promptIds: number[],
288
+ ): FillSession | null => {
289
+ const mode = resolveFillMode();
290
+ if (mode === "off") return null;
291
+ if (!tools?.length) return null;
292
+ // A user-fixed seed means reproducibility: the sampler's step index would
293
+ // skip the injected positions, so an identical request could not replay.
294
+ if (req.seed !== undefined) return null;
295
+ // Injected tokens are never sampled — they have no logprob row.
296
+ if (req.logprobs === true) return null;
297
+ if (typeof req.top_logprobs === "number" && req.top_logprobs > 0) return null;
298
+ // Structured output owns forced-token production (grammar jump-forward).
299
+ if (req.response_format != null || req.guided_grammar || req.guided_regex ||
300
+ req.guided_choice?.length || req.structured_outputs != null) return null;
301
+
302
+ const opts = templateOptionsFor(req, tools);
303
+ const key = `${opts.enableThinking}|${opts.reasoningEffort ?? ""}|` +
304
+ `${opts.preserveThinking ?? ""}|${toolsSignature(tools)}`;
305
+ let plan = fillRowsByMode.get(key);
306
+ if (plan === undefined) {
307
+ plan = compileStrictFillRows({
308
+ template: ctx.template,
309
+ tokenizer: ctx.tokenizer,
310
+ messages: normalizeMessages(req.messages),
311
+ tools,
312
+ renderOptions: opts,
313
+ });
314
+ fillRowsByMode.set(key, plan);
315
+ }
316
+ // The echo tier can carry a request whose template yields no strict rows;
317
+ // strict-only mode cannot.
318
+ if (!plan.rows.length && mode !== "echo") return null;
319
+ return new FillSession(
320
+ {
321
+ rows: plan.rows,
322
+ echo: mode === "echo" ? fillEchoConfig() : null,
323
+ delimiters: new Set(plan.delimiters),
324
+ eos: ctx.model.config.eosTokenIds,
325
+ },
326
+ promptIds,
327
+ {
328
+ maxSpan: fillMaxSpan(), decode: (ids) => ctx.tokenizer.decode([...ids]),
329
+ strictContext: plan.createContext?.(promptIds),
330
+ },
331
+ );
332
+ };
333
+
334
+ return {
335
+ resolveEnableThinking,
336
+ toOptions,
337
+ compileGrammarForRequest,
338
+ templateOptionsFor,
339
+ promptIdsFor,
340
+ stableLenFor,
341
+ toolRouter,
342
+ fillPlanFor,
343
+ };
344
+ }
345
+
346
+ export type RequestPrep = ReturnType<typeof createRequestPrep>;
@@ -0,0 +1,24 @@
1
+ import type { DisposableResource } from "../contracts/resources";
2
+
3
+ /** Keep host activity owned through response EOF/error/cancel, without buffering
4
+ * the response. The parent additionally drains worker cleanup before GPU jobs. */
5
+ export function retainResponseLease(response: Response, lease: DisposableResource): Response {
6
+ if (!response.body) { lease.dispose(); return response; }
7
+ const reader = response.body.getReader();
8
+ let closed = false;
9
+ const release = () => {
10
+ if (closed) return;
11
+ closed = true;
12
+ reader.releaseLock(); lease.dispose();
13
+ };
14
+ return new Response(new ReadableStream<Uint8Array>({
15
+ async pull(controller) {
16
+ try {
17
+ const item = await reader.read();
18
+ if (item.done) { release(); controller.close(); }
19
+ else controller.enqueue(item.value);
20
+ } catch (error) { release(); controller.error(error); }
21
+ },
22
+ async cancel(reason) { try { await reader.cancel(reason); } finally { release(); } },
23
+ }), { status: response.status, statusText: response.statusText, headers: response.headers });
24
+ }
@@ -0,0 +1,69 @@
1
+ import { ResponseStore, resolveResponsesConversation, type ResponseHistory, type ResponsesRequest } from "../responses";
2
+
3
+ /** Application-owned conversation history over a data-only HTTP host. Workers
4
+ * translate/run the request; the parent stores only completed response records. */
5
+ export function createResponsesClient(store: ResponseHistory = new ResponseStore()) {
6
+ return {
7
+ get stats() { return { entries: store.size, bytes: store.totalBytes, max_bytes: store.maxBytes, ttl_ms: store.ttlMs }; },
8
+ async forward(request: Request, forward: (request: Request) => Promise<Response>): Promise<Response> {
9
+ const error = (status: number, message: string) => Response.json({ error: {
10
+ message, type: "invalid_request_error", param: null, code: null,
11
+ } }, { status });
12
+ let body: ResponsesRequest;
13
+ try { body = await request.json() as ResponsesRequest; }
14
+ catch { return error(400, "invalid JSON body"); }
15
+ if (!body || typeof body !== "object" || Array.isArray(body)) return error(400, "invalid JSON body");
16
+ let conversation: ReturnType<typeof resolveResponsesConversation>;
17
+ try { conversation = resolveResponsesConversation(body, store); }
18
+ catch (e) { return error(404, (e as Error).message); }
19
+ const headers = new Headers(request.headers);
20
+ headers.delete("content-length");
21
+ headers.set("x-mlx-bun-response-owner", "parent");
22
+ const response = await forward(new Request(request.url, {
23
+ method: "POST", headers, signal: request.signal,
24
+ body: JSON.stringify({ ...conversation.body, previous_response_id: undefined }),
25
+ }));
26
+ if (!response.ok) return response;
27
+ const remember = (value: Record<string, unknown>) => {
28
+ if (typeof value.id === "string" && Array.isArray(value.output))
29
+ store.put(value.id, { input: conversation.input, output: value.output, instructions: conversation.instructions });
30
+ };
31
+ const restorePrevious = (value: Record<string, unknown>) => ({ ...value, previous_response_id: conversation.previousId });
32
+ if (!body.stream) {
33
+ const value = await response.json() as Record<string, unknown>;
34
+ remember(value);
35
+ const headers = new Headers(response.headers); headers.delete("content-length");
36
+ return Response.json(restorePrevious(value), { status: response.status, headers });
37
+ }
38
+ if (!response.body) return response;
39
+ const decoder = new TextDecoder(), encoder = new TextEncoder();
40
+ let pending = "";
41
+ const rewrite = (frame: string): string => frame.replace(/^data: (.+)$/m, (line, data: string) => {
42
+ if (data === "[DONE]") return line;
43
+ const event = JSON.parse(data) as Record<string, unknown>;
44
+ if (event.response && typeof event.response === "object") {
45
+ const value = event.response as Record<string, unknown>;
46
+ if (event.type === "response.completed") remember(value);
47
+ event.response = restorePrevious(value);
48
+ }
49
+ return `data: ${JSON.stringify(event)}`;
50
+ });
51
+ const outputHeaders = new Headers(response.headers); outputHeaders.delete("content-length");
52
+ return new Response(response.body.pipeThrough(new TransformStream<Uint8Array, Uint8Array>({
53
+ transform(chunk, controller) {
54
+ pending += decoder.decode(chunk, { stream: true });
55
+ let split: number;
56
+ while ((split = pending.indexOf("\n\n")) !== -1) {
57
+ const frame = pending.slice(0, split);
58
+ pending = pending.slice(split + 2);
59
+ controller.enqueue(encoder.encode(`${rewrite(frame)}\n\n`));
60
+ }
61
+ },
62
+ flush(controller) {
63
+ pending += decoder.decode();
64
+ if (pending) controller.enqueue(encoder.encode(rewrite(pending)));
65
+ },
66
+ })), { status: response.status, headers: outputHeaders });
67
+ },
68
+ };
69
+ }
@@ -0,0 +1,110 @@
1
+ import { cleanupFailure } from "../engine/resources";
2
+ import type { ExecutionPlan, InferenceMethod, Timer } from "../contracts/generation";
3
+ import { CancellationSource, throwIfCancelled } from "../engine/cancellation";
4
+ import { createInferenceEngine } from "../engine/engine";
5
+ import type { GenerateStats } from "../generate";
6
+ import type { CompletionEngine } from "./completion-executor";
7
+
8
+ const timer: Timer = {
9
+ after(ms, callback) {
10
+ const handle = setTimeout(callback, ms);
11
+ return () => clearTimeout(handle);
12
+ },
13
+ };
14
+
15
+ type RuntimeArguments = Parameters<CompletionEngine["run"]>;
16
+
17
+ export interface CompletionRunRequest {
18
+ prompt: RuntimeArguments[0]; options: RuntimeArguments[1]; vision?: RuntimeArguments[3];
19
+ shape: RuntimeArguments[4]; placement: RuntimeArguments[5];
20
+ signal?: RuntimeArguments[6]; trace?: RuntimeArguments[7];
21
+ }
22
+
23
+ /** Bind one selected runtime operation to the method-neutral session. */
24
+ export function createCompletionMethod(
25
+ runtime: CompletionEngine, request: CompletionRunRequest,
26
+ onStarted?: () => void, onFailure?: (error: unknown) => void,
27
+ ): InferenceMethod<GenerateStats> {
28
+ const { prompt, options, vision, shape, placement, signal, trace } = request;
29
+ const maxTokens = options.maxTokens ?? 512;
30
+ return {
31
+ id: placement.execution?.method ?? placement.mechanism,
32
+ async createRun(cancel) {
33
+ throwIfCancelled(cancel);
34
+ const controller = new AbortController();
35
+ const unsubscribe = cancel.subscribe(() => controller.abort(
36
+ signal?.reason ?? new DOMException("generation cancelled", "AbortError"),
37
+ ));
38
+ return {
39
+ async execute(output) {
40
+ onStarted?.();
41
+ try {
42
+ const stats = await runtime.run(prompt, options,
43
+ (token, logprobs) => output.commit([token], logprobs ? [logprobs] : undefined),
44
+ vision, shape, placement, controller.signal, trace);
45
+ return {
46
+ finishReason: stats.finishReason ?? (stats.generatedTokens >= maxTokens ? "length" : "stop"),
47
+ metrics: stats,
48
+ };
49
+ } catch (error) { onFailure?.(error); throw error; }
50
+ },
51
+ async close() { unsubscribe(); },
52
+ };
53
+ },
54
+ };
55
+ }
56
+
57
+ /** HTTP already bounds its output in CompletionSink. Deliver through the same
58
+ * session lifecycle without inserting another queue between sampling and the
59
+ * stop/tool parser. The runtime retains scheduling and native resource ownership. */
60
+ export function createSessionCompletionEngine(
61
+ runtime: CompletionEngine,
62
+ releaseUnstarted: (options: Parameters<CompletionEngine["run"]>[1],
63
+ vision: Parameters<CompletionEngine["run"]>[3]) => void,
64
+ ): CompletionEngine & { close(): Promise<void> } {
65
+ const engine = createInferenceEngine<ExecutionPlan<GenerateStats>, GenerateStats>({
66
+ async plan(plan, cancellation) { throwIfCancelled(cancellation); return plan; },
67
+ }, { timer });
68
+ return {
69
+ place: (shape, options) => runtime.place(shape, options),
70
+ async run(prompt, options, onToken, vision, shape, placement, signal, trace) {
71
+ const cancellation = new CancellationSource();
72
+ const abort = () => cancellation.cancel("requested");
73
+ signal?.addEventListener("abort", abort, { once: true });
74
+ if (signal?.aborted) abort();
75
+ let started = false;
76
+ let failure: unknown;
77
+ let failed = false;
78
+ const maxTokens = options.maxTokens ?? 512;
79
+ const method = createCompletionMethod(runtime,
80
+ { prompt, options, vision, shape, placement, signal, trace },
81
+ () => { started = true; },
82
+ (error) => { failed = true; failure = error; });
83
+ try {
84
+ const session = await engine.open({ id: method.id, method, outputTokenLimit: maxTokens }, {
85
+ output: "callback", cancellation,
86
+ async onTokens(ids, logprobs) {
87
+ for (let i = 0; i < ids.length; i++)
88
+ if (await onToken(ids[i]!, logprobs?.[i]) === false) return false;
89
+ },
90
+ });
91
+ const outcome = await session.result;
92
+ if (outcome.status === "completed") return outcome.result.metrics;
93
+ if (failed) throw failure; // preserve native/HTTP error identity and stack
94
+ if (outcome.status === "cancelled")
95
+ throw signal?.reason ?? new DOMException("generation cancelled", "AbortError");
96
+ throw new Error(outcome.error.message);
97
+ } catch (error) {
98
+ if (!started) {
99
+ started = true; // release once, preserving the original execution error
100
+ cleanupFailure(error, () => releaseUnstarted(options, vision));
101
+ }
102
+ throw error;
103
+ } finally {
104
+ signal?.removeEventListener("abort", abort);
105
+ if (!started) releaseUnstarted(options, vision);
106
+ }
107
+ },
108
+ close: () => engine.close(),
109
+ };
110
+ }
@@ -258,7 +258,7 @@ export async function searchSessions(sessionDir: string, query: string): Promise
258
258
  REST handlers — pure (URL) => Response, no `ctx` dependency, same
259
259
  convention as src/memory/rest.ts / src/hub-rest.ts (dispatched from
260
260
  server.ts by path+method; unit-tested directly in
261
- tests/session-search.test.ts without booting a model context).
261
+ tests/serve/session-search.test.ts without booting a model context).
262
262
  ──────────────────────────────────────────────────────────────────── */
263
263
 
264
264
  function jsonOk<T extends object>(body: T, init?: ResponseInit): Response {
@@ -0,0 +1,101 @@
1
+ import { readFileSync } from "node:fs";
2
+
3
+ export interface StaticRouteAssets {
4
+ appPage: string;
5
+ appJs: string;
6
+ hljsJs: string;
7
+ hljsCss: string;
8
+ manifest: string;
9
+ iconSvg: string;
10
+ serviceWorker: string;
11
+ curvePage: string;
12
+ }
13
+
14
+ const ASSET_HEADERS = { "cache-control": "public, max-age=3600" } as const;
15
+ const LEGACY_PATHS = new Set(["/status", "/chat", "/quantize", "/finetune", "/dataset"]);
16
+
17
+ /** Serve the web shell and its static companion pages. Returns null when the
18
+ * request belongs to another route family. */
19
+ export function handleStaticRoute(
20
+ url: URL,
21
+ request: Request,
22
+ assets: StaticRouteAssets,
23
+ ): Response | null {
24
+ if (request.method !== "GET") return null;
25
+
26
+ switch (url.pathname) {
27
+ case "/":
28
+ return new Response(assets.appPage, {
29
+ headers: { "content-type": "text/html; charset=utf-8" },
30
+ });
31
+ case "/assets/hljs.js":
32
+ return new Response(assets.hljsJs, {
33
+ headers: { "content-type": "text/javascript; charset=utf-8", ...ASSET_HEADERS },
34
+ });
35
+ case "/assets/hljs.css":
36
+ return new Response(assets.hljsCss, {
37
+ headers: { "content-type": "text/css; charset=utf-8", ...ASSET_HEADERS },
38
+ });
39
+ case "/assets/app.js":
40
+ return new Response(assets.appJs, {
41
+ headers: { "content-type": "text/javascript; charset=utf-8", ...ASSET_HEADERS },
42
+ });
43
+ case "/manifest.webmanifest":
44
+ return new Response(assets.manifest, {
45
+ headers: { "content-type": "application/manifest+json; charset=utf-8", ...ASSET_HEADERS },
46
+ });
47
+ case "/assets/icon.svg":
48
+ return new Response(assets.iconSvg, {
49
+ headers: { "content-type": "image/svg+xml; charset=utf-8", ...ASSET_HEADERS },
50
+ });
51
+ case "/sw.js":
52
+ return new Response(assets.serviceWorker, {
53
+ headers: { "content-type": "text/javascript; charset=utf-8", "cache-control": "no-store" },
54
+ });
55
+ case "/curves": {
56
+ let html = assets.curvePage;
57
+ try {
58
+ html = readFileSync(new URL("../lab/curve/curve-designer.html", import.meta.url), "utf8");
59
+ } catch {
60
+ // Compiled binaries use the embedded copy.
61
+ }
62
+ return new Response(html, {
63
+ headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" },
64
+ });
65
+ }
66
+ case "/dag":
67
+ try {
68
+ const html = readFileSync(
69
+ new URL("./assets/training-inference-map.html", import.meta.url),
70
+ "utf8",
71
+ );
72
+ return new Response(html, {
73
+ headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" },
74
+ });
75
+ } catch {
76
+ return new Response(
77
+ "DAG map artifact not found",
78
+ { status: 404 },
79
+ );
80
+ }
81
+ case "/curve-terrain":
82
+ try {
83
+ const html = readFileSync(
84
+ new URL("./assets/curve-terrain.html", import.meta.url),
85
+ "utf8",
86
+ );
87
+ return new Response(html, {
88
+ headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" },
89
+ });
90
+ } catch {
91
+ return new Response(
92
+ "curve terrain artifact not found",
93
+ { status: 404 },
94
+ );
95
+ }
96
+ }
97
+
98
+ return LEGACY_PATHS.has(url.pathname)
99
+ ? Response.redirect(`/#${url.pathname}`, 302)
100
+ : null;
101
+ }