mlx-bun 0.5.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (640) hide show
  1. package/README.md +1449 -109
  2. package/THIRD_PARTY_NOTICES.md +882 -0
  3. package/bin/mlx-bun.convert.mjs +4 -0
  4. package/bin/mlx-bun.fuse.mjs +4 -0
  5. package/bin/mlx-bun.generate.mjs +4 -0
  6. package/bin/mlx-bun.lora.mjs +4 -0
  7. package/bin/mlx-bun.mjs +13 -21
  8. package/bin/mlx-bun.server.mjs +4 -0
  9. package/bin/mlx-bun.upload.mjs +4 -0
  10. package/dist/web/app.js +9584 -0
  11. package/package.json +50 -53
  12. package/src/cli/ane-install.ts +76 -0
  13. package/src/cli/args.ts +179 -0
  14. package/src/cli/engine-entry.ts +68 -0
  15. package/src/cli/inference.ts +214 -0
  16. package/src/cli/job-entry.ts +97 -0
  17. package/src/cli/library-host.ts +117 -0
  18. package/src/cli/main.ts +57 -0
  19. package/src/cli/memory-engine.ts +163 -0
  20. package/src/cli/memory.ts +36 -0
  21. package/src/cli/mlx-lm-aliases.ts +309 -0
  22. package/src/cli/model-choice.ts +33 -0
  23. package/src/cli/model-selection.ts +84 -0
  24. package/src/cli/module-host.ts +11 -0
  25. package/src/cli/module-verbs.ts +50 -0
  26. package/src/cli/numerical-policy.ts +12 -0
  27. package/src/cli/perplexity.ts +89 -0
  28. package/src/cli/serve-host.ts +423 -0
  29. package/src/cli/serve-isolated.ts +217 -0
  30. package/src/cli/serve-options.ts +106 -0
  31. package/src/cli/serve-state.ts +205 -0
  32. package/src/cli/serve.ts +500 -0
  33. package/src/cli/served-model-host.ts +102 -0
  34. package/src/cli/server-entry.ts +148 -0
  35. package/src/cli/serving-unit.ts +177 -0
  36. package/src/{tui.ts → cli/terminal.ts} +13 -8
  37. package/src/cli/worker-adapters.ts +34 -0
  38. package/src/cli/worker-entry.ts +272 -0
  39. package/src/cli/worker-unit.ts +179 -0
  40. package/src/engine/adapter-operation.ts +26 -0
  41. package/src/engine/cache-services.ts +114 -0
  42. package/src/engine/completion.ts +39 -0
  43. package/src/engine/generation-gateway.ts +358 -0
  44. package/src/engine/index.ts +59 -0
  45. package/src/engine/kv-budget.ts +120 -0
  46. package/src/engine/media-preparation.ts +232 -0
  47. package/src/engine/model-binding.ts +24 -0
  48. package/src/engine/model-host.ts +513 -0
  49. package/src/engine/model-serving.ts +89 -0
  50. package/src/{serve → engine}/preparation.ts +7 -5
  51. package/src/{serve → engine}/session-completion-engine.ts +6 -6
  52. package/src/engine/telemetry.ts +109 -0
  53. package/src/hub/downloads.ts +116 -0
  54. package/src/jobs/db.ts +16 -7
  55. package/src/jobs/events.ts +29 -0
  56. package/src/jobs/executable.ts +5 -0
  57. package/src/jobs/host.ts +51 -0
  58. package/src/jobs/in-process.ts +55 -0
  59. package/src/{contracts/jobs.ts → jobs/protocol.ts} +7 -9
  60. package/src/jobs/runner.ts +150 -231
  61. package/src/jobs/service.ts +75 -0
  62. package/src/jobs/sse.ts +59 -23
  63. package/src/jobs/worker-memory.ts +46 -0
  64. package/src/jobs/worker-process.ts +163 -0
  65. package/src/jobs/worker-supervisor.ts +258 -0
  66. package/src/modules.ts +58 -0
  67. package/src/publishing/catalog-hub.ts +18 -0
  68. package/src/publishing/credentials.ts +31 -0
  69. package/src/publishing/upload.ts +37 -0
  70. package/src/residency/leased-adapters.ts +21 -0
  71. package/src/residency/local-records.ts +16 -0
  72. package/src/residency/model-residency.ts +405 -0
  73. package/src/residency/record-index.ts +28 -0
  74. package/src/residency/resident-estimate.ts +50 -0
  75. package/src/{anthropic.ts → server/anthropic.ts} +3 -81
  76. package/src/server/cache-routes.ts +36 -0
  77. package/src/{serve → server}/chat-request.ts +10 -10
  78. package/src/{serve → server}/chat-stage.ts +14 -14
  79. package/src/{client.ts → server/client.ts} +28 -2
  80. package/src/{serve → server}/completion-executor.ts +33 -41
  81. package/src/server/decision-body.ts +55 -0
  82. package/src/server/decision-routes.ts +134 -0
  83. package/src/server/decision-wire.ts +228 -0
  84. package/src/{serve → server}/discovery-routes.ts +25 -82
  85. package/src/{serve → server}/http.ts +25 -13
  86. package/src/{serve → server}/inference-request.ts +5 -5
  87. package/src/server/job-routes.ts +20 -0
  88. package/src/server/media-prompt.ts +63 -0
  89. package/src/server/memory-completion-client.ts +46 -0
  90. package/src/server/model-routes.ts +192 -0
  91. package/src/{serve → server}/openai-wire.ts +3 -2
  92. package/src/{serve → server}/pipeline.ts +1 -1
  93. package/src/server/prompt-builder.ts +13 -0
  94. package/src/server/prompt-contracts.ts +17 -0
  95. package/src/server/proxy-routes.ts +187 -0
  96. package/src/server/publishing-routes.ts +41 -0
  97. package/src/{serve → server}/request-plan.ts +6 -8
  98. package/src/{serve → server}/request-prep.ts +47 -50
  99. package/src/{serve → server}/responses-client.ts +30 -13
  100. package/src/{responses.ts → server/responses.ts} +12 -93
  101. package/src/server/routes.ts +205 -0
  102. package/src/server/start.ts +72 -0
  103. package/src/server/status-routes.ts +108 -0
  104. package/src/{serve → server}/text-completion-stage.ts +25 -4
  105. package/src/server/text-prompt.ts +12 -0
  106. package/src/{serve → server}/token-streams.ts +36 -38
  107. package/src/server/worker-routes.ts +311 -0
  108. package/src/storage/paths.ts +61 -0
  109. package/src/web/assets.ts +37 -0
  110. package/src/web/{src → browser}/api.ts +5 -23
  111. package/src/web/browser/chat-host.ts +17 -0
  112. package/src/web/browser/installed-panels.ts +6 -0
  113. package/src/web/browser/main.ts +55 -0
  114. package/src/web/browser/markdown.ts +279 -0
  115. package/src/web/browser/memory-panel.ts +9 -0
  116. package/src/web/browser/palette.ts +64 -0
  117. package/src/web/browser/protocol.ts +13 -0
  118. package/src/web/browser/shell.ts +467 -0
  119. package/src/web/{src → browser}/status.ts +32 -36
  120. package/src/web/build.ts +51 -0
  121. package/src/web/public/app.html +1054 -0
  122. package/src/web/{sw.js → public/sw.js} +4 -3
  123. package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
  124. package/src/web/{vendor → public/vendor}/hljs.js +2 -2
  125. package/THIRD_PARTY_LICENSES.md +0 -51
  126. package/docs/README.md +0 -107
  127. package/docs/archive/hlg-sampling.md +0 -350
  128. package/docs/archive/investigations/audio-input-plan.md +0 -356
  129. package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +0 -3617
  130. package/docs/archive/investigations/chunk-finetune-experiment.md +0 -84
  131. package/docs/archive/investigations/colibri-glm52-port.md +0 -1486
  132. package/docs/archive/investigations/colibri-oracle-pin.md +0 -1284
  133. package/docs/archive/investigations/curve-bisector-routing.md +0 -123
  134. package/docs/archive/investigations/decode-roofline-lookagain.md +0 -348
  135. package/docs/archive/investigations/diffusion-gemma-port.md +0 -602
  136. package/docs/archive/investigations/dspark-handoff.md +0 -224
  137. package/docs/archive/investigations/expert-offload-single-user-moe.md +0 -292
  138. package/docs/archive/investigations/faithful-l1-consolidation.md +0 -310
  139. package/docs/archive/investigations/hlg-sampling-investigation.md +0 -970
  140. package/docs/archive/investigations/indexshare-performance-spike.md +0 -357
  141. package/docs/archive/investigations/kernel-perf-review-2026-07.md +0 -171
  142. package/docs/archive/investigations/lab-build-journal.md +0 -157
  143. package/docs/archive/investigations/minicpm5-decode-megakernel.md +0 -489
  144. package/docs/archive/investigations/openjarvis-fit.md +0 -198
  145. package/docs/archive/investigations/openwiki-evaluation.md +0 -249
  146. package/docs/archive/investigations/optimization_plan.md +0 -346
  147. package/docs/archive/investigations/orpo-base-uf-experiment-and-directions.md +0 -120
  148. package/docs/archive/investigations/orpo-flash-cce-pin-leak.md +0 -167
  149. package/docs/archive/investigations/orpo-uf-testing-handoff.md +0 -126
  150. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +0 -92
  151. package/docs/archive/investigations/pi-builtin-investigation.md +0 -308
  152. package/docs/archive/investigations/pre-colibri-stabilization.md +0 -329
  153. package/docs/archive/investigations/qwen38-27b-campaign-log.md +0 -1092
  154. package/docs/archive/investigations/segmented-backward-handoff.md +0 -148
  155. package/docs/archive/investigations/starter-model-port-handoff.md +0 -342
  156. package/docs/archive/investigations/steel-flash-cce-handoff.md +0 -489
  157. package/docs/archive/investigations/the-dreaming-handoff.md +0 -141
  158. package/docs/archive/investigations/trainer-validation-experiment.md +0 -173
  159. package/docs/archive/planning/chat-ui-vision-memory-first.md +0 -199
  160. package/docs/archive/planning/chat-ui-vision.md +0 -50
  161. package/docs/archive/planning/chunk-task-roadmap.md +0 -77
  162. package/docs/archive/planning/curve-sampler-research-plan.md +0 -331
  163. package/docs/archive/planning/journal.md +0 -316
  164. package/docs/archive/planning/memory-docs-and-dag-plan.md +0 -194
  165. package/docs/archive/planning/pi-integration-review.md +0 -178
  166. package/docs/archive/planning/release-notes-v0.0.10.md +0 -57
  167. package/docs/archive/planning/release-notes-v0.0.11.md +0 -239
  168. package/docs/archive/planning/release-notes-v0.0.12.md +0 -80
  169. package/docs/archive/planning/release-notes-v0.0.13.md +0 -77
  170. package/docs/archive/planning/release-notes-v0.0.9.md +0 -122
  171. package/docs/archive/planning/release-notes-v0.2.0.md +0 -49
  172. package/docs/archive/planning/release-notes-v0.3.0.md +0 -35
  173. package/docs/archive/planning/release-notes-v0.4.0.md +0 -45
  174. package/docs/archive/planning/release-notes-v0.5.0.md +0 -37
  175. package/docs/archive/planning/web-ui-pass-plan.md +0 -213
  176. package/docs/archive/planning/website-readme-pass-plan.md +0 -224
  177. package/docs/design/batching.md +0 -2054
  178. package/docs/design/decode-speed-program.md +0 -2404
  179. package/docs/design/dreaming-nightly-pipeline.md +0 -684
  180. package/docs/design/generic-model-support.md +0 -821
  181. package/docs/design/kv-cache.md +0 -993
  182. package/docs/design/orpo-training.md +0 -680
  183. package/docs/design/speculative-decoding.md +0 -2129
  184. package/docs/design/turboquant.md +0 -2225
  185. package/docs/design/unified-engine-frontier-plan.md +0 -1987
  186. package/docs/design/web-chat-redesign.md +0 -695
  187. package/docs/planning/PRODUCT_ROADMAP.md +0 -593
  188. package/docs/planning/ResearchTopics.md +0 -119
  189. package/docs/planning/public-readiness-handoff.md +0 -244
  190. package/docs/planning/public-readiness.md +0 -149
  191. package/docs/planning/video-series.md +0 -421
  192. package/docs/reference/benchmarks.md +0 -3587
  193. package/docs/reference/cli.md +0 -646
  194. package/docs/reference/distribution.md +0 -380
  195. package/docs/reference/environment.md +0 -494
  196. package/docs/reference/glossary.md +0 -39
  197. package/docs/reference/library-api.md +0 -688
  198. package/docs/reference/memory.md +0 -260
  199. package/docs/reference/models.md +0 -346
  200. package/docs/reference/server-api.md +0 -1945
  201. package/docs/reference/server-config.md +0 -973
  202. package/docs/reference/training.md +0 -462
  203. package/docs/reference/troubleshooting.md +0 -158
  204. package/src/admission-plan.ts +0 -18
  205. package/src/audio/audiotoolbox.ts +0 -112
  206. package/src/audio/conformer.ts +0 -655
  207. package/src/audio/decode.ts +0 -133
  208. package/src/audio/features.ts +0 -202
  209. package/src/audio/mic-capture.ts +0 -98
  210. package/src/audio/silero-vad.ts +0 -379
  211. package/src/audio/transcode.ts +0 -67
  212. package/src/audio/whisper-decode.ts +0 -774
  213. package/src/audio/whisper-fast.ts +0 -732
  214. package/src/audio/whisper-format.ts +0 -73
  215. package/src/audio/whisper-mel.ts +0 -185
  216. package/src/audio/whisper-timing.ts +0 -274
  217. package/src/audio/whisper-tokenizer.ts +0 -239
  218. package/src/audio/whisper-transcribe.ts +0 -410
  219. package/src/backends/mlx/assistant-target.ts +0 -46
  220. package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
  221. package/src/backends/mlx/autoregressive.ts +0 -129
  222. package/src/backends/mlx/batch-group.ts +0 -1874
  223. package/src/backends/mlx/cache-attachment.ts +0 -67
  224. package/src/backends/mlx/cache-layout.ts +0 -31
  225. package/src/backends/mlx/checkpoint-state.ts +0 -53
  226. package/src/backends/mlx/continuation-persistence.ts +0 -107
  227. package/src/backends/mlx/continuation-request.ts +0 -43
  228. package/src/backends/mlx/continuation.ts +0 -77
  229. package/src/backends/mlx/diffusion.ts +0 -39
  230. package/src/backends/mlx/draft-target.ts +0 -32
  231. package/src/backends/mlx/encoder-cache.ts +0 -25
  232. package/src/backends/mlx/evaluation.ts +0 -105
  233. package/src/backends/mlx/fill-append.ts +0 -58
  234. package/src/backends/mlx/fill-group.ts +0 -497
  235. package/src/backends/mlx/full-kv-row-append.ts +0 -32
  236. package/src/backends/mlx/full-kv-row-donor.ts +0 -54
  237. package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
  238. package/src/backends/mlx/gateway-binding.ts +0 -174
  239. package/src/backends/mlx/graph.ts +0 -38
  240. package/src/backends/mlx/kv-codec.ts +0 -9
  241. package/src/backends/mlx/kv-maintenance.ts +0 -161
  242. package/src/backends/mlx/kv-tensor-rows.ts +0 -212
  243. package/src/backends/mlx/memory-client.ts +0 -189
  244. package/src/backends/mlx/methods.ts +0 -90
  245. package/src/backends/mlx/mixed-iteration.ts +0 -62
  246. package/src/backends/mlx/model-prompt.ts +0 -178
  247. package/src/backends/mlx/model-serving.ts +0 -80
  248. package/src/backends/mlx/paged-cache-codec.ts +0 -27
  249. package/src/backends/mlx/prefill-cohort.ts +0 -100
  250. package/src/backends/mlx/prefill-policy.ts +0 -26
  251. package/src/backends/mlx/prefill-rows.ts +0 -244
  252. package/src/backends/mlx/prefill.ts +0 -33
  253. package/src/backends/mlx/projected-context-rows.ts +0 -72
  254. package/src/backends/mlx/prompt-input.ts +0 -29
  255. package/src/backends/mlx/qwen-prompt-input.ts +0 -40
  256. package/src/backends/mlx/request-policy.ts +0 -20
  257. package/src/backends/mlx/request-state-policy.ts +0 -54
  258. package/src/backends/mlx/rollback.ts +0 -92
  259. package/src/backends/mlx/row-sampling.ts +0 -49
  260. package/src/backends/mlx/serial-executor.ts +0 -251
  261. package/src/backends/mlx/speculative-group.ts +0 -438
  262. package/src/backends/mlx/speculative-round.ts +0 -161
  263. package/src/backends/mlx/speculative.ts +0 -131
  264. package/src/backends/mlx/state-rows.ts +0 -104
  265. package/src/backends/mlx/state-views.ts +0 -46
  266. package/src/backends/mlx/target-layout-capability.ts +0 -35
  267. package/src/backends/mlx/text-engine.ts +0 -73
  268. package/src/backends/mlx/transitioning-kv-rows.ts +0 -173
  269. package/src/chat-template.ts +0 -382
  270. package/src/cli.ts +0 -3490
  271. package/src/config.ts +0 -399
  272. package/src/contracts/completion.ts +0 -16
  273. package/src/contracts/execution.ts +0 -70
  274. package/src/contracts/generation.ts +0 -103
  275. package/src/contracts/host.ts +0 -7
  276. package/src/contracts/object-cache.ts +0 -8
  277. package/src/contracts/pi-provider.ts +0 -30
  278. package/src/contracts/pi.ts +0 -261
  279. package/src/contracts/prefix-cache.ts +0 -33
  280. package/src/contracts/resources.ts +0 -25
  281. package/src/contracts/sampling.ts +0 -23
  282. package/src/contracts/scheduling.ts +0 -54
  283. package/src/contracts/tsconfig.json +0 -14
  284. package/src/dataset/generators.ts +0 -1087
  285. package/src/dataset/index.ts +0 -12
  286. package/src/dataset/job.ts +0 -81
  287. package/src/dataset/llm.ts +0 -71
  288. package/src/dataset/registry.ts +0 -528
  289. package/src/diffusion/diffusion-generate.ts +0 -507
  290. package/src/download.ts +0 -505
  291. package/src/embed.ts +0 -77
  292. package/src/engine/admission.ts +0 -59
  293. package/src/engine/cancellation.ts +0 -38
  294. package/src/engine/engine.ts +0 -56
  295. package/src/engine/execution-coordinator.ts +0 -70
  296. package/src/engine/execution-plan.ts +0 -64
  297. package/src/engine/execution-tasks.ts +0 -44
  298. package/src/engine/namespaced-cache.ts +0 -13
  299. package/src/engine/resources.ts +0 -53
  300. package/src/engine/scheduler.ts +0 -92
  301. package/src/engine/session.ts +0 -247
  302. package/src/eval/README.md +0 -130
  303. package/src/eval/ifeval.ts +0 -81
  304. package/src/eval/kl-prompts.ts +0 -29
  305. package/src/eval/kl.ts +0 -590
  306. package/src/eval/perplexity.ts +0 -164
  307. package/src/eval/quality-db.ts +0 -92
  308. package/src/eval/reference-logits.ts +0 -207
  309. package/src/eval/runner.ts +0 -124
  310. package/src/eval/score.ts +0 -49
  311. package/src/eval/tasks/bfcl.ts +0 -535
  312. package/src/eval/tasks/gsm8k.ts +0 -98
  313. package/src/eval/tasks/hashhop.ts +0 -139
  314. package/src/eval/tasks/humaneval.ts +0 -275
  315. package/src/eval/tasks/ifeval.ts +0 -525
  316. package/src/eval/tasks/mmlu.ts +0 -157
  317. package/src/eval/ultrafeedback.ts +0 -60
  318. package/src/evaldb.ts +0 -199
  319. package/src/expert-io.ts +0 -393
  320. package/src/expert-offload-build.ts +0 -77
  321. package/src/expert-offload.ts +0 -76
  322. package/src/expert-residency.ts +0 -1134
  323. package/src/expert-usage.ts +0 -415
  324. package/src/fill/echo-index.ts +0 -188
  325. package/src/fill/fill-session.ts +0 -463
  326. package/src/fill/proposal.ts +0 -68
  327. package/src/fill/schema-rows.ts +0 -506
  328. package/src/fill/tool-boundary.ts +0 -103
  329. package/src/fit.ts +0 -249
  330. package/src/generate.ts +0 -1352
  331. package/src/grammar.ts +0 -604
  332. package/src/harness-pi.ts +0 -184
  333. package/src/hf-push.ts +0 -628
  334. package/src/hub-rest.ts +0 -242
  335. package/src/index.ts +0 -63
  336. package/src/inference/denoising.ts +0 -17
  337. package/src/inference/draft-acceptance.ts +0 -41
  338. package/src/inference/draft-output.ts +0 -49
  339. package/src/inference/graph.ts +0 -33
  340. package/src/inference/prefill.ts +0 -45
  341. package/src/inference/rollback.ts +0 -7
  342. package/src/jobs/index.ts +0 -31
  343. package/src/jobs/job-entry.ts +0 -107
  344. package/src/jobs/task-client.ts +0 -19
  345. package/src/jobs/types.ts +0 -2
  346. package/src/kv-scheme.ts +0 -279
  347. package/src/kv-store.ts +0 -1135
  348. package/src/lab/README.md +0 -22
  349. package/src/lab/curve/curve-designer.html +0 -303
  350. package/src/lab/curve/curve-sampler.ts +0 -122
  351. package/src/lab/expert-trace/expert-trace.ts +0 -69
  352. package/src/lab/paged-kv/paged-attention.ts +0 -117
  353. package/src/lab/paged-kv/paged-kv-rows.ts +0 -111
  354. package/src/lab/paged-kv/paged-kv.ts +0 -336
  355. package/src/library.ts +0 -40
  356. package/src/lora.ts +0 -408
  357. package/src/md-modules.d.ts +0 -54
  358. package/src/media-fetch.ts +0 -442
  359. package/src/memory/article.ts +0 -558
  360. package/src/memory/chunk-validation.ts +0 -150
  361. package/src/memory/chunk.ts +0 -318
  362. package/src/memory/cluster.ts +0 -351
  363. package/src/memory/coverage.ts +0 -134
  364. package/src/memory/crosslink.ts +0 -0
  365. package/src/memory/db.ts +0 -253
  366. package/src/memory/entity.ts +0 -203
  367. package/src/memory/gate.ts +0 -187
  368. package/src/memory/history.ts +0 -152
  369. package/src/memory/ingest.ts +0 -335
  370. package/src/memory/model.ts +0 -183
  371. package/src/memory/normalize.ts +0 -671
  372. package/src/memory/parse.ts +0 -22
  373. package/src/memory/pipeline.ts +0 -275
  374. package/src/memory/prompts.ts +0 -65
  375. package/src/memory/query.ts +0 -334
  376. package/src/memory/reconcile.ts +0 -734
  377. package/src/memory/redirect.ts +0 -108
  378. package/src/memory/reindex.ts +0 -325
  379. package/src/memory/resolve.ts +0 -307
  380. package/src/memory/rest.ts +0 -245
  381. package/src/memory/route.ts +0 -272
  382. package/src/memory/schedule.ts +0 -165
  383. package/src/memory/stages.ts +0 -777
  384. package/src/memory/synthesize.ts +0 -1638
  385. package/src/memory/tools.ts +0 -749
  386. package/src/memory/vault.ts +0 -1194
  387. package/src/memory/wikify.ts +0 -1064
  388. package/src/mlx/array.ts +0 -425
  389. package/src/mlx/autograd.ts +0 -304
  390. package/src/mlx/checkpoint.ts +0 -149
  391. package/src/mlx/compile.ts +0 -168
  392. package/src/mlx/custom-vjp.ts +0 -188
  393. package/src/mlx/ffi.ts +0 -531
  394. package/src/mlx/materialize.ts +0 -23
  395. package/src/mlx/metal-kernel.ts +0 -159
  396. package/src/mlx/normalized-argmax.ts +0 -116
  397. package/src/mlx/ops.ts +0 -1108
  398. package/src/mlx/quantized-rows.ts +0 -36
  399. package/src/mlx/token-bitmask.ts +0 -52
  400. package/src/mlx/turboquant-kv-decode.ts +0 -82
  401. package/src/mlx/turboquant-kv-inverse.ts +0 -122
  402. package/src/mlx/turboquant-ops.ts +0 -861
  403. package/src/mlx/turboquant-tables.ts +0 -237
  404. package/src/mmap.ts +0 -90
  405. package/src/model/artifact-identity.ts +0 -73
  406. package/src/model/batched-kv.ts +0 -235
  407. package/src/model/batched-mask.ts +0 -279
  408. package/src/model/batched-quant.ts +0 -212
  409. package/src/model/batched-quantized-kv.ts +0 -144
  410. package/src/model/batched-rotating-quant.ts +0 -353
  411. package/src/model/batched-rotating-state.ts +0 -149
  412. package/src/model/batched-rotating.ts +0 -338
  413. package/src/model/batched-row-storage.ts +0 -179
  414. package/src/model/batched-ssm.ts +0 -52
  415. package/src/model/batched-turboquant-kv.ts +0 -94
  416. package/src/model/compiled-decode.ts +0 -712
  417. package/src/model/decoded-kv-donor.ts +0 -12
  418. package/src/model/delayed-quantized-kv.ts +0 -72
  419. package/src/model/delayed-rotating-quantized-kv.ts +0 -93
  420. package/src/model/delayed-turboquant-kv.ts +0 -57
  421. package/src/model/diffusion-gemma.ts +0 -1026
  422. package/src/model/factory.ts +0 -215
  423. package/src/model/fingerprint.ts +0 -45
  424. package/src/model/flash-attention.ts +0 -694
  425. package/src/model/full-prefill-padding.ts +0 -56
  426. package/src/model/full-prefill-row.ts +0 -105
  427. package/src/model/gemma4-base.ts +0 -2370
  428. package/src/model/gemma4.ts +0 -1395
  429. package/src/model/generated/gemma4-12b.ts +0 -429
  430. package/src/model/generated/gemma4-26b.ts +0 -448
  431. package/src/model/generated/gemma4-e4b.ts +0 -717
  432. package/src/model/generated/index.ts +0 -18
  433. package/src/model/glm52-atlas.ts +0 -511
  434. package/src/model/glm52-cache.ts +0 -792
  435. package/src/model/glm52-config.ts +0 -262
  436. package/src/model/glm52-container.ts +0 -305
  437. package/src/model/glm52-coupling.ts +0 -388
  438. package/src/model/glm52-dsa.ts +0 -606
  439. package/src/model/glm52-expert-layout.ts +0 -253
  440. package/src/model/glm52-layout.ts +0 -152
  441. package/src/model/glm52-memory.ts +0 -500
  442. package/src/model/glm52-mla.ts +0 -792
  443. package/src/model/glm52-moe.ts +0 -223
  444. package/src/model/glm52-pilot.ts +0 -537
  445. package/src/model/glm52-reference.ts +0 -445
  446. package/src/model/glm52-residency.ts +0 -557
  447. package/src/model/glm52-resident-weights.ts +0 -135
  448. package/src/model/glm52-streamed-experts.ts +0 -453
  449. package/src/model/glm52-streamed-metal.ts +0 -610
  450. package/src/model/glm52-weights.ts +0 -503
  451. package/src/model/glm52.ts +0 -1132
  452. package/src/model/implementation.ts +0 -69
  453. package/src/model/kv-attention-view.ts +0 -70
  454. package/src/model/minicpm5.ts +0 -376
  455. package/src/model/profile.ts +0 -468
  456. package/src/model/quantized-append-attention.ts +0 -31
  457. package/src/model/qwen-append.ts +0 -12
  458. package/src/model/qwen-conv.ts +0 -52
  459. package/src/model/qwen3-delta-state.ts +0 -74
  460. package/src/model/qwen3-delta.ts +0 -451
  461. package/src/model/qwen3-moe.ts +0 -404
  462. package/src/model/qwen3-mrope.ts +0 -250
  463. package/src/model/qwen3.ts +0 -277
  464. package/src/model/qwen3_5-checkpoint.ts +0 -164
  465. package/src/model/qwen3_5.ts +0 -881
  466. package/src/model/rotating-kv-donor.ts +0 -35
  467. package/src/model/rotating-kv-layout.ts +0 -262
  468. package/src/model/rotating-row-transaction.ts +0 -35
  469. package/src/model/speculative-rotating-kv.ts +0 -101
  470. package/src/model/ssm-prefill-padding.ts +0 -39
  471. package/src/model/support.ts +0 -110
  472. package/src/model/token-groups.ts +0 -66
  473. package/src/model/trellis-balanced-scatter.ts +0 -51
  474. package/src/model/trellis-linear.ts +0 -672
  475. package/src/model/trellis-shared-m.ts +0 -151
  476. package/src/model/trellis-shared-scatter.ts +0 -144
  477. package/src/model/trellis-splitk-prefill.ts +0 -158
  478. package/src/model/trellis-tiled-prefill.ts +0 -136
  479. package/src/model/trellis-vector-expand.ts +0 -62
  480. package/src/model/trellis-wide-prefill.ts +0 -94
  481. package/src/model/turboquant-codec.ts +0 -89
  482. package/src/model/universal/archs.ts +0 -332
  483. package/src/model/universal/dense.ts +0 -612
  484. package/src/model/universal/modules.ts +0 -170
  485. package/src/model/universal/rope.ts +0 -381
  486. package/src/model/whisper.ts +0 -435
  487. package/src/native/expert_io.c +0 -523
  488. package/src/native/frame_extract.swift +0 -140
  489. package/src/native/mic_capture.swift +0 -92
  490. package/src/native-pack.ts +0 -163
  491. package/src/pi-provider.ts +0 -53
  492. package/src/pi-session.ts +0 -81
  493. package/src/pi-terminal.ts +0 -274
  494. package/src/pi-web.ts +0 -1940
  495. package/src/preflight.ts +0 -158
  496. package/src/prompt-cache.ts +0 -682
  497. package/src/quantize/allocator.ts +0 -627
  498. package/src/quantize/atomic-output.ts +0 -38
  499. package/src/quantize/calibration.ts +0 -181
  500. package/src/quantize/config-writer.ts +0 -145
  501. package/src/quantize/index.ts +0 -84
  502. package/src/quantize/job.ts +0 -162
  503. package/src/quantize/quantizer.ts +0 -666
  504. package/src/quantize/rotate.ts +0 -603
  505. package/src/quantize/safetensors-writer.ts +0 -249
  506. package/src/quantize/sensitivity.ts +0 -216
  507. package/src/quantize/trellis.ts +0 -586
  508. package/src/quantize/weight-transform.ts +0 -292
  509. package/src/registry.ts +0 -602
  510. package/src/runtime-config.ts +0 -77
  511. package/src/safetensors.ts +0 -165
  512. package/src/sampler.ts +0 -1012
  513. package/src/serve/admin-routes.ts +0 -184
  514. package/src/serve/assets/curve-terrain.html +0 -105
  515. package/src/serve/assets/training-inference-map.html +0 -575
  516. package/src/serve/audio-routes.ts +0 -323
  517. package/src/serve/aux-routes.ts +0 -132
  518. package/src/serve/batch-scheduler.ts +0 -8
  519. package/src/serve/checkpoint-identity.ts +0 -64
  520. package/src/serve/generation-gateway.ts +0 -537
  521. package/src/serve/isolate.ts +0 -609
  522. package/src/serve/kv-budget.ts +0 -14
  523. package/src/serve/lab-routes.ts +0 -341
  524. package/src/serve/lane-registry.ts +0 -45
  525. package/src/serve/model-admin-routes.ts +0 -155
  526. package/src/serve/model-binding.ts +0 -49
  527. package/src/serve/model-host.ts +0 -511
  528. package/src/serve/parent-application.ts +0 -56
  529. package/src/serve/prompt-response-trace.ts +0 -161
  530. package/src/serve/response-lease.ts +0 -24
  531. package/src/serve/session-search.ts +0 -326
  532. package/src/serve/static-routes.ts +0 -101
  533. package/src/serve/transcription-server.ts +0 -64
  534. package/src/serve/transcription-service.ts +0 -502
  535. package/src/server.ts +0 -1282
  536. package/src/spec/assistant-rows.ts +0 -93
  537. package/src/spec/assistant-source.ts +0 -126
  538. package/src/spec/cache-identity.ts +0 -22
  539. package/src/spec/deepspec-rows.ts +0 -29
  540. package/src/spec/deepspec-source.ts +0 -145
  541. package/src/spec/dflash-source.ts +0 -149
  542. package/src/spec/drafter.ts +0 -304
  543. package/src/spec/dspark/ab-stats.ts +0 -136
  544. package/src/spec/dspark/calibration.ts +0 -112
  545. package/src/spec/dspark/data-dflash.ts +0 -156
  546. package/src/spec/dspark/data.ts +0 -201
  547. package/src/spec/dspark/deepspec-module.ts +0 -776
  548. package/src/spec/dspark/generate-dflash.ts +0 -245
  549. package/src/spec/dspark/generate.ts +0 -290
  550. package/src/spec/dspark/loader.ts +0 -23
  551. package/src/spec/dspark/loss.ts +0 -213
  552. package/src/spec/dspark/module-dflash.ts +0 -560
  553. package/src/spec/dspark/module.ts +0 -540
  554. package/src/spec/dspark/quantize-drafter.ts +0 -91
  555. package/src/spec/dspark/sample.ts +0 -112
  556. package/src/spec/generate.ts +0 -276
  557. package/src/spec/glm52-mtp-graph.ts +0 -29
  558. package/src/spec/glm52-mtp-rows.ts +0 -132
  559. package/src/spec/glm52-mtp-source.ts +0 -318
  560. package/src/spec/glm52-mtp-state.ts +0 -33
  561. package/src/spec/ngram-source.ts +0 -226
  562. package/src/spec/projected-draft-rows.ts +0 -73
  563. package/src/spec/qwen-mtp-module.ts +0 -171
  564. package/src/spec/qwen-mtp-rows.ts +0 -230
  565. package/src/spec/qwen-mtp-source.ts +0 -386
  566. package/src/spec/qwen-mtp-state.ts +0 -30
  567. package/src/spec/serve-loop.ts +0 -587
  568. package/src/spec/source.ts +0 -228
  569. package/src/spec/two-model-rows.ts +0 -62
  570. package/src/spec/two-model.ts +0 -113
  571. package/src/ssd-cache.ts +0 -431
  572. package/src/ssd-durability.ts +0 -209
  573. package/src/storage/host-buffer.ts +0 -24
  574. package/src/storage/kv-writer.ts +0 -83
  575. package/src/storage/kv-writer.worker.js +0 -264
  576. package/src/storage/retention-policy.ts +0 -37
  577. package/src/tiered-prompt-cache.ts +0 -45
  578. package/src/tokenizer.ts +0 -335
  579. package/src/tool-approvals.ts +0 -131
  580. package/src/tool-call.ts +0 -695
  581. package/src/train/dataset.ts +0 -484
  582. package/src/train/diffusion-lora.ts +0 -216
  583. package/src/train/export.ts +0 -32
  584. package/src/train/flash-cce.ts +0 -1234
  585. package/src/train/forward.ts +0 -266
  586. package/src/train/fuse.ts +0 -243
  587. package/src/train/index.ts +0 -46
  588. package/src/train/job.ts +0 -205
  589. package/src/train/lora-params.ts +0 -254
  590. package/src/train/loss.ts +0 -1591
  591. package/src/train/merge.ts +0 -195
  592. package/src/train/optimizer.ts +0 -183
  593. package/src/train/prefix-shared.ts +0 -490
  594. package/src/train/rank.ts +0 -137
  595. package/src/train/segmented.ts +0 -1828
  596. package/src/train/steel-qmm-header.ts +0 -1461
  597. package/src/train/trainer.ts +0 -1221
  598. package/src/train/watch.ts +0 -542
  599. package/src/vision/diffusion-vision.ts +0 -620
  600. package/src/vision/embedder.ts +0 -176
  601. package/src/vision/preprocess.ts +0 -247
  602. package/src/vision/prompt.ts +0 -409
  603. package/src/vision/qwen3vl-preprocess.ts +0 -409
  604. package/src/vision/qwen3vl-prompt.ts +0 -233
  605. package/src/vision/qwen3vl-tower.ts +0 -566
  606. package/src/vision/siglip.ts +0 -607
  607. package/src/vision/video-frames.ts +0 -148
  608. package/src/web/app.html +0 -2155
  609. package/src/web/app.js +0 -5863
  610. package/src/web/skills/memory/SKILL.md +0 -120
  611. package/src/web/skills/web-research/SKILL.md +0 -46
  612. package/src/web/skills.ts +0 -97
  613. package/src/web/src/adapters-panel.ts +0 -288
  614. package/src/web/src/assistant.ts +0 -451
  615. package/src/web/src/chat.ts +0 -1060
  616. package/src/web/src/composer.ts +0 -962
  617. package/src/web/src/dataset.ts +0 -132
  618. package/src/web/src/finetune.ts +0 -267
  619. package/src/web/src/hub.ts +0 -428
  620. package/src/web/src/main.ts +0 -98
  621. package/src/web/src/markdown.ts +0 -0
  622. package/src/web/src/memory-panel.ts +0 -572
  623. package/src/web/src/model-picker.ts +0 -159
  624. package/src/web/src/palette.ts +0 -346
  625. package/src/web/src/protocol.ts +0 -26
  626. package/src/web/src/quantize.ts +0 -167
  627. package/src/web/src/rag.ts +0 -274
  628. package/src/web/src/sessions.ts +0 -469
  629. package/src/web/src/shell.ts +0 -804
  630. package/src/web/src/ui-catalog.ts +0 -78
  631. package/src/web/src/voice.ts +0 -141
  632. package/src/web/vendor/README.md +0 -61
  633. package/src/web-assets.ts +0 -47
  634. package/src/web-tools.ts +0 -478
  635. package/src/weights.ts +0 -255
  636. /package/src/{serve → server}/completion-sink.ts +0 -0
  637. /package/src/{serve → server}/generated-token-history.ts +0 -0
  638. /package/src/web/{icon.svg → public/icon.svg} +0 -0
  639. /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
  640. /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
@@ -2,7 +2,7 @@
2
2
  // resolved generation settings, ready for a model. InferenceStage first
3
3
  // ADMITS it (memory ceiling → reject or clamp; the resolved settings become
4
4
  // an opaque, single-use plan) and then RUNS it — the gateway routes it to
5
- // the lane it belongs to (serial / batch / spec) and the executor returns
5
+ // continuous scheduling with the selected generation method and the executor returns
6
6
  // the terminal InferenceResult. Every completion surface — chat, raw text,
7
7
  // Anthropic, Responses — produces one of these and reads one of those.
8
8
  import {
@@ -14,7 +14,7 @@ import {
14
14
  type PreparedCompletion,
15
15
  } from "./completion-executor";
16
16
  import { createTimedFlowControl } from "./completion-sink";
17
- import { runtimeValue } from "../runtime-config";
17
+ import { runtimeValue } from "@mlx-bun/inference/runtime/config";
18
18
  import { RequestError, type RunControl } from "./pipeline";
19
19
 
20
20
  export interface InferenceRequest
@@ -37,7 +37,7 @@ export interface AdmittedRequest {
37
37
  export type InferenceResult = CompletionSummary;
38
38
 
39
39
  export class InferenceStage {
40
- constructor(private readonly executor: Pick<CompletionExecutor, "execute">) {}
40
+ constructor(private readonly executor: Pick<CompletionExecutor, "place" | "execute">) {}
41
41
 
42
42
  /** Admission: throws RequestError(400, memory_admission) when the prompt
43
43
  * leaves no room to generate inside the safe context; otherwise clamps
@@ -49,8 +49,7 @@ export class InferenceStage {
49
49
  ...preparation,
50
50
  ...(stream
51
51
  ? {
52
- createFlowControl: ({ mechanism }: { mechanism: string }) =>
53
- createTimedFlowControl(mechanism === "serial"),
52
+ createFlowControl: () => createTimedFlowControl(false),
54
53
  }
55
54
  : {}),
56
55
  onPlacement: ({ mechanism, shape }) => {
@@ -61,6 +60,7 @@ export class InferenceStage {
61
60
  );
62
61
  },
63
62
  });
63
+ this.executor.place(prepared);
64
64
  return { requestId: request.requestId, warnings, prepared };
65
65
  } catch (error) {
66
66
  if (!(error instanceof CompletionRejected)) throw error;
@@ -0,0 +1,20 @@
1
+ import type { JobStore } from "../jobs/db";
2
+ import { streamJobResponse } from "../jobs/sse";
3
+
4
+ export function createJobRoutes(host: { ensureStore(): JobStore; signal: AbortSignal }) {
5
+ return { async handle(request: Request): Promise<Response | null> {
6
+ const url = new URL(request.url);
7
+ if (request.method !== "GET") return null;
8
+ if (url.pathname === "/api/jobs") {
9
+ const limit = Number(url.searchParams.get("limit") ?? "50");
10
+ const kind = url.searchParams.get("kind") ?? undefined;
11
+ return Response.json({ ok: true, jobs: host.ensureStore().recent(limit, kind) });
12
+ }
13
+ const match = url.pathname.match(/^\/api\/jobs\/([^/]+?)(\/stream)?$/);
14
+ if (!match) return null;
15
+ const store = host.ensureStore(), jobId = match[1]!;
16
+ if (match[2]) return streamJobResponse(store, jobId, AbortSignal.any([host.signal, request.signal]));
17
+ const job = store.get(jobId);
18
+ return job ? Response.json({ ok: true, job }) : Response.json({ ok: false, error: "job not found" }, { status: 404 });
19
+ } };
20
+ }
@@ -0,0 +1,63 @@
1
+ import { normalizeMessages, type ChatRequestParams } from "./chat-request";
2
+ import { requireChatTemplate, type LoadedModelContext as ModelContext } from "../engine/model-host";
3
+ import { mediaRefusalMessage } from "../engine/media-preparation";
4
+ import { RequestError } from "./pipeline";
5
+ import type { RequestOwnership } from "./request-plan";
6
+ import type { RequestPrep } from "./request-prep";
7
+ import type { BuiltPrompt, PromptNativeWork } from "./prompt-contracts";
8
+
9
+ export async function buildModelPrompt(
10
+ ctx: ModelContext,
11
+ prep: RequestPrep,
12
+ body: ChatRequestParams,
13
+ tools: ChatRequestParams["tools"] | null,
14
+ _ownership: RequestOwnership,
15
+ nativeWork: PromptNativeWork = (work) => work(),
16
+ objects?: import("@mlx-bun/inference/contracts/portable").ObjectCache<import("@mlx-bun/inference/state").CheckpointAttachment[]>,
17
+ ): Promise<BuiltPrompt> {
18
+ requireChatTemplate(ctx);
19
+ const toolList = tools ?? null;
20
+ const partOf = (types: string[]) => body.messages.some(
21
+ (m) => Array.isArray(m.content) &&
22
+ m.content.some((p: any) => types.includes(p.type)),
23
+ );
24
+ const hasImages = partOf(["image_url", "image"]);
25
+ // Same shapes extractAudio accepts: OpenAI-canonical input_audio plus
26
+ // optiq's audio / audio_url aliases (`02d723a:docs/design/generic-model-support.md` §6.6).
27
+ const hasAudio = partOf(["input_audio", "audio", "audio_url"]);
28
+ // Video content parts (video_url / video with base64 data) — accepted only
29
+ // by graphs whose media route declares video (decoded to sampled frames via
30
+ // the AVFoundation sidecar, packages/inference/src/input/vision/video-frames.ts).
31
+ const hasVideos = partOf(["video_url", "video"]);
32
+
33
+ // Video never composes with audio — one early guard so no downstream
34
+ // branch can silently drop a video part.
35
+ if (hasVideos && (hasAudio || !ctx.media.video))
36
+ throw new RequestError(400, mediaRefusalMessage(hasAudio ? "video-with-audio" : "video-unsupported", ctx.modelId));
37
+ if (hasImages || hasAudio || hasVideos) {
38
+ const prepared = await ctx.media.prepare({
39
+ messages: normalizeMessages(body.messages),
40
+ parts: { images: hasImages, audio: hasAudio, videos: hasVideos },
41
+ tools: toolList,
42
+ templateOptions: () => prep.templateOptionsFor(body, toolList),
43
+ nativeWork,
44
+ objects,
45
+ });
46
+ if ("rejected" in prepared) throw new RequestError(400, mediaRefusalMessage(prepared.rejected, ctx.modelId));
47
+ return {
48
+ promptIds: prepared.promptIds,
49
+ vision: prepared.vision,
50
+ startInThinking: false,
51
+ probeStableLen: false,
52
+ diffusionPixels: prepared.pixels ?? null,
53
+ };
54
+ }
55
+ const text = prep.promptIdsFor(body, toolList);
56
+ return {
57
+ promptIds: text.ids,
58
+ vision: undefined,
59
+ startInThinking: text.startInThinking,
60
+ probeStableLen: true,
61
+ diffusionPixels: null,
62
+ };
63
+ }
@@ -0,0 +1,46 @@
1
+ import type { TaskCompletionClient as MemoryCompletionClient } from "@mlx-bun/app-core";
2
+ import type { WorkerMemoryCall } from "./worker-routes";
3
+ export interface MemoryTarget { worker: { fetch(url: string, init?: RequestInit): Promise<Response> }; snapshot: string }
4
+ /** The isolated parent's client for one synthesis run: each `complete` or
5
+ * `completeBatch` is one POST of the rows and the selected snapshot to the
6
+ * private `/admin/memory/complete` of the worker `select` returns (the
7
+ * model worker), answered with the ordered raw outputs. A failed
8
+ * selection fails the call as it is. The worker's task model runs the call
9
+ * under the worker's own execution lease; none is taken here (one
10
+ * would wait on that worker). The run's signal aborts the request, and the
11
+ * worker then aborts and joins every row. A call is never retried: a worker
12
+ * that stops mid-call fails it rather than replay a POST. */
13
+ export function createWorkerMemoryClient(select: (signal: AbortSignal) => Promise<MemoryTarget>, signal: AbortSignal): MemoryCompletionClient {
14
+ const send = async (call: Omit<WorkerMemoryCall, "snapshot">): Promise<string[]> => {
15
+ signal.throwIfAborted();
16
+ const { worker, snapshot } = await select(signal);
17
+ const body: WorkerMemoryCall = { call: call.call, snapshot, requests: call.requests };
18
+ let response: Response;
19
+ try {
20
+ response = await worker.fetch("http://engine/admin/memory/complete", { method: "POST",
21
+ headers: { "content-type": "application/json" }, body: JSON.stringify(body), signal });
22
+ } catch (error) {
23
+ if (signal.aborted) throw signal.reason;
24
+ throw new Error(`memory: the model worker did not complete the task model ${body.call} (${error instanceof Error ? error.message : String(error)}); it is not retried`);
25
+ }
26
+ if (!response.ok) {
27
+ const detail = await response.text().catch(() => "");
28
+ let message = detail;
29
+ try {
30
+ const parsed = JSON.parse(detail) as { error?: { message?: unknown } } | null;
31
+ if (typeof parsed?.error?.message === "string") message = parsed.error.message;
32
+ } catch { /* not JSON: the text itself */ }
33
+ throw new Error(`memory: the model worker's task model ${body.call} failed (${response.status}): ${message.slice(0, 400)}`);
34
+ }
35
+ const { outputs } = await response.json() as { outputs?: unknown };
36
+ signal.throwIfAborted();
37
+ if (!Array.isArray(outputs) || outputs.length !== body.requests.length || !outputs.every(output => typeof output === "string"))
38
+ throw new Error(`memory: the model worker's task model ${body.call} answered no ordered outputs`);
39
+ return outputs as string[];
40
+ };
41
+ return {
42
+ complete: async request => (await send({ call: "complete", requests: [request] }))[0]!,
43
+ // An empty batch is answered here, as the task model answers it: without a call.
44
+ completeBatch: async requests => requests.length ? send({ call: "completeBatch", requests: [...requests] }) : [],
45
+ };
46
+ }
@@ -0,0 +1,192 @@
1
+ // Routes model-scoped requests to the model that should answer them. The
2
+ // inference endpoints carry a `model` id in the JSON body: an exact local id
3
+ // (the ones `/v1/models` lists) leases that model from the model host, loading
4
+ // it (and draining another) when it is not resident; anything else (no id, an
5
+ // alias such as Pi's `local`, a name another server would know) is answered by
6
+ // the current model. Every other model-scoped path (`/stats`, `/fit`, cache
7
+ // administration) belongs to the current model too. The response
8
+ // body holds the lease until it ends, so a model is never released under a
9
+ // stream. `/v1/models` lists every local model with which are
10
+ // resident and which is current; `/stats` adds the host's residency.
11
+ import type { ResidencyHost, ResidentUnit } from "../residency/model-residency";
12
+ import { ResidencyError } from "../residency/model-residency";
13
+ import { errorResponse } from "./http";
14
+ import { RequestError } from "./pipeline";
15
+ import { readDecisionBody } from "./decision-body";
16
+
17
+ /** Inference endpoints whose JSON body carries the routing `model` field. */
18
+ export const MODEL_ROUTED = new Set(["/v1/chat/completions", "/v1/completions", "/v1/messages", "/v1/responses", "/v1/embeddings", "/v1/decisions", "/v1/systemone"]);
19
+
20
+ const decoder = new TextDecoder();
21
+ /** The routing key from a buffered JSON body. A body that is not a JSON object with a string `model` names no model; the current model answers it and its own parser rejects a malformed body. */
22
+ export function modelField(body: Uint8Array | string): string | undefined {
23
+ try {
24
+ const value = JSON.parse(typeof body === "string" ? body : decoder.decode(body)) as { model?: unknown } | null;
25
+ return value && typeof value === "object" && typeof value.model === "string" ? value.model : undefined;
26
+ } catch { return undefined; }
27
+ }
28
+
29
+ /** A unit whose model-scoped routes this router can delegate to. */
30
+ export interface RoutedUnit extends ResidentUnit {
31
+ readonly routes: { handle(request: Request): Promise<Response | null> };
32
+ }
33
+
34
+ export interface ModelRoutesOptions<U extends RoutedUnit> {
35
+ host: Pick<ResidencyHost<U>, "acquire" | "peek" | "resident" | "policy">;
36
+ /** The model that answers requests naming no local model. */
37
+ current(): string;
38
+ /** Whether `id` is an exact id this host can serve (the ids `/v1/models` lists), resident or not. */
39
+ serves(id: string): Promise<boolean>;
40
+ /** The companion model (Whisper) that answers this path, when it is one of its own; the router leases it like any model. */
41
+ companion?(pathname: string): Promise<string | undefined>;
42
+ /** The configured transcription companion, including before its worker starts. */
43
+ transcription?(): Promise<string | undefined>;
44
+ }
45
+
46
+ /** A response that keeps its lease until the body has ended, been cancelled, or failed. */
47
+ function holding(response: Response, release: () => void): Response {
48
+ if (!response.body) { release(); return response; }
49
+ const reader = response.body.getReader();
50
+ let done = false;
51
+ const finish = () => { if (!done) { done = true; release(); } };
52
+ const body = new ReadableStream<Uint8Array>({
53
+ async pull(controller) {
54
+ try {
55
+ const chunk = await reader.read();
56
+ if (chunk.done) { finish(); controller.close(); } else controller.enqueue(chunk.value);
57
+ } catch (error) { finish(); controller.error(error); }
58
+ },
59
+ cancel(reason) { finish(); return reader.cancel(reason).catch(() => {}); },
60
+ });
61
+ return new Response(body, { status: response.status, statusText: response.statusText, headers: response.headers });
62
+ }
63
+
64
+ /** The typed answer for a model that could not be leased. */
65
+ function unavailable(error: unknown, context: string, signal: AbortSignal): Response {
66
+ if (error instanceof ResidencyError) {
67
+ const status = error.code === "load-failed" ? 502 : error.code === "closed" ? 503 : 400;
68
+ const type = error.code === "load-failed" ? "model_load_failed" : error.code === "closed" ? "server_closing" : "invalid_request_error";
69
+ return errorResponse(new RequestError(status, error.message, { message: error.message, type, code: error.code }), context, undefined, signal);
70
+ }
71
+ return errorResponse(error, context, undefined, signal);
72
+ }
73
+
74
+ export function createModelRoutes<U extends RoutedUnit>(options: ModelRoutesOptions<U>) {
75
+ const { host } = options;
76
+ /** The unit that answers model-scoped requests that name no model, without loading anything. */
77
+ const active = (): U | undefined => {
78
+ const current = host.peek(options.current());
79
+ if (current) return current;
80
+ const latest = host.resident().filter(model => model.state === "ready" && model.role === "primary").sort((a, b) => b.lastUsedAt - a.lastUsedAt)[0];
81
+ return latest ? host.peek(latest.id) : undefined;
82
+ };
83
+ const listing = async (request: Request, path: string): Promise<Response | null> => {
84
+ const unit = active();
85
+ return unit ? await unit.routes.handle(new Request(new URL(path, request.url), { headers: request.headers, signal: request.signal })) : null;
86
+ };
87
+ const residentIds = () => new Set(host.resident().filter(model => model.state === "ready").map(model => model.id));
88
+
89
+ /** Lease `id`, run `serve` on its unit, and hold the lease until the response has been delivered. */
90
+ async function leased(id: string, request: Request, serve: (unit: U, lease: { release(): void }) => Promise<Response | null>): Promise<Response | null> {
91
+ const path = new URL(request.url).pathname;
92
+ let lease;
93
+ try { lease = await host.acquire(id, { signal: request.signal }); }
94
+ catch (error) { return unavailable(error, path, request.signal); }
95
+ try {
96
+ const response = await serve(lease.unit, lease);
97
+ return response ? holding(response, () => lease.release()) : (lease.release(), null);
98
+ } catch (error) { lease.release(); return errorResponse(error, path, undefined, request.signal); }
99
+ }
100
+
101
+ return {
102
+ async handle(request: Request): Promise<Response | null> {
103
+ const url = new URL(request.url);
104
+ const { pathname } = url;
105
+ if (request.method === "POST" && MODEL_ROUTED.has(pathname)) {
106
+ // The body is read once here and handed on as bytes; the model's own parser answers a malformed one.
107
+ const headers = new Headers(request.headers);
108
+ const body = new Uint8Array(await request.arrayBuffer());
109
+ let named = modelField(body);
110
+ if (pathname === "/v1/systemone" || pathname === "/v1/decisions") {
111
+ try {
112
+ const parsed = await readDecisionBody(new Request(request.url, { method: "POST", headers, body }));
113
+ if (parsed && typeof parsed === "object" && typeof (parsed as { model?: unknown }).model === "string")
114
+ named = (parsed as { model: string }).model;
115
+ } catch { /* The model's parser supplies the protocol's error. */ }
116
+ }
117
+ const id = named !== undefined && await options.serves(named) ? named : options.current();
118
+ return leased(id, request, unit => unit.operationsFor().generate!(new Request(request.url, {
119
+ method: request.method, headers, body, signal: request.signal })));
120
+ }
121
+ const listsModels = pathname === "/v1/models" || pathname.startsWith("/v1/models/");
122
+ if (request.method === "GET" && listsModels) {
123
+ // `/v1/models/{id}` lists that one model; `/v1/models` lists them all.
124
+ const filter = decodeURIComponent(pathname.slice("/v1/models/".length)) || null;
125
+ const own = filter === null ? undefined : options.host.peek(filter);
126
+ if (own && !own.operations.includes("transcribe")) return own.routes.handle(request);
127
+ const base = await listing(request, "/v1/models");
128
+ if (!base) return null;
129
+ // A unit that cannot answer (a worker that is down) is the answer, not something to decorate.
130
+ if (!base.ok) return base;
131
+ const list = await base.json() as { object: string; data: Record<string, unknown>[] };
132
+ // Chat workers have no companion of their own. Discovery belongs to this host's
133
+ // resolved companion, not whichever Whisper snapshot a worker discovers locally.
134
+ if (options.transcription) {
135
+ const id = await options.transcription();
136
+ list.data = list.data.filter(entry => entry.transcription !== true && entry.id !== id);
137
+ for (const entry of list.data) {
138
+ if (entry.id === options.current() && entry.capabilities)
139
+ entry.capabilities = { ...(entry.capabilities as Record<string, unknown>), transcription: id !== undefined };
140
+ }
141
+ if (id !== undefined) list.data.push({ id, object: "model", owned_by: "mlx-bun", transcription: true,
142
+ resident: false, capabilities: { transcription: true, translation: true, chat_completions: false } });
143
+ }
144
+ const resident = residentIds(), current = options.current();
145
+ // Every resident model answers for itself; the rest are listed as the registry knows them.
146
+ const full = await Promise.all([...resident].map(async id => {
147
+ const unit = host.peek(id);
148
+ const companion = unit?.operations.includes("transcribe");
149
+ const path = companion ? "/v1/models" : `/v1/models/${encodeURIComponent(id)}`;
150
+ const entry = unit ? await unit.routes.handle(new Request(new URL(path, request.url))) : null;
151
+ const rows = entry?.ok ? ((await entry.json()) as { data?: Record<string, unknown>[] }).data : undefined;
152
+ const row = companion ? rows?.find(row => row.transcription === true) : rows?.[0];
153
+ return [id, row ? { ...row, id } : undefined] as const;
154
+ }));
155
+ const bySelf = new Map(full.flatMap(([id, entry]) => entry ? [[id, entry] as const] : []));
156
+ const data: Record<string, unknown>[] = list.data.map(entry => {
157
+ const id = entry.id as string, row = bySelf.get(id) ?? entry;
158
+ // A companion's own row says whether it is loaded; the host knows when it holds it resident.
159
+ return entry.transcription === true ? row : { ...row, resident: resident.has(id), current: id === current };
160
+ });
161
+ const selected = filter === null ? data : data.filter(entry => entry.id === filter);
162
+ return Response.json({ ...list, data: selected,
163
+ // Jev clients use `models`, OpenAI clients use `data`; one discovery
164
+ // response can carry both views without changing either contract.
165
+ models: selected.filter(entry => entry.transcription !== true).map(entry => ({ name: entry.id,
166
+ description: "Local mlx-bun inference model", release_date: "" })) });
167
+ }
168
+ if (request.method === "GET" && pathname === "/stats") {
169
+ const unit = active();
170
+ const base = unit ? await unit.routes.handle(request) : null;
171
+ if (!base) return null;
172
+ if (!base.ok) return base;
173
+ const stats = await base.json() as Record<string, unknown>;
174
+ const models = host.resident();
175
+ return Response.json({ ...stats, models: { current: options.current(), budget_bytes: host.policy.budgetBytes,
176
+ resident_bytes: models.reduce((sum, model) => sum + model.bytes, 0),
177
+ resident: models.map(model => ({ id: model.id, state: model.state, bytes: model.bytes, pinned: model.pinned, leases: model.leases,
178
+ last_used_at: model.lastUsedAt })) } });
179
+ }
180
+ if (request.method === "GET" && pathname === "/health")
181
+ return await active()?.routes.handle(request) ?? Response.json({ status: "ok" });
182
+ const companion = await options.companion?.(pathname);
183
+ if (companion !== undefined) return leased(companion, request, served => served.routes.handle(request));
184
+ // Everything else model-scoped is the current model's, when one is resident: a read answers as it is, and
185
+ // a change (flushing a cache) holds the model resident until it is done.
186
+ const unit = active();
187
+ if (!unit) return null;
188
+ if (["GET", "HEAD"].includes(request.method)) return unit.routes.handle(request);
189
+ return leased(unit.id, request, served => served.routes.handle(request));
190
+ },
191
+ };
192
+ }
@@ -1,6 +1,7 @@
1
1
  // OpenAI wire formats: InferenceResult → chat.completion / text_completion
2
2
  // JSON, and the SSE chunk protocols for both. Anthropic and Responses have
3
- // their own (src/anthropic.ts, src/responses.ts); all three implement the
3
+ // their own (apps/mlx-bun/src/server/anthropic.ts, apps/mlx-bun/src/server/responses.ts);
4
+ // all three implement the
4
5
  // same CompletionStreamProtocol so the HTTP writer (http.ts) is shared.
5
6
  import type { CompletionSummary, CompletionUsage } from "./completion-executor";
6
7
  import type { CompletionEvent, CompletionStreamProtocol } from "./completion-sink";
@@ -22,7 +23,7 @@ export const openAiUsage = (usage: CompletionUsage) => ({
22
23
  ...(usage.fill ? { fill: usage.fill } : {}),
23
24
  });
24
25
 
25
- /** Terminal usage: the wire block plus mlx-bun's `lane` (batch|serial|…). */
26
+ /** Terminal usage: the wire block plus mlx-bun's `lane` (always `batched`: one request is a batch of one). */
26
27
  export const openAiSummaryUsage = (summary: CompletionSummary) => ({
27
28
  ...openAiUsage(summary.usage),
28
29
  lane: summary.lane,
@@ -8,7 +8,7 @@
8
8
  // A stage knows only the interface it reads and the one it writes.
9
9
  import type { CompletionEvent } from "./completion-sink";
10
10
  import type { CompletionUsage } from "./completion-executor";
11
- import type { PromptResponseTrace } from "./prompt-response-trace";
11
+ import type { PromptResponseTrace } from "@mlx-bun/inference/runtime/trace";
12
12
 
13
13
  /** A request a stage refuses. `status` is the HTTP status the wire adapter
14
14
  * answers with; `body` the protocol-neutral error object (adapters reshape
@@ -0,0 +1,13 @@
1
+ import type { LoadedModelContext as ModelContext } from "../engine/model-host";
2
+ import type { ModelPromptBuilder } from "./prompt-contracts";
3
+ export function modelPromptBuilder(context: ModelContext): ModelPromptBuilder {
4
+ return async (body, tools, ownership, prep, nativeWork, objects) => {
5
+ const media = body.messages.some(message => Array.isArray(message.content) && message.content.some(part => part.type !== "text"));
6
+ if (media) {
7
+ const { buildModelPrompt } = await import("./media-prompt");
8
+ return buildModelPrompt(context, prep, body, tools, ownership, nativeWork, objects);
9
+ }
10
+ const text = prep.promptIdsFor(body, tools ?? null);
11
+ return { promptIds: text.ids, vision: undefined, startInThinking: text.startInThinking, probeStableLen: true, diffusionPixels: null };
12
+ };
13
+ }
@@ -0,0 +1,17 @@
1
+ import type { MlxArray } from "@mlx-bun/mlx/array";
2
+ import type { ObjectCache } from "@mlx-bun/inference/contracts/portable";
3
+ import type { CheckpointAttachment } from "@mlx-bun/inference/state";
4
+ import type { Vision } from "../engine/completion";
5
+ import type { ChatRequestParams } from "./chat-request";
6
+ import type { RequestOwnership } from "./request-plan";
7
+ import type { RequestPrep } from "./request-prep";
8
+ export interface BuiltPrompt {
9
+ promptIds: number[]; vision: Vision | undefined; startInThinking: boolean;
10
+ probeStableLen: boolean; diffusionPixels: MlxArray | null;
11
+ }
12
+ export type PromptNativeWork = <T>(work: () => Promise<T>) => Promise<T>;
13
+ export interface ModelPromptBuilder {
14
+ (body: ChatRequestParams, tools: ChatRequestParams["tools"] | null,
15
+ ownership: RequestOwnership, prep: RequestPrep, nativeWork?: PromptNativeWork,
16
+ objects?: ObjectCache<CheckpointAttachment[]>): Promise<BuiltPrompt>;
17
+ }
@@ -0,0 +1,187 @@
1
+ // The isolated host's route group: what the parent owns, and the way a request
2
+ // reaches a model worker. Each resident model runs in its own worker process;
3
+ // the model router (model-routes.ts) picks the model a request names and leases
4
+ // its worker, and `forwardToWorker` sends the request over the worker's Unix
5
+ // socket with the body streaming through (hop-by-hop headers stripped, a client
6
+ // abort aborting the proxied fetch so the worker sees the disconnect). The
7
+ // parent answers `/engine`, `/health` and `/stats` (with the workers'
8
+ // contribution) and `/v1/responses` through the history client. The workers' private admin paths never forward.
9
+ import type { WorkerMemory } from "../jobs/worker-memory";
10
+ import { describeExit, EngineUnavailableError, type WorkerSupervisor, type WorkerSupervisorState } from "../jobs/worker-supervisor";
11
+ import type { createResponsesClient } from "./responses-client";
12
+
13
+ const HOP_BY_HOP = new Set([
14
+ "connection", "keep-alive", "proxy-authenticate", "proxy-authorization",
15
+ "te", "trailer", "transfer-encoding", "upgrade",
16
+ ]);
17
+ /** Never forwarded to a worker: its private socket surface (which only the
18
+ * parent itself calls; unmatched here, the listener answers 404) and the
19
+ * parent's own synthesis route (served by its earlier group). */
20
+ const PARENT_ONLY = new Set(["/admin/lease", "/admin/drain", "/admin/memory/complete", "/admin/events", "/admin/adapters", "/v1/memory/synthesize"]);
21
+
22
+ const encoder = new TextEncoder();
23
+ /** A copy without the hop-by-hop headers and the ones `Connection` names
24
+ * (also used by the library host, `cli/library-host.ts`). */
25
+ export const stripHopByHop = (headers: Headers) => {
26
+ const excluded = new Set(HOP_BY_HOP);
27
+ for (const name of (headers.get("connection") ?? "").split(",")) excluded.add(name.trim().toLowerCase());
28
+ const out = new Headers();
29
+ headers.forEach((value, key) => { if (!excluded.has(key.toLowerCase())) out.set(key, value); });
30
+ return out;
31
+ };
32
+
33
+ /** The SSE error frame each protocol's own error path emits, so a client
34
+ * parser ends the stream with an error instead of a silent truncation. */
35
+ export function unavailableFrame(pathname: string, message: string): string {
36
+ if (pathname === "/v1/messages")
37
+ return `event: error\ndata: ${JSON.stringify({ type: "error", error: { type: "api_error", message } })}\n\n`;
38
+ if (pathname === "/v1/responses")
39
+ return `event: error\ndata: ${JSON.stringify({ type: "error", code: "engine_unavailable", message, param: null })}\n\n`;
40
+ return `data: ${JSON.stringify({ error: { message, type: "engine_unavailable" } })}\n\n`;
41
+ }
42
+
43
+ /** Pass an event stream through unchanged; when the worker dies under it,
44
+ * end with the protocol's error frame instead of dropping the connection. */
45
+ function guardEventStream(body: ReadableStream<Uint8Array>, failure: () => string): ReadableStream<Uint8Array> {
46
+ const reader = body.getReader();
47
+ return new ReadableStream<Uint8Array>({
48
+ async pull(controller) {
49
+ let item: Awaited<ReturnType<typeof reader.read>>;
50
+ try { item = await reader.read(); }
51
+ catch { controller.enqueue(encoder.encode(failure())); controller.close(); return; }
52
+ if (item.done) controller.close(); else controller.enqueue(item.value);
53
+ },
54
+ cancel(reason) { return reader.cancel(reason).catch(() => {}); },
55
+ });
56
+ }
57
+
58
+ export interface WorkerEngine {
59
+ readonly id: string;
60
+ readonly role: "primary" | "companion";
61
+ readonly supervisor: WorkerSupervisor;
62
+ /** The MLX memory the worker last reported; undefined before its first report and while it is down. */
63
+ measured?(): WorkerMemory | undefined;
64
+ }
65
+
66
+ export interface ProxyRoutesOptions {
67
+ /** The resident workers, and the id of the model requests that name none are answered by. */
68
+ workers(): readonly WorkerEngine[];
69
+ current(): string;
70
+ /** The model router: it leases the worker a request belongs to and forwards to it. */
71
+ models: { handle(request: Request): Promise<Response | null> };
72
+ responses: ReturnType<typeof createResponsesClient>;
73
+ /** The model the server started with. */
74
+ modelId: string;
75
+ startedAt: number;
76
+ }
77
+
78
+ /** `memory` is the worker's own MLX reading (`active_bytes` and `cache_bytes` are what residency counts), null until it reports. */
79
+ export interface WorkerReport { id: string; role: "primary" | "companion"; pid: number | null; state: WorkerSupervisorState; restarts: number; socket: string;
80
+ memory: { active_bytes: number; cache_bytes: number; peak_bytes: number } | null }
81
+
82
+ /** The current model's worker fields, then every resident worker. `state` is `none` while no worker is resident. */
83
+ export interface EngineReport {
84
+ isolated: true;
85
+ state: WorkerSupervisorState | "none";
86
+ pid: number | null;
87
+ restarts: number | null;
88
+ socket: string | null;
89
+ model: string;
90
+ last_exit: { code: number | null; signal: string | null } | null;
91
+ response_store: { entries: number; bytes: number; max_bytes: number; ttl_ms: number };
92
+ workers: WorkerReport[];
93
+ }
94
+
95
+ const unavailableMessage = (error: unknown) => {
96
+ if (error instanceof EngineUnavailableError) return `inference engine unavailable: ${error.message}${error.state === "restarting" ? " — retry shortly" : ""}`;
97
+ return "inference engine unavailable: the engine worker stopped mid-request; respawning — retry shortly";
98
+ };
99
+ /** The 502 a request gets when its worker cannot take it. */
100
+ export function workerUnavailable(error: unknown, worker: WorkerSupervisor): Response {
101
+ return Response.json({ error: { message: unavailableMessage(error), type: "engine_unavailable",
102
+ state: error instanceof EngineUnavailableError ? error.state : worker.state } }, { status: 502 });
103
+ }
104
+
105
+ /** The request goes to the worker as it came, its body streaming through. A worker that cannot take it, or dies under
106
+ * it, answers with the protocol's own unavailable error; nothing is retried. */
107
+ export async function forwardToWorker(worker: WorkerSupervisor, request: Request): Promise<Response> {
108
+ const { pathname } = new URL(request.url);
109
+ try {
110
+ request.signal.throwIfAborted();
111
+ const upstream = await worker.fetch(request.url, {
112
+ method: request.method, headers: stripHopByHop(request.headers), body: request.body,
113
+ signal: request.signal, redirect: "manual", duplex: "half",
114
+ });
115
+ const headers = stripHopByHop(upstream.headers);
116
+ const failure = () => {
117
+ const exit = worker.lastExit;
118
+ return `inference engine unavailable: the engine worker stopped while streaming this response${exit ? ` (${describeExit(exit)})` : ""}; it is being respawned`;
119
+ };
120
+ const stream = upstream.body && (upstream.headers.get("content-type") ?? "").startsWith("text/event-stream")
121
+ ? guardEventStream(upstream.body, () => unavailableFrame(pathname, failure()))
122
+ : upstream.body;
123
+ return new Response(stream, { status: upstream.status, statusText: upstream.statusText, headers });
124
+ } catch (error) {
125
+ if (request.signal.aborted) return new Response(null, { status: 499 });
126
+ return workerUnavailable(error, worker);
127
+ }
128
+ }
129
+
130
+ export function createProxyRoutes(options: ProxyRoutesOptions) {
131
+ const { responses } = options;
132
+ const currentWorker = () => options.workers().find(worker => worker.id === options.current() && worker.role === "primary")?.supervisor;
133
+ const report = (): EngineReport => {
134
+ const engine = currentWorker();
135
+ return {
136
+ isolated: true, state: engine?.state ?? "none", pid: engine?.pid ?? null, restarts: engine?.restarts ?? null,
137
+ socket: engine?.socketPath ?? null, model: options.current(), last_exit: engine?.lastExit ?? null, response_store: responses.stats,
138
+ workers: options.workers().map(({ id, role, supervisor, measured }) => {
139
+ const memory = measured?.();
140
+ return { id, role, pid: supervisor.pid, state: supervisor.state, restarts: supervisor.restarts, socket: supervisor.socketPath,
141
+ memory: memory ? { active_bytes: memory.activeBytes, cache_bytes: memory.cacheBytes, peak_bytes: memory.peakBytes } : null };
142
+ }),
143
+ };
144
+ };
145
+ const health = async (): Promise<Response> => {
146
+ const engine = currentWorker();
147
+ let contribution: Record<string, unknown> = {};
148
+ if (engine?.state === "ready") {
149
+ try {
150
+ const upstream = await engine.fetch("http://engine/health", { signal: AbortSignal.timeout(2_000) });
151
+ const body = await upstream.json() as Record<string, unknown>;
152
+ if (upstream.ok) contribution = { state: body.state, in_flight: body.in_flight, leases: body.leases };
153
+ else contribution = { state: "unreachable" };
154
+ } catch { contribution = { state: "unreachable" }; }
155
+ }
156
+ const { response_store: _store, isolated: _isolated, workers, ...current } = report();
157
+ return Response.json({ status: "ok", isolated: true, engine: { ...current, ...contribution }, workers });
158
+ };
159
+ const stats = async (request: Request): Promise<Response> => {
160
+ const own = { response_store: responses.stats, engine: report() };
161
+ const upstream = await options.models.handle(request).catch(() => null);
162
+ if (upstream?.ok && (upstream.headers.get("content-type") ?? "").includes("json"))
163
+ return Response.json({ ...(await upstream.json() as Record<string, unknown>), ...own });
164
+ if (upstream && !upstream.ok && upstream.status !== 502) return upstream;
165
+ // The worker's own reason (a 502 from the forward), else that no worker is resident.
166
+ const reason = await upstream?.json().then((body: { error?: { message?: string } }) => body.error?.message).catch(() => undefined);
167
+ if (request.signal.aborted) return new Response(null, { status: 499 });
168
+ return Response.json({ server: { owner: "serve", model: options.current(), started_at: options.startedAt }, ...own,
169
+ unavailable: reason ?? "inference engine unavailable: no model worker is resident" });
170
+ };
171
+
172
+ return {
173
+ report,
174
+ async handle(request: Request): Promise<Response | null> {
175
+ const { pathname } = new URL(request.url);
176
+ if (PARENT_ONLY.has(pathname)) return null;
177
+ if (pathname === "/engine") return request.method === "GET" ? Response.json(report()) : null;
178
+ if (request.method === "GET") {
179
+ if (pathname === "/health") return health();
180
+ if (pathname === "/stats") return stats(request);
181
+ }
182
+ if (pathname === "/v1/responses" && request.method === "POST")
183
+ return responses.forward(request, async inner => await options.models.handle(inner) ?? Response.json({ error: { message: "Not found" } }, { status: 404 }));
184
+ return options.models.handle(request);
185
+ },
186
+ };
187
+ }