@simulatte/doppler 0.1.6 → 0.1.8
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 +145 -0
- package/README.md +16 -23
- package/package.json +30 -32
- 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 +31 -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 +5 -20
- 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.d.ts +5 -0
- package/src/config/kernel-path-loader.js +18 -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 +81 -5
- package/src/config/loader.d.ts +1 -1
- package/src/config/loader.js +15 -2
- package/src/config/merge-contract-check.js +66 -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/gemma3-q4k-dequant-f32w-f32a-online.json +56 -0
- package/src/config/presets/kernel-paths/lfm2-q4k-dequant-f32a-nosubgroups.json +61 -0
- package/src/config/presets/kernel-paths/registry.json +43 -8
- package/src/config/presets/models/gemma2.json +3 -2
- package/src/config/presets/models/gemma3.json +2 -0
- package/src/config/presets/models/qwen3.json +4 -3
- package/src/config/presets/models/qwen3_5.json +16 -0
- 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/model/qwen3-5-layer-probe.json +52 -0
- package/src/config/presets/runtime/model/qwen3-5-linear-attn-debug.json +90 -0
- package/src/config/presets/runtime/platform/metal-apple-q4k.json +1 -1
- package/src/config/runtime.js +6 -1
- package/src/config/schema/conversion.schema.d.ts +1 -0
- 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 +3 -2
- package/src/config/schema/manifest.schema.js +17 -4
- package/src/config/schema/storage.schema.js +1 -1
- package/src/config/training-defaults.js +30 -22
- package/src/converter/conversion-plan.js +104 -11
- package/src/converter/core.d.ts +7 -0
- package/src/converter/core.js +16 -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 +50 -29
- package/src/converter/parsers/diffusion.js +0 -3
- package/src/converter/parsers/transformer.js +4 -0
- package/src/converter/quantization-info.js +40 -16
- package/src/converter/quantizer.js +19 -12
- package/src/converter/rope-config.js +8 -6
- package/src/converter/shard-packer.d.ts +1 -1
- package/src/converter/shard-packer.js +4 -1
- package/src/converter/tokenizer-utils.d.ts +1 -0
- package/src/converter/tokenizer-utils.js +4 -1
- package/src/debug/config.js +123 -11
- package/src/debug/reference/hf_qwen35_linear_attn_debug.py +268 -0
- 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 +83 -27
- 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.d.ts +4 -0
- package/src/formats/rdrr/parsing.js +53 -3
- 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/index.d.ts +8 -0
- package/src/gpu/kernels/index.js +6 -0
- 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-selection.js +47 -4
- package/src/gpu/kernels/matmul.d.ts +2 -0
- package/src/gpu/kernels/matmul.js +59 -40
- 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 +66 -43
- 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_qg.d.ts +50 -0
- package/src/gpu/kernels/split_qg.js +46 -0
- package/src/gpu/kernels/split_qg.wgsl +58 -0
- package/src/gpu/kernels/split_qg_f16.wgsl +62 -0
- 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/gpu/weight-buffer.d.ts +1 -1
- package/src/gpu/weight-buffer.js +1 -1
- 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 +8 -0
- package/src/inference/browser-harness.js +149 -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 +10 -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/text-encoder-gpu.js +8 -2
- 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/output-projection.d.ts +12 -0
- package/src/inference/pipelines/text/attention/output-projection.js +8 -0
- package/src/inference/pipelines/text/attention/projections.d.ts +10 -1
- package/src/inference/pipelines/text/attention/projections.js +192 -112
- package/src/inference/pipelines/text/attention/record.js +77 -14
- package/src/inference/pipelines/text/attention/run.js +112 -14
- package/src/inference/pipelines/text/config.js +17 -4
- package/src/inference/pipelines/text/embed.js +2 -8
- package/src/inference/pipelines/text/execution-plan.js +46 -23
- 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-runtime.js +5 -0
- package/src/inference/pipelines/text/generator-steps.d.ts +52 -0
- package/src/inference/pipelines/text/generator-steps.js +340 -221
- package/src/inference/pipelines/text/generator.js +56 -40
- package/src/inference/pipelines/text/init.d.ts +13 -0
- package/src/inference/pipelines/text/init.js +94 -25
- 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 +4 -9
- package/src/inference/pipelines/text/linear-attention.d.ts +15 -0
- package/src/inference/pipelines/text/linear-attention.js +113 -9
- package/src/inference/pipelines/text/logits/gpu.js +12 -7
- package/src/inference/pipelines/text/logits/index.d.ts +6 -1
- package/src/inference/pipelines/text/logits/index.js +13 -12
- 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 +282 -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/sampling.js +52 -6
- 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 +17 -7
- 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 +10 -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 +84 -14
- package/src/loader/shard-resolver.js +25 -3
- package/src/loader/tensors/tensor-loader.js +214 -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/dtype.rules.json +5 -0
- package/src/rules/inference/kernel-path.rules.json +24 -8
- package/src/rules/kernels/split-qg.rules.json +6 -0
- package/src/rules/rule-registry.js +27 -1
- package/src/storage/backends/opfs-store.js +68 -24
- package/src/storage/downloader.js +365 -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 +55 -6
- 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/conversion-config-materializer.js +3 -5
- 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 +30 -1
- package/src/tooling/node-source-runtime.d.ts +1 -1
- package/src/tooling/node-source-runtime.js +120 -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/types/model.d.ts +5 -0
- package/src/version.js +1 -1
- package/tools/convert-safetensors-node.js +22 -16
- package/tools/doppler-cli.js +50 -26
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
parseManifest,
|
|
5
|
+
getExpectedShardHash,
|
|
5
6
|
getManifestUrl,
|
|
6
7
|
} from '../formats/rdrr/index.js';
|
|
7
8
|
|
|
8
9
|
import {
|
|
9
10
|
openModelStore,
|
|
11
|
+
createFileWriter,
|
|
12
|
+
createStreamingHasher,
|
|
10
13
|
writeShard,
|
|
11
14
|
shardExists,
|
|
12
15
|
loadShard,
|
|
16
|
+
loadFileFromStore,
|
|
13
17
|
deleteShard,
|
|
18
|
+
deleteFileFromStore,
|
|
14
19
|
saveManifest,
|
|
15
20
|
saveTokenizer,
|
|
16
21
|
saveTokenizerModel,
|
|
@@ -40,6 +45,7 @@ import {
|
|
|
40
45
|
} from './download-types.js';
|
|
41
46
|
|
|
42
47
|
import { downloadShard as downloadShardFromDistribution } from '../distribution/shard-delivery.js';
|
|
48
|
+
import { resolveSourceArtifact, normalizeSourceArtifactPath } from './source-artifact-store.js';
|
|
43
49
|
|
|
44
50
|
// ============================================================================
|
|
45
51
|
// Module State
|
|
@@ -51,6 +57,7 @@ let db = null;
|
|
|
51
57
|
const activeDownloads = new Map();
|
|
52
58
|
|
|
53
59
|
function buildManifestVersionSet(manifest) {
|
|
60
|
+
const sourceArtifact = resolveSourceArtifact(manifest);
|
|
54
61
|
if (!manifest || typeof manifest !== 'object') return 'manifest:invalid';
|
|
55
62
|
const shards = Array.isArray(manifest.shards)
|
|
56
63
|
? manifest.shards.map((shard, index) => ({
|
|
@@ -67,6 +74,7 @@ function buildManifestVersionSet(manifest) {
|
|
|
67
74
|
tensorCount: manifest.tensorCount ?? null,
|
|
68
75
|
totalSize: manifest.totalSize ?? null,
|
|
69
76
|
shards,
|
|
77
|
+
sourceRuntime: sourceArtifact?.sourceRuntime ?? null,
|
|
70
78
|
};
|
|
71
79
|
return JSON.stringify(payload);
|
|
72
80
|
}
|
|
@@ -93,6 +101,31 @@ function normalizeSourceStats(value) {
|
|
|
93
101
|
};
|
|
94
102
|
}
|
|
95
103
|
|
|
104
|
+
function isTokenizerJsonRequired(tokenizer) {
|
|
105
|
+
return Boolean(
|
|
106
|
+
tokenizer
|
|
107
|
+
&& (tokenizer.type === 'bundled' || tokenizer.type === 'huggingface')
|
|
108
|
+
&& typeof tokenizer.file === 'string'
|
|
109
|
+
&& tokenizer.file.length > 0
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getTokenizerModelPath(tokenizer) {
|
|
114
|
+
if (!tokenizer || typeof tokenizer !== 'object') {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const explicit = typeof tokenizer.sentencepieceModel === 'string'
|
|
118
|
+
? tokenizer.sentencepieceModel
|
|
119
|
+
: null;
|
|
120
|
+
if (explicit && explicit.length > 0) {
|
|
121
|
+
return explicit;
|
|
122
|
+
}
|
|
123
|
+
if (tokenizer.type === 'sentencepiece') {
|
|
124
|
+
return 'tokenizer.model';
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
96
129
|
// ============================================================================
|
|
97
130
|
// IndexedDB Operations
|
|
98
131
|
// ============================================================================
|
|
@@ -230,6 +263,12 @@ function isDatabaseClosingError(error) {
|
|
|
230
263
|
|| (error)?.name === 'InvalidStateError';
|
|
231
264
|
}
|
|
232
265
|
|
|
266
|
+
function createAbortError(message = 'Download aborted') {
|
|
267
|
+
const error = new Error(message);
|
|
268
|
+
error.name = 'AbortError';
|
|
269
|
+
return error;
|
|
270
|
+
}
|
|
271
|
+
|
|
233
272
|
// ============================================================================
|
|
234
273
|
// Fetch Operations
|
|
235
274
|
// ============================================================================
|
|
@@ -276,6 +315,106 @@ async function fetchWithRetry(url, options = {}) {
|
|
|
276
315
|
throw (lastError);
|
|
277
316
|
}
|
|
278
317
|
|
|
318
|
+
function joinArtifactUrl(baseUrl, relativePath) {
|
|
319
|
+
const root = String(baseUrl || '').trim();
|
|
320
|
+
const rel = normalizeSourceArtifactPath(relativePath);
|
|
321
|
+
if (!root || !rel) {
|
|
322
|
+
throw new Error('joinArtifactUrl requires baseUrl and relativePath.');
|
|
323
|
+
}
|
|
324
|
+
return new URL(rel, root.endsWith('/') ? root : `${root}/`).href;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async function fileExistsInStore(path) {
|
|
328
|
+
try {
|
|
329
|
+
await loadFileFromStore(path);
|
|
330
|
+
return true;
|
|
331
|
+
} catch (error) {
|
|
332
|
+
const message = String(error?.message || '');
|
|
333
|
+
return error?.name === 'NotFoundError' || message.toLowerCase().includes('not found')
|
|
334
|
+
? false
|
|
335
|
+
: Promise.reject(error);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async function computeAssetHash(payload, algorithm = 'sha256') {
|
|
340
|
+
const bytes = payload instanceof Uint8Array ? payload : new Uint8Array(payload);
|
|
341
|
+
const hasher = await createStreamingHasher(String(algorithm || 'sha256').trim().toLowerCase());
|
|
342
|
+
hasher.update(bytes);
|
|
343
|
+
const digest = await hasher.finalize();
|
|
344
|
+
return Array.from(digest)
|
|
345
|
+
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
346
|
+
.join('');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function downloadSourceAsset(url, asset, options = {}) {
|
|
350
|
+
const response = await fetchWithRetry(url, { signal: options.signal });
|
|
351
|
+
const expectedSize = Number.isFinite(asset?.size) ? Math.max(0, Math.floor(Number(asset.size))) : null;
|
|
352
|
+
const expectedHash = typeof asset?.hash === 'string' && asset.hash.trim() ? asset.hash.trim().toLowerCase() : null;
|
|
353
|
+
const hashAlgorithm = typeof asset?.hashAlgorithm === 'string' && asset.hashAlgorithm.trim()
|
|
354
|
+
? asset.hashAlgorithm.trim().toLowerCase()
|
|
355
|
+
: 'sha256';
|
|
356
|
+
const writer = await createFileWriter(asset.path);
|
|
357
|
+
const hasher = expectedHash ? await createStreamingHasher(hashAlgorithm) : null;
|
|
358
|
+
let receivedBytes = 0;
|
|
359
|
+
try {
|
|
360
|
+
if (response.body && typeof response.body.getReader === 'function') {
|
|
361
|
+
const reader = response.body.getReader();
|
|
362
|
+
while (true) {
|
|
363
|
+
const { done, value } = await reader.read();
|
|
364
|
+
if (done) {
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
if (!(value instanceof Uint8Array) || value.byteLength <= 0) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
await writer.write(value);
|
|
371
|
+
hasher?.update(value);
|
|
372
|
+
receivedBytes += value.byteLength;
|
|
373
|
+
options.onProgress?.(receivedBytes);
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
377
|
+
await writer.write(bytes);
|
|
378
|
+
hasher?.update(bytes);
|
|
379
|
+
receivedBytes += bytes.byteLength;
|
|
380
|
+
options.onProgress?.(receivedBytes);
|
|
381
|
+
}
|
|
382
|
+
await writer.close();
|
|
383
|
+
|
|
384
|
+
if (expectedSize != null && receivedBytes !== expectedSize) {
|
|
385
|
+
throw new Error(
|
|
386
|
+
`Asset size mismatch for ${asset.path}: expected ${expectedSize}, got ${receivedBytes}`
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (hasher && expectedHash) {
|
|
391
|
+
const computedHashBytes = await hasher.finalize();
|
|
392
|
+
const computedHash = Array.from(computedHashBytes)
|
|
393
|
+
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
394
|
+
.join('');
|
|
395
|
+
if (computedHash !== expectedHash) {
|
|
396
|
+
throw new Error(
|
|
397
|
+
`Asset hash mismatch for ${asset.path}: expected ${expectedHash}, got ${computedHash}`
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
source: 'http',
|
|
404
|
+
path: asset.path,
|
|
405
|
+
bytes: receivedBytes,
|
|
406
|
+
};
|
|
407
|
+
} catch (error) {
|
|
408
|
+
try {
|
|
409
|
+
await writer.abort();
|
|
410
|
+
} catch {}
|
|
411
|
+
try {
|
|
412
|
+
await deleteFileFromStore(asset.path);
|
|
413
|
+
} catch {}
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
279
418
|
|
|
280
419
|
async function downloadShard(
|
|
281
420
|
baseUrl,
|
|
@@ -327,9 +466,14 @@ export async function downloadModel(
|
|
|
327
466
|
const {
|
|
328
467
|
concurrency = getDefaultConcurrency(),
|
|
329
468
|
requestPersist = true,
|
|
330
|
-
modelId: overrideModelId = undefined
|
|
469
|
+
modelId: overrideModelId = undefined,
|
|
470
|
+
signal: externalSignal = null,
|
|
331
471
|
} = options;
|
|
332
472
|
|
|
473
|
+
if (externalSignal?.aborted) {
|
|
474
|
+
throw createAbortError();
|
|
475
|
+
}
|
|
476
|
+
|
|
333
477
|
// Request persistent storage if needed
|
|
334
478
|
if (requestPersist) {
|
|
335
479
|
await requestPersistence();
|
|
@@ -340,14 +484,19 @@ export async function downloadModel(
|
|
|
340
484
|
const manifestResponse = await fetchWithRetry(manifestUrl);
|
|
341
485
|
const manifestJson = await manifestResponse.text();
|
|
342
486
|
const manifest = parseManifest(manifestJson);
|
|
487
|
+
const directSourceArtifact = resolveSourceArtifact(manifest);
|
|
488
|
+
const trackedShards = directSourceArtifact ? directSourceArtifact.sourceFiles : manifest.shards;
|
|
489
|
+
const trackedTotalBytes = directSourceArtifact
|
|
490
|
+
? directSourceArtifact.totalBytes
|
|
491
|
+
: manifest.totalSize;
|
|
343
492
|
|
|
344
493
|
// Use override modelId for storage, or fall back to manifest's modelId
|
|
345
494
|
const storageModelId = overrideModelId || manifest.modelId;
|
|
346
495
|
|
|
347
496
|
// Check available space
|
|
348
|
-
const spaceCheck = await checkSpaceAvailable(
|
|
497
|
+
const spaceCheck = await checkSpaceAvailable(trackedTotalBytes);
|
|
349
498
|
if (!spaceCheck.hasSpace) {
|
|
350
|
-
throw new QuotaExceededError(
|
|
499
|
+
throw new QuotaExceededError(trackedTotalBytes, spaceCheck.info.available);
|
|
351
500
|
}
|
|
352
501
|
|
|
353
502
|
// Open model directory
|
|
@@ -377,7 +526,14 @@ export async function downloadModel(
|
|
|
377
526
|
if (savedVersionSet !== manifestVersionSet) {
|
|
378
527
|
log.warn('Downloader', `Manifest version-set changed for ${storageModelId}, resetting cached shards`);
|
|
379
528
|
for (const idx of state.completedShards) {
|
|
380
|
-
|
|
529
|
+
if (directSourceArtifact) {
|
|
530
|
+
const sourceEntry = directSourceArtifact.sourceFiles[idx];
|
|
531
|
+
if (sourceEntry) {
|
|
532
|
+
await deleteFileFromStore(sourceEntry.path);
|
|
533
|
+
}
|
|
534
|
+
} else {
|
|
535
|
+
await deleteShard(idx);
|
|
536
|
+
}
|
|
381
537
|
}
|
|
382
538
|
state.completedShards.clear();
|
|
383
539
|
}
|
|
@@ -389,6 +545,13 @@ export async function downloadModel(
|
|
|
389
545
|
state.lastSourcePath = typeof state.lastSourcePath === 'string' ? state.lastSourcePath : null;
|
|
390
546
|
// Check which shards actually exist (in case OPFS was cleared)
|
|
391
547
|
for (const idx of state.completedShards) {
|
|
548
|
+
if (directSourceArtifact) {
|
|
549
|
+
const sourceEntry = directSourceArtifact.sourceFiles[idx];
|
|
550
|
+
if (!sourceEntry || !(await fileExistsInStore(sourceEntry.path))) {
|
|
551
|
+
state.completedShards.delete(idx);
|
|
552
|
+
}
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
392
555
|
if (!(await shardExists(idx))) {
|
|
393
556
|
state.completedShards.delete(idx);
|
|
394
557
|
}
|
|
@@ -396,23 +559,50 @@ export async function downloadModel(
|
|
|
396
559
|
// Verify hashes for completed shards; drop and re-download corrupt shards
|
|
397
560
|
for (const idx of Array.from(state.completedShards)) {
|
|
398
561
|
try {
|
|
399
|
-
|
|
562
|
+
if (directSourceArtifact) {
|
|
563
|
+
const sourceEntry = directSourceArtifact.sourceFiles[idx];
|
|
564
|
+
if (!sourceEntry?.hash) {
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
const payload = await loadFileFromStore(sourceEntry.path);
|
|
568
|
+
const computedHash = await computeAssetHash(payload, sourceEntry.hashAlgorithm);
|
|
569
|
+
if (computedHash !== sourceEntry.hash) {
|
|
570
|
+
throw new Error(
|
|
571
|
+
`Hash mismatch for source asset ${sourceEntry.path}: expected ${sourceEntry.hash}, got ${computedHash}`
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
} else {
|
|
575
|
+
await loadShard(idx, { verify: true });
|
|
576
|
+
}
|
|
400
577
|
} catch (err) {
|
|
401
578
|
log.warn('Downloader', `Shard ${idx} failed verification, re-downloading`);
|
|
402
579
|
state.completedShards.delete(idx);
|
|
403
|
-
|
|
580
|
+
if (directSourceArtifact) {
|
|
581
|
+
const sourceEntry = directSourceArtifact.sourceFiles[idx];
|
|
582
|
+
if (sourceEntry) {
|
|
583
|
+
await deleteFileFromStore(sourceEntry.path);
|
|
584
|
+
}
|
|
585
|
+
} else {
|
|
586
|
+
await deleteShard(idx);
|
|
587
|
+
}
|
|
404
588
|
}
|
|
405
589
|
}
|
|
406
590
|
}
|
|
407
591
|
|
|
408
592
|
// Create abort controller
|
|
409
593
|
const abortController = new AbortController();
|
|
594
|
+
const abortFromExternalSignal = () => {
|
|
595
|
+
abortController.abort();
|
|
596
|
+
};
|
|
597
|
+
if (externalSignal && typeof externalSignal.addEventListener === 'function') {
|
|
598
|
+
externalSignal.addEventListener('abort', abortFromExternalSignal, { once: true });
|
|
599
|
+
}
|
|
410
600
|
activeDownloads.set(storageModelId, {
|
|
411
601
|
state,
|
|
412
602
|
abortController
|
|
413
603
|
});
|
|
414
604
|
|
|
415
|
-
const totalShards =
|
|
605
|
+
const totalShards = trackedShards.length;
|
|
416
606
|
const requiredEncoding = getRequiredContentEncoding();
|
|
417
607
|
|
|
418
608
|
const pendingShards = [];
|
|
@@ -427,7 +617,7 @@ export async function downloadModel(
|
|
|
427
617
|
// Progress tracking
|
|
428
618
|
let downloadedBytes = 0;
|
|
429
619
|
for (const idx of state.completedShards) {
|
|
430
|
-
const info =
|
|
620
|
+
const info = trackedShards[idx];
|
|
431
621
|
if (info) downloadedBytes += info.size;
|
|
432
622
|
}
|
|
433
623
|
|
|
@@ -464,9 +654,9 @@ export async function downloadModel(
|
|
|
464
654
|
manifest,
|
|
465
655
|
totalShards,
|
|
466
656
|
completedShards: (state).completedShards.size,
|
|
467
|
-
totalBytes:
|
|
657
|
+
totalBytes: trackedTotalBytes,
|
|
468
658
|
downloadedBytes,
|
|
469
|
-
percent: (downloadedBytes /
|
|
659
|
+
percent: trackedTotalBytes > 0 ? (downloadedBytes / trackedTotalBytes) * 100 : 0,
|
|
470
660
|
status: (state).status,
|
|
471
661
|
currentShard,
|
|
472
662
|
speed: speedTracker.speed,
|
|
@@ -492,58 +682,96 @@ export async function downloadModel(
|
|
|
492
682
|
updateProgress(shardIndex);
|
|
493
683
|
|
|
494
684
|
try {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
const algorithm = manifest.hashAlgorithm;
|
|
500
|
-
if (!algorithm) {
|
|
501
|
-
throw new Error('Manifest missing hashAlgorithm for download verification.');
|
|
502
|
-
}
|
|
503
|
-
const expectedHash = shardInfo.hash;
|
|
504
|
-
if (!expectedHash) {
|
|
505
|
-
throw new Error(`Shard ${shardIndex} is missing hash in manifest`);
|
|
506
|
-
}
|
|
507
|
-
const expectedSize = Number.isFinite(shardInfo.size) ? Math.floor(shardInfo.size) : null;
|
|
508
|
-
const result = await downloadShard(baseUrl, shardIndex, shardInfo, {
|
|
509
|
-
signal: abortController.signal,
|
|
510
|
-
algorithm,
|
|
511
|
-
requiredEncoding,
|
|
512
|
-
expectedHash,
|
|
513
|
-
expectedSize,
|
|
514
|
-
expectedManifestVersionSet: manifestVersionSet,
|
|
515
|
-
writeToStore: true,
|
|
516
|
-
onProgress: ( p) => {
|
|
517
|
-
const prev = shardProgress.get(shardIndex) || 0;
|
|
518
|
-
const delta = Math.max(0, p.receivedBytes - prev);
|
|
519
|
-
shardProgress.set(shardIndex, p.receivedBytes);
|
|
520
|
-
downloadedBytes += delta;
|
|
521
|
-
updateProgress(shardIndex);
|
|
685
|
+
if (directSourceArtifact) {
|
|
686
|
+
const sourceAsset = directSourceArtifact.sourceFiles[shardIndex];
|
|
687
|
+
if (!sourceAsset) {
|
|
688
|
+
throw new Error(`Invalid source asset index: ${shardIndex}`);
|
|
522
689
|
}
|
|
523
|
-
|
|
690
|
+
const result = await downloadSourceAsset(
|
|
691
|
+
joinArtifactUrl(baseUrl, sourceAsset.path),
|
|
692
|
+
sourceAsset,
|
|
693
|
+
{
|
|
694
|
+
signal: abortController.signal,
|
|
695
|
+
onProgress: (receivedBytes) => {
|
|
696
|
+
const prev = shardProgress.get(shardIndex) || 0;
|
|
697
|
+
const delta = Math.max(0, receivedBytes - prev);
|
|
698
|
+
shardProgress.set(shardIndex, receivedBytes);
|
|
699
|
+
downloadedBytes += delta;
|
|
700
|
+
updateProgress(shardIndex);
|
|
701
|
+
},
|
|
702
|
+
}
|
|
703
|
+
);
|
|
524
704
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
705
|
+
const source = typeof result.source === 'string' ? result.source : 'unknown';
|
|
706
|
+
const sourceStats = normalizeSourceStats(state.sourceStats);
|
|
707
|
+
if (source in sourceStats) {
|
|
708
|
+
sourceStats[source] += 1;
|
|
709
|
+
} else {
|
|
710
|
+
sourceStats.unknown += 1;
|
|
711
|
+
}
|
|
712
|
+
state.sourceStats = sourceStats;
|
|
713
|
+
state.lastSource = source;
|
|
714
|
+
state.lastSourcePath = typeof result.path === 'string' ? result.path : null;
|
|
715
|
+
|
|
716
|
+
const observedBytes = shardProgress.get(shardIndex) || 0;
|
|
717
|
+
const shardBytes = sourceAsset.size ?? result.bytes ?? observedBytes;
|
|
718
|
+
if (shardBytes > observedBytes) {
|
|
719
|
+
downloadedBytes += shardBytes - observedBytes;
|
|
720
|
+
}
|
|
721
|
+
} else {
|
|
722
|
+
const shardInfo = manifest.shards[shardIndex];
|
|
723
|
+
if (!shardInfo) {
|
|
724
|
+
throw new Error(`Invalid shard index: ${shardIndex}`);
|
|
725
|
+
}
|
|
726
|
+
const algorithm = manifest.hashAlgorithm;
|
|
727
|
+
if (!algorithm) {
|
|
728
|
+
throw new Error('Manifest missing hashAlgorithm for download verification.');
|
|
729
|
+
}
|
|
730
|
+
const expectedHash = getExpectedShardHash(shardInfo, algorithm);
|
|
731
|
+
if (!expectedHash) {
|
|
732
|
+
throw new Error(`Shard ${shardIndex} is missing hash in manifest`);
|
|
733
|
+
}
|
|
734
|
+
const expectedSize = Number.isFinite(shardInfo.size) ? Math.floor(shardInfo.size) : null;
|
|
735
|
+
const result = await downloadShard(baseUrl, shardIndex, shardInfo, {
|
|
736
|
+
signal: abortController.signal,
|
|
737
|
+
algorithm,
|
|
738
|
+
requiredEncoding,
|
|
739
|
+
expectedHash,
|
|
740
|
+
expectedSize,
|
|
741
|
+
expectedManifestVersionSet: manifestVersionSet,
|
|
742
|
+
writeToStore: true,
|
|
743
|
+
onProgress: ( p) => {
|
|
744
|
+
const prev = shardProgress.get(shardIndex) || 0;
|
|
745
|
+
const delta = Math.max(0, p.receivedBytes - prev);
|
|
746
|
+
shardProgress.set(shardIndex, p.receivedBytes);
|
|
747
|
+
downloadedBytes += delta;
|
|
748
|
+
updateProgress(shardIndex);
|
|
749
|
+
}
|
|
750
|
+
});
|
|
529
751
|
|
|
530
|
-
|
|
752
|
+
if (result.hash !== expectedHash) {
|
|
753
|
+
await deleteShard(shardIndex);
|
|
754
|
+
throw new Error(`Hash mismatch for shard ${shardIndex}: expected ${expectedHash}, got ${result.hash}`);
|
|
755
|
+
}
|
|
531
756
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
sourceStats
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
757
|
+
await persistDownloadedShardIfNeeded(result, shardIndex);
|
|
758
|
+
|
|
759
|
+
const source = typeof result.source === 'string' ? result.source : 'unknown';
|
|
760
|
+
const sourceStats = normalizeSourceStats(state.sourceStats);
|
|
761
|
+
if (source in sourceStats) {
|
|
762
|
+
sourceStats[source] += 1;
|
|
763
|
+
} else {
|
|
764
|
+
sourceStats.unknown += 1;
|
|
765
|
+
}
|
|
766
|
+
state.sourceStats = sourceStats;
|
|
767
|
+
state.lastSource = source;
|
|
768
|
+
state.lastSourcePath = typeof result.path === 'string' ? result.path : null;
|
|
769
|
+
|
|
770
|
+
const observedBytes = shardProgress.get(shardIndex) || 0;
|
|
771
|
+
const shardBytes = shardInfo.size ?? result.bytes ?? observedBytes;
|
|
772
|
+
if (shardBytes > observedBytes) {
|
|
773
|
+
downloadedBytes += shardBytes - observedBytes;
|
|
774
|
+
}
|
|
547
775
|
}
|
|
548
776
|
|
|
549
777
|
// Update state
|
|
@@ -599,6 +827,10 @@ export async function downloadModel(
|
|
|
599
827
|
// Wait for any remaining downloads to complete
|
|
600
828
|
await Promise.all([...downloadPromises]);
|
|
601
829
|
|
|
830
|
+
if (abortController.signal.aborted) {
|
|
831
|
+
throw createAbortError();
|
|
832
|
+
}
|
|
833
|
+
|
|
602
834
|
// Verify all shards completed
|
|
603
835
|
if (state.completedShards.size === totalShards) {
|
|
604
836
|
state.status = 'completed';
|
|
@@ -606,35 +838,50 @@ export async function downloadModel(
|
|
|
606
838
|
// Save manifest to OPFS
|
|
607
839
|
await saveManifest(manifestJson);
|
|
608
840
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
841
|
+
if (directSourceArtifact) {
|
|
842
|
+
for (const asset of directSourceArtifact.auxiliaryFiles) {
|
|
843
|
+
const alreadyPresent = await fileExistsInStore(asset.path);
|
|
844
|
+
if (alreadyPresent) {
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
await downloadSourceAsset(joinArtifactUrl(baseUrl, asset.path), asset, {
|
|
848
|
+
signal: abortController.signal,
|
|
849
|
+
onProgress: (receivedBytes) => {
|
|
850
|
+
const previous = shardProgress.get(asset.path) || 0;
|
|
851
|
+
const delta = Math.max(0, receivedBytes - previous);
|
|
852
|
+
shardProgress.set(asset.path, receivedBytes);
|
|
853
|
+
downloadedBytes += delta;
|
|
854
|
+
updateProgress(null);
|
|
855
|
+
},
|
|
856
|
+
});
|
|
857
|
+
const observedBytes = shardProgress.get(asset.path) || 0;
|
|
858
|
+
shardProgress.delete(asset.path);
|
|
859
|
+
const assetBytes = asset.size ?? observedBytes;
|
|
860
|
+
if (assetBytes > observedBytes) {
|
|
861
|
+
downloadedBytes += assetBytes - observedBytes;
|
|
862
|
+
}
|
|
863
|
+
updateProgress(null, true);
|
|
864
|
+
}
|
|
865
|
+
} else {
|
|
866
|
+
// Download tokenizer assets if specified
|
|
867
|
+
const tokenizer = (manifest.tokenizer);
|
|
868
|
+
if (isTokenizerJsonRequired(tokenizer)) {
|
|
614
869
|
const tokenizerUrl = `${baseUrl}/${ (tokenizer).file}`;
|
|
615
870
|
log.verbose('Downloader', `Fetching bundled tokenizer from ${tokenizerUrl}`);
|
|
616
871
|
const tokenizerResponse = await fetchWithRetry(tokenizerUrl);
|
|
617
872
|
const tokenizerJson = await tokenizerResponse.text();
|
|
618
873
|
await saveTokenizer(tokenizerJson);
|
|
619
874
|
log.verbose('Downloader', 'Saved bundled tokenizer.json');
|
|
620
|
-
} catch (err) {
|
|
621
|
-
log.warn('Downloader', `Failed to download tokenizer.json: ${ (err).message}`);
|
|
622
|
-
// Non-fatal - model will fall back to HuggingFace tokenizer
|
|
623
875
|
}
|
|
624
|
-
}
|
|
625
876
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
if (sentencepieceModel) {
|
|
629
|
-
try {
|
|
877
|
+
const sentencepieceModel = getTokenizerModelPath(tokenizer);
|
|
878
|
+
if (sentencepieceModel) {
|
|
630
879
|
const modelUrl = `${baseUrl}/${sentencepieceModel}`;
|
|
631
880
|
log.verbose('Downloader', `Fetching sentencepiece model from ${modelUrl}`);
|
|
632
881
|
const modelResponse = await fetchWithRetry(modelUrl);
|
|
633
882
|
const modelBuffer = await modelResponse.arrayBuffer();
|
|
634
883
|
await saveTokenizerModel(modelBuffer);
|
|
635
884
|
log.verbose('Downloader', 'Saved tokenizer.model');
|
|
636
|
-
} catch (err) {
|
|
637
|
-
log.warn('Downloader', `Failed to download tokenizer.model: ${ (err).message}`);
|
|
638
885
|
}
|
|
639
886
|
}
|
|
640
887
|
|
|
@@ -660,6 +907,9 @@ export async function downloadModel(
|
|
|
660
907
|
throw error;
|
|
661
908
|
|
|
662
909
|
} finally {
|
|
910
|
+
if (externalSignal && typeof externalSignal.removeEventListener === 'function') {
|
|
911
|
+
externalSignal.removeEventListener('abort', abortFromExternalSignal);
|
|
912
|
+
}
|
|
663
913
|
activeDownloads.delete(storageModelId);
|
|
664
914
|
}
|
|
665
915
|
}
|
|
@@ -684,7 +934,10 @@ export async function resumeDownload(
|
|
|
684
934
|
throw new Error(`No download state found for model: ${modelId}`);
|
|
685
935
|
}
|
|
686
936
|
|
|
687
|
-
return downloadModel(state.baseUrl, onProgress,
|
|
937
|
+
return downloadModel(state.baseUrl, onProgress, {
|
|
938
|
+
...options,
|
|
939
|
+
modelId: options.modelId ?? state.modelId,
|
|
940
|
+
});
|
|
688
941
|
}
|
|
689
942
|
|
|
690
943
|
|
|
@@ -694,11 +947,13 @@ export async function getDownloadProgress(modelId) {
|
|
|
694
947
|
if (active) {
|
|
695
948
|
const state = active.state;
|
|
696
949
|
const manifest = state.manifest;
|
|
697
|
-
const
|
|
950
|
+
const directSourceArtifact = resolveSourceArtifact(manifest);
|
|
951
|
+
const trackedShards = directSourceArtifact ? directSourceArtifact.sourceFiles : (manifest?.shards || []);
|
|
952
|
+
const totalShards = trackedShards.length;
|
|
698
953
|
|
|
699
954
|
let downloadedBytes = 0;
|
|
700
955
|
for (const idx of state.completedShards) {
|
|
701
|
-
const info =
|
|
956
|
+
const info = trackedShards[idx];
|
|
702
957
|
if (info) downloadedBytes += info.size;
|
|
703
958
|
}
|
|
704
959
|
|
|
@@ -706,9 +961,14 @@ export async function getDownloadProgress(modelId) {
|
|
|
706
961
|
modelId,
|
|
707
962
|
totalShards,
|
|
708
963
|
completedShards: state.completedShards.size,
|
|
709
|
-
totalBytes: manifest?.totalSize || 0,
|
|
964
|
+
totalBytes: directSourceArtifact ? directSourceArtifact.totalBytes : (manifest?.totalSize || 0),
|
|
710
965
|
downloadedBytes,
|
|
711
|
-
percent: manifest
|
|
966
|
+
percent: manifest
|
|
967
|
+
? (
|
|
968
|
+
downloadedBytes
|
|
969
|
+
/ (directSourceArtifact ? directSourceArtifact.totalBytes : manifest.totalSize || 1)
|
|
970
|
+
) * 100
|
|
971
|
+
: 0,
|
|
712
972
|
status: state.status,
|
|
713
973
|
currentShard: null,
|
|
714
974
|
speed: 0,
|
|
@@ -721,20 +981,25 @@ export async function getDownloadProgress(modelId) {
|
|
|
721
981
|
// Check saved state
|
|
722
982
|
const state = await loadDownloadState(modelId);
|
|
723
983
|
if (!state) return null;
|
|
984
|
+
const directSourceArtifact = resolveSourceArtifact(state.manifest);
|
|
985
|
+
const trackedShards = directSourceArtifact ? directSourceArtifact.sourceFiles : state.manifest.shards;
|
|
724
986
|
|
|
725
987
|
let downloadedBytes = 0;
|
|
726
988
|
for (const idx of state.completedShards) {
|
|
727
|
-
const shard =
|
|
989
|
+
const shard = trackedShards[idx];
|
|
728
990
|
if (shard) downloadedBytes += shard.size;
|
|
729
991
|
}
|
|
730
992
|
|
|
731
993
|
return {
|
|
732
994
|
modelId,
|
|
733
|
-
totalShards:
|
|
995
|
+
totalShards: trackedShards.length,
|
|
734
996
|
completedShards: state.completedShards.size,
|
|
735
|
-
totalBytes: state.manifest.totalSize,
|
|
997
|
+
totalBytes: directSourceArtifact ? directSourceArtifact.totalBytes : state.manifest.totalSize,
|
|
736
998
|
downloadedBytes,
|
|
737
|
-
percent: (
|
|
999
|
+
percent: (
|
|
1000
|
+
downloadedBytes
|
|
1001
|
+
/ (directSourceArtifact ? directSourceArtifact.totalBytes : state.manifest.totalSize || 1)
|
|
1002
|
+
) * 100,
|
|
738
1003
|
status: state.status,
|
|
739
1004
|
currentShard: null,
|
|
740
1005
|
speed: 0,
|
|
@@ -790,7 +1055,8 @@ export async function checkDownloadNeeded(modelId) {
|
|
|
790
1055
|
};
|
|
791
1056
|
}
|
|
792
1057
|
|
|
793
|
-
const
|
|
1058
|
+
const directSourceArtifact = resolveSourceArtifact(state.manifest);
|
|
1059
|
+
const totalShards = directSourceArtifact ? directSourceArtifact.sourceFiles.length : state.manifest.shards.length;
|
|
794
1060
|
|
|
795
1061
|
const missingShards = [];
|
|
796
1062
|
|
|
@@ -808,6 +1074,22 @@ export async function checkDownloadNeeded(modelId) {
|
|
|
808
1074
|
};
|
|
809
1075
|
}
|
|
810
1076
|
|
|
1077
|
+
if (directSourceArtifact) {
|
|
1078
|
+
const missingAuxiliaryFiles = [];
|
|
1079
|
+
for (const entry of directSourceArtifact.auxiliaryFiles) {
|
|
1080
|
+
if (!(await fileExistsInStore(entry.path))) {
|
|
1081
|
+
missingAuxiliaryFiles.push(entry.path);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
if (missingAuxiliaryFiles.length > 0) {
|
|
1085
|
+
return {
|
|
1086
|
+
needed: true,
|
|
1087
|
+
reason: `Missing ${missingAuxiliaryFiles.length} direct-source auxiliary file(s)`,
|
|
1088
|
+
missingShards: [],
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
811
1093
|
return {
|
|
812
1094
|
needed: false,
|
|
813
1095
|
reason: 'Model fully downloaded',
|
package/src/storage/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export {
|
|
|
20
20
|
writeShard,
|
|
21
21
|
createShardWriter,
|
|
22
22
|
createConversionShardWriter,
|
|
23
|
+
createFileWriter,
|
|
23
24
|
loadShard,
|
|
24
25
|
loadShardRange,
|
|
25
26
|
streamShardRange,
|
|
@@ -32,6 +33,7 @@ export {
|
|
|
32
33
|
listModels,
|
|
33
34
|
listFilesInStore,
|
|
34
35
|
loadFileFromStore,
|
|
36
|
+
loadFileRangeFromStore,
|
|
35
37
|
streamFileFromStore,
|
|
36
38
|
getModelInfo,
|
|
37
39
|
modelExists,
|
|
@@ -46,6 +48,7 @@ export {
|
|
|
46
48
|
saveAuxFile,
|
|
47
49
|
loadAuxFile,
|
|
48
50
|
loadAuxText,
|
|
51
|
+
deleteFileFromStore,
|
|
49
52
|
cleanup,
|
|
50
53
|
} from './shard-manager.js';
|
|
51
54
|
export type {
|