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