mlx-bun 0.0.12 → 0.2.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 (226) hide show
  1. package/README.md +40 -24
  2. package/bin/mlx-bun.mjs +1 -1
  3. package/docs/{design → archive}/hlg-sampling.md +3 -3
  4. package/docs/{investigations → archive/investigations}/hlg-sampling-investigation.md +1 -1
  5. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +92 -0
  6. package/docs/{planning → archive/planning}/chat-ui-vision.md +1 -1
  7. package/docs/archive/planning/release-notes-v0.0.13.md +77 -0
  8. package/docs/{planning → archive/planning}/release-notes-v0.0.9.md +1 -1
  9. package/docs/{planning → archive/planning}/website-readme-pass-plan.md +1 -1
  10. package/docs/design/colibri-glm52-port.md +8 -1
  11. package/docs/design/compat-cli-surface-design.md +1 -1
  12. package/docs/design/decode-speed-program.md +1 -1
  13. package/docs/design/docs-reorg-plan.md +1 -1
  14. package/docs/design/dspark-serving-program.md +1 -1
  15. package/docs/design/dspark-speculative-decoding.md +53 -3
  16. package/docs/design/grammar-spec-batching-integration.md +1 -1
  17. package/docs/design/mlx-lm-serving-execution-seam.md +81 -0
  18. package/docs/design/orpo-dynamic-lambda.md +1 -1
  19. package/docs/design/orpo-future-enhancements.md +1 -1
  20. package/docs/design/orpo-training.md +2 -2
  21. package/docs/design/parity-tier-dag.md +1 -1
  22. package/docs/design/repo-cleanup-plan.md +46 -4
  23. package/docs/design/segmented-backward-training.md +2 -2
  24. package/docs/design/ssd-kv-cold-tier.md +127 -0
  25. package/docs/design/turboquant-weights.md +467 -0
  26. package/docs/design/w4a16-compute-precision-spike.md +1 -1
  27. package/docs/design/web-chat-redesign.md +5 -5
  28. package/docs/planning/chunk-task-roadmap.md +1 -1
  29. package/docs/planning/curve-sampler-research-plan.md +4 -4
  30. package/docs/planning/memory-docs-and-dag-plan.md +1 -1
  31. package/docs/planning/release-notes-v0.2.0.md +49 -0
  32. package/docs/planning/video-series.md +421 -0
  33. package/docs/reference/cli.md +15 -4
  34. package/docs/reference/distribution.md +1 -0
  35. package/docs/reference/features-matrix.md +3 -1
  36. package/docs/reference/library-api.md +41 -1
  37. package/docs/reference/models.md +33 -0
  38. package/docs/reference/server-api.md +86 -19
  39. package/docs/reference/server-config.md +57 -33
  40. package/package.json +2 -2
  41. package/src/anthropic.ts +92 -3
  42. package/src/chat-template.ts +34 -1
  43. package/src/cli.ts +131 -36
  44. package/src/config.ts +10 -0
  45. package/src/eval/kl.ts +27 -20
  46. package/src/eval/runner.ts +23 -4
  47. package/src/eval/tasks/bfcl.ts +2 -1
  48. package/src/eval/tasks/gsm8k.ts +2 -1
  49. package/src/eval/tasks/hashhop.ts +2 -1
  50. package/src/eval/tasks/humaneval.ts +5 -4
  51. package/src/eval/tasks/ifeval.ts +2 -1
  52. package/src/eval/tasks/mmlu.ts +2 -1
  53. package/src/expert-io.ts +3 -1
  54. package/src/expert-offload.ts +3 -1
  55. package/src/expert-trace.ts +3 -1
  56. package/src/fit.ts +24 -43
  57. package/src/flags.ts +7 -9
  58. package/src/generate.ts +107 -92
  59. package/src/grammar.ts +2 -1
  60. package/src/harness-pi.ts +1 -1
  61. package/src/index.ts +20 -0
  62. package/src/jobs/job-entry.ts +3 -2
  63. package/src/kv-scheme.ts +199 -0
  64. package/src/kv-store.ts +370 -205
  65. package/src/media-fetch.ts +19 -3
  66. package/src/memory/model.ts +2 -1
  67. package/src/memory/vault.ts +2 -1
  68. package/src/memory-plan.ts +18 -0
  69. package/src/mlx/ffi.ts +8 -11
  70. package/src/mlx/ops.ts +31 -6
  71. package/src/model/batched-mask.ts +1 -1
  72. package/src/model/batched-quant.ts +1 -1
  73. package/src/model/batched-rotating-quant.ts +43 -97
  74. package/src/model/batched-rotating-state.ts +73 -0
  75. package/src/model/batched-rotating.ts +56 -105
  76. package/src/model/batched-row-storage.ts +127 -0
  77. package/src/model/compiled-decode.ts +3 -2
  78. package/src/model/factory.ts +44 -49
  79. package/src/model/gemma4-base.ts +163 -10
  80. package/src/model/generated/gemma4-12b.ts +50 -146
  81. package/src/model/generated/gemma4-26b.ts +32 -92
  82. package/src/model/generated/gemma4-e4b.ts +26 -74
  83. package/src/model/glm52-cache.ts +19 -9
  84. package/src/model/glm52-memory.ts +28 -1
  85. package/src/model/minicpm5.ts +2 -1
  86. package/src/model/profile.ts +433 -0
  87. package/src/model/qwen3-delta.ts +98 -2
  88. package/src/model/qwen3-mrope.ts +252 -0
  89. package/src/model/qwen3_5.ts +217 -25
  90. package/src/native/frame_extract.swift +140 -0
  91. package/src/native-pack.ts +13 -6
  92. package/src/pi-web.ts +6 -5
  93. package/src/prompt-cache.ts +2 -2
  94. package/src/quantize/atomic-output.ts +38 -0
  95. package/src/quantize/calibration.ts +2 -1
  96. package/src/quantize/config-writer.ts +6 -0
  97. package/src/quantize/index.ts +28 -1
  98. package/src/quantize/job.ts +11 -0
  99. package/src/quantize/quantizer.ts +265 -100
  100. package/src/quantize/rotate.ts +603 -0
  101. package/src/quantize/safetensors-writer.ts +79 -1
  102. package/src/quantize/sensitivity.ts +41 -59
  103. package/src/quantize/weight-transform.ts +292 -0
  104. package/src/responses.ts +102 -2
  105. package/src/runtime-config.ts +67 -0
  106. package/src/sampler.ts +272 -0
  107. package/src/serve/admin-routes.ts +184 -0
  108. package/src/serve/aux-routes.ts +132 -0
  109. package/src/serve/batch-scheduler.ts +200 -87
  110. package/src/serve/completion-executor.ts +346 -0
  111. package/src/serve/completion-sink.ts +157 -0
  112. package/src/serve/discovery-routes.ts +205 -0
  113. package/src/serve/generation-gateway.ts +116 -109
  114. package/src/serve/kv-budget.ts +14 -0
  115. package/src/serve/lab-routes.ts +338 -0
  116. package/src/serve/model-admin-routes.ts +153 -0
  117. package/src/serve/prompt-response-trace.ts +147 -0
  118. package/src/serve/request-plan.ts +158 -0
  119. package/src/serve/static-routes.ts +101 -0
  120. package/src/server.ts +768 -1367
  121. package/src/spec/dflash-source.ts +2 -1
  122. package/src/spec/dspark/module-dflash.ts +1 -1
  123. package/src/spec/glm52-mtp-source.ts +8 -0
  124. package/src/spec/qwen-mtp-source.ts +431 -0
  125. package/src/spec/serve-loop.ts +36 -68
  126. package/src/ssd-cache.ts +17 -14
  127. package/src/ssd-durability.ts +195 -0
  128. package/src/train/flash-cce.ts +12 -11
  129. package/src/train/forward.ts +32 -1
  130. package/src/train/loss.ts +2 -1
  131. package/src/train/segmented.ts +4 -3
  132. package/src/train/trainer.ts +3 -2
  133. package/src/vision/preprocess.ts +1 -1
  134. package/src/vision/prompt.ts +47 -1
  135. package/src/vision/qwen3vl-preprocess.ts +409 -0
  136. package/src/vision/qwen3vl-prompt.ts +196 -0
  137. package/src/vision/qwen3vl-tower.ts +513 -0
  138. package/src/vision/video-frames.ts +148 -0
  139. package/src/web/app.js +10 -3
  140. package/src/web/src/chat.ts +12 -5
  141. package/src/weights.ts +81 -26
  142. /package/docs/{investigations → archive/investigations}/chunk-finetune-experiment.md +0 -0
  143. /package/docs/{investigations → archive/investigations}/colibri-oracle-pin.md +0 -0
  144. /package/docs/{investigations → archive/investigations}/curve-bisector-routing.md +0 -0
  145. /package/docs/{investigations → archive/investigations}/curve-runs/bisector-route.json +0 -0
  146. /package/docs/{investigations → archive/investigations}/curve-runs/bisector-trace.json +0 -0
  147. /package/docs/{investigations → archive/investigations}/curve-runs/curve-terrain.json +0 -0
  148. /package/docs/{investigations → archive/investigations}/curve-runs/curve-terrain.svg +0 -0
  149. /package/docs/{investigations → archive/investigations}/curve-terrain.html +0 -0
  150. /package/docs/{investigations → archive/investigations}/decode-roofline-lookagain.md +0 -0
  151. /package/docs/{investigations → archive/investigations}/dspark-handoff.md +0 -0
  152. /package/docs/{investigations → archive/investigations}/expert-offload-single-user-moe.md +0 -0
  153. /package/docs/{investigations → archive/investigations}/hlg-ab.html +0 -0
  154. /package/docs/{investigations → archive/investigations}/hlg-explorer.html +0 -0
  155. /package/docs/{investigations → archive/investigations}/hlg-figs/cliff.svg +0 -0
  156. /package/docs/{investigations → archive/investigations}/hlg-figs/cross.svg +0 -0
  157. /package/docs/{investigations → archive/investigations}/hlg-figs/edges.svg +0 -0
  158. /package/docs/{investigations → archive/investigations}/hlg-figs/k20-grids.svg +0 -0
  159. /package/docs/{investigations → archive/investigations}/hlg-figs/k20-peaks.svg +0 -0
  160. /package/docs/{investigations → archive/investigations}/hlg-figs/lw-curve.svg +0 -0
  161. /package/docs/{investigations → archive/investigations}/hlg-figs/terrain.svg +0 -0
  162. /package/docs/{investigations → archive/investigations}/hlg-report.html +0 -0
  163. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-ab.json +0 -0
  164. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-blind-prompts.md +0 -0
  165. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-blind.json +0 -0
  166. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-cliff.json +0 -0
  167. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-cross.json +0 -0
  168. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-edge.json +0 -0
  169. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-eval1.json +0 -0
  170. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-frontier.json +0 -0
  171. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-grid.json +0 -0
  172. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-grid2.json +0 -0
  173. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-lw-coarse.json +0 -0
  174. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-map.json +0 -0
  175. /package/docs/{investigations → archive/investigations}/hlg-runs/run0-original.log +0 -0
  176. /package/docs/{investigations → archive/investigations}/hlg-runs/runA-recommended.log +0 -0
  177. /package/docs/{investigations → archive/investigations}/hlg-runs/runAA-cliff-cpm5.log +0 -0
  178. /package/docs/{investigations → archive/investigations}/hlg-runs/runAB-grid2-e4b.log +0 -0
  179. /package/docs/{investigations → archive/investigations}/hlg-runs/runAC-peak-e4b-partial.log +0 -0
  180. /package/docs/{investigations → archive/investigations}/hlg-runs/runAD-ab-samples.log +0 -0
  181. /package/docs/{investigations → archive/investigations}/hlg-runs/runAE-edge-e4b.log +0 -0
  182. /package/docs/{investigations → archive/investigations}/hlg-runs/runAF-logit-calibration.log +0 -0
  183. /package/docs/{investigations → archive/investigations}/hlg-runs/runAG-eval-config2.log +0 -0
  184. /package/docs/{investigations → archive/investigations}/hlg-runs/runB-gain-variants.log +0 -0
  185. /package/docs/{investigations → archive/investigations}/hlg-runs/runC-pivots.log +0 -0
  186. /package/docs/{investigations → archive/investigations}/hlg-runs/runD-oetf.log +0 -0
  187. /package/docs/{investigations → archive/investigations}/hlg-runs/runE-eotf.log +0 -0
  188. /package/docs/{investigations → archive/investigations}/hlg-runs/runF-shaper-working.log +0 -0
  189. /package/docs/{investigations → archive/investigations}/hlg-runs/runG-shaper-sweep.log +0 -0
  190. /package/docs/{investigations → archive/investigations}/hlg-runs/runH-wsweep.log +0 -0
  191. /package/docs/{investigations → archive/investigations}/hlg-runs/runI-asweep.log +0 -0
  192. /package/docs/{investigations → archive/investigations}/hlg-runs/runJ-knobmap.log +0 -0
  193. /package/docs/{investigations → archive/investigations}/hlg-runs/runK-frontier.log +0 -0
  194. /package/docs/{investigations → archive/investigations}/hlg-runs/runL-widemap.log +0 -0
  195. /package/docs/{investigations → archive/investigations}/hlg-runs/runM-frontier-emb.log +0 -0
  196. /package/docs/{investigations → archive/investigations}/hlg-runs/runN-verify.log +0 -0
  197. /package/docs/{investigations → archive/investigations}/hlg-runs/runO-frontier-seed5000.log +0 -0
  198. /package/docs/{investigations → archive/investigations}/hlg-runs/runP-grid-AxSm.log +0 -0
  199. /package/docs/{investigations → archive/investigations}/hlg-runs/runQ-grid-tgW.log +0 -0
  200. /package/docs/{investigations → archive/investigations}/hlg-runs/runR-blind-transcript.log +0 -0
  201. /package/docs/{investigations → archive/investigations}/hlg-runs/runS-cross-coherence.log +0 -0
  202. /package/docs/{investigations → archive/investigations}/hlg-runs/runT-cross-cpm5.log +0 -0
  203. /package/docs/{investigations → archive/investigations}/hlg-runs/runU-cross-12b.log +0 -0
  204. /package/docs/{investigations → archive/investigations}/hlg-runs/runV-lw-e4b.log +0 -0
  205. /package/docs/{investigations → archive/investigations}/hlg-runs/runW-lw-12b.log +0 -0
  206. /package/docs/{investigations → archive/investigations}/hlg-runs/runX-lw-cpm5.log +0 -0
  207. /package/docs/{investigations → archive/investigations}/hlg-runs/runY-cliff-e4b.log +0 -0
  208. /package/docs/{investigations → archive/investigations}/hlg-runs/runZ-cliff-12b.log +0 -0
  209. /package/docs/{investigations → archive/investigations}/indexshare-performance-spike.md +0 -0
  210. /package/docs/{investigations → archive/investigations}/kernel-perf-review-2026-07.md +0 -0
  211. /package/docs/{investigations → archive/investigations}/lab-build-journal.md +0 -0
  212. /package/docs/{investigations → archive/investigations}/openwiki-evaluation.md +0 -0
  213. /package/docs/{investigations → archive/investigations}/orpo-base-uf-experiment-and-directions.md +0 -0
  214. /package/docs/{investigations → archive/investigations}/orpo-flash-cce-pin-leak.md +0 -0
  215. /package/docs/{investigations → archive/investigations}/orpo-uf-testing-handoff.md +0 -0
  216. /package/docs/{investigations → archive/investigations}/pi-builtin-investigation.md +0 -0
  217. /package/docs/{investigations → archive/investigations}/segmented-backward-handoff.md +0 -0
  218. /package/docs/{investigations → archive/investigations}/starter-model-port-handoff.md +0 -0
  219. /package/docs/{investigations → archive/investigations}/steel-flash-cce-handoff.md +0 -0
  220. /package/docs/{investigations → archive/investigations}/trainer-validation-experiment.md +0 -0
  221. /package/docs/{planning → archive/planning}/chat-ui-vision-memory-first.md +0 -0
  222. /package/docs/{planning → archive/planning}/journal.md +0 -0
  223. /package/docs/{planning → archive/planning}/release-notes-v0.0.10.md +0 -0
  224. /package/docs/{planning → archive/planning}/release-notes-v0.0.11.md +0 -0
  225. /package/docs/{planning → archive/planning}/release-notes-v0.0.12.md +0 -0
  226. /package/docs/{planning → archive/planning}/web-ui-pass-plan.md +0 -0
package/src/web/app.js CHANGED
@@ -3496,15 +3496,21 @@ function createChatController() {
3496
3496
  t0: performance.now(),
3497
3497
  tFirst: 0,
3498
3498
  tokens: 0,
3499
+ settled: false,
3499
3500
  blockState: { blocks: [] },
3500
3501
  scheduleText: () => {},
3501
3502
  scheduleThinking: () => {},
3502
3503
  citations: pendingCitations
3503
3504
  };
3504
3505
  pendingCitations = [];
3505
- curAssistant.scheduleText = makeFrameScheduler(() => renderBlocksIncremental(curAssistant.textNode, curAssistant.text, curAssistant.blockState), atBottom, stick);
3506
- curAssistant.scheduleThinking = makeFrameScheduler(() => {
3507
- curAssistant.thinkBody.textContent = curAssistant.thinking;
3506
+ const assistant = curAssistant;
3507
+ assistant.scheduleText = makeFrameScheduler(() => {
3508
+ if (!assistant.settled)
3509
+ renderBlocksIncremental(assistant.textNode, assistant.text, assistant.blockState);
3510
+ }, atBottom, stick);
3511
+ assistant.scheduleThinking = makeFrameScheduler(() => {
3512
+ if (!assistant.settled)
3513
+ assistant.thinkBody.textContent = assistant.thinking;
3508
3514
  }, atBottom, stick);
3509
3515
  stick(true);
3510
3516
  }
@@ -3670,6 +3676,7 @@ function createChatController() {
3670
3676
  function finishStreaming(a) {
3671
3677
  if (!a)
3672
3678
  return;
3679
+ a.settled = true;
3673
3680
  const previewIdx = captureCanvasViewStates(a.textNode);
3674
3681
  a.textNode.innerHTML = mdToHtml(a.text);
3675
3682
  restoreCanvasViewStates(a.textNode, previewIdx);
@@ -142,6 +142,7 @@ interface AssistantMsgState {
142
142
  t0: number;
143
143
  tFirst: number;
144
144
  tokens: number;
145
+ settled: boolean;
145
146
  blockState: BlockState;
146
147
  scheduleText: () => void;
147
148
  scheduleThinking: () => void;
@@ -373,7 +374,7 @@ export function createChatController() {
373
374
  const meta = el("div", "meta", m);
374
375
  curAssistant = {
375
376
  m, bubble, thinkBox, thinkBody, textNode, cursor, meta, tools: new Map(),
376
- text: "", thinking: "", t0: performance.now(), tFirst: 0, tokens: 0,
377
+ text: "", thinking: "", t0: performance.now(), tFirst: 0, tokens: 0, settled: false,
377
378
  // Block-memoization state (see renderBlocksIncremental) for the main
378
379
  // markdown text stream, plus a shared rAF scheduler so a burst of
379
380
  // deltas within one frame (e.g. spec-decode emitting several tokens
@@ -390,11 +391,16 @@ export function createChatController() {
390
391
  // Consumed — a later turn with no RAG'd attachments must not inherit
391
392
  // this turn's citation set (see the pendingCitations declaration above).
392
393
  pendingCitations = [];
393
- curAssistant.scheduleText = makeFrameScheduler(
394
- () => renderBlocksIncremental(curAssistant!.textNode, curAssistant!.text, curAssistant!.blockState),
394
+ const assistant = curAssistant;
395
+ assistant.scheduleText = makeFrameScheduler(
396
+ () => {
397
+ if (!assistant.settled) renderBlocksIncremental(assistant.textNode, assistant.text, assistant.blockState);
398
+ },
395
399
  atBottom, stick);
396
- curAssistant.scheduleThinking = makeFrameScheduler(
397
- () => { curAssistant!.thinkBody.textContent = curAssistant!.thinking; },
400
+ assistant.scheduleThinking = makeFrameScheduler(
401
+ () => {
402
+ if (!assistant.settled) assistant.thinkBody.textContent = assistant.thinking;
403
+ },
398
404
  atBottom, stick);
399
405
  stick(true);
400
406
  }
@@ -612,6 +618,7 @@ export function createChatController() {
612
618
  * directly on the full string. */
613
619
  function finishStreaming(a: AssistantMsgState | null): void {
614
620
  if (!a) return;
621
+ a.settled = true;
615
622
  // Preserve any Canvas Preview toggle the user made mid-stream (Principle
616
623
  // 9: this full non-memoized re-render must not throw away user
617
624
  // interaction state) — mdCodeBlock always emits qualifying fences back
package/src/weights.ts CHANGED
@@ -82,30 +82,7 @@ export class Weights {
82
82
  const shards = await (options.openShards ?? ShardedSafetensors.open)(modelDir);
83
83
  const self = new Weights(shards, native);
84
84
  try {
85
- for (const [file, sf] of self.shards.files) {
86
- // out-param slots read back via read.u64, not [0] (DFG stale-read
87
- // bug — see outArray in mlx/ffi.ts). Cold path, but the rule is
88
- // uniform: native wrote it, read.* reads it.
89
- const arrMap = new BigUint64Array([native.newArrayMap()]);
90
- const arrMapPtr = ptr(arrMap);
91
- let arrayMapTransferred = false;
92
- try {
93
- const metaMap = new BigUint64Array([native.newStringMap()]);
94
- const metaMapPtr = ptr(metaMap);
95
- try {
96
- const status = native.loadSafetensors(arrMap, metaMap, sf.path);
97
- if (status !== 0)
98
- throw new Error(`mlx_load_safetensors(${sf.path}) failed`);
99
- self.#maps.set(file, read.u64(arrMapPtr, 0));
100
- arrayMapTransferred = true;
101
- } finally {
102
- native.freeStringMap(read.u64(metaMapPtr, 0));
103
- }
104
- } finally {
105
- if (!arrayMapTransferred)
106
- native.freeArrayMap(read.u64(arrMapPtr, 0));
107
- }
108
- }
85
+ for (const [file] of self.shards.files) self.#openShard(file);
109
86
  return self;
110
87
  } catch (error) {
111
88
  // A failure on shard N must release every array map transferred by
@@ -115,6 +92,64 @@ export class Weights {
115
92
  }
116
93
  }
117
94
 
95
+ /** Load one shard's native string→array map (lazy re-open path too). */
96
+ #openShard(file: string): void {
97
+ const sf = this.shards.files.get(file);
98
+ if (!sf) throw new Error(`no shard file ${file}`);
99
+ // out-param slots read back via read.u64, not [0] (DFG stale-read
100
+ // bug — see outArray in mlx/ffi.ts). Cold path, but the rule is
101
+ // uniform: native wrote it, read.* reads it.
102
+ const arrMap = new BigUint64Array([this.#native.newArrayMap()]);
103
+ const arrMapPtr = ptr(arrMap);
104
+ let arrayMapTransferred = false;
105
+ try {
106
+ const metaMap = new BigUint64Array([this.#native.newStringMap()]);
107
+ const metaMapPtr = ptr(metaMap);
108
+ try {
109
+ const status = this.#native.loadSafetensors(arrMap, metaMap, sf.path);
110
+ if (status !== 0)
111
+ throw new Error(`mlx_load_safetensors(${sf.path}) failed`);
112
+ this.#maps.set(file, read.u64(arrMapPtr, 0));
113
+ arrayMapTransferred = true;
114
+ } finally {
115
+ this.#native.freeStringMap(read.u64(metaMapPtr, 0));
116
+ }
117
+ } finally {
118
+ if (!arrayMapTransferred)
119
+ this.#native.freeArrayMap(read.u64(arrMapPtr, 0));
120
+ }
121
+ }
122
+
123
+ /** Free one shard's native map AND every cached tensor from it, releasing
124
+ * all bytes materialized while consuming that shard. The shard transparently
125
+ * re-opens on the next tensor() touching it (header parse only — lazy).
126
+ * Required by streaming whole-model transforms at 27B scale: the native map
127
+ * retains every evaluated source array, so per-tensor dispose alone frees
128
+ * nothing — the whole model accumulates in RAM (2026-08-18 fold OOM). */
129
+ releaseShard(file: string): void {
130
+ const sf = this.shards.files.get(file);
131
+ if (!sf) return;
132
+ for (const [name, arr] of this.#arrays) {
133
+ if (this.shards.tensorToFile.get(name) === sf) {
134
+ arr.dispose();
135
+ this.#arrays.delete(name);
136
+ }
137
+ }
138
+ const map = this.#maps.get(file);
139
+ if (map !== undefined) {
140
+ this.#native.freeArrayMap(map);
141
+ this.#maps.delete(file);
142
+ }
143
+ }
144
+
145
+ /** The shard filename holding `name` (for releaseShard scheduling). */
146
+ fileOf(name: string): string | undefined {
147
+ const sf = this.shards.tensorToFile.get(name);
148
+ if (!sf) return undefined;
149
+ for (const [file, cand] of this.shards.files) if (cand === sf) return file;
150
+ return undefined;
151
+ }
152
+
118
153
  get tensorNames(): string[] {
119
154
  return this.shards.tensorNames;
120
155
  }
@@ -133,8 +168,15 @@ export class Weights {
133
168
  if (!arr) {
134
169
  const sf = this.shards.tensorToFile.get(name);
135
170
  if (!sf) throw new Error(`no tensor named ${name}`);
136
- const mapHandle = [...this.#maps.entries()]
137
- .find(([file]) => this.shards.files.get(file) === sf)![1];
171
+ let entry = [...this.#maps.entries()]
172
+ .find(([file]) => this.shards.files.get(file) === sf);
173
+ if (!entry) {
174
+ // Shard was releaseShard()'d — transparently re-open it.
175
+ const file = this.fileOf(name)!;
176
+ this.#openShard(file);
177
+ entry = [file, this.#maps.get(file)!];
178
+ }
179
+ const mapHandle = entry[1];
138
180
  const slot = new BigUint64Array([C.mlx_array_new()]);
139
181
  const slotPtr = ptr(slot);
140
182
  if (C.mlx_map_string_to_array_get(slotPtr, mapHandle, ptr(cstr(name))) !== 0)
@@ -145,6 +187,19 @@ export class Weights {
145
187
  return arr;
146
188
  }
147
189
 
190
+ /** Dispose + evict one cached tensor so its materialized bytes are freed
191
+ * mid-walk. Streaming whole-model transforms (fold/quantize at 27B scale)
192
+ * MUST release each source tensor after its consumer evaluates, or the
193
+ * cache accumulates the entire model in RAM. Safe to call for names never
194
+ * accessed; the next tensor(name) call re-opens it from the map. */
195
+ release(name: string): void {
196
+ const arr = this.#arrays.get(name);
197
+ if (arr) {
198
+ arr.dispose();
199
+ this.#arrays.delete(name);
200
+ }
201
+ }
202
+
148
203
  dispose(): void {
149
204
  for (const arr of this.#arrays.values()) arr.dispose();
150
205
  this.#arrays.clear();
File without changes