@simulatte/doppler 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +126 -0
- package/README.md +16 -23
- package/package.json +14 -1
- package/src/adapters/adapter-registry.js +12 -1
- package/src/adapters/lora-loader.js +23 -6
- package/src/bridge/extension-client.d.ts +5 -0
- package/src/bridge/extension-client.js +40 -0
- package/src/bridge/index.d.ts +2 -1
- package/src/bridge/index.js +6 -4
- package/src/browser/browser-converter.js +26 -1
- package/src/browser/file-picker.js +6 -0
- package/src/browser/safetensors-parser-browser.js +84 -1
- package/src/browser/shard-io-browser.js +2 -2
- package/src/browser/tensor-source-download.js +8 -2
- package/src/browser/tensor-source-http.d.ts +1 -0
- package/src/browser/tensor-source-http.js +5 -1
- package/src/client/doppler-api.browser.js +20 -4
- package/src/client/doppler-api.js +19 -3
- package/src/client/doppler-provider/generation.js +12 -0
- package/src/client/doppler-provider/model-manager.d.ts +10 -0
- package/src/client/doppler-provider/model-manager.js +91 -19
- package/src/client/doppler-provider/source-runtime.d.ts +2 -1
- package/src/client/doppler-provider/source-runtime.js +132 -13
- package/src/client/doppler-registry.json +8 -7
- package/src/config/backward-registry-loader.js +17 -2
- package/src/config/execution-v0-contract-check.js +113 -15
- package/src/config/kernel-path-contract-check.js +57 -29
- package/src/config/kernel-path-loader.js +5 -36
- package/src/config/kernels/kernel-ref-digests.js +1 -1
- package/src/config/kernels/registry.js +14 -1
- package/src/config/kernels/registry.json +7 -5
- package/src/config/loader.d.ts +1 -1
- package/src/config/loader.js +12 -2
- package/src/config/merge-contract-check.js +59 -4
- package/src/config/merge-helpers.js +128 -7
- package/src/config/merge.d.ts +1 -0
- package/src/config/merge.js +10 -0
- package/src/config/param-validator.js +47 -2
- package/src/config/presets/kernel-paths/{gemma2-q4k-dequant-f32a.json → gemma2-q4k-dequant-f32a-nosubgroups.json} +3 -3
- package/src/config/presets/kernel-paths/gemma3-f16-fused-f32a-online-streamingprefill.json +223 -0
- package/src/config/presets/kernel-paths/{gemma3-q4k-dequant-f32a.json → gemma3-q4k-dequant-f32a-nosubgroups.json} +3 -3
- package/src/config/presets/kernel-paths/registry.json +29 -8
- package/src/config/presets/models/gemma2.json +2 -2
- package/src/config/presets/runtime/experiments/bench/gemma3-bench-q4k.json +1 -1
- package/src/config/presets/runtime/experiments/debug/gemma3-debug-q4k.json +1 -1
- package/src/config/presets/runtime/experiments/verify/gemma3-verify.json +1 -1
- package/src/config/presets/runtime/kernels/dequant-f16-q4k.json +6 -13
- package/src/config/presets/runtime/kernels/dequant-f32-q4k.json +6 -13
- package/src/config/presets/runtime/kernels/embeddinggemma-q4k-dequant-f32a.json +37 -0
- package/src/config/presets/runtime/kernels/fused-q4k.json +6 -13
- package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f16a.json +33 -0
- package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f32a-nosubgroups.json +33 -0
- package/src/config/presets/runtime/kernels/gemma2-q4k-fused-f32a.json +33 -0
- package/src/config/presets/runtime/kernels/safe-q4k.json +6 -13
- package/src/config/presets/runtime/platform/metal-apple-q4k.json +1 -1
- package/src/config/runtime.js +6 -1
- package/src/config/schema/debug.schema.d.ts +5 -0
- package/src/config/schema/doppler.schema.js +16 -21
- package/src/config/schema/inference-defaults.schema.js +3 -3
- package/src/config/schema/kernel-path.schema.d.ts +5 -1
- package/src/config/schema/kernel-thresholds.schema.js +12 -4
- package/src/config/schema/manifest.schema.d.ts +2 -1
- package/src/config/schema/manifest.schema.js +16 -3
- package/src/config/training-defaults.js +30 -22
- package/src/converter/conversion-plan.js +94 -9
- package/src/converter/core.d.ts +7 -0
- package/src/converter/core.js +14 -9
- package/src/converter/execution-v0-manifest.js +4 -1
- package/src/converter/index.d.ts +1 -0
- package/src/converter/index.js +1 -0
- package/src/converter/manifest-inference.js +43 -12
- package/src/converter/parsers/diffusion.js +0 -3
- package/src/converter/quantization-info.js +35 -15
- package/src/converter/shard-packer.d.ts +1 -1
- package/src/converter/shard-packer.js +4 -1
- package/src/debug/config.js +123 -11
- package/src/debug/signals.js +7 -1
- package/src/debug/tensor.d.ts +2 -0
- package/src/debug/tensor.js +13 -2
- package/src/distribution/p2p-control-plane.js +52 -12
- package/src/distribution/p2p-observability.js +43 -7
- package/src/distribution/p2p-webrtc-browser.js +20 -0
- package/src/distribution/shard-delivery.js +77 -26
- package/src/formats/gguf/types.js +33 -16
- package/src/formats/rdrr/groups.d.ts +12 -4
- package/src/formats/rdrr/groups.js +3 -6
- package/src/formats/rdrr/parsing.js +39 -2
- package/src/formats/rdrr/types.d.ts +2 -1
- package/src/gpu/command-recorder.js +86 -61
- package/src/gpu/device.d.ts +1 -0
- package/src/gpu/device.js +73 -19
- package/src/gpu/kernel-tuner/benchmarks.js +326 -316
- package/src/gpu/kernel-tuner/cache.js +71 -4
- package/src/gpu/kernel-tuner/tuner.js +22 -4
- package/src/gpu/kernels/attention.js +15 -34
- package/src/gpu/kernels/backward/adam.js +62 -58
- package/src/gpu/kernels/backward/attention_backward.js +257 -169
- package/src/gpu/kernels/backward/conv2d_backward.js +14 -1
- package/src/gpu/kernels/cast.js +191 -149
- package/src/gpu/kernels/check-stop.js +33 -44
- package/src/gpu/kernels/conv2d.js +27 -17
- package/src/gpu/kernels/cross_entropy_loss.js +21 -15
- package/src/gpu/kernels/depthwise_conv2d.js +36 -26
- package/src/gpu/kernels/dequant.js +178 -126
- package/src/gpu/kernels/energy.d.ts +3 -21
- package/src/gpu/kernels/energy.js +111 -88
- package/src/gpu/kernels/feature-check.js +1 -1
- package/src/gpu/kernels/fused_ffn.js +84 -65
- package/src/gpu/kernels/fused_matmul_residual.js +56 -33
- package/src/gpu/kernels/fused_matmul_rmsnorm.js +62 -45
- package/src/gpu/kernels/gather.js +33 -15
- package/src/gpu/kernels/gelu.js +19 -11
- package/src/gpu/kernels/grouped_pointwise_conv2d.js +33 -23
- package/src/gpu/kernels/groupnorm.js +34 -23
- package/src/gpu/kernels/kv-quantize.js +5 -2
- package/src/gpu/kernels/layernorm.js +35 -19
- package/src/gpu/kernels/logit-merge.js +5 -3
- package/src/gpu/kernels/matmul.js +58 -39
- package/src/gpu/kernels/modulate.js +23 -15
- package/src/gpu/kernels/moe.js +221 -175
- package/src/gpu/kernels/pixel_shuffle.js +22 -14
- package/src/gpu/kernels/relu.js +18 -10
- package/src/gpu/kernels/repeat_channels.js +25 -17
- package/src/gpu/kernels/residual.js +37 -27
- package/src/gpu/kernels/rmsnorm.js +57 -41
- package/src/gpu/kernels/rope.js +3 -0
- package/src/gpu/kernels/sample.js +27 -38
- package/src/gpu/kernels/sana_linear_attention.js +18 -10
- package/src/gpu/kernels/scale.js +18 -11
- package/src/gpu/kernels/shader-cache.js +4 -2
- package/src/gpu/kernels/silu.js +120 -72
- package/src/gpu/kernels/softmax.js +44 -25
- package/src/gpu/kernels/split_qkv.js +23 -13
- package/src/gpu/kernels/transpose.js +18 -10
- package/src/gpu/kernels/transpose.wgsl +5 -3
- package/src/gpu/kernels/upsample2d.js +21 -13
- package/src/gpu/kernels/utils.js +20 -13
- package/src/gpu/partitioned-buffer-pool.js +10 -2
- package/src/gpu/perf-guards.js +2 -9
- package/src/gpu/profiler.js +27 -22
- package/src/gpu/readback-utils.d.ts +16 -0
- package/src/gpu/readback-utils.js +41 -0
- package/src/gpu/submit-tracker.js +13 -0
- package/src/gpu/uniform-cache.d.ts +1 -0
- package/src/gpu/uniform-cache.js +30 -9
- package/src/hotswap/intent-bundle.js +6 -0
- package/src/hotswap/manifest.d.ts +10 -1
- package/src/hotswap/manifest.js +12 -2
- package/src/hotswap/runtime.js +30 -8
- package/src/index-browser.d.ts +44 -0
- package/src/index-browser.js +14 -0
- package/src/inference/browser-harness-contract-helpers.d.ts +5 -0
- package/src/inference/browser-harness-contract-helpers.js +28 -0
- package/src/inference/browser-harness-diffusion-energy-suites.d.ts +2 -0
- package/src/inference/browser-harness-diffusion-energy-suites.js +269 -0
- package/src/inference/browser-harness-model-helpers.d.ts +16 -0
- package/src/inference/browser-harness-model-helpers.js +217 -0
- package/src/inference/browser-harness-report-helpers.d.ts +7 -0
- package/src/inference/browser-harness-report-helpers.js +42 -0
- package/src/inference/browser-harness-runtime-helpers.d.ts +61 -0
- package/src/inference/browser-harness-runtime-helpers.js +415 -0
- package/src/inference/browser-harness-suite-helpers.d.ts +28 -0
- package/src/inference/browser-harness-suite-helpers.js +268 -0
- package/src/inference/browser-harness-text-helpers.d.ts +27 -0
- package/src/inference/browser-harness-text-helpers.js +788 -0
- package/src/inference/browser-harness.d.ts +6 -0
- package/src/inference/browser-harness.js +130 -1996
- package/src/inference/kv-cache/base.js +140 -94
- package/src/inference/kv-cache/tiered.js +5 -3
- package/src/inference/moe-router.js +88 -56
- package/src/inference/multi-model-network.js +5 -3
- package/src/inference/network-evolution.d.ts +11 -2
- package/src/inference/network-evolution.js +20 -21
- package/src/inference/pipelines/context.d.ts +3 -0
- package/src/inference/pipelines/context.js +142 -2
- package/src/inference/pipelines/diffusion/helpers.js +7 -2
- package/src/inference/pipelines/diffusion/pipeline.js +2 -1
- package/src/inference/pipelines/diffusion/sd3-transformer.js +10 -10
- package/src/inference/pipelines/diffusion/vae.js +3 -7
- package/src/inference/pipelines/energy/pipeline.js +27 -21
- package/src/inference/pipelines/energy/quintel.d.ts +5 -0
- package/src/inference/pipelines/energy/quintel.js +11 -0
- package/src/inference/pipelines/energy-head/row-head-pipeline.js +17 -13
- package/src/inference/pipelines/structured/json-head-pipeline.js +26 -11
- package/src/inference/pipelines/text/attention/projections.js +151 -101
- package/src/inference/pipelines/text/attention/record.js +62 -8
- package/src/inference/pipelines/text/attention/run.js +62 -8
- package/src/inference/pipelines/text/config.js +3 -4
- package/src/inference/pipelines/text/embed.js +2 -8
- package/src/inference/pipelines/text/execution-plan.js +41 -19
- package/src/inference/pipelines/text/execution-v0-contract-helpers.d.ts +59 -0
- package/src/inference/pipelines/text/execution-v0-contract-helpers.js +937 -0
- package/src/inference/pipelines/text/execution-v0-runtime-builders.d.ts +15 -0
- package/src/inference/pipelines/text/execution-v0-runtime-builders.js +279 -0
- package/src/inference/pipelines/text/execution-v0.js +62 -1013
- package/src/inference/pipelines/text/generator-steps.d.ts +46 -0
- package/src/inference/pipelines/text/generator-steps.js +298 -207
- package/src/inference/pipelines/text/generator.js +6 -23
- package/src/inference/pipelines/text/init.js +78 -20
- package/src/inference/pipelines/text/kernel-path-auto-select.js +2 -0
- package/src/inference/pipelines/text/kernel-trace.d.ts +2 -0
- package/src/inference/pipelines/text/kernel-trace.js +6 -0
- package/src/inference/pipelines/text/layer.js +3 -9
- package/src/inference/pipelines/text/linear-attention.d.ts +10 -0
- package/src/inference/pipelines/text/linear-attention.js +80 -6
- package/src/inference/pipelines/text/logits/gpu.js +10 -5
- package/src/inference/pipelines/text/logits/index.js +10 -11
- package/src/inference/pipelines/text/logits/utils.d.ts +7 -0
- package/src/inference/pipelines/text/logits/utils.js +9 -0
- package/src/inference/pipelines/text/lora-apply.js +50 -32
- package/src/inference/pipelines/text/model-load.js +279 -104
- package/src/inference/pipelines/text/moe-cache.js +5 -4
- package/src/inference/pipelines/text/moe-cpu-gptoss.js +74 -69
- package/src/inference/pipelines/text/moe-cpu.js +42 -38
- package/src/inference/pipelines/text/moe-gpu.js +110 -86
- package/src/inference/pipelines/text/ops.js +90 -90
- package/src/inference/pipelines/text/probes.js +9 -9
- package/src/inference/pipelines/text/weights.js +17 -7
- package/src/inference/pipelines/text.js +13 -1
- package/src/inference/speculative.d.ts +2 -2
- package/src/inference/speculative.js +4 -18
- package/src/inference/test-harness.d.ts +1 -1
- package/src/inference/test-harness.js +15 -5
- package/src/inference/tokenizer.d.ts +0 -5
- package/src/inference/tokenizer.js +4 -23
- package/src/inference/tokenizers/bpe.js +9 -0
- package/src/inference/tokenizers/bundled.js +20 -0
- package/src/inference/tokenizers/sentencepiece.js +12 -0
- package/src/loader/doppler-loader.js +38 -22
- package/src/loader/dtype-utils.js +3 -44
- package/src/loader/embedding-loader.js +7 -3
- package/src/loader/experts/expert-cache.js +13 -6
- package/src/loader/experts/expert-loader.js +10 -6
- package/src/loader/final-weights-loader.js +8 -4
- package/src/loader/layer-loader.js +2 -1
- package/src/loader/loader-state.js +2 -2
- package/src/loader/memory-monitor.js +8 -0
- package/src/loader/multi-model-loader.d.ts +14 -0
- package/src/loader/multi-model-loader.js +70 -24
- package/src/loader/shard-cache.js +81 -12
- package/src/loader/shard-resolver.js +25 -3
- package/src/loader/tensors/tensor-loader.js +209 -144
- package/src/loader/tensors/tensor-reader.js +76 -19
- package/src/loader/weight-downcast.js +1 -1
- package/src/memory/buffer-pool.d.ts +9 -1
- package/src/memory/buffer-pool.js +109 -44
- package/src/memory/unified-detect.js +1 -1
- package/src/rules/inference/kernel-path.rules.json +24 -8
- package/src/rules/rule-registry.js +25 -1
- package/src/storage/backends/opfs-store.js +68 -24
- package/src/storage/downloader.js +364 -83
- package/src/storage/index.d.ts +3 -0
- package/src/storage/index.js +3 -0
- package/src/storage/preflight.d.ts +2 -2
- package/src/storage/preflight.js +24 -2
- package/src/storage/quickstart-downloader.js +11 -5
- package/src/storage/registry.js +10 -4
- package/src/storage/reports.js +1 -1
- package/src/storage/shard-manager.d.ts +15 -1
- package/src/storage/shard-manager.js +51 -3
- package/src/storage/source-artifact-store.d.ts +52 -0
- package/src/storage/source-artifact-store.js +234 -0
- package/src/tooling/command-api-constants.d.ts +9 -0
- package/src/tooling/command-api-constants.js +9 -0
- package/src/tooling/command-api-family-normalizers.d.ts +9 -0
- package/src/tooling/command-api-family-normalizers.js +343 -0
- package/src/tooling/command-api-helpers.d.ts +25 -0
- package/src/tooling/command-api-helpers.js +262 -0
- package/src/tooling/command-api.js +16 -602
- package/src/tooling/command-envelope.js +4 -1
- package/src/tooling/command-runner-shared.js +52 -18
- package/src/tooling/lean-execution-contract.js +150 -3
- package/src/tooling/node-browser-command-runner.js +161 -271
- package/src/tooling/node-command-runner.js +29 -3
- package/src/tooling/node-converter.js +27 -1
- package/src/tooling/node-source-runtime.d.ts +1 -1
- package/src/tooling/node-source-runtime.js +84 -3
- package/src/tooling/node-webgpu.js +24 -21
- package/src/tooling/opfs-cache.js +21 -4
- package/src/tooling/runtime-input-composition.d.ts +38 -0
- package/src/tooling/runtime-input-composition.js +86 -0
- package/src/tooling/source-runtime-bundle.d.ts +40 -5
- package/src/tooling/source-runtime-bundle.js +261 -34
- package/src/tooling/source-runtime-materializer.d.ts +6 -0
- package/src/tooling/source-runtime-materializer.js +93 -0
- package/src/training/attention-backward.js +32 -17
- package/src/training/autograd.js +80 -52
- package/src/training/checkpoint-watch.d.ts +2 -1
- package/src/training/checkpoint-watch.js +39 -6
- package/src/training/checkpoint.js +40 -11
- package/src/training/clip.js +2 -1
- package/src/training/datasets/token-batch.js +20 -8
- package/src/training/distillation/checkpoint-watch.js +1 -0
- package/src/training/distillation/student-fixture.d.ts +22 -0
- package/src/training/distillation/student-fixture.js +846 -0
- package/src/training/distillation/suite-data.d.ts +45 -0
- package/src/training/distillation/suite-data.js +189 -0
- package/src/training/lora-pipeline.js +4 -7
- package/src/training/lora.js +26 -12
- package/src/training/loss.js +5 -6
- package/src/training/objectives/cross_entropy.js +2 -5
- package/src/training/objectives/distill_kd.js +4 -8
- package/src/training/objectives/distill_triplet.js +4 -8
- package/src/training/objectives/ul_stage2_base.js +4 -8
- package/src/training/operator-command.js +2 -0
- package/src/training/optimizer.js +19 -7
- package/src/training/runner.js +2 -1
- package/src/training/suite.js +18 -978
- package/src/training/tensor-factory.d.ts +9 -0
- package/src/training/tensor-factory.js +13 -0
- package/src/training/trainer.js +3 -5
- package/src/training/ul_dataset.js +3 -5
- package/src/training/workloads.js +70 -79
- package/src/version.js +1 -1
- package/tools/convert-safetensors-node.js +22 -16
- package/tools/doppler-cli.js +44 -25
|
@@ -9,7 +9,10 @@ import {
|
|
|
9
9
|
ensureCommandSupportedOnSurface,
|
|
10
10
|
normalizeToolingCommandRequest,
|
|
11
11
|
} from './command-api.js';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
isToolingSuccessEnvelope,
|
|
14
|
+
normalizeToToolingCommandError,
|
|
15
|
+
} from './command-envelope.js';
|
|
13
16
|
|
|
14
17
|
const DEFAULT_HOST = '127.0.0.1';
|
|
15
18
|
const DEFAULT_RUNNER_PATH = '/src/tooling/command-runner.html';
|
|
@@ -359,6 +362,26 @@ function asNonEmptyString(value) {
|
|
|
359
362
|
return normalized === '' ? null : normalized;
|
|
360
363
|
}
|
|
361
364
|
|
|
365
|
+
function createPersistentContextRequiredError(requestedLoadMode, cause = null) {
|
|
366
|
+
const baseMessage = requestedLoadMode === 'opfs'
|
|
367
|
+
? 'browser command: loadMode=opfs requires persistent browser context; persistent launch failed.'
|
|
368
|
+
: 'browser command: persistent browser context is required when OPFS cache is enabled; persistent launch failed.';
|
|
369
|
+
const causeMessage = asNonEmptyString(cause?.message || cause);
|
|
370
|
+
return new Error(
|
|
371
|
+
`${baseMessage} Re-run with run.browser.opfsCache=false to use a non-persistent browser session.${causeMessage ? ` (${causeMessage})` : ''}`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export function finalizeBrowserRelayResponse(response, request) {
|
|
376
|
+
if (!isToolingSuccessEnvelope(response)) {
|
|
377
|
+
throw new Error('browser command: runner returned an invalid success envelope.');
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
...response,
|
|
381
|
+
request,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
362
385
|
function normalizeWebgpuBackend(value) {
|
|
363
386
|
const raw = asNonEmptyString(value);
|
|
364
387
|
if (!raw) return null;
|
|
@@ -570,8 +593,10 @@ async function launchPersistentBrowser(chromium, userDataDir, launchOptions, opt
|
|
|
570
593
|
|
|
571
594
|
export async function runBrowserCommandInNode(commandRequest, options = {}) {
|
|
572
595
|
let request = null;
|
|
596
|
+
let sourceRequest = null;
|
|
573
597
|
try {
|
|
574
598
|
({ request } = ensureCommandSupportedOnSurface(commandRequest, 'browser'));
|
|
599
|
+
sourceRequest = request;
|
|
575
600
|
|
|
576
601
|
if (request.keepPipeline) {
|
|
577
602
|
throw new Error(
|
|
@@ -583,321 +608,186 @@ export async function runBrowserCommandInNode(commandRequest, options = {}) {
|
|
|
583
608
|
throw new Error('browser command relay does not support convert. Use --surface node for convert commands.');
|
|
584
609
|
}
|
|
585
610
|
|
|
586
|
-
|
|
587
|
-
|
|
611
|
+
let useOpfsCache = options.opfsCache !== false;
|
|
612
|
+
let relayRequest = request;
|
|
613
|
+
const userDataDir = options.userDataDir || DEFAULT_OPFS_CACHE_DIR;
|
|
588
614
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
615
|
+
if (options.wipeCacheBeforeLaunch && useOpfsCache) {
|
|
616
|
+
await fs.rm(userDataDir, { recursive: true, force: true }).catch(() => {});
|
|
617
|
+
}
|
|
592
618
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
619
|
+
const { chromium } = await import('playwright');
|
|
620
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
621
|
+
// When OPFS caching is enabled, use a fixed port so the browser origin stays the same
|
|
622
|
+
// across runs (OPFS is origin-scoped). Without this, random ports create new origins.
|
|
623
|
+
const serverPort = options.port ?? (useOpfsCache ? DEFAULT_OPFS_CACHE_PORT : 0);
|
|
624
|
+
const server = baseUrl
|
|
625
|
+
? null
|
|
626
|
+
: await createStaticFileServer({
|
|
627
|
+
rootDir: options.staticRootDir,
|
|
628
|
+
staticMounts: options.staticMounts,
|
|
629
|
+
host: options.host,
|
|
630
|
+
port: serverPort,
|
|
631
|
+
}).catch((error) => {
|
|
632
|
+
const message = error?.message || String(error);
|
|
633
|
+
throw new Error(
|
|
634
|
+
`browser command: failed to start static server (${message}). Pass --browser-base-url to reuse an existing server.`
|
|
635
|
+
);
|
|
636
|
+
});
|
|
611
637
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
638
|
+
const launchOptions = {
|
|
639
|
+
headless: normalizeHeadless(options.headless),
|
|
640
|
+
args: browserLaunchArgs(normalizeBrowserArgs(options.browserArgs)),
|
|
641
|
+
};
|
|
616
642
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
643
|
+
if (options.channel) {
|
|
644
|
+
launchOptions.channel = String(options.channel);
|
|
645
|
+
}
|
|
646
|
+
if (options.executablePath) {
|
|
647
|
+
launchOptions.executablePath = String(options.executablePath);
|
|
648
|
+
}
|
|
623
649
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
637
|
-
}
|
|
650
|
+
const timeoutMs = normalizeTimeoutMs(options.timeoutMs);
|
|
651
|
+
const runnerPath = normalizeRunnerPath(options.runnerPath);
|
|
652
|
+
const resolvedBaseUrl = baseUrl || server.baseUrl;
|
|
653
|
+
const requestedLoadMode = sourceRequest.loadMode;
|
|
654
|
+
const requireOpfsLoad = requestedLoadMode === 'opfs';
|
|
655
|
+
if (requireOpfsLoad && useOpfsCache === false) {
|
|
656
|
+
throw new Error('browser command: loadMode=opfs requires OPFS cache support (remove --no-opfs-cache).');
|
|
657
|
+
}
|
|
658
|
+
if (requireOpfsLoad && sourceRequest.modelUrl && !sourceRequest.modelId) {
|
|
659
|
+
throw new Error(
|
|
660
|
+
'browser command: loadMode=opfs requires modelId when modelUrl is provided so the relay can verify and load the cached OPFS artifact.'
|
|
661
|
+
);
|
|
662
|
+
}
|
|
638
663
|
|
|
639
664
|
let browser = null;
|
|
640
665
|
let context = null;
|
|
641
666
|
try {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
try {
|
|
646
|
-
context = await launchPersistentBrowser(chromium, userDataDir, launchOptions, {
|
|
647
|
-
explicitChannel: Boolean(options.channel),
|
|
648
|
-
explicitExecutablePath: Boolean(options.executablePath),
|
|
649
|
-
});
|
|
650
|
-
} catch (error) {
|
|
651
|
-
if (!isRecoverablePersistentLaunchError(error)) {
|
|
652
|
-
throw error;
|
|
653
|
-
}
|
|
654
|
-
if (typeof options.onConsole === 'function') {
|
|
655
|
-
options.onConsole({
|
|
656
|
-
type: 'warning',
|
|
657
|
-
text: '[browser] Persistent browser launch failed; retrying with a clean OPFS profile.',
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
await fs.rm(userDataDir, { recursive: true, force: true }).catch(() => {});
|
|
667
|
+
if (useOpfsCache) {
|
|
668
|
+
// Persistent context: OPFS data survives between runs.
|
|
669
|
+
// launchPersistentContext returns a BrowserContext directly (no separate Browser).
|
|
661
670
|
try {
|
|
662
671
|
context = await launchPersistentBrowser(chromium, userDataDir, launchOptions, {
|
|
663
672
|
explicitChannel: Boolean(options.channel),
|
|
664
673
|
explicitExecutablePath: Boolean(options.executablePath),
|
|
665
674
|
});
|
|
666
|
-
} catch (
|
|
667
|
-
if (!isRecoverablePersistentLaunchError(
|
|
668
|
-
throw
|
|
675
|
+
} catch (error) {
|
|
676
|
+
if (!isRecoverablePersistentLaunchError(error)) {
|
|
677
|
+
throw error;
|
|
669
678
|
}
|
|
670
679
|
if (typeof options.onConsole === 'function') {
|
|
671
680
|
options.onConsole({
|
|
672
681
|
type: 'warning',
|
|
673
|
-
text: '[browser] Persistent launch
|
|
682
|
+
text: '[browser] Persistent browser launch failed; retrying with a clean OPFS profile.',
|
|
674
683
|
});
|
|
675
684
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
685
|
+
await fs.rm(userDataDir, { recursive: true, force: true }).catch(() => {});
|
|
686
|
+
try {
|
|
687
|
+
context = await launchPersistentBrowser(chromium, userDataDir, launchOptions, {
|
|
688
|
+
explicitChannel: Boolean(options.channel),
|
|
689
|
+
explicitExecutablePath: Boolean(options.executablePath),
|
|
690
|
+
});
|
|
691
|
+
} catch (retryError) {
|
|
692
|
+
if (!isRecoverablePersistentLaunchError(retryError)) {
|
|
693
|
+
throw retryError;
|
|
694
|
+
}
|
|
695
|
+
throw createPersistentContextRequiredError(requestedLoadMode, retryError);
|
|
687
696
|
}
|
|
688
|
-
browser = await launchBrowser(chromium, launchOptions, {
|
|
689
|
-
explicitChannel: Boolean(options.channel),
|
|
690
|
-
explicitExecutablePath: Boolean(options.executablePath),
|
|
691
|
-
});
|
|
692
|
-
context = await browser.newContext();
|
|
693
697
|
}
|
|
698
|
+
} else {
|
|
699
|
+
browser = await launchBrowser(chromium, launchOptions, {
|
|
700
|
+
explicitChannel: Boolean(options.channel),
|
|
701
|
+
explicitExecutablePath: Boolean(options.executablePath),
|
|
702
|
+
});
|
|
703
|
+
context = await browser.newContext();
|
|
694
704
|
}
|
|
695
|
-
} else {
|
|
696
|
-
browser = await launchBrowser(chromium, launchOptions, {
|
|
697
|
-
explicitChannel: Boolean(options.channel),
|
|
698
|
-
explicitExecutablePath: Boolean(options.executablePath),
|
|
699
|
-
});
|
|
700
|
-
context = await browser.newContext();
|
|
701
|
-
}
|
|
702
705
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
+
const page = await context.newPage();
|
|
707
|
+
page.setDefaultTimeout(timeoutMs);
|
|
708
|
+
const pageDiagnostics = [];
|
|
706
709
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
710
|
+
if (typeof options.onConsole === 'function') {
|
|
711
|
+
page.on('console', (message) => {
|
|
712
|
+
options.onConsole({
|
|
713
|
+
type: message.type(),
|
|
714
|
+
text: message.text(),
|
|
715
|
+
});
|
|
712
716
|
});
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
page.on('pageerror', (error) => {
|
|
717
|
-
pageDiagnostics.push(`pageerror: ${error?.message || String(error)}`);
|
|
718
|
-
});
|
|
719
|
-
page.on('requestfailed', (request) => {
|
|
720
|
-
const failure = request.failure();
|
|
721
|
-
pageDiagnostics.push(
|
|
722
|
-
`requestfailed: ${request.url()} (${failure?.errorText || 'unknown error'})`
|
|
723
|
-
);
|
|
724
|
-
});
|
|
717
|
+
}
|
|
725
718
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
719
|
+
page.on('pageerror', (error) => {
|
|
720
|
+
pageDiagnostics.push(`pageerror: ${error?.message || String(error)}`);
|
|
721
|
+
});
|
|
722
|
+
page.on('requestfailed', (request) => {
|
|
723
|
+
const failure = request.failure();
|
|
724
|
+
pageDiagnostics.push(
|
|
725
|
+
`requestfailed: ${request.url()} (${failure?.errorText || 'unknown error'})`
|
|
726
|
+
);
|
|
732
727
|
});
|
|
733
|
-
} catch (error) {
|
|
734
|
-
const diagnostics = pageDiagnostics.length
|
|
735
|
-
? pageDiagnostics.slice(0, 10).join(' | ')
|
|
736
|
-
: 'no page diagnostics captured';
|
|
737
|
-
throw new Error(
|
|
738
|
-
`browser command: runner did not become ready within ${timeoutMs}ms (${diagnostics}).`
|
|
739
|
-
);
|
|
740
|
-
}
|
|
741
728
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
729
|
+
const runnerUrl = new URL(runnerPath, resolvedBaseUrl);
|
|
730
|
+
runnerUrl.searchParams.set('_dopplerRunner', String(Date.now()));
|
|
731
|
+
await page.goto(runnerUrl.toString(), { waitUntil: 'load' });
|
|
745
732
|
try {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
return { cached: false, error: '__dopplerEnsureCached not available' };
|
|
749
|
-
}
|
|
750
|
-
return globalThis.__dopplerEnsureCached(payload.modelId, payload.modelBaseUrl);
|
|
751
|
-
}, {
|
|
752
|
-
modelId: request.modelId,
|
|
753
|
-
modelBaseUrl: request.modelUrl,
|
|
733
|
+
await page.waitForFunction(() => globalThis.__dopplerRunnerReady === true, null, {
|
|
734
|
+
timeout: timeoutMs,
|
|
754
735
|
});
|
|
736
|
+
} catch (error) {
|
|
737
|
+
const diagnostics = pageDiagnostics.length
|
|
738
|
+
? pageDiagnostics.slice(0, 10).join(' | ')
|
|
739
|
+
: 'no page diagnostics captured';
|
|
740
|
+
throw new Error(
|
|
741
|
+
`browser command: runner did not become ready within ${timeoutMs}ms (${diagnostics}).`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Explicit loadMode=opfs must be satisfied without rewriting the shared request contract.
|
|
746
|
+
if (useOpfsCache && requireOpfsLoad && relayRequest.modelId && relayRequest.modelUrl) {
|
|
747
|
+
try {
|
|
748
|
+
const cacheResult = await page.evaluate(async (payload) => {
|
|
749
|
+
if (typeof globalThis.__dopplerEnsureCached !== 'function') {
|
|
750
|
+
return { cached: false, error: '__dopplerEnsureCached not available' };
|
|
751
|
+
}
|
|
752
|
+
return globalThis.__dopplerEnsureCached(payload.modelId, payload.modelBaseUrl);
|
|
753
|
+
}, {
|
|
754
|
+
modelId: relayRequest.modelId,
|
|
755
|
+
modelBaseUrl: relayRequest.modelUrl,
|
|
756
|
+
});
|
|
755
757
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
request.loadMode = 'opfs';
|
|
761
|
-
} else {
|
|
762
|
-
if (requireOpfsLoad) {
|
|
758
|
+
if (cacheResult.cached) {
|
|
759
|
+
relayRequest = { ...relayRequest };
|
|
760
|
+
delete relayRequest.modelUrl;
|
|
761
|
+
} else {
|
|
763
762
|
const cacheError = cacheResult?.error || 'model not cached';
|
|
764
763
|
throw new Error(
|
|
765
|
-
`[opfs-cache]
|
|
764
|
+
`[opfs-cache] model cache is unavailable for "${relayRequest.modelId || 'unknown-model'}": ${cacheError}.`
|
|
766
765
|
);
|
|
767
766
|
}
|
|
768
|
-
|
|
769
|
-
request = { ...request, loadMode: 'http' };
|
|
770
|
-
}
|
|
771
|
-
if (cacheResult.error) {
|
|
772
|
-
if (typeof options.onConsole === 'function') {
|
|
773
|
-
options.onConsole({
|
|
774
|
-
type: 'warning',
|
|
775
|
-
text: `[opfs-cache] Cache check failed (${cacheResult.error}), falling back to HTTP`,
|
|
776
|
-
});
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
} catch (error) {
|
|
781
|
-
if (requireOpfsLoad) {
|
|
767
|
+
} catch (error) {
|
|
782
768
|
throw new Error(
|
|
783
|
-
`[opfs-cache]
|
|
769
|
+
`[opfs-cache] cache priming failed: ${error?.message || error}.`
|
|
784
770
|
);
|
|
785
771
|
}
|
|
786
|
-
if (!requestedLoadMode && request.modelUrl) {
|
|
787
|
-
request = { ...request, loadMode: 'http' };
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
// OPFS cache is best-effort; fall back to HTTP on any error.
|
|
791
|
-
if (typeof options.onConsole === 'function') {
|
|
792
|
-
options.onConsole({
|
|
793
|
-
type: 'warning',
|
|
794
|
-
text: `[opfs-cache] Error (${error?.message || error}), falling back to HTTP`,
|
|
795
|
-
});
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
const response = await page.evaluate(async (payload) => {
|
|
801
|
-
if (typeof globalThis.__dopplerRunBrowserCommand !== 'function') {
|
|
802
|
-
throw new Error('browser command runner is missing globalThis.__dopplerRunBrowserCommand');
|
|
803
772
|
}
|
|
804
|
-
return globalThis.__dopplerRunBrowserCommand(payload.request, payload.options || {});
|
|
805
|
-
}, {
|
|
806
|
-
request,
|
|
807
|
-
options: {
|
|
808
|
-
runtimeLoadOptions: options.runtimeLoadOptions || {},
|
|
809
|
-
},
|
|
810
|
-
});
|
|
811
773
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
};
|
|
822
|
-
const webgpuBackend = inferWebgpuBackendFromArgs(launchOptions.args, hostEnvironment.platform);
|
|
823
|
-
const env = result.env && typeof result.env === 'object' ? result.env : {};
|
|
824
|
-
const deviceInfo = result.deviceInfo && typeof result.deviceInfo === 'object'
|
|
825
|
-
? result.deviceInfo
|
|
826
|
-
: {};
|
|
827
|
-
result.env = {
|
|
828
|
-
...env,
|
|
829
|
-
webgpuBackend: normalizeWebgpuBackend(env.webgpuBackend)
|
|
830
|
-
|| normalizeWebgpuBackend(env.gpuBackend)
|
|
831
|
-
|| normalizeWebgpuBackend(env.graphicsBackend)
|
|
832
|
-
|| webgpuBackend,
|
|
833
|
-
};
|
|
834
|
-
const existingEnvironment = result.environment && typeof result.environment === 'object'
|
|
835
|
-
? result.environment
|
|
836
|
-
: {};
|
|
837
|
-
result.environment = {
|
|
838
|
-
...existingEnvironment,
|
|
839
|
-
host: {
|
|
840
|
-
...(existingEnvironment.host && typeof existingEnvironment.host === 'object' ? existingEnvironment.host : {}),
|
|
841
|
-
platform: asNonEmptyString(existingEnvironment?.host?.platform) || hostEnvironment.platform,
|
|
842
|
-
arch: asNonEmptyString(existingEnvironment?.host?.arch) || hostEnvironment.arch,
|
|
843
|
-
nodeVersion: asNonEmptyString(existingEnvironment?.host?.nodeVersion) || hostEnvironment.nodeVersion,
|
|
844
|
-
osRelease: asNonEmptyString(existingEnvironment?.host?.osRelease) || hostEnvironment.osRelease,
|
|
845
|
-
cpuModel: asNonEmptyString(existingEnvironment?.host?.cpuModel) || hostEnvironment.cpuModel,
|
|
846
|
-
},
|
|
847
|
-
browser: {
|
|
848
|
-
...(existingEnvironment.browser && typeof existingEnvironment.browser === 'object' ? existingEnvironment.browser : {}),
|
|
849
|
-
userAgent: asNonEmptyString(existingEnvironment?.browser?.userAgent) || asNonEmptyString(env.browserUserAgent),
|
|
850
|
-
platform: asNonEmptyString(existingEnvironment?.browser?.platform) || asNonEmptyString(env.browserPlatform),
|
|
851
|
-
language: asNonEmptyString(existingEnvironment?.browser?.language) || asNonEmptyString(env.browserLanguage),
|
|
852
|
-
vendor: asNonEmptyString(existingEnvironment?.browser?.vendor) || asNonEmptyString(env.browserVendor),
|
|
853
|
-
executable: asNonEmptyString(existingEnvironment?.browser?.executable) || asNonEmptyString(options.executablePath),
|
|
854
|
-
channel: asNonEmptyString(existingEnvironment?.browser?.channel) || asNonEmptyString(options.channel),
|
|
855
|
-
},
|
|
856
|
-
gpu: {
|
|
857
|
-
...(existingEnvironment.gpu && typeof existingEnvironment.gpu === 'object' ? existingEnvironment.gpu : {}),
|
|
858
|
-
api: asNonEmptyString(existingEnvironment?.gpu?.api) || 'webgpu',
|
|
859
|
-
backend: normalizeWebgpuBackend(existingEnvironment?.gpu?.backend)
|
|
860
|
-
|| normalizeWebgpuBackend(env.webgpuBackend)
|
|
861
|
-
|| webgpuBackend,
|
|
862
|
-
vendor: asNonEmptyString(existingEnvironment?.gpu?.vendor) || asNonEmptyString(deviceInfo.vendor),
|
|
863
|
-
architecture: asNonEmptyString(existingEnvironment?.gpu?.architecture) || asNonEmptyString(deviceInfo.architecture),
|
|
864
|
-
device: asNonEmptyString(existingEnvironment?.gpu?.device) || asNonEmptyString(deviceInfo.device),
|
|
865
|
-
description: asNonEmptyString(existingEnvironment?.gpu?.description) || asNonEmptyString(deviceInfo.description),
|
|
866
|
-
hasF16: typeof existingEnvironment?.gpu?.hasF16 === 'boolean'
|
|
867
|
-
? existingEnvironment.gpu.hasF16
|
|
868
|
-
: (typeof deviceInfo.hasF16 === 'boolean' ? deviceInfo.hasF16 : null),
|
|
869
|
-
hasSubgroups: typeof existingEnvironment?.gpu?.hasSubgroups === 'boolean'
|
|
870
|
-
? existingEnvironment.gpu.hasSubgroups
|
|
871
|
-
: (typeof deviceInfo.hasSubgroups === 'boolean' ? deviceInfo.hasSubgroups : null),
|
|
872
|
-
hasTimestampQuery: typeof existingEnvironment?.gpu?.hasTimestampQuery === 'boolean'
|
|
873
|
-
? existingEnvironment.gpu.hasTimestampQuery
|
|
874
|
-
: (typeof deviceInfo.hasTimestampQuery === 'boolean' ? deviceInfo.hasTimestampQuery : null),
|
|
875
|
-
},
|
|
876
|
-
runtime: {
|
|
877
|
-
...(existingEnvironment.runtime && typeof existingEnvironment.runtime === 'object' ? existingEnvironment.runtime : {}),
|
|
878
|
-
library: asNonEmptyString(existingEnvironment?.runtime?.library) || asNonEmptyString(env.library) || 'doppler',
|
|
879
|
-
version: asNonEmptyString(existingEnvironment?.runtime?.version) || asNonEmptyString(env.version),
|
|
880
|
-
surface: asNonEmptyString(existingEnvironment?.runtime?.surface) || asNonEmptyString(env.runtime) || 'browser',
|
|
881
|
-
device: asNonEmptyString(existingEnvironment?.runtime?.device) || asNonEmptyString(env.device),
|
|
882
|
-
dtype: asNonEmptyString(existingEnvironment?.runtime?.dtype) || asNonEmptyString(env.dtype),
|
|
883
|
-
requestedDtype: asNonEmptyString(existingEnvironment?.runtime?.requestedDtype) || asNonEmptyString(env.requestedDtype),
|
|
884
|
-
executionProviderMode: asNonEmptyString(existingEnvironment?.runtime?.executionProviderMode)
|
|
885
|
-
|| asNonEmptyString(env.executionProviderMode),
|
|
886
|
-
cacheMode: asNonEmptyString(existingEnvironment?.runtime?.cacheMode)
|
|
887
|
-
|| asNonEmptyString(result.cacheMode)
|
|
888
|
-
|| asNonEmptyString(result?.timing?.cacheMode),
|
|
889
|
-
loadMode: asNonEmptyString(existingEnvironment?.runtime?.loadMode)
|
|
890
|
-
|| asNonEmptyString(result.loadMode)
|
|
891
|
-
|| asNonEmptyString(result?.timing?.loadMode),
|
|
774
|
+
const response = await page.evaluate(async (payload) => {
|
|
775
|
+
if (typeof globalThis.__dopplerRunBrowserCommand !== 'function') {
|
|
776
|
+
throw new Error('browser command runner is missing globalThis.__dopplerRunBrowserCommand');
|
|
777
|
+
}
|
|
778
|
+
return globalThis.__dopplerRunBrowserCommand(payload.request, payload.options || {});
|
|
779
|
+
}, {
|
|
780
|
+
request: relayRequest,
|
|
781
|
+
options: {
|
|
782
|
+
runtimeLoadOptions: options.runtimeLoadOptions || {},
|
|
892
783
|
},
|
|
893
|
-
};
|
|
894
|
-
}
|
|
784
|
+
});
|
|
895
785
|
|
|
896
|
-
return response;
|
|
786
|
+
return finalizeBrowserRelayResponse(response, sourceRequest);
|
|
897
787
|
} catch (error) {
|
|
898
788
|
throw normalizeToToolingCommandError(error, {
|
|
899
789
|
surface: 'browser',
|
|
900
|
-
request,
|
|
790
|
+
request: sourceRequest,
|
|
901
791
|
});
|
|
902
792
|
} finally {
|
|
903
793
|
if (context) {
|
|
@@ -913,7 +803,7 @@ export async function runBrowserCommandInNode(commandRequest, options = {}) {
|
|
|
913
803
|
} catch (error) {
|
|
914
804
|
throw normalizeToToolingCommandError(error, {
|
|
915
805
|
surface: 'browser',
|
|
916
|
-
request,
|
|
806
|
+
request: sourceRequest,
|
|
917
807
|
});
|
|
918
808
|
}
|
|
919
809
|
}
|
|
@@ -28,6 +28,28 @@ function asOptionalPlainObject(value, label) {
|
|
|
28
28
|
return value;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function assertNoUnsupportedRuntimeInputs(request) {
|
|
32
|
+
const runtimeFields = [];
|
|
33
|
+
if (Array.isArray(request?.configChain) && request.configChain.length > 0) {
|
|
34
|
+
runtimeFields.push('configChain');
|
|
35
|
+
}
|
|
36
|
+
if (typeof request?.runtimePreset === 'string' && request.runtimePreset.trim()) {
|
|
37
|
+
runtimeFields.push('runtimePreset');
|
|
38
|
+
}
|
|
39
|
+
if (typeof request?.runtimeConfigUrl === 'string' && request.runtimeConfigUrl.trim()) {
|
|
40
|
+
runtimeFields.push('runtimeConfigUrl');
|
|
41
|
+
}
|
|
42
|
+
if (request?.runtimeConfig != null) {
|
|
43
|
+
runtimeFields.push('runtimeConfig');
|
|
44
|
+
}
|
|
45
|
+
if (runtimeFields.length > 0) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`${request.command} does not support runtime input fields on the node operator surface: ` +
|
|
48
|
+
`${runtimeFields.join(', ')}. Put those settings into the workload/config asset instead.`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
31
53
|
let runtimeModulesPromise = null;
|
|
32
54
|
|
|
33
55
|
async function loadRuntimeModules() {
|
|
@@ -51,16 +73,19 @@ export function hasNodeWebGPUSupport() {
|
|
|
51
73
|
}
|
|
52
74
|
|
|
53
75
|
async function assertNodeWebGPUSupport() {
|
|
76
|
+
let bootstrapProvider = null;
|
|
54
77
|
if (!hasNodeWebGPUSupport()) {
|
|
55
78
|
const bootstrap = await bootstrapNodeWebGPU();
|
|
56
|
-
if (bootstrap.
|
|
57
|
-
|
|
79
|
+
if (bootstrap.provider) {
|
|
80
|
+
bootstrapProvider = bootstrap.provider;
|
|
58
81
|
}
|
|
59
82
|
}
|
|
60
83
|
|
|
61
84
|
if (hasNodeWebGPUSupport()) return;
|
|
62
85
|
throw new Error(
|
|
63
|
-
'node command: WebGPU runtime is incomplete in Node.
|
|
86
|
+
'node command: WebGPU runtime is incomplete in Node.' +
|
|
87
|
+
(bootstrapProvider ? ` Provider resolution stopped at "${bootstrapProvider}".` : '') +
|
|
88
|
+
' Run in browser relay, or run under a WebGPU-enabled Node build.'
|
|
64
89
|
);
|
|
65
90
|
}
|
|
66
91
|
|
|
@@ -94,6 +119,7 @@ export async function runNodeCommand(commandRequest, options = {}) {
|
|
|
94
119
|
if (request.command === 'lora' || request.command === 'distill') {
|
|
95
120
|
const gpuOptionalActions = new Set(['compare', 'quality-gate', 'subsets']);
|
|
96
121
|
installNodeFileFetchShim();
|
|
122
|
+
assertNoUnsupportedRuntimeInputs(request);
|
|
97
123
|
if (!gpuOptionalActions.has(request.action)) {
|
|
98
124
|
await assertNodeWebGPUSupport();
|
|
99
125
|
}
|
|
@@ -52,6 +52,7 @@ function normalizeExecutionConfig(value, defaults) {
|
|
|
52
52
|
const useGpuCast = value.useGpuCast == null
|
|
53
53
|
? defaults.useGpuCast === true
|
|
54
54
|
: value.useGpuCast === true;
|
|
55
|
+
const gpuCastRequestedExplicitly = value.useGpuCast === true;
|
|
55
56
|
if (value.useGpuCast != null && typeof value.useGpuCast !== 'boolean') {
|
|
56
57
|
throw new Error('node convert: execution.useGpuCast must be a boolean when provided.');
|
|
57
58
|
}
|
|
@@ -69,6 +70,7 @@ function normalizeExecutionConfig(value, defaults) {
|
|
|
69
70
|
rowChunkMinTensorBytes,
|
|
70
71
|
maxInFlightJobs,
|
|
71
72
|
useGpuCast,
|
|
73
|
+
gpuCastRequestedExplicitly,
|
|
72
74
|
gpuCastMinTensorBytes,
|
|
73
75
|
};
|
|
74
76
|
}
|
|
@@ -222,6 +224,7 @@ function createNodeGpuTensorTransformer(options) {
|
|
|
222
224
|
const {
|
|
223
225
|
runtime,
|
|
224
226
|
gpuCastMinTensorBytes,
|
|
227
|
+
requireGpuCast,
|
|
225
228
|
resolveTensorTargetQuant,
|
|
226
229
|
} = options;
|
|
227
230
|
const {
|
|
@@ -276,6 +279,11 @@ function createNodeGpuTensorTransformer(options) {
|
|
|
276
279
|
try {
|
|
277
280
|
const device = getDevice();
|
|
278
281
|
if (!device) {
|
|
282
|
+
if (requireGpuCast) {
|
|
283
|
+
throw new Error(
|
|
284
|
+
`node convert: execution.useGpuCast failed for tensor "${tensor.name}": GPU device is unavailable.`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
279
287
|
return null;
|
|
280
288
|
}
|
|
281
289
|
inputBuffer = acquireBuffer(tensorData.byteLength, undefined, `convert_gpu_cast_in_${tensor.name}`);
|
|
@@ -292,6 +300,11 @@ function createNodeGpuTensorTransformer(options) {
|
|
|
292
300
|
|
|
293
301
|
const readback = await getBufferPool().readBuffer(outputBuffer, outputBytes);
|
|
294
302
|
if (!(readback instanceof ArrayBuffer) || readback.byteLength !== outputBytes) {
|
|
303
|
+
if (requireGpuCast) {
|
|
304
|
+
throw new Error(
|
|
305
|
+
`node convert: execution.useGpuCast failed for tensor "${tensor.name}": invalid GPU readback.`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
295
308
|
return null;
|
|
296
309
|
}
|
|
297
310
|
reportProgress?.(tensorData.byteLength, tensorData.byteLength);
|
|
@@ -301,6 +314,10 @@ function createNodeGpuTensorTransformer(options) {
|
|
|
301
314
|
outLayout: null,
|
|
302
315
|
};
|
|
303
316
|
} catch (error) {
|
|
317
|
+
if (requireGpuCast) {
|
|
318
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
319
|
+
throw new Error(`node convert: execution.useGpuCast failed for tensor "${tensor.name}": ${message}`);
|
|
320
|
+
}
|
|
304
321
|
if (!warnedFallback) {
|
|
305
322
|
warnedFallback = true;
|
|
306
323
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1168,10 +1185,19 @@ export async function convertSafetensorsDirectory(options) {
|
|
|
1168
1185
|
let result = null;
|
|
1169
1186
|
try {
|
|
1170
1187
|
if (executionPlan.useGpuCast) {
|
|
1171
|
-
|
|
1188
|
+
let gpuRuntime;
|
|
1189
|
+
try {
|
|
1190
|
+
gpuRuntime = await loadNodeGpuCastRuntime();
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1193
|
+
throw new Error(
|
|
1194
|
+
`node convert: execution.useGpuCast requires a WebGPU-capable Node runtime. ${message}`
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1172
1197
|
gpuTensorTransformer = createNodeGpuTensorTransformer({
|
|
1173
1198
|
runtime: gpuRuntime,
|
|
1174
1199
|
gpuCastMinTensorBytes: executionPlan.gpuCastMinTensorBytes,
|
|
1200
|
+
requireGpuCast: executionPlan.gpuCastRequestedExplicitly === true,
|
|
1175
1201
|
resolveTensorTargetQuant,
|
|
1176
1202
|
});
|
|
1177
1203
|
}
|
|
@@ -4,6 +4,7 @@ import type { PipelineStorageContext } from '../inference/pipelines/text/init.js
|
|
|
4
4
|
export interface ResolveNodeSourceRuntimeBundleOptions {
|
|
5
5
|
inputPath: string;
|
|
6
6
|
modelId?: string | null;
|
|
7
|
+
verifyHashes?: boolean;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export interface NodeSourceRuntimeBundle {
|
|
@@ -16,4 +17,3 @@ export interface NodeSourceRuntimeBundle {
|
|
|
16
17
|
export declare function resolveNodeSourceRuntimeBundle(
|
|
17
18
|
options: ResolveNodeSourceRuntimeBundleOptions
|
|
18
19
|
): Promise<NodeSourceRuntimeBundle | null>;
|
|
19
|
-
|