@simulatte/doppler 0.1.5 → 0.1.7

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 (392) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +25 -17
  3. package/package.json +20 -4
  4. package/src/adapters/adapter-registry.js +12 -1
  5. package/src/adapters/lora-loader.js +23 -6
  6. package/src/bridge/extension-client.d.ts +5 -0
  7. package/src/bridge/extension-client.js +40 -0
  8. package/src/bridge/index.d.ts +2 -1
  9. package/src/bridge/index.js +6 -4
  10. package/src/browser/browser-converter.js +26 -1
  11. package/src/browser/file-picker.js +6 -0
  12. package/src/browser/safetensors-parser-browser.js +84 -1
  13. package/src/browser/shard-io-browser.js +2 -2
  14. package/src/browser/tensor-source-download.js +8 -2
  15. package/src/browser/tensor-source-http.d.ts +1 -0
  16. package/src/browser/tensor-source-http.js +5 -1
  17. package/src/client/doppler-api.browser.js +20 -4
  18. package/src/client/doppler-api.js +19 -3
  19. package/src/client/doppler-provider/generation.js +12 -0
  20. package/src/client/doppler-provider/model-manager.d.ts +10 -0
  21. package/src/client/doppler-provider/model-manager.js +91 -19
  22. package/src/client/doppler-provider/source-runtime.d.ts +2 -1
  23. package/src/client/doppler-provider/source-runtime.js +132 -13
  24. package/src/client/doppler-registry.json +8 -7
  25. package/src/config/backward-registry-loader.js +17 -2
  26. package/src/config/execution-v0-contract-check.js +113 -15
  27. package/src/config/kernel-path-contract-check.js +57 -29
  28. package/src/config/kernel-path-loader.js +5 -36
  29. package/src/config/kernels/kernel-ref-digests.js +39 -39
  30. package/src/config/kernels/registry.js +14 -1
  31. package/src/config/kernels/registry.json +49 -7
  32. package/src/config/loader.d.ts +1 -1
  33. package/src/config/loader.js +43 -4
  34. package/src/config/merge-contract-check.js +59 -4
  35. package/src/config/merge-helpers.js +128 -7
  36. package/src/config/merge.d.ts +1 -0
  37. package/src/config/merge.js +28 -0
  38. package/src/config/param-validator.js +47 -2
  39. package/src/config/presets/kernel-paths/{gemma2-q4k-dequant-f32a.json → gemma2-q4k-dequant-f32a-nosubgroups.json} +3 -3
  40. package/src/config/presets/kernel-paths/gemma3-f16-fused-f32a-online-streamingprefill.json +223 -0
  41. package/src/config/presets/kernel-paths/{gemma3-q4k-dequant-f32a.json → gemma3-q4k-dequant-f32a-nosubgroups.json} +3 -3
  42. package/src/config/presets/kernel-paths/registry.json +29 -8
  43. package/src/config/presets/models/gemma2.json +2 -2
  44. package/src/config/presets/models/qwen3.json +9 -2
  45. package/src/config/presets/models/transformer.json +5 -0
  46. package/src/config/presets/runtime/experiments/bench/gemma3-bench-q4k.json +1 -1
  47. package/src/config/presets/runtime/experiments/debug/gemma3-debug-q4k.json +1 -1
  48. package/src/config/presets/runtime/experiments/verify/gemma3-verify.json +1 -1
  49. package/src/config/presets/runtime/kernels/dequant-f16-q4k.json +6 -13
  50. package/src/config/presets/runtime/kernels/dequant-f32-q4k.json +6 -13
  51. package/src/config/presets/runtime/kernels/embeddinggemma-q4k-dequant-f32a.json +37 -0
  52. package/src/config/presets/runtime/kernels/fused-q4k.json +6 -13
  53. package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f16a.json +33 -0
  54. package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f32a-nosubgroups.json +33 -0
  55. package/src/config/presets/runtime/kernels/gemma2-q4k-fused-f32a.json +33 -0
  56. package/src/config/presets/runtime/kernels/safe-q4k.json +6 -13
  57. package/src/config/presets/runtime/platform/metal-apple-q4k.json +1 -1
  58. package/src/config/required-inference-fields-contract-check.js +6 -0
  59. package/src/config/runtime.js +6 -1
  60. package/src/config/schema/debug.schema.d.ts +5 -0
  61. package/src/config/schema/doppler.schema.js +16 -21
  62. package/src/config/schema/inference-defaults.schema.js +6 -3
  63. package/src/config/schema/inference.schema.d.ts +9 -0
  64. package/src/config/schema/kernel-path.schema.d.ts +11 -1
  65. package/src/config/schema/kernel-thresholds.schema.js +12 -4
  66. package/src/config/schema/manifest.schema.d.ts +8 -1
  67. package/src/config/schema/manifest.schema.js +19 -3
  68. package/src/config/training-defaults.js +30 -22
  69. package/src/converter/conversion-plan.js +94 -9
  70. package/src/converter/core.d.ts +7 -0
  71. package/src/converter/core.js +14 -9
  72. package/src/converter/execution-v0-manifest.js +4 -1
  73. package/src/converter/index.d.ts +1 -0
  74. package/src/converter/index.js +1 -0
  75. package/src/converter/manifest-inference.js +43 -12
  76. package/src/converter/parsers/diffusion.js +0 -3
  77. package/src/converter/quantization-info.js +35 -15
  78. package/src/converter/rope-config.js +42 -0
  79. package/src/converter/shard-packer.d.ts +1 -1
  80. package/src/converter/shard-packer.js +4 -1
  81. package/src/debug/config.js +123 -11
  82. package/src/debug/signals.js +7 -1
  83. package/src/debug/tensor.d.ts +2 -0
  84. package/src/debug/tensor.js +13 -2
  85. package/src/distribution/p2p-control-plane.js +52 -12
  86. package/src/distribution/p2p-observability.js +43 -7
  87. package/src/distribution/p2p-webrtc-browser.js +20 -0
  88. package/src/distribution/shard-delivery.js +77 -26
  89. package/src/formats/gguf/types.js +33 -16
  90. package/src/formats/rdrr/groups.d.ts +12 -4
  91. package/src/formats/rdrr/groups.js +3 -6
  92. package/src/formats/rdrr/parsing.js +39 -2
  93. package/src/formats/rdrr/types.d.ts +2 -1
  94. package/src/gpu/command-recorder.js +86 -61
  95. package/src/gpu/device.d.ts +1 -0
  96. package/src/gpu/device.js +131 -19
  97. package/src/gpu/kernel-tuner/benchmarks.js +326 -316
  98. package/src/gpu/kernel-tuner/cache.js +71 -4
  99. package/src/gpu/kernel-tuner/tuner.js +22 -4
  100. package/src/gpu/kernels/attention.js +113 -34
  101. package/src/gpu/kernels/backward/adam.js +62 -58
  102. package/src/gpu/kernels/backward/attention_backward.js +257 -169
  103. package/src/gpu/kernels/backward/conv2d_backward.js +14 -1
  104. package/src/gpu/kernels/bias_add.wgsl +8 -6
  105. package/src/gpu/kernels/bias_add_f16.wgsl +8 -5
  106. package/src/gpu/kernels/cast.js +191 -149
  107. package/src/gpu/kernels/check-stop.js +33 -44
  108. package/src/gpu/kernels/conv2d.js +27 -17
  109. package/src/gpu/kernels/conv2d.wgsl +7 -8
  110. package/src/gpu/kernels/conv2d_f16.wgsl +7 -8
  111. package/src/gpu/kernels/cross_entropy_loss.js +21 -15
  112. package/src/gpu/kernels/depthwise_conv2d.js +37 -26
  113. package/src/gpu/kernels/depthwise_conv2d.wgsl +6 -9
  114. package/src/gpu/kernels/depthwise_conv2d_f16.wgsl +6 -9
  115. package/src/gpu/kernels/dequant.js +178 -126
  116. package/src/gpu/kernels/energy.d.ts +3 -21
  117. package/src/gpu/kernels/energy.js +111 -88
  118. package/src/gpu/kernels/feature-check.js +1 -1
  119. package/src/gpu/kernels/fused_ffn.js +84 -65
  120. package/src/gpu/kernels/fused_matmul_residual.js +56 -33
  121. package/src/gpu/kernels/fused_matmul_rmsnorm.js +62 -45
  122. package/src/gpu/kernels/gather.js +33 -15
  123. package/src/gpu/kernels/gelu.js +19 -11
  124. package/src/gpu/kernels/grouped_pointwise_conv2d.js +34 -23
  125. package/src/gpu/kernels/grouped_pointwise_conv2d.wgsl +6 -9
  126. package/src/gpu/kernels/grouped_pointwise_conv2d_f16.wgsl +6 -9
  127. package/src/gpu/kernels/groupnorm.js +34 -23
  128. package/src/gpu/kernels/kv-quantize.js +5 -2
  129. package/src/gpu/kernels/layernorm.js +35 -19
  130. package/src/gpu/kernels/logit-merge.js +5 -3
  131. package/src/gpu/kernels/matmul.js +83 -39
  132. package/src/gpu/kernels/modulate.js +23 -15
  133. package/src/gpu/kernels/moe.js +221 -175
  134. package/src/gpu/kernels/pixel_shuffle.js +22 -14
  135. package/src/gpu/kernels/pixel_shuffle.wgsl +4 -5
  136. package/src/gpu/kernels/pixel_shuffle_f16.wgsl +4 -5
  137. package/src/gpu/kernels/relu.js +31 -10
  138. package/src/gpu/kernels/relu.wgsl +2 -1
  139. package/src/gpu/kernels/relu_f16.wgsl +2 -1
  140. package/src/gpu/kernels/repeat_channels.js +25 -17
  141. package/src/gpu/kernels/repeat_channels.wgsl +4 -5
  142. package/src/gpu/kernels/repeat_channels_f16.wgsl +4 -5
  143. package/src/gpu/kernels/residual.js +69 -23
  144. package/src/gpu/kernels/residual.wgsl +6 -3
  145. package/src/gpu/kernels/residual_f16.wgsl +2 -1
  146. package/src/gpu/kernels/residual_f16_vec4.wgsl +2 -1
  147. package/src/gpu/kernels/residual_vec4.wgsl +2 -1
  148. package/src/gpu/kernels/rmsnorm.js +96 -28
  149. package/src/gpu/kernels/rmsnorm.wgsl +14 -6
  150. package/src/gpu/kernels/rmsnorm_f16.wgsl +10 -2
  151. package/src/gpu/kernels/rope.d.ts +2 -0
  152. package/src/gpu/kernels/rope.js +14 -1
  153. package/src/gpu/kernels/rope.wgsl +56 -40
  154. package/src/gpu/kernels/sample.js +27 -38
  155. package/src/gpu/kernels/sana_linear_attention.js +19 -12
  156. package/src/gpu/kernels/sana_linear_attention_apply.wgsl +4 -5
  157. package/src/gpu/kernels/sana_linear_attention_apply_f16.wgsl +4 -5
  158. package/src/gpu/kernels/sana_linear_attention_summary.wgsl +4 -0
  159. package/src/gpu/kernels/sana_linear_attention_summary_f16.wgsl +4 -0
  160. package/src/gpu/kernels/scale.js +18 -11
  161. package/src/gpu/kernels/shader-cache.js +4 -2
  162. package/src/gpu/kernels/silu.d.ts +1 -0
  163. package/src/gpu/kernels/silu.js +148 -82
  164. package/src/gpu/kernels/silu.wgsl +19 -9
  165. package/src/gpu/kernels/silu_f16.wgsl +19 -9
  166. package/src/gpu/kernels/softmax.js +44 -25
  167. package/src/gpu/kernels/split_qkv.js +23 -13
  168. package/src/gpu/kernels/transpose.js +31 -10
  169. package/src/gpu/kernels/transpose.wgsl +6 -5
  170. package/src/gpu/kernels/upsample2d.js +22 -13
  171. package/src/gpu/kernels/upsample2d.wgsl +6 -9
  172. package/src/gpu/kernels/upsample2d_f16.wgsl +6 -9
  173. package/src/gpu/kernels/utils.js +35 -13
  174. package/src/gpu/partitioned-buffer-pool.js +10 -2
  175. package/src/gpu/perf-guards.js +2 -9
  176. package/src/gpu/profiler.js +27 -22
  177. package/src/gpu/readback-utils.d.ts +16 -0
  178. package/src/gpu/readback-utils.js +41 -0
  179. package/src/gpu/submit-tracker.js +13 -0
  180. package/src/gpu/uniform-cache.d.ts +1 -0
  181. package/src/gpu/uniform-cache.js +30 -9
  182. package/src/hotswap/intent-bundle.js +6 -0
  183. package/src/hotswap/manifest.d.ts +10 -1
  184. package/src/hotswap/manifest.js +12 -2
  185. package/src/hotswap/runtime.js +30 -8
  186. package/src/index-browser.d.ts +44 -0
  187. package/src/index-browser.js +14 -0
  188. package/src/inference/browser-harness-contract-helpers.d.ts +5 -0
  189. package/src/inference/browser-harness-contract-helpers.js +28 -0
  190. package/src/inference/browser-harness-diffusion-energy-suites.d.ts +2 -0
  191. package/src/inference/browser-harness-diffusion-energy-suites.js +269 -0
  192. package/src/inference/browser-harness-model-helpers.d.ts +16 -0
  193. package/src/inference/browser-harness-model-helpers.js +217 -0
  194. package/src/inference/browser-harness-report-helpers.d.ts +7 -0
  195. package/src/inference/browser-harness-report-helpers.js +42 -0
  196. package/src/inference/browser-harness-runtime-helpers.d.ts +61 -0
  197. package/src/inference/browser-harness-runtime-helpers.js +415 -0
  198. package/src/inference/browser-harness-suite-helpers.d.ts +28 -0
  199. package/src/inference/browser-harness-suite-helpers.js +268 -0
  200. package/src/inference/browser-harness-text-helpers.d.ts +27 -0
  201. package/src/inference/browser-harness-text-helpers.js +788 -0
  202. package/src/inference/browser-harness.d.ts +6 -0
  203. package/src/inference/browser-harness.js +130 -1950
  204. package/src/inference/kv-cache/base.js +140 -94
  205. package/src/inference/kv-cache/tiered.js +5 -3
  206. package/src/inference/moe-router.js +88 -56
  207. package/src/inference/multi-model-network.js +5 -3
  208. package/src/inference/network-evolution.d.ts +11 -2
  209. package/src/inference/network-evolution.js +20 -21
  210. package/src/inference/pipelines/context.d.ts +3 -0
  211. package/src/inference/pipelines/context.js +142 -2
  212. package/src/inference/pipelines/diffusion/helpers.js +7 -2
  213. package/src/inference/pipelines/diffusion/pipeline.js +17 -7
  214. package/src/inference/pipelines/diffusion/sd3-transformer.js +10 -10
  215. package/src/inference/pipelines/diffusion/text-encoder-gpu.d.ts +5 -0
  216. package/src/inference/pipelines/diffusion/text-encoder-gpu.js +27 -15
  217. package/src/inference/pipelines/diffusion/vae.js +3 -7
  218. package/src/inference/pipelines/energy/pipeline.js +27 -21
  219. package/src/inference/pipelines/energy/quintel.d.ts +5 -0
  220. package/src/inference/pipelines/energy/quintel.js +11 -0
  221. package/src/inference/pipelines/energy-head/row-head-pipeline.js +17 -13
  222. package/src/inference/pipelines/structured/json-head-pipeline.js +26 -11
  223. package/src/inference/pipelines/text/attention/projections.js +151 -101
  224. package/src/inference/pipelines/text/attention/record.js +73 -10
  225. package/src/inference/pipelines/text/attention/run.js +73 -10
  226. package/src/inference/pipelines/text/chat-format.js +25 -1
  227. package/src/inference/pipelines/text/config.d.ts +4 -0
  228. package/src/inference/pipelines/text/config.js +71 -5
  229. package/src/inference/pipelines/text/embed.js +2 -8
  230. package/src/inference/pipelines/text/execution-plan.js +64 -50
  231. package/src/inference/pipelines/text/execution-v0-contract-helpers.d.ts +59 -0
  232. package/src/inference/pipelines/text/execution-v0-contract-helpers.js +937 -0
  233. package/src/inference/pipelines/text/execution-v0-runtime-builders.d.ts +15 -0
  234. package/src/inference/pipelines/text/execution-v0-runtime-builders.js +279 -0
  235. package/src/inference/pipelines/text/execution-v0.js +78 -1002
  236. package/src/inference/pipelines/text/ffn/standard.js +3 -0
  237. package/src/inference/pipelines/text/generator-steps.d.ts +46 -0
  238. package/src/inference/pipelines/text/generator-steps.js +298 -207
  239. package/src/inference/pipelines/text/generator.js +6 -23
  240. package/src/inference/pipelines/text/init.d.ts +4 -0
  241. package/src/inference/pipelines/text/init.js +134 -29
  242. package/src/inference/pipelines/text/kernel-path-auto-select.js +2 -0
  243. package/src/inference/pipelines/text/kernel-trace.d.ts +2 -0
  244. package/src/inference/pipelines/text/kernel-trace.js +6 -0
  245. package/src/inference/pipelines/text/layer.js +14 -9
  246. package/src/inference/pipelines/text/linear-attention.d.ts +10 -0
  247. package/src/inference/pipelines/text/linear-attention.js +80 -6
  248. package/src/inference/pipelines/text/logits/gpu.js +10 -5
  249. package/src/inference/pipelines/text/logits/index.js +10 -11
  250. package/src/inference/pipelines/text/logits/utils.d.ts +7 -0
  251. package/src/inference/pipelines/text/logits/utils.js +9 -0
  252. package/src/inference/pipelines/text/lora-apply.js +50 -32
  253. package/src/inference/pipelines/text/model-load.js +279 -104
  254. package/src/inference/pipelines/text/moe-cache.js +5 -4
  255. package/src/inference/pipelines/text/moe-cpu-gptoss.js +74 -69
  256. package/src/inference/pipelines/text/moe-cpu.js +42 -38
  257. package/src/inference/pipelines/text/moe-gpu.js +110 -86
  258. package/src/inference/pipelines/text/ops.js +90 -90
  259. package/src/inference/pipelines/text/probes.js +9 -9
  260. package/src/inference/pipelines/text/weights.js +17 -7
  261. package/src/inference/pipelines/text.js +17 -1
  262. package/src/inference/speculative.d.ts +2 -2
  263. package/src/inference/speculative.js +4 -18
  264. package/src/inference/test-harness.d.ts +1 -1
  265. package/src/inference/test-harness.js +15 -5
  266. package/src/inference/tokenizer.d.ts +0 -5
  267. package/src/inference/tokenizer.js +4 -23
  268. package/src/inference/tokenizers/bpe.js +9 -0
  269. package/src/inference/tokenizers/bundled.js +176 -33
  270. package/src/inference/tokenizers/sentencepiece.js +12 -0
  271. package/src/loader/doppler-loader.js +38 -22
  272. package/src/loader/dtype-utils.js +3 -44
  273. package/src/loader/embedding-loader.js +7 -3
  274. package/src/loader/experts/expert-cache.js +13 -6
  275. package/src/loader/experts/expert-loader.js +10 -6
  276. package/src/loader/final-weights-loader.js +8 -4
  277. package/src/loader/layer-loader.js +2 -1
  278. package/src/loader/loader-state.js +2 -2
  279. package/src/loader/memory-monitor.js +8 -0
  280. package/src/loader/multi-model-loader.d.ts +14 -0
  281. package/src/loader/multi-model-loader.js +70 -24
  282. package/src/loader/shard-cache.js +81 -12
  283. package/src/loader/shard-resolver.js +25 -3
  284. package/src/loader/tensors/tensor-loader.js +209 -144
  285. package/src/loader/tensors/tensor-reader.js +76 -19
  286. package/src/loader/weight-downcast.js +1 -1
  287. package/src/memory/buffer-pool.d.ts +9 -1
  288. package/src/memory/buffer-pool.js +109 -44
  289. package/src/memory/unified-detect.js +1 -1
  290. package/src/rules/inference/kernel-path.rules.json +24 -8
  291. package/src/rules/rule-registry.js +25 -1
  292. package/src/rules/tooling/command-runtime.rules.json +18 -0
  293. package/src/storage/backends/opfs-store.js +68 -24
  294. package/src/storage/downloader.js +364 -83
  295. package/src/storage/index.d.ts +3 -0
  296. package/src/storage/index.js +3 -0
  297. package/src/storage/preflight.d.ts +2 -2
  298. package/src/storage/preflight.js +24 -2
  299. package/src/storage/quickstart-downloader.js +11 -5
  300. package/src/storage/registry.js +10 -4
  301. package/src/storage/reports.js +1 -1
  302. package/src/storage/shard-manager.d.ts +15 -1
  303. package/src/storage/shard-manager.js +51 -3
  304. package/src/storage/source-artifact-store.d.ts +52 -0
  305. package/src/storage/source-artifact-store.js +234 -0
  306. package/src/tooling/command-api-constants.d.ts +9 -0
  307. package/src/tooling/command-api-constants.js +9 -0
  308. package/src/tooling/command-api-family-normalizers.d.ts +9 -0
  309. package/src/tooling/command-api-family-normalizers.js +343 -0
  310. package/src/tooling/command-api-helpers.d.ts +25 -0
  311. package/src/tooling/command-api-helpers.js +262 -0
  312. package/src/tooling/command-api.d.ts +27 -1
  313. package/src/tooling/command-api.js +26 -473
  314. package/src/tooling/command-envelope.js +4 -1
  315. package/src/tooling/command-runner-shared.js +52 -18
  316. package/src/tooling/lean-execution-contract.js +150 -3
  317. package/src/tooling/node-browser-command-runner.d.ts +4 -0
  318. package/src/tooling/node-browser-command-runner.js +218 -273
  319. package/src/tooling/node-command-runner.js +44 -3
  320. package/src/tooling/node-converter.js +27 -1
  321. package/src/tooling/node-source-runtime.d.ts +1 -1
  322. package/src/tooling/node-source-runtime.js +84 -3
  323. package/src/tooling/node-webgpu.js +30 -105
  324. package/src/tooling/opfs-cache.js +21 -4
  325. package/src/tooling/runtime-input-composition.d.ts +38 -0
  326. package/src/tooling/runtime-input-composition.js +86 -0
  327. package/src/tooling/source-runtime-bundle.d.ts +40 -5
  328. package/src/tooling/source-runtime-bundle.js +261 -34
  329. package/src/tooling/source-runtime-materializer.d.ts +6 -0
  330. package/src/tooling/source-runtime-materializer.js +93 -0
  331. package/src/training/attention-backward.js +32 -17
  332. package/src/training/autograd.js +80 -52
  333. package/src/training/checkpoint-watch.d.ts +8 -0
  334. package/src/training/checkpoint-watch.js +139 -0
  335. package/src/training/checkpoint.d.ts +6 -1
  336. package/src/training/checkpoint.js +46 -7
  337. package/src/training/clip.js +2 -1
  338. package/src/training/datasets/token-batch.js +20 -8
  339. package/src/training/distillation/artifacts.d.ts +71 -0
  340. package/src/training/distillation/artifacts.js +132 -0
  341. package/src/training/distillation/checkpoint-watch.d.ts +10 -0
  342. package/src/training/distillation/checkpoint-watch.js +58 -0
  343. package/src/training/distillation/dataset.d.ts +59 -0
  344. package/src/training/distillation/dataset.js +337 -0
  345. package/src/training/distillation/eval.d.ts +34 -0
  346. package/src/training/distillation/eval.js +310 -0
  347. package/src/training/distillation/index.d.ts +29 -0
  348. package/src/training/distillation/index.js +29 -0
  349. package/src/training/distillation/runtime.d.ts +20 -0
  350. package/src/training/distillation/runtime.js +121 -0
  351. package/src/training/distillation/scoreboard.d.ts +6 -0
  352. package/src/training/distillation/scoreboard.js +8 -0
  353. package/src/training/distillation/stage-a.d.ts +45 -0
  354. package/src/training/distillation/stage-a.js +338 -0
  355. package/src/training/distillation/stage-b.d.ts +24 -0
  356. package/src/training/distillation/stage-b.js +20 -0
  357. package/src/training/distillation/student-fixture.d.ts +22 -0
  358. package/src/training/distillation/student-fixture.js +846 -0
  359. package/src/training/distillation/suite-data.d.ts +45 -0
  360. package/src/training/distillation/suite-data.js +189 -0
  361. package/src/training/index.d.ts +10 -0
  362. package/src/training/index.js +10 -0
  363. package/src/training/lora-pipeline.d.ts +40 -0
  364. package/src/training/lora-pipeline.js +793 -0
  365. package/src/training/lora.js +26 -12
  366. package/src/training/loss.js +5 -6
  367. package/src/training/objectives/cross_entropy.js +2 -5
  368. package/src/training/objectives/distill_kd.js +4 -8
  369. package/src/training/objectives/distill_triplet.js +4 -8
  370. package/src/training/objectives/ul_stage2_base.js +4 -8
  371. package/src/training/operator-artifacts.d.ts +62 -0
  372. package/src/training/operator-artifacts.js +140 -0
  373. package/src/training/operator-command.d.ts +5 -0
  374. package/src/training/operator-command.js +455 -0
  375. package/src/training/operator-eval.d.ts +48 -0
  376. package/src/training/operator-eval.js +230 -0
  377. package/src/training/operator-scoreboard.d.ts +5 -0
  378. package/src/training/operator-scoreboard.js +44 -0
  379. package/src/training/optimizer.js +19 -7
  380. package/src/training/runner.d.ts +52 -0
  381. package/src/training/runner.js +31 -5
  382. package/src/training/suite.d.ts +112 -0
  383. package/src/training/suite.js +24 -984
  384. package/src/training/tensor-factory.d.ts +9 -0
  385. package/src/training/tensor-factory.js +13 -0
  386. package/src/training/trainer.js +3 -5
  387. package/src/training/ul_dataset.js +3 -5
  388. package/src/training/workloads.d.ts +164 -0
  389. package/src/training/workloads.js +530 -0
  390. package/src/version.js +1 -1
  391. package/tools/convert-safetensors-node.js +22 -16
  392. package/tools/doppler-cli.js +179 -63
@@ -1,6 +1,6 @@
1
1
 
2
2
 
3
- import { getDevice, getDeviceLimits } from '../gpu/device.js';
3
+ import { getDevice, getDeviceEpoch, getDeviceLimits } from '../gpu/device.js';
4
4
  import { allowReadback, trackAllocation } from '../gpu/perf-guards.js';
5
5
  import { log, trace, isTraceEnabled } from '../debug/index.js';
6
6
  import { getRuntimeConfig } from '../config/runtime.js';
@@ -137,6 +137,12 @@ export class BufferPool {
137
137
 
138
138
  #debugMode;
139
139
 
140
+ // Device-scoped ownership for pooled buffers
141
+
142
+ #device;
143
+
144
+ #deviceEpoch;
145
+
140
146
 
141
147
  constructor(debugMode = false, schemaConfig) {
142
148
  if (!schemaConfig) {
@@ -153,6 +159,8 @@ export class BufferPool {
153
159
  this.#schemaConfig = schemaConfig;
154
160
  this.#pendingDestruction = new Set();
155
161
  this.#destructionScheduled = false;
162
+ this.#device = getDevice();
163
+ this.#deviceEpoch = getDeviceEpoch();
156
164
 
157
165
  this.#stats = {
158
166
  allocations: 0,
@@ -174,9 +182,32 @@ export class BufferPool {
174
182
  };
175
183
  }
176
184
 
185
+ #isEmpty() {
186
+ return this.#activeBuffers.size === 0
187
+ && this.#getTotalPooledCount() === 0
188
+ && this.#pendingDestruction.size === 0;
189
+ }
190
+
191
+ #getBoundDevice() {
192
+ const currentEpoch = getDeviceEpoch();
193
+ if (this.#deviceEpoch !== currentEpoch) {
194
+ if (!this.#isEmpty()) {
195
+ throw new Error(
196
+ `BufferPool belongs to stale device epoch ${this.#deviceEpoch}; ` +
197
+ `current epoch is ${currentEpoch}. Create a new pool.`
198
+ );
199
+ }
200
+ this.#deviceEpoch = currentEpoch;
201
+ this.#device = getDevice();
202
+ } else if (!this.#device) {
203
+ this.#device = getDevice();
204
+ }
205
+ return this.#device;
206
+ }
207
+
177
208
 
178
209
  acquire(size, usage = BufferUsage.STORAGE, label = 'pooled_buffer') {
179
- const device = getDevice();
210
+ const device = this.#getBoundDevice();
180
211
  if (!device) {
181
212
  throw new Error('Device not initialized');
182
213
  }
@@ -274,25 +305,57 @@ export class BufferPool {
274
305
  log.warn('BufferPool', 'Releasing buffer not tracked as active');
275
306
  return;
276
307
  }
308
+ this.#releaseTrackedBuffer(buffer, true);
309
+ }
310
+
311
+
312
+ discard(buffer) {
313
+ if (!this.#activeBuffers.has(buffer)) {
314
+ log.warn('BufferPool', 'Discarding buffer not tracked as active');
315
+ return;
316
+ }
317
+ this.#releaseTrackedBuffer(buffer, false);
318
+ }
319
+
320
+
321
+ isActiveBuffer(buffer) {
322
+ return this.#activeBuffers.has(buffer);
323
+ }
324
+
325
+
326
+ getRequestedSize(buffer) {
327
+ return this.#requestedSizes.get(buffer) ?? buffer.size;
328
+ }
277
329
 
330
+ #destroyPendingBuffers() {
331
+ const pending = Array.from(this.#pendingDestruction);
332
+ this.#pendingDestruction.clear();
333
+ for (const buffer of pending) {
334
+ try {
335
+ buffer.destroy();
336
+ } catch (error) {
337
+ log.warn('BufferPool', `Pending buffer destroy failed: ${error?.message ?? error}`);
338
+ }
339
+ }
340
+ }
341
+
342
+ #releaseTrackedBuffer(buffer, allowPooling) {
278
343
  this.#activeBuffers.delete(buffer);
279
344
  const requestedSize = this.#requestedSizes.get(buffer) ?? 0;
280
345
  this.#stats.currentBytesRequested -= requestedSize;
281
346
  this.#requestedSizes.delete(buffer);
282
347
 
283
- // Remove metadata in debug mode
284
348
  if (this.#debugMode) {
285
349
  this.#bufferMetadata.delete(buffer);
286
350
  }
287
351
 
288
- if (!this.#config.enablePooling) {
352
+ if (!allowPooling || !this.#config.enablePooling) {
289
353
  this.#deferDestroy(buffer);
290
354
  this.#stats.currentBytesAllocated -= buffer.size;
291
355
  this.#traceRelease(buffer, requestedSize, false);
292
356
  return;
293
357
  }
294
358
 
295
- // Return to pool if there's room
296
359
  const bucket = buffer.size;
297
360
  const usage = buffer.usage;
298
361
 
@@ -312,7 +375,6 @@ export class BufferPool {
312
375
  bucketPool.push(buffer);
313
376
  pooled = true;
314
377
  } else {
315
- // Pool is full; defer destruction until GPU work completes.
316
378
  this.#deferDestroy(buffer);
317
379
  this.#stats.currentBytesAllocated -= buffer.size;
318
380
  }
@@ -321,45 +383,29 @@ export class BufferPool {
321
383
  }
322
384
 
323
385
 
324
- isActiveBuffer(buffer) {
325
- return this.#activeBuffers.has(buffer);
326
- }
327
-
328
-
329
- getRequestedSize(buffer) {
330
- return this.#requestedSizes.get(buffer) ?? buffer.size;
331
- }
332
-
333
-
334
386
  #deferDestroy(buffer) {
335
387
  this.#pendingDestruction.add(buffer);
336
388
  if (this.#destructionScheduled) {
337
389
  return;
338
390
  }
339
- const device = getDevice();
391
+ const device = this.#device;
340
392
  if (!device) {
341
393
  // No device context; destroy immediately as a fallback.
342
- for (const pending of this.#pendingDestruction) {
343
- pending.destroy();
344
- }
345
- this.#pendingDestruction.clear();
346
394
  this.#destructionScheduled = false;
395
+ this.#destroyPendingBuffers();
347
396
  return;
348
397
  }
349
398
 
350
399
  this.#destructionScheduled = true;
351
400
  device.queue.onSubmittedWorkDone()
352
401
  .then(() => {
353
- for (const pending of this.#pendingDestruction) {
354
- pending.destroy();
355
- }
356
- this.#pendingDestruction.clear();
357
402
  this.#destructionScheduled = false;
403
+ this.#destroyPendingBuffers();
358
404
  })
359
405
  .catch((err) => {
360
406
  log.warn('BufferPool', `Deferred destruction failed: ${ (err).message}`);
361
- this.#pendingDestruction.clear();
362
407
  this.#destructionScheduled = false;
408
+ this.#destroyPendingBuffers();
363
409
  });
364
410
  }
365
411
 
@@ -536,7 +582,7 @@ export class BufferPool {
536
582
 
537
583
 
538
584
  uploadData(buffer, data, offset = 0) {
539
- const device = getDevice();
585
+ const device = this.#getBoundDevice();
540
586
  if (!device) {
541
587
  throw new Error('Device not initialized');
542
588
  }
@@ -554,7 +600,7 @@ export class BufferPool {
554
600
  return new ArrayBuffer(0);
555
601
  }
556
602
 
557
- const device = getDevice();
603
+ const device = this.#getBoundDevice();
558
604
  if (!device) {
559
605
  throw new Error('Device not initialized');
560
606
  }
@@ -583,21 +629,31 @@ export class BufferPool {
583
629
  const alignedSize = Math.ceil(size / 4) * 4;
584
630
  // Create staging buffer
585
631
  const staging = this.createStagingBuffer(alignedSize);
586
-
587
- // Copy to staging
588
- const encoder = device.createCommandEncoder({ label: 'readback_encoder' });
589
- encoder.copyBufferToBuffer(buffer, offset, staging, 0, alignedSize);
590
- device.queue.submit([encoder.finish()]);
591
-
592
- // Map and read
593
- await staging.mapAsync(RESOLVED_GPU_MAP_MODE.READ);
594
- const data = staging.getMappedRange(0, alignedSize).slice(0, size);
595
- staging.unmap();
596
-
597
- // Release staging buffer
598
- this.release(staging);
599
-
600
- return data;
632
+ let mapped = false;
633
+
634
+ try {
635
+ // Copy to staging
636
+ const encoder = device.createCommandEncoder({ label: 'readback_encoder' });
637
+ encoder.copyBufferToBuffer(buffer, offset, staging, 0, alignedSize);
638
+ device.queue.submit([encoder.finish()]);
639
+
640
+ // Map and read
641
+ await staging.mapAsync(RESOLVED_GPU_MAP_MODE.READ);
642
+ mapped = true;
643
+ return staging.getMappedRange(0, alignedSize).slice(0, size);
644
+ } catch (error) {
645
+ if (this.#activeBuffers.has(staging)) {
646
+ this.#releaseTrackedBuffer(staging, false);
647
+ }
648
+ throw error;
649
+ } finally {
650
+ if (mapped) {
651
+ staging.unmap();
652
+ if (this.#activeBuffers.has(staging)) {
653
+ this.#releaseTrackedBuffer(staging, true);
654
+ }
655
+ }
656
+ }
601
657
  }
602
658
 
603
659
 
@@ -684,11 +740,17 @@ export class BufferPool {
684
740
  // Global buffer pool instance
685
741
 
686
742
  let globalPool = null;
743
+ let globalPoolEpoch = -1;
687
744
 
688
745
 
689
746
  export function getBufferPool() {
690
- if (!globalPool) {
747
+ const epoch = getDeviceEpoch();
748
+ if (!globalPool || globalPoolEpoch !== epoch) {
749
+ if (globalPool) {
750
+ globalPool.destroy();
751
+ }
691
752
  globalPool = new BufferPool(false, getRuntimeConfig().shared.bufferPool);
753
+ globalPoolEpoch = epoch;
692
754
  }
693
755
  return globalPool;
694
756
  }
@@ -707,6 +769,7 @@ export function destroyBufferPool() {
707
769
  globalPool.destroy();
708
770
  globalPool = null;
709
771
  }
772
+ globalPoolEpoch = -1;
710
773
  }
711
774
 
712
775
  // Convenience exports for common operations
@@ -722,6 +785,8 @@ export const acquireBuffer = (size, usage, label) =>
722
785
 
723
786
  export const releaseBuffer = (buffer) => getBufferPool().release(buffer);
724
787
 
788
+ export const discardBuffer = (buffer) => getBufferPool().discard(buffer);
789
+
725
790
  export const isBufferActive = (buffer) =>
726
791
  getBufferPool().isActiveBuffer(buffer);
727
792
 
@@ -114,7 +114,7 @@ function checkUnifiedIndicators(
114
114
 
115
115
  export async function detectUnifiedMemory() {
116
116
  // Need WebGPU for detection
117
- if (!navigator.gpu) {
117
+ if (typeof navigator === 'undefined' || !navigator.gpu) {
118
118
  return {
119
119
  isUnified: false,
120
120
  reason: 'WebGPU not available',
@@ -1,5 +1,13 @@
1
1
  {
2
2
  "autoSelect": [
3
+ {
4
+ "match": {
5
+ "allowCapabilityAutoSelection": true,
6
+ "hasSubgroups": true,
7
+ "kernelPathRef": "gemma3-q4k-dequant-f32a-nosubgroups"
8
+ },
9
+ "value": "gemma3-q4k-dequant-f32a-online"
10
+ },
3
11
  {
4
12
  "match": {
5
13
  "allowCapabilityAutoSelection": true,
@@ -8,13 +16,21 @@
8
16
  },
9
17
  "value": "gemma3-q4k-dequant-f32a-online"
10
18
  },
19
+ {
20
+ "match": {
21
+ "allowCapabilityAutoSelection": true,
22
+ "hasSubgroups": false,
23
+ "kernelPathRef": "gemma3-q4k-dequant-f32a"
24
+ },
25
+ "value": "gemma3-q4k-dequant-f32a-nosubgroups"
26
+ },
11
27
  {
12
28
  "match": {
13
29
  "allowCapabilityAutoSelection": true,
14
30
  "hasSubgroups": false,
15
31
  "kernelPathRef": "gemma3-q4k-dequant-f16a-online"
16
32
  },
17
- "value": "gemma3-q4k-dequant-f32a"
33
+ "value": "gemma3-q4k-dequant-f32a-nosubgroups"
18
34
  },
19
35
  {
20
36
  "match": {
@@ -22,7 +38,7 @@
22
38
  "hasSubgroups": false,
23
39
  "kernelPathRef": "gemma3-q4k-dequant-f32a-online"
24
40
  },
25
- "value": "gemma3-q4k-dequant-f32a"
41
+ "value": "gemma3-q4k-dequant-f32a-nosubgroups"
26
42
  },
27
43
  {
28
44
  "match": {
@@ -30,7 +46,7 @@
30
46
  "hasSubgroups": false,
31
47
  "kernelPathRef": "lfm2-q4k-dequant-f32a-online"
32
48
  },
33
- "value": "gemma3-q4k-dequant-f32a"
49
+ "value": "gemma3-q4k-dequant-f32a-nosubgroups"
34
50
  },
35
51
  {
36
52
  "match": {
@@ -38,7 +54,7 @@
38
54
  "hasSubgroups": false,
39
55
  "kernelPathRef": "gemma2-q4k-dequant-f16a"
40
56
  },
41
- "value": "gemma2-q4k-dequant-f32a"
57
+ "value": "gemma2-q4k-dequant-f32a-nosubgroups"
42
58
  },
43
59
  {
44
60
  "match": {
@@ -46,7 +62,7 @@
46
62
  "hasSubgroups": false,
47
63
  "kernelPathRef": "gemma2-q4k-fused-f32a"
48
64
  },
49
- "value": "gemma2-q4k-dequant-f32a"
65
+ "value": "gemma2-q4k-dequant-f32a-nosubgroups"
50
66
  },
51
67
  { "match": {}, "value": { "context": "kernelPathRef" } }
52
68
  ],
@@ -57,11 +73,11 @@
57
73
  },
58
74
  {
59
75
  "match": { "kernelPathId": "gemma3-q4k-dequant-f16a-online" },
60
- "value": "gemma3-q4k-dequant-f32a"
76
+ "value": "gemma3-q4k-dequant-f32a-nosubgroups"
61
77
  },
62
78
  {
63
79
  "match": { "kernelPathId": "lfm2-q4k-dequant-f32a-online" },
64
- "value": "gemma3-q4k-dequant-f32a"
80
+ "value": "gemma3-q4k-dequant-f32a-nosubgroups"
65
81
  },
66
82
  {
67
83
  "match": { "kernelPathId": "gemma2-f16-f16a" },
@@ -69,7 +85,7 @@
69
85
  },
70
86
  {
71
87
  "match": { "kernelPathId": "gemma2-q4k-dequant-f16a" },
72
- "value": "gemma2-q4k-dequant-f32a"
88
+ "value": "gemma2-q4k-dequant-f32a-nosubgroups"
73
89
  },
74
90
  { "match": {}, "value": null }
75
91
  ]
@@ -3,6 +3,24 @@ import { buildInferenceExecutionRulesContractArtifact } from './execution-rules-
3
3
  import { buildLayerPatternContractArtifact } from './layer-pattern-contract-check.js';
4
4
  import { loadJson } from '../utils/load-json.js';
5
5
 
6
+ function cloneRuleValue(value) {
7
+ if (typeof structuredClone === 'function') {
8
+ return structuredClone(value);
9
+ }
10
+ return JSON.parse(JSON.stringify(value));
11
+ }
12
+
13
+ function deepFreeze(value, seen = new WeakSet()) {
14
+ if (!value || typeof value !== 'object' || seen.has(value)) {
15
+ return value;
16
+ }
17
+ seen.add(value);
18
+ for (const entry of Object.values(value)) {
19
+ deepFreeze(entry, seen);
20
+ }
21
+ return Object.freeze(value);
22
+ }
23
+
6
24
  const attentionRules = await loadJson('./kernels/attention.rules.json', import.meta.url, 'Failed to load rules');
7
25
  const conv2dRules = await loadJson('./kernels/conv2d.rules.json', import.meta.url, 'Failed to load rules');
8
26
  const depthwiseConv2dRules = await loadJson('./kernels/depthwise-conv2d.rules.json', import.meta.url, 'Failed to load rules');
@@ -160,7 +178,7 @@ export function registerRuleGroup(domain, group, rules) {
160
178
  if (!RULE_SETS[domain]) {
161
179
  RULE_SETS[domain] = {};
162
180
  }
163
- RULE_SETS[domain][group] = rules;
181
+ RULE_SETS[domain][group] = deepFreeze(cloneRuleValue(rules));
164
182
  }
165
183
 
166
184
  export function getInferenceExecutionRulesContractArtifact() {
@@ -213,3 +231,9 @@ function applyTemplate(template, context) {
213
231
  return String(context[key]);
214
232
  });
215
233
  }
234
+
235
+ for (const domainRules of Object.values(RULE_SETS)) {
236
+ for (const rules of Object.values(domainRules)) {
237
+ deepFreeze(rules);
238
+ }
239
+ }
@@ -27,6 +27,24 @@
27
27
  "intent": "verify"
28
28
  }
29
29
  },
30
+ {
31
+ "match": {
32
+ "command": "lora"
33
+ },
34
+ "value": {
35
+ "suite": null,
36
+ "intent": null
37
+ }
38
+ },
39
+ {
40
+ "match": {
41
+ "command": "distill"
42
+ },
43
+ "value": {
44
+ "suite": null,
45
+ "intent": null
46
+ }
47
+ },
30
48
  {
31
49
  "match": {},
32
50
  "value": {
@@ -34,10 +34,13 @@ export function createOpfsStore(config) {
34
34
  let modelsDir = null;
35
35
  let currentModelDir = null;
36
36
  let currentModelId = null;
37
- // SyncAccessHandle is typically only available in dedicated workers, but we
38
- // allow an optimistic attempt anywhere and fall back gracefully if the
39
- // browser rejects it (NotAllowedError / InvalidStateError).
40
- const syncAccessEnabled = !!useSyncAccessHandle
37
+ const syncAccessRequested = useSyncAccessHandle === true;
38
+ if (syncAccessRequested && typeof FileSystemSyncAccessHandle === 'undefined') {
39
+ throw new Error(
40
+ 'OPFS sync access handles were explicitly requested but are unavailable in this runtime.'
41
+ );
42
+ }
43
+ const syncAccessEnabled = syncAccessRequested
41
44
  && typeof FileSystemSyncAccessHandle !== 'undefined';
42
45
  const handleLimiter = syncAccessEnabled ? createLimiter(maxConcurrentHandles) : null;
43
46
 
@@ -84,7 +87,9 @@ export function createOpfsStore(config) {
84
87
  } catch (error) {
85
88
  handleLimiter.release();
86
89
  if (error?.name === 'InvalidStateError' || error?.name === 'NotAllowedError') {
87
- return null;
90
+ throw new Error(
91
+ `OPFS sync access handles were explicitly requested but could not be opened: ${error.name}.`
92
+ );
88
93
  }
89
94
  throw error;
90
95
  }
@@ -96,6 +101,44 @@ export function createOpfsStore(config) {
96
101
  }
97
102
  }
98
103
 
104
+ function normalizePathSegments(filename) {
105
+ const normalized = String(filename || '').replace(/\\/g, '/').trim();
106
+ if (!normalized) {
107
+ throw new Error('Filename is required');
108
+ }
109
+ const parts = normalized.split('/').filter(Boolean);
110
+ if (parts.length === 0) {
111
+ throw new Error('Filename is required');
112
+ }
113
+ for (const part of parts) {
114
+ if (part === '.' || part === '..') {
115
+ throw new Error(`Invalid relative storage path: ${filename}`);
116
+ }
117
+ }
118
+ return parts;
119
+ }
120
+
121
+ async function resolveDirectoryForPath(filename, options = {}) {
122
+ await ensureModelDir();
123
+ const parts = normalizePathSegments(filename);
124
+ const leafName = parts.pop();
125
+ let dir = currentModelDir;
126
+ for (const part of parts) {
127
+ dir = await dir.getDirectoryHandle(part, { create: options.createDirs === true });
128
+ }
129
+ return {
130
+ dir,
131
+ leafName,
132
+ };
133
+ }
134
+
135
+ async function getFileHandle(filename, options = {}) {
136
+ const { dir, leafName } = await resolveDirectoryForPath(filename, {
137
+ createDirs: options.create === true,
138
+ });
139
+ return dir.getFileHandle(leafName, { create: options.create === true });
140
+ }
141
+
99
142
  function normalizeWriteStreamOptions(options = {}) {
100
143
  const append = options?.append === true;
101
144
  const expectedOffsetRaw = options?.expectedOffset;
@@ -112,8 +155,7 @@ export function createOpfsStore(config) {
112
155
  }
113
156
 
114
157
  async function getFileSize(filename) {
115
- await ensureModelDir();
116
- const fileHandle = await currentModelDir.getFileHandle(filename);
158
+ const fileHandle = await getFileHandle(filename, { create: false });
117
159
  const access = await openSyncAccessHandle(fileHandle);
118
160
  if (access) {
119
161
  try {
@@ -127,8 +169,7 @@ export function createOpfsStore(config) {
127
169
  }
128
170
 
129
171
  async function readFile(filename) {
130
- await ensureModelDir();
131
- const fileHandle = await currentModelDir.getFileHandle(filename);
172
+ const fileHandle = await getFileHandle(filename, { create: false });
132
173
  const access = await openSyncAccessHandle(fileHandle);
133
174
  if (access) {
134
175
  try {
@@ -154,8 +195,7 @@ export function createOpfsStore(config) {
154
195
  }
155
196
 
156
197
  async function readFileRange(filename, offset = 0, length = null) {
157
- await ensureModelDir();
158
- const fileHandle = await currentModelDir.getFileHandle(filename);
198
+ const fileHandle = await getFileHandle(filename, { create: false });
159
199
  const access = await openSyncAccessHandle(fileHandle);
160
200
 
161
201
  const startRaw = Number(offset);
@@ -197,8 +237,7 @@ export function createOpfsStore(config) {
197
237
  const startRaw = Number(offset);
198
238
  const start = Number.isFinite(startRaw) ? Math.max(0, Math.floor(startRaw)) : 0;
199
239
 
200
- await ensureModelDir();
201
- const fileHandle = await currentModelDir.getFileHandle(filename);
240
+ const fileHandle = await getFileHandle(filename, { create: false });
202
241
  const access = await openSyncAccessHandle(fileHandle);
203
242
 
204
243
  if (access) {
@@ -236,9 +275,8 @@ export function createOpfsStore(config) {
236
275
  }
237
276
 
238
277
  async function readText(filename) {
239
- await ensureModelDir();
240
278
  try {
241
- const fileHandle = await currentModelDir.getFileHandle(filename);
279
+ const fileHandle = await getFileHandle(filename, { create: false });
242
280
  const file = await fileHandle.getFile();
243
281
  return await file.text();
244
282
  } catch (error) {
@@ -250,8 +288,7 @@ export function createOpfsStore(config) {
250
288
  }
251
289
 
252
290
  async function writeFile(filename, data) {
253
- await ensureModelDir();
254
- const fileHandle = await currentModelDir.getFileHandle(filename, { create: true });
291
+ const fileHandle = await getFileHandle(filename, { create: true });
255
292
  const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
256
293
  const access = await openSyncAccessHandle(fileHandle);
257
294
  if (access) {
@@ -271,9 +308,8 @@ export function createOpfsStore(config) {
271
308
  }
272
309
 
273
310
  async function createWriteStream(filename, options = {}) {
274
- await ensureModelDir();
275
311
  const { append, expectedOffset } = normalizeWriteStreamOptions(options);
276
- const fileHandle = await currentModelDir.getFileHandle(filename, { create: true });
312
+ const fileHandle = await getFileHandle(filename, { create: true });
277
313
  const existingSize = await getFileSize(filename).catch((error) => {
278
314
  if (error?.name === 'NotFoundError') {
279
315
  return 0;
@@ -347,9 +383,9 @@ export function createOpfsStore(config) {
347
383
  }
348
384
 
349
385
  async function deleteFile(filename) {
350
- await ensureModelDir();
386
+ const { dir, leafName } = await resolveDirectoryForPath(filename, { createDirs: false });
351
387
  try {
352
- await currentModelDir.removeEntry(filename);
388
+ await dir.removeEntry(leafName);
353
389
  return true;
354
390
  } catch (error) {
355
391
  if (error.name === 'NotFoundError') {
@@ -362,11 +398,19 @@ export function createOpfsStore(config) {
362
398
  async function listFiles() {
363
399
  await ensureModelDir();
364
400
  const files = [];
365
- for await (const [name, handle] of currentModelDir.entries()) {
366
- if (handle.kind === 'file') {
367
- files.push(name);
401
+ async function walk(dir, prefix = '') {
402
+ for await (const [name, handle] of dir.entries()) {
403
+ const relativePath = prefix ? `${prefix}/${name}` : name;
404
+ if (handle.kind === 'file') {
405
+ files.push(relativePath);
406
+ continue;
407
+ }
408
+ if (handle.kind === 'directory') {
409
+ await walk(handle, relativePath);
410
+ }
368
411
  }
369
412
  }
413
+ await walk(currentModelDir);
370
414
  return files;
371
415
  }
372
416