@simulatte/doppler 0.1.6 → 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.
- package/CHANGELOG.md +126 -0
- package/README.md +16 -23
- package/package.json +14 -1
- package/src/adapters/adapter-registry.js +12 -1
- package/src/adapters/lora-loader.js +23 -6
- package/src/bridge/extension-client.d.ts +5 -0
- package/src/bridge/extension-client.js +40 -0
- package/src/bridge/index.d.ts +2 -1
- package/src/bridge/index.js +6 -4
- package/src/browser/browser-converter.js +26 -1
- package/src/browser/file-picker.js +6 -0
- package/src/browser/safetensors-parser-browser.js +84 -1
- package/src/browser/shard-io-browser.js +2 -2
- package/src/browser/tensor-source-download.js +8 -2
- package/src/browser/tensor-source-http.d.ts +1 -0
- package/src/browser/tensor-source-http.js +5 -1
- package/src/client/doppler-api.browser.js +20 -4
- package/src/client/doppler-api.js +19 -3
- package/src/client/doppler-provider/generation.js +12 -0
- package/src/client/doppler-provider/model-manager.d.ts +10 -0
- package/src/client/doppler-provider/model-manager.js +91 -19
- package/src/client/doppler-provider/source-runtime.d.ts +2 -1
- package/src/client/doppler-provider/source-runtime.js +132 -13
- package/src/client/doppler-registry.json +8 -7
- package/src/config/backward-registry-loader.js +17 -2
- package/src/config/execution-v0-contract-check.js +113 -15
- package/src/config/kernel-path-contract-check.js +57 -29
- package/src/config/kernel-path-loader.js +5 -36
- package/src/config/kernels/kernel-ref-digests.js +1 -1
- package/src/config/kernels/registry.js +14 -1
- package/src/config/kernels/registry.json +7 -5
- package/src/config/loader.d.ts +1 -1
- package/src/config/loader.js +12 -2
- package/src/config/merge-contract-check.js +59 -4
- package/src/config/merge-helpers.js +128 -7
- package/src/config/merge.d.ts +1 -0
- package/src/config/merge.js +10 -0
- package/src/config/param-validator.js +47 -2
- package/src/config/presets/kernel-paths/{gemma2-q4k-dequant-f32a.json → gemma2-q4k-dequant-f32a-nosubgroups.json} +3 -3
- package/src/config/presets/kernel-paths/gemma3-f16-fused-f32a-online-streamingprefill.json +223 -0
- package/src/config/presets/kernel-paths/{gemma3-q4k-dequant-f32a.json → gemma3-q4k-dequant-f32a-nosubgroups.json} +3 -3
- package/src/config/presets/kernel-paths/registry.json +29 -8
- package/src/config/presets/models/gemma2.json +2 -2
- package/src/config/presets/runtime/experiments/bench/gemma3-bench-q4k.json +1 -1
- package/src/config/presets/runtime/experiments/debug/gemma3-debug-q4k.json +1 -1
- package/src/config/presets/runtime/experiments/verify/gemma3-verify.json +1 -1
- package/src/config/presets/runtime/kernels/dequant-f16-q4k.json +6 -13
- package/src/config/presets/runtime/kernels/dequant-f32-q4k.json +6 -13
- package/src/config/presets/runtime/kernels/embeddinggemma-q4k-dequant-f32a.json +37 -0
- package/src/config/presets/runtime/kernels/fused-q4k.json +6 -13
- package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f16a.json +33 -0
- package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f32a-nosubgroups.json +33 -0
- package/src/config/presets/runtime/kernels/gemma2-q4k-fused-f32a.json +33 -0
- package/src/config/presets/runtime/kernels/safe-q4k.json +6 -13
- package/src/config/presets/runtime/platform/metal-apple-q4k.json +1 -1
- package/src/config/runtime.js +6 -1
- package/src/config/schema/debug.schema.d.ts +5 -0
- package/src/config/schema/doppler.schema.js +16 -21
- package/src/config/schema/inference-defaults.schema.js +3 -3
- package/src/config/schema/kernel-path.schema.d.ts +5 -1
- package/src/config/schema/kernel-thresholds.schema.js +12 -4
- package/src/config/schema/manifest.schema.d.ts +2 -1
- package/src/config/schema/manifest.schema.js +16 -3
- package/src/config/training-defaults.js +30 -22
- package/src/converter/conversion-plan.js +94 -9
- package/src/converter/core.d.ts +7 -0
- package/src/converter/core.js +14 -9
- package/src/converter/execution-v0-manifest.js +4 -1
- package/src/converter/index.d.ts +1 -0
- package/src/converter/index.js +1 -0
- package/src/converter/manifest-inference.js +43 -12
- package/src/converter/parsers/diffusion.js +0 -3
- package/src/converter/quantization-info.js +35 -15
- package/src/converter/shard-packer.d.ts +1 -1
- package/src/converter/shard-packer.js +4 -1
- package/src/debug/config.js +123 -11
- package/src/debug/signals.js +7 -1
- package/src/debug/tensor.d.ts +2 -0
- package/src/debug/tensor.js +13 -2
- package/src/distribution/p2p-control-plane.js +52 -12
- package/src/distribution/p2p-observability.js +43 -7
- package/src/distribution/p2p-webrtc-browser.js +20 -0
- package/src/distribution/shard-delivery.js +77 -26
- package/src/formats/gguf/types.js +33 -16
- package/src/formats/rdrr/groups.d.ts +12 -4
- package/src/formats/rdrr/groups.js +3 -6
- package/src/formats/rdrr/parsing.js +39 -2
- package/src/formats/rdrr/types.d.ts +2 -1
- package/src/gpu/command-recorder.js +86 -61
- package/src/gpu/device.d.ts +1 -0
- package/src/gpu/device.js +73 -19
- package/src/gpu/kernel-tuner/benchmarks.js +326 -316
- package/src/gpu/kernel-tuner/cache.js +71 -4
- package/src/gpu/kernel-tuner/tuner.js +22 -4
- package/src/gpu/kernels/attention.js +15 -34
- package/src/gpu/kernels/backward/adam.js +62 -58
- package/src/gpu/kernels/backward/attention_backward.js +257 -169
- package/src/gpu/kernels/backward/conv2d_backward.js +14 -1
- package/src/gpu/kernels/cast.js +191 -149
- package/src/gpu/kernels/check-stop.js +33 -44
- package/src/gpu/kernels/conv2d.js +27 -17
- package/src/gpu/kernels/cross_entropy_loss.js +21 -15
- package/src/gpu/kernels/depthwise_conv2d.js +36 -26
- package/src/gpu/kernels/dequant.js +178 -126
- package/src/gpu/kernels/energy.d.ts +3 -21
- package/src/gpu/kernels/energy.js +111 -88
- package/src/gpu/kernels/feature-check.js +1 -1
- package/src/gpu/kernels/fused_ffn.js +84 -65
- package/src/gpu/kernels/fused_matmul_residual.js +56 -33
- package/src/gpu/kernels/fused_matmul_rmsnorm.js +62 -45
- package/src/gpu/kernels/gather.js +33 -15
- package/src/gpu/kernels/gelu.js +19 -11
- package/src/gpu/kernels/grouped_pointwise_conv2d.js +33 -23
- package/src/gpu/kernels/groupnorm.js +34 -23
- package/src/gpu/kernels/kv-quantize.js +5 -2
- package/src/gpu/kernels/layernorm.js +35 -19
- package/src/gpu/kernels/logit-merge.js +5 -3
- package/src/gpu/kernels/matmul.js +58 -39
- package/src/gpu/kernels/modulate.js +23 -15
- package/src/gpu/kernels/moe.js +221 -175
- package/src/gpu/kernels/pixel_shuffle.js +22 -14
- package/src/gpu/kernels/relu.js +18 -10
- package/src/gpu/kernels/repeat_channels.js +25 -17
- package/src/gpu/kernels/residual.js +37 -27
- package/src/gpu/kernels/rmsnorm.js +57 -41
- package/src/gpu/kernels/rope.js +3 -0
- package/src/gpu/kernels/sample.js +27 -38
- package/src/gpu/kernels/sana_linear_attention.js +18 -10
- package/src/gpu/kernels/scale.js +18 -11
- package/src/gpu/kernels/shader-cache.js +4 -2
- package/src/gpu/kernels/silu.js +120 -72
- package/src/gpu/kernels/softmax.js +44 -25
- package/src/gpu/kernels/split_qkv.js +23 -13
- package/src/gpu/kernels/transpose.js +18 -10
- package/src/gpu/kernels/transpose.wgsl +5 -3
- package/src/gpu/kernels/upsample2d.js +21 -13
- package/src/gpu/kernels/utils.js +20 -13
- package/src/gpu/partitioned-buffer-pool.js +10 -2
- package/src/gpu/perf-guards.js +2 -9
- package/src/gpu/profiler.js +27 -22
- package/src/gpu/readback-utils.d.ts +16 -0
- package/src/gpu/readback-utils.js +41 -0
- package/src/gpu/submit-tracker.js +13 -0
- package/src/gpu/uniform-cache.d.ts +1 -0
- package/src/gpu/uniform-cache.js +30 -9
- package/src/hotswap/intent-bundle.js +6 -0
- package/src/hotswap/manifest.d.ts +10 -1
- package/src/hotswap/manifest.js +12 -2
- package/src/hotswap/runtime.js +30 -8
- package/src/index-browser.d.ts +44 -0
- package/src/index-browser.js +14 -0
- package/src/inference/browser-harness-contract-helpers.d.ts +5 -0
- package/src/inference/browser-harness-contract-helpers.js +28 -0
- package/src/inference/browser-harness-diffusion-energy-suites.d.ts +2 -0
- package/src/inference/browser-harness-diffusion-energy-suites.js +269 -0
- package/src/inference/browser-harness-model-helpers.d.ts +16 -0
- package/src/inference/browser-harness-model-helpers.js +217 -0
- package/src/inference/browser-harness-report-helpers.d.ts +7 -0
- package/src/inference/browser-harness-report-helpers.js +42 -0
- package/src/inference/browser-harness-runtime-helpers.d.ts +61 -0
- package/src/inference/browser-harness-runtime-helpers.js +415 -0
- package/src/inference/browser-harness-suite-helpers.d.ts +28 -0
- package/src/inference/browser-harness-suite-helpers.js +268 -0
- package/src/inference/browser-harness-text-helpers.d.ts +27 -0
- package/src/inference/browser-harness-text-helpers.js +788 -0
- package/src/inference/browser-harness.d.ts +6 -0
- package/src/inference/browser-harness.js +130 -1996
- package/src/inference/kv-cache/base.js +140 -94
- package/src/inference/kv-cache/tiered.js +5 -3
- package/src/inference/moe-router.js +88 -56
- package/src/inference/multi-model-network.js +5 -3
- package/src/inference/network-evolution.d.ts +11 -2
- package/src/inference/network-evolution.js +20 -21
- package/src/inference/pipelines/context.d.ts +3 -0
- package/src/inference/pipelines/context.js +142 -2
- package/src/inference/pipelines/diffusion/helpers.js +7 -2
- package/src/inference/pipelines/diffusion/pipeline.js +2 -1
- package/src/inference/pipelines/diffusion/sd3-transformer.js +10 -10
- package/src/inference/pipelines/diffusion/vae.js +3 -7
- package/src/inference/pipelines/energy/pipeline.js +27 -21
- package/src/inference/pipelines/energy/quintel.d.ts +5 -0
- package/src/inference/pipelines/energy/quintel.js +11 -0
- package/src/inference/pipelines/energy-head/row-head-pipeline.js +17 -13
- package/src/inference/pipelines/structured/json-head-pipeline.js +26 -11
- package/src/inference/pipelines/text/attention/projections.js +151 -101
- package/src/inference/pipelines/text/attention/record.js +62 -8
- package/src/inference/pipelines/text/attention/run.js +62 -8
- package/src/inference/pipelines/text/config.js +3 -4
- package/src/inference/pipelines/text/embed.js +2 -8
- package/src/inference/pipelines/text/execution-plan.js +41 -19
- package/src/inference/pipelines/text/execution-v0-contract-helpers.d.ts +59 -0
- package/src/inference/pipelines/text/execution-v0-contract-helpers.js +937 -0
- package/src/inference/pipelines/text/execution-v0-runtime-builders.d.ts +15 -0
- package/src/inference/pipelines/text/execution-v0-runtime-builders.js +279 -0
- package/src/inference/pipelines/text/execution-v0.js +62 -1013
- package/src/inference/pipelines/text/generator-steps.d.ts +46 -0
- package/src/inference/pipelines/text/generator-steps.js +298 -207
- package/src/inference/pipelines/text/generator.js +6 -23
- package/src/inference/pipelines/text/init.js +78 -20
- package/src/inference/pipelines/text/kernel-path-auto-select.js +2 -0
- package/src/inference/pipelines/text/kernel-trace.d.ts +2 -0
- package/src/inference/pipelines/text/kernel-trace.js +6 -0
- package/src/inference/pipelines/text/layer.js +3 -9
- package/src/inference/pipelines/text/linear-attention.d.ts +10 -0
- package/src/inference/pipelines/text/linear-attention.js +80 -6
- package/src/inference/pipelines/text/logits/gpu.js +10 -5
- package/src/inference/pipelines/text/logits/index.js +10 -11
- package/src/inference/pipelines/text/logits/utils.d.ts +7 -0
- package/src/inference/pipelines/text/logits/utils.js +9 -0
- package/src/inference/pipelines/text/lora-apply.js +50 -32
- package/src/inference/pipelines/text/model-load.js +279 -104
- package/src/inference/pipelines/text/moe-cache.js +5 -4
- package/src/inference/pipelines/text/moe-cpu-gptoss.js +74 -69
- package/src/inference/pipelines/text/moe-cpu.js +42 -38
- package/src/inference/pipelines/text/moe-gpu.js +110 -86
- package/src/inference/pipelines/text/ops.js +90 -90
- package/src/inference/pipelines/text/probes.js +9 -9
- package/src/inference/pipelines/text/weights.js +17 -7
- package/src/inference/pipelines/text.js +13 -1
- package/src/inference/speculative.d.ts +2 -2
- package/src/inference/speculative.js +4 -18
- package/src/inference/test-harness.d.ts +1 -1
- package/src/inference/test-harness.js +15 -5
- package/src/inference/tokenizer.d.ts +0 -5
- package/src/inference/tokenizer.js +4 -23
- package/src/inference/tokenizers/bpe.js +9 -0
- package/src/inference/tokenizers/bundled.js +20 -0
- package/src/inference/tokenizers/sentencepiece.js +12 -0
- package/src/loader/doppler-loader.js +38 -22
- package/src/loader/dtype-utils.js +3 -44
- package/src/loader/embedding-loader.js +7 -3
- package/src/loader/experts/expert-cache.js +13 -6
- package/src/loader/experts/expert-loader.js +10 -6
- package/src/loader/final-weights-loader.js +8 -4
- package/src/loader/layer-loader.js +2 -1
- package/src/loader/loader-state.js +2 -2
- package/src/loader/memory-monitor.js +8 -0
- package/src/loader/multi-model-loader.d.ts +14 -0
- package/src/loader/multi-model-loader.js +70 -24
- package/src/loader/shard-cache.js +81 -12
- package/src/loader/shard-resolver.js +25 -3
- package/src/loader/tensors/tensor-loader.js +209 -144
- package/src/loader/tensors/tensor-reader.js +76 -19
- package/src/loader/weight-downcast.js +1 -1
- package/src/memory/buffer-pool.d.ts +9 -1
- package/src/memory/buffer-pool.js +109 -44
- package/src/memory/unified-detect.js +1 -1
- package/src/rules/inference/kernel-path.rules.json +24 -8
- package/src/rules/rule-registry.js +25 -1
- package/src/storage/backends/opfs-store.js +68 -24
- package/src/storage/downloader.js +364 -83
- package/src/storage/index.d.ts +3 -0
- package/src/storage/index.js +3 -0
- package/src/storage/preflight.d.ts +2 -2
- package/src/storage/preflight.js +24 -2
- package/src/storage/quickstart-downloader.js +11 -5
- package/src/storage/registry.js +10 -4
- package/src/storage/reports.js +1 -1
- package/src/storage/shard-manager.d.ts +15 -1
- package/src/storage/shard-manager.js +51 -3
- package/src/storage/source-artifact-store.d.ts +52 -0
- package/src/storage/source-artifact-store.js +234 -0
- package/src/tooling/command-api-constants.d.ts +9 -0
- package/src/tooling/command-api-constants.js +9 -0
- package/src/tooling/command-api-family-normalizers.d.ts +9 -0
- package/src/tooling/command-api-family-normalizers.js +343 -0
- package/src/tooling/command-api-helpers.d.ts +25 -0
- package/src/tooling/command-api-helpers.js +262 -0
- package/src/tooling/command-api.js +16 -602
- package/src/tooling/command-envelope.js +4 -1
- package/src/tooling/command-runner-shared.js +52 -18
- package/src/tooling/lean-execution-contract.js +150 -3
- package/src/tooling/node-browser-command-runner.js +161 -271
- package/src/tooling/node-command-runner.js +29 -3
- package/src/tooling/node-converter.js +27 -1
- package/src/tooling/node-source-runtime.d.ts +1 -1
- package/src/tooling/node-source-runtime.js +84 -3
- package/src/tooling/node-webgpu.js +24 -21
- package/src/tooling/opfs-cache.js +21 -4
- package/src/tooling/runtime-input-composition.d.ts +38 -0
- package/src/tooling/runtime-input-composition.js +86 -0
- package/src/tooling/source-runtime-bundle.d.ts +40 -5
- package/src/tooling/source-runtime-bundle.js +261 -34
- package/src/tooling/source-runtime-materializer.d.ts +6 -0
- package/src/tooling/source-runtime-materializer.js +93 -0
- package/src/training/attention-backward.js +32 -17
- package/src/training/autograd.js +80 -52
- package/src/training/checkpoint-watch.d.ts +2 -1
- package/src/training/checkpoint-watch.js +39 -6
- package/src/training/checkpoint.js +40 -11
- package/src/training/clip.js +2 -1
- package/src/training/datasets/token-batch.js +20 -8
- package/src/training/distillation/checkpoint-watch.js +1 -0
- package/src/training/distillation/student-fixture.d.ts +22 -0
- package/src/training/distillation/student-fixture.js +846 -0
- package/src/training/distillation/suite-data.d.ts +45 -0
- package/src/training/distillation/suite-data.js +189 -0
- package/src/training/lora-pipeline.js +4 -7
- package/src/training/lora.js +26 -12
- package/src/training/loss.js +5 -6
- package/src/training/objectives/cross_entropy.js +2 -5
- package/src/training/objectives/distill_kd.js +4 -8
- package/src/training/objectives/distill_triplet.js +4 -8
- package/src/training/objectives/ul_stage2_base.js +4 -8
- package/src/training/operator-command.js +2 -0
- package/src/training/optimizer.js +19 -7
- package/src/training/runner.js +2 -1
- package/src/training/suite.js +18 -978
- package/src/training/tensor-factory.d.ts +9 -0
- package/src/training/tensor-factory.js +13 -0
- package/src/training/trainer.js +3 -5
- package/src/training/ul_dataset.js +3 -5
- package/src/training/workloads.js +70 -79
- package/src/version.js +1 -1
- package/tools/convert-safetensors-node.js +22 -16
- package/tools/doppler-cli.js +44 -25
|
@@ -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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -34,10 +34,13 @@ export function createOpfsStore(config) {
|
|
|
34
34
|
let modelsDir = null;
|
|
35
35
|
let currentModelDir = null;
|
|
36
36
|
let currentModelId = null;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
386
|
+
const { dir, leafName } = await resolveDirectoryForPath(filename, { createDirs: false });
|
|
351
387
|
try {
|
|
352
|
-
await
|
|
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
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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
|
|