@simulatte/doppler 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +25 -17
  3. package/package.json +20 -4
  4. package/src/adapters/adapter-registry.js +12 -1
  5. package/src/adapters/lora-loader.js +23 -6
  6. package/src/bridge/extension-client.d.ts +5 -0
  7. package/src/bridge/extension-client.js +40 -0
  8. package/src/bridge/index.d.ts +2 -1
  9. package/src/bridge/index.js +6 -4
  10. package/src/browser/browser-converter.js +26 -1
  11. package/src/browser/file-picker.js +6 -0
  12. package/src/browser/safetensors-parser-browser.js +84 -1
  13. package/src/browser/shard-io-browser.js +2 -2
  14. package/src/browser/tensor-source-download.js +8 -2
  15. package/src/browser/tensor-source-http.d.ts +1 -0
  16. package/src/browser/tensor-source-http.js +5 -1
  17. package/src/client/doppler-api.browser.js +20 -4
  18. package/src/client/doppler-api.js +19 -3
  19. package/src/client/doppler-provider/generation.js +12 -0
  20. package/src/client/doppler-provider/model-manager.d.ts +10 -0
  21. package/src/client/doppler-provider/model-manager.js +91 -19
  22. package/src/client/doppler-provider/source-runtime.d.ts +2 -1
  23. package/src/client/doppler-provider/source-runtime.js +132 -13
  24. package/src/client/doppler-registry.json +8 -7
  25. package/src/config/backward-registry-loader.js +17 -2
  26. package/src/config/execution-v0-contract-check.js +113 -15
  27. package/src/config/kernel-path-contract-check.js +57 -29
  28. package/src/config/kernel-path-loader.js +5 -36
  29. package/src/config/kernels/kernel-ref-digests.js +39 -39
  30. package/src/config/kernels/registry.js +14 -1
  31. package/src/config/kernels/registry.json +49 -7
  32. package/src/config/loader.d.ts +1 -1
  33. package/src/config/loader.js +43 -4
  34. package/src/config/merge-contract-check.js +59 -4
  35. package/src/config/merge-helpers.js +128 -7
  36. package/src/config/merge.d.ts +1 -0
  37. package/src/config/merge.js +28 -0
  38. package/src/config/param-validator.js +47 -2
  39. package/src/config/presets/kernel-paths/{gemma2-q4k-dequant-f32a.json → gemma2-q4k-dequant-f32a-nosubgroups.json} +3 -3
  40. package/src/config/presets/kernel-paths/gemma3-f16-fused-f32a-online-streamingprefill.json +223 -0
  41. package/src/config/presets/kernel-paths/{gemma3-q4k-dequant-f32a.json → gemma3-q4k-dequant-f32a-nosubgroups.json} +3 -3
  42. package/src/config/presets/kernel-paths/registry.json +29 -8
  43. package/src/config/presets/models/gemma2.json +2 -2
  44. package/src/config/presets/models/qwen3.json +9 -2
  45. package/src/config/presets/models/transformer.json +5 -0
  46. package/src/config/presets/runtime/experiments/bench/gemma3-bench-q4k.json +1 -1
  47. package/src/config/presets/runtime/experiments/debug/gemma3-debug-q4k.json +1 -1
  48. package/src/config/presets/runtime/experiments/verify/gemma3-verify.json +1 -1
  49. package/src/config/presets/runtime/kernels/dequant-f16-q4k.json +6 -13
  50. package/src/config/presets/runtime/kernels/dequant-f32-q4k.json +6 -13
  51. package/src/config/presets/runtime/kernels/embeddinggemma-q4k-dequant-f32a.json +37 -0
  52. package/src/config/presets/runtime/kernels/fused-q4k.json +6 -13
  53. package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f16a.json +33 -0
  54. package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f32a-nosubgroups.json +33 -0
  55. package/src/config/presets/runtime/kernels/gemma2-q4k-fused-f32a.json +33 -0
  56. package/src/config/presets/runtime/kernels/safe-q4k.json +6 -13
  57. package/src/config/presets/runtime/platform/metal-apple-q4k.json +1 -1
  58. package/src/config/required-inference-fields-contract-check.js +6 -0
  59. package/src/config/runtime.js +6 -1
  60. package/src/config/schema/debug.schema.d.ts +5 -0
  61. package/src/config/schema/doppler.schema.js +16 -21
  62. package/src/config/schema/inference-defaults.schema.js +6 -3
  63. package/src/config/schema/inference.schema.d.ts +9 -0
  64. package/src/config/schema/kernel-path.schema.d.ts +11 -1
  65. package/src/config/schema/kernel-thresholds.schema.js +12 -4
  66. package/src/config/schema/manifest.schema.d.ts +8 -1
  67. package/src/config/schema/manifest.schema.js +19 -3
  68. package/src/config/training-defaults.js +30 -22
  69. package/src/converter/conversion-plan.js +94 -9
  70. package/src/converter/core.d.ts +7 -0
  71. package/src/converter/core.js +14 -9
  72. package/src/converter/execution-v0-manifest.js +4 -1
  73. package/src/converter/index.d.ts +1 -0
  74. package/src/converter/index.js +1 -0
  75. package/src/converter/manifest-inference.js +43 -12
  76. package/src/converter/parsers/diffusion.js +0 -3
  77. package/src/converter/quantization-info.js +35 -15
  78. package/src/converter/rope-config.js +42 -0
  79. package/src/converter/shard-packer.d.ts +1 -1
  80. package/src/converter/shard-packer.js +4 -1
  81. package/src/debug/config.js +123 -11
  82. package/src/debug/signals.js +7 -1
  83. package/src/debug/tensor.d.ts +2 -0
  84. package/src/debug/tensor.js +13 -2
  85. package/src/distribution/p2p-control-plane.js +52 -12
  86. package/src/distribution/p2p-observability.js +43 -7
  87. package/src/distribution/p2p-webrtc-browser.js +20 -0
  88. package/src/distribution/shard-delivery.js +77 -26
  89. package/src/formats/gguf/types.js +33 -16
  90. package/src/formats/rdrr/groups.d.ts +12 -4
  91. package/src/formats/rdrr/groups.js +3 -6
  92. package/src/formats/rdrr/parsing.js +39 -2
  93. package/src/formats/rdrr/types.d.ts +2 -1
  94. package/src/gpu/command-recorder.js +86 -61
  95. package/src/gpu/device.d.ts +1 -0
  96. package/src/gpu/device.js +131 -19
  97. package/src/gpu/kernel-tuner/benchmarks.js +326 -316
  98. package/src/gpu/kernel-tuner/cache.js +71 -4
  99. package/src/gpu/kernel-tuner/tuner.js +22 -4
  100. package/src/gpu/kernels/attention.js +113 -34
  101. package/src/gpu/kernels/backward/adam.js +62 -58
  102. package/src/gpu/kernels/backward/attention_backward.js +257 -169
  103. package/src/gpu/kernels/backward/conv2d_backward.js +14 -1
  104. package/src/gpu/kernels/bias_add.wgsl +8 -6
  105. package/src/gpu/kernels/bias_add_f16.wgsl +8 -5
  106. package/src/gpu/kernels/cast.js +191 -149
  107. package/src/gpu/kernels/check-stop.js +33 -44
  108. package/src/gpu/kernels/conv2d.js +27 -17
  109. package/src/gpu/kernels/conv2d.wgsl +7 -8
  110. package/src/gpu/kernels/conv2d_f16.wgsl +7 -8
  111. package/src/gpu/kernels/cross_entropy_loss.js +21 -15
  112. package/src/gpu/kernels/depthwise_conv2d.js +37 -26
  113. package/src/gpu/kernels/depthwise_conv2d.wgsl +6 -9
  114. package/src/gpu/kernels/depthwise_conv2d_f16.wgsl +6 -9
  115. package/src/gpu/kernels/dequant.js +178 -126
  116. package/src/gpu/kernels/energy.d.ts +3 -21
  117. package/src/gpu/kernels/energy.js +111 -88
  118. package/src/gpu/kernels/feature-check.js +1 -1
  119. package/src/gpu/kernels/fused_ffn.js +84 -65
  120. package/src/gpu/kernels/fused_matmul_residual.js +56 -33
  121. package/src/gpu/kernels/fused_matmul_rmsnorm.js +62 -45
  122. package/src/gpu/kernels/gather.js +33 -15
  123. package/src/gpu/kernels/gelu.js +19 -11
  124. package/src/gpu/kernels/grouped_pointwise_conv2d.js +34 -23
  125. package/src/gpu/kernels/grouped_pointwise_conv2d.wgsl +6 -9
  126. package/src/gpu/kernels/grouped_pointwise_conv2d_f16.wgsl +6 -9
  127. package/src/gpu/kernels/groupnorm.js +34 -23
  128. package/src/gpu/kernels/kv-quantize.js +5 -2
  129. package/src/gpu/kernels/layernorm.js +35 -19
  130. package/src/gpu/kernels/logit-merge.js +5 -3
  131. package/src/gpu/kernels/matmul.js +83 -39
  132. package/src/gpu/kernels/modulate.js +23 -15
  133. package/src/gpu/kernels/moe.js +221 -175
  134. package/src/gpu/kernels/pixel_shuffle.js +22 -14
  135. package/src/gpu/kernels/pixel_shuffle.wgsl +4 -5
  136. package/src/gpu/kernels/pixel_shuffle_f16.wgsl +4 -5
  137. package/src/gpu/kernels/relu.js +31 -10
  138. package/src/gpu/kernels/relu.wgsl +2 -1
  139. package/src/gpu/kernels/relu_f16.wgsl +2 -1
  140. package/src/gpu/kernels/repeat_channels.js +25 -17
  141. package/src/gpu/kernels/repeat_channels.wgsl +4 -5
  142. package/src/gpu/kernels/repeat_channels_f16.wgsl +4 -5
  143. package/src/gpu/kernels/residual.js +69 -23
  144. package/src/gpu/kernels/residual.wgsl +6 -3
  145. package/src/gpu/kernels/residual_f16.wgsl +2 -1
  146. package/src/gpu/kernels/residual_f16_vec4.wgsl +2 -1
  147. package/src/gpu/kernels/residual_vec4.wgsl +2 -1
  148. package/src/gpu/kernels/rmsnorm.js +96 -28
  149. package/src/gpu/kernels/rmsnorm.wgsl +14 -6
  150. package/src/gpu/kernels/rmsnorm_f16.wgsl +10 -2
  151. package/src/gpu/kernels/rope.d.ts +2 -0
  152. package/src/gpu/kernels/rope.js +14 -1
  153. package/src/gpu/kernels/rope.wgsl +56 -40
  154. package/src/gpu/kernels/sample.js +27 -38
  155. package/src/gpu/kernels/sana_linear_attention.js +19 -12
  156. package/src/gpu/kernels/sana_linear_attention_apply.wgsl +4 -5
  157. package/src/gpu/kernels/sana_linear_attention_apply_f16.wgsl +4 -5
  158. package/src/gpu/kernels/sana_linear_attention_summary.wgsl +4 -0
  159. package/src/gpu/kernels/sana_linear_attention_summary_f16.wgsl +4 -0
  160. package/src/gpu/kernels/scale.js +18 -11
  161. package/src/gpu/kernels/shader-cache.js +4 -2
  162. package/src/gpu/kernels/silu.d.ts +1 -0
  163. package/src/gpu/kernels/silu.js +148 -82
  164. package/src/gpu/kernels/silu.wgsl +19 -9
  165. package/src/gpu/kernels/silu_f16.wgsl +19 -9
  166. package/src/gpu/kernels/softmax.js +44 -25
  167. package/src/gpu/kernels/split_qkv.js +23 -13
  168. package/src/gpu/kernels/transpose.js +31 -10
  169. package/src/gpu/kernels/transpose.wgsl +6 -5
  170. package/src/gpu/kernels/upsample2d.js +22 -13
  171. package/src/gpu/kernels/upsample2d.wgsl +6 -9
  172. package/src/gpu/kernels/upsample2d_f16.wgsl +6 -9
  173. package/src/gpu/kernels/utils.js +35 -13
  174. package/src/gpu/partitioned-buffer-pool.js +10 -2
  175. package/src/gpu/perf-guards.js +2 -9
  176. package/src/gpu/profiler.js +27 -22
  177. package/src/gpu/readback-utils.d.ts +16 -0
  178. package/src/gpu/readback-utils.js +41 -0
  179. package/src/gpu/submit-tracker.js +13 -0
  180. package/src/gpu/uniform-cache.d.ts +1 -0
  181. package/src/gpu/uniform-cache.js +30 -9
  182. package/src/hotswap/intent-bundle.js +6 -0
  183. package/src/hotswap/manifest.d.ts +10 -1
  184. package/src/hotswap/manifest.js +12 -2
  185. package/src/hotswap/runtime.js +30 -8
  186. package/src/index-browser.d.ts +44 -0
  187. package/src/index-browser.js +14 -0
  188. package/src/inference/browser-harness-contract-helpers.d.ts +5 -0
  189. package/src/inference/browser-harness-contract-helpers.js +28 -0
  190. package/src/inference/browser-harness-diffusion-energy-suites.d.ts +2 -0
  191. package/src/inference/browser-harness-diffusion-energy-suites.js +269 -0
  192. package/src/inference/browser-harness-model-helpers.d.ts +16 -0
  193. package/src/inference/browser-harness-model-helpers.js +217 -0
  194. package/src/inference/browser-harness-report-helpers.d.ts +7 -0
  195. package/src/inference/browser-harness-report-helpers.js +42 -0
  196. package/src/inference/browser-harness-runtime-helpers.d.ts +61 -0
  197. package/src/inference/browser-harness-runtime-helpers.js +415 -0
  198. package/src/inference/browser-harness-suite-helpers.d.ts +28 -0
  199. package/src/inference/browser-harness-suite-helpers.js +268 -0
  200. package/src/inference/browser-harness-text-helpers.d.ts +27 -0
  201. package/src/inference/browser-harness-text-helpers.js +788 -0
  202. package/src/inference/browser-harness.d.ts +6 -0
  203. package/src/inference/browser-harness.js +130 -1950
  204. package/src/inference/kv-cache/base.js +140 -94
  205. package/src/inference/kv-cache/tiered.js +5 -3
  206. package/src/inference/moe-router.js +88 -56
  207. package/src/inference/multi-model-network.js +5 -3
  208. package/src/inference/network-evolution.d.ts +11 -2
  209. package/src/inference/network-evolution.js +20 -21
  210. package/src/inference/pipelines/context.d.ts +3 -0
  211. package/src/inference/pipelines/context.js +142 -2
  212. package/src/inference/pipelines/diffusion/helpers.js +7 -2
  213. package/src/inference/pipelines/diffusion/pipeline.js +17 -7
  214. package/src/inference/pipelines/diffusion/sd3-transformer.js +10 -10
  215. package/src/inference/pipelines/diffusion/text-encoder-gpu.d.ts +5 -0
  216. package/src/inference/pipelines/diffusion/text-encoder-gpu.js +27 -15
  217. package/src/inference/pipelines/diffusion/vae.js +3 -7
  218. package/src/inference/pipelines/energy/pipeline.js +27 -21
  219. package/src/inference/pipelines/energy/quintel.d.ts +5 -0
  220. package/src/inference/pipelines/energy/quintel.js +11 -0
  221. package/src/inference/pipelines/energy-head/row-head-pipeline.js +17 -13
  222. package/src/inference/pipelines/structured/json-head-pipeline.js +26 -11
  223. package/src/inference/pipelines/text/attention/projections.js +151 -101
  224. package/src/inference/pipelines/text/attention/record.js +73 -10
  225. package/src/inference/pipelines/text/attention/run.js +73 -10
  226. package/src/inference/pipelines/text/chat-format.js +25 -1
  227. package/src/inference/pipelines/text/config.d.ts +4 -0
  228. package/src/inference/pipelines/text/config.js +71 -5
  229. package/src/inference/pipelines/text/embed.js +2 -8
  230. package/src/inference/pipelines/text/execution-plan.js +64 -50
  231. package/src/inference/pipelines/text/execution-v0-contract-helpers.d.ts +59 -0
  232. package/src/inference/pipelines/text/execution-v0-contract-helpers.js +937 -0
  233. package/src/inference/pipelines/text/execution-v0-runtime-builders.d.ts +15 -0
  234. package/src/inference/pipelines/text/execution-v0-runtime-builders.js +279 -0
  235. package/src/inference/pipelines/text/execution-v0.js +78 -1002
  236. package/src/inference/pipelines/text/ffn/standard.js +3 -0
  237. package/src/inference/pipelines/text/generator-steps.d.ts +46 -0
  238. package/src/inference/pipelines/text/generator-steps.js +298 -207
  239. package/src/inference/pipelines/text/generator.js +6 -23
  240. package/src/inference/pipelines/text/init.d.ts +4 -0
  241. package/src/inference/pipelines/text/init.js +134 -29
  242. package/src/inference/pipelines/text/kernel-path-auto-select.js +2 -0
  243. package/src/inference/pipelines/text/kernel-trace.d.ts +2 -0
  244. package/src/inference/pipelines/text/kernel-trace.js +6 -0
  245. package/src/inference/pipelines/text/layer.js +14 -9
  246. package/src/inference/pipelines/text/linear-attention.d.ts +10 -0
  247. package/src/inference/pipelines/text/linear-attention.js +80 -6
  248. package/src/inference/pipelines/text/logits/gpu.js +10 -5
  249. package/src/inference/pipelines/text/logits/index.js +10 -11
  250. package/src/inference/pipelines/text/logits/utils.d.ts +7 -0
  251. package/src/inference/pipelines/text/logits/utils.js +9 -0
  252. package/src/inference/pipelines/text/lora-apply.js +50 -32
  253. package/src/inference/pipelines/text/model-load.js +279 -104
  254. package/src/inference/pipelines/text/moe-cache.js +5 -4
  255. package/src/inference/pipelines/text/moe-cpu-gptoss.js +74 -69
  256. package/src/inference/pipelines/text/moe-cpu.js +42 -38
  257. package/src/inference/pipelines/text/moe-gpu.js +110 -86
  258. package/src/inference/pipelines/text/ops.js +90 -90
  259. package/src/inference/pipelines/text/probes.js +9 -9
  260. package/src/inference/pipelines/text/weights.js +17 -7
  261. package/src/inference/pipelines/text.js +17 -1
  262. package/src/inference/speculative.d.ts +2 -2
  263. package/src/inference/speculative.js +4 -18
  264. package/src/inference/test-harness.d.ts +1 -1
  265. package/src/inference/test-harness.js +15 -5
  266. package/src/inference/tokenizer.d.ts +0 -5
  267. package/src/inference/tokenizer.js +4 -23
  268. package/src/inference/tokenizers/bpe.js +9 -0
  269. package/src/inference/tokenizers/bundled.js +176 -33
  270. package/src/inference/tokenizers/sentencepiece.js +12 -0
  271. package/src/loader/doppler-loader.js +38 -22
  272. package/src/loader/dtype-utils.js +3 -44
  273. package/src/loader/embedding-loader.js +7 -3
  274. package/src/loader/experts/expert-cache.js +13 -6
  275. package/src/loader/experts/expert-loader.js +10 -6
  276. package/src/loader/final-weights-loader.js +8 -4
  277. package/src/loader/layer-loader.js +2 -1
  278. package/src/loader/loader-state.js +2 -2
  279. package/src/loader/memory-monitor.js +8 -0
  280. package/src/loader/multi-model-loader.d.ts +14 -0
  281. package/src/loader/multi-model-loader.js +70 -24
  282. package/src/loader/shard-cache.js +81 -12
  283. package/src/loader/shard-resolver.js +25 -3
  284. package/src/loader/tensors/tensor-loader.js +209 -144
  285. package/src/loader/tensors/tensor-reader.js +76 -19
  286. package/src/loader/weight-downcast.js +1 -1
  287. package/src/memory/buffer-pool.d.ts +9 -1
  288. package/src/memory/buffer-pool.js +109 -44
  289. package/src/memory/unified-detect.js +1 -1
  290. package/src/rules/inference/kernel-path.rules.json +24 -8
  291. package/src/rules/rule-registry.js +25 -1
  292. package/src/rules/tooling/command-runtime.rules.json +18 -0
  293. package/src/storage/backends/opfs-store.js +68 -24
  294. package/src/storage/downloader.js +364 -83
  295. package/src/storage/index.d.ts +3 -0
  296. package/src/storage/index.js +3 -0
  297. package/src/storage/preflight.d.ts +2 -2
  298. package/src/storage/preflight.js +24 -2
  299. package/src/storage/quickstart-downloader.js +11 -5
  300. package/src/storage/registry.js +10 -4
  301. package/src/storage/reports.js +1 -1
  302. package/src/storage/shard-manager.d.ts +15 -1
  303. package/src/storage/shard-manager.js +51 -3
  304. package/src/storage/source-artifact-store.d.ts +52 -0
  305. package/src/storage/source-artifact-store.js +234 -0
  306. package/src/tooling/command-api-constants.d.ts +9 -0
  307. package/src/tooling/command-api-constants.js +9 -0
  308. package/src/tooling/command-api-family-normalizers.d.ts +9 -0
  309. package/src/tooling/command-api-family-normalizers.js +343 -0
  310. package/src/tooling/command-api-helpers.d.ts +25 -0
  311. package/src/tooling/command-api-helpers.js +262 -0
  312. package/src/tooling/command-api.d.ts +27 -1
  313. package/src/tooling/command-api.js +26 -473
  314. package/src/tooling/command-envelope.js +4 -1
  315. package/src/tooling/command-runner-shared.js +52 -18
  316. package/src/tooling/lean-execution-contract.js +150 -3
  317. package/src/tooling/node-browser-command-runner.d.ts +4 -0
  318. package/src/tooling/node-browser-command-runner.js +218 -273
  319. package/src/tooling/node-command-runner.js +44 -3
  320. package/src/tooling/node-converter.js +27 -1
  321. package/src/tooling/node-source-runtime.d.ts +1 -1
  322. package/src/tooling/node-source-runtime.js +84 -3
  323. package/src/tooling/node-webgpu.js +30 -105
  324. package/src/tooling/opfs-cache.js +21 -4
  325. package/src/tooling/runtime-input-composition.d.ts +38 -0
  326. package/src/tooling/runtime-input-composition.js +86 -0
  327. package/src/tooling/source-runtime-bundle.d.ts +40 -5
  328. package/src/tooling/source-runtime-bundle.js +261 -34
  329. package/src/tooling/source-runtime-materializer.d.ts +6 -0
  330. package/src/tooling/source-runtime-materializer.js +93 -0
  331. package/src/training/attention-backward.js +32 -17
  332. package/src/training/autograd.js +80 -52
  333. package/src/training/checkpoint-watch.d.ts +8 -0
  334. package/src/training/checkpoint-watch.js +139 -0
  335. package/src/training/checkpoint.d.ts +6 -1
  336. package/src/training/checkpoint.js +46 -7
  337. package/src/training/clip.js +2 -1
  338. package/src/training/datasets/token-batch.js +20 -8
  339. package/src/training/distillation/artifacts.d.ts +71 -0
  340. package/src/training/distillation/artifacts.js +132 -0
  341. package/src/training/distillation/checkpoint-watch.d.ts +10 -0
  342. package/src/training/distillation/checkpoint-watch.js +58 -0
  343. package/src/training/distillation/dataset.d.ts +59 -0
  344. package/src/training/distillation/dataset.js +337 -0
  345. package/src/training/distillation/eval.d.ts +34 -0
  346. package/src/training/distillation/eval.js +310 -0
  347. package/src/training/distillation/index.d.ts +29 -0
  348. package/src/training/distillation/index.js +29 -0
  349. package/src/training/distillation/runtime.d.ts +20 -0
  350. package/src/training/distillation/runtime.js +121 -0
  351. package/src/training/distillation/scoreboard.d.ts +6 -0
  352. package/src/training/distillation/scoreboard.js +8 -0
  353. package/src/training/distillation/stage-a.d.ts +45 -0
  354. package/src/training/distillation/stage-a.js +338 -0
  355. package/src/training/distillation/stage-b.d.ts +24 -0
  356. package/src/training/distillation/stage-b.js +20 -0
  357. package/src/training/distillation/student-fixture.d.ts +22 -0
  358. package/src/training/distillation/student-fixture.js +846 -0
  359. package/src/training/distillation/suite-data.d.ts +45 -0
  360. package/src/training/distillation/suite-data.js +189 -0
  361. package/src/training/index.d.ts +10 -0
  362. package/src/training/index.js +10 -0
  363. package/src/training/lora-pipeline.d.ts +40 -0
  364. package/src/training/lora-pipeline.js +793 -0
  365. package/src/training/lora.js +26 -12
  366. package/src/training/loss.js +5 -6
  367. package/src/training/objectives/cross_entropy.js +2 -5
  368. package/src/training/objectives/distill_kd.js +4 -8
  369. package/src/training/objectives/distill_triplet.js +4 -8
  370. package/src/training/objectives/ul_stage2_base.js +4 -8
  371. package/src/training/operator-artifacts.d.ts +62 -0
  372. package/src/training/operator-artifacts.js +140 -0
  373. package/src/training/operator-command.d.ts +5 -0
  374. package/src/training/operator-command.js +455 -0
  375. package/src/training/operator-eval.d.ts +48 -0
  376. package/src/training/operator-eval.js +230 -0
  377. package/src/training/operator-scoreboard.d.ts +5 -0
  378. package/src/training/operator-scoreboard.js +44 -0
  379. package/src/training/optimizer.js +19 -7
  380. package/src/training/runner.d.ts +52 -0
  381. package/src/training/runner.js +31 -5
  382. package/src/training/suite.d.ts +112 -0
  383. package/src/training/suite.js +24 -984
  384. package/src/training/tensor-factory.d.ts +9 -0
  385. package/src/training/tensor-factory.js +13 -0
  386. package/src/training/trainer.js +3 -5
  387. package/src/training/ul_dataset.js +3 -5
  388. package/src/training/workloads.d.ts +164 -0
  389. package/src/training/workloads.js +530 -0
  390. package/src/version.js +1 -1
  391. package/tools/convert-safetensors-node.js +22 -16
  392. package/tools/doppler-cli.js +179 -63
@@ -9,7 +9,10 @@ import {
9
9
  ensureCommandSupportedOnSurface,
10
10
  normalizeToolingCommandRequest,
11
11
  } from './command-api.js';
12
- import { normalizeToToolingCommandError } from './command-envelope.js';
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';
@@ -67,8 +70,61 @@ function resolveStaticPath(rootDir, requestPath) {
67
70
  return candidate;
68
71
  }
69
72
 
70
- async function resolveFileForRequest(rootDir, requestPath) {
71
- const resolved = resolveStaticPath(rootDir, requestPath);
73
+ function normalizeStaticMounts(mounts = []) {
74
+ if (!Array.isArray(mounts)) {
75
+ throw new Error('browser command: staticMounts must be an array.');
76
+ }
77
+
78
+ return mounts.map((mount, index) => {
79
+ if (!mount || typeof mount !== 'object' || Array.isArray(mount)) {
80
+ throw new Error(`browser command: staticMounts[${index}] must be an object.`);
81
+ }
82
+ const urlPrefix = String(mount.urlPrefix || '').trim();
83
+ const rootDir = String(mount.rootDir || '').trim();
84
+ if (!urlPrefix.startsWith('/')) {
85
+ throw new Error(`browser command: staticMounts[${index}].urlPrefix must start with "/".`);
86
+ }
87
+ if (!rootDir) {
88
+ throw new Error(`browser command: staticMounts[${index}].rootDir is required.`);
89
+ }
90
+ return {
91
+ urlPrefix: urlPrefix.replace(/\/+$/u, '') || '/',
92
+ rootDir: path.resolve(rootDir),
93
+ };
94
+ });
95
+ }
96
+
97
+ function findStaticRootForRequest(rootDir, mounts, requestPath) {
98
+ const normalizedPath = String(requestPath || '/');
99
+ let bestMount = null;
100
+
101
+ for (const mount of mounts) {
102
+ const prefix = mount.urlPrefix;
103
+ if (normalizedPath !== prefix && !normalizedPath.startsWith(`${prefix}/`)) {
104
+ continue;
105
+ }
106
+ if (!bestMount || prefix.length > bestMount.urlPrefix.length) {
107
+ bestMount = mount;
108
+ }
109
+ }
110
+
111
+ if (!bestMount) {
112
+ return {
113
+ effectiveRootDir: rootDir,
114
+ effectivePath: normalizedPath,
115
+ };
116
+ }
117
+
118
+ const relativePath = normalizedPath.slice(bestMount.urlPrefix.length) || '/';
119
+ return {
120
+ effectiveRootDir: bestMount.rootDir,
121
+ effectivePath: relativePath.startsWith('/') ? relativePath : `/${relativePath}`,
122
+ };
123
+ }
124
+
125
+ async function resolveFileForRequest(rootDir, mounts, requestPath) {
126
+ const { effectiveRootDir, effectivePath } = findStaticRootForRequest(rootDir, mounts, requestPath);
127
+ const resolved = resolveStaticPath(effectiveRootDir, effectivePath);
72
128
  if (!resolved) return null;
73
129
 
74
130
  let stats;
@@ -99,6 +155,7 @@ async function createStaticFileServer(options = {}) {
99
155
  const rootDir = path.resolve(
100
156
  options.rootDir || fileURLToPath(new URL('../../', import.meta.url))
101
157
  );
158
+ const staticMounts = normalizeStaticMounts(options.staticMounts || []);
102
159
  const host = String(options.host || DEFAULT_HOST);
103
160
  const port = Number.isFinite(options.port) ? Math.max(0, Math.floor(options.port)) : 0;
104
161
 
@@ -120,7 +177,7 @@ async function createStaticFileServer(options = {}) {
120
177
  return;
121
178
  }
122
179
 
123
- const resolved = await resolveFileForRequest(rootDir, pathname);
180
+ const resolved = await resolveFileForRequest(rootDir, staticMounts, pathname);
124
181
  if (!resolved) {
125
182
  res.statusCode = 404;
126
183
  res.end('File not found');
@@ -305,6 +362,26 @@ function asNonEmptyString(value) {
305
362
  return normalized === '' ? null : normalized;
306
363
  }
307
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
+
308
385
  function normalizeWebgpuBackend(value) {
309
386
  const raw = asNonEmptyString(value);
310
387
  if (!raw) return null;
@@ -516,8 +593,10 @@ async function launchPersistentBrowser(chromium, userDataDir, launchOptions, opt
516
593
 
517
594
  export async function runBrowserCommandInNode(commandRequest, options = {}) {
518
595
  let request = null;
596
+ let sourceRequest = null;
519
597
  try {
520
598
  ({ request } = ensureCommandSupportedOnSurface(commandRequest, 'browser'));
599
+ sourceRequest = request;
521
600
 
522
601
  if (request.keepPipeline) {
523
602
  throw new Error(
@@ -529,320 +608,186 @@ export async function runBrowserCommandInNode(commandRequest, options = {}) {
529
608
  throw new Error('browser command relay does not support convert. Use --surface node for convert commands.');
530
609
  }
531
610
 
532
- let useOpfsCache = options.opfsCache !== false;
533
- const userDataDir = options.userDataDir || DEFAULT_OPFS_CACHE_DIR;
611
+ let useOpfsCache = options.opfsCache !== false;
612
+ let relayRequest = request;
613
+ const userDataDir = options.userDataDir || DEFAULT_OPFS_CACHE_DIR;
534
614
 
535
- if (options.wipeCacheBeforeLaunch && useOpfsCache) {
536
- await fs.rm(userDataDir, { recursive: true, force: true }).catch(() => {});
537
- }
615
+ if (options.wipeCacheBeforeLaunch && useOpfsCache) {
616
+ await fs.rm(userDataDir, { recursive: true, force: true }).catch(() => {});
617
+ }
538
618
 
539
- const { chromium } = await import('playwright');
540
- const baseUrl = normalizeBaseUrl(options.baseUrl);
541
- // When OPFS caching is enabled, use a fixed port so the browser origin stays the same
542
- // across runs (OPFS is origin-scoped). Without this, random ports create new origins.
543
- const serverPort = options.port ?? (useOpfsCache ? DEFAULT_OPFS_CACHE_PORT : 0);
544
- const server = baseUrl
545
- ? null
546
- : await createStaticFileServer({
547
- rootDir: options.staticRootDir,
548
- host: options.host,
549
- port: serverPort,
550
- }).catch((error) => {
551
- const message = error?.message || String(error);
552
- throw new Error(
553
- `browser command: failed to start static server (${message}). Pass --browser-base-url to reuse an existing server.`
554
- );
555
- });
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
+ });
556
637
 
557
- const launchOptions = {
558
- headless: normalizeHeadless(options.headless),
559
- args: browserLaunchArgs(normalizeBrowserArgs(options.browserArgs)),
560
- };
638
+ const launchOptions = {
639
+ headless: normalizeHeadless(options.headless),
640
+ args: browserLaunchArgs(normalizeBrowserArgs(options.browserArgs)),
641
+ };
561
642
 
562
- if (options.channel) {
563
- launchOptions.channel = String(options.channel);
564
- }
565
- if (options.executablePath) {
566
- launchOptions.executablePath = String(options.executablePath);
567
- }
643
+ if (options.channel) {
644
+ launchOptions.channel = String(options.channel);
645
+ }
646
+ if (options.executablePath) {
647
+ launchOptions.executablePath = String(options.executablePath);
648
+ }
568
649
 
569
- const timeoutMs = normalizeTimeoutMs(options.timeoutMs);
570
- const runnerPath = normalizeRunnerPath(options.runnerPath);
571
- const resolvedBaseUrl = baseUrl || server.baseUrl;
572
- const requestedLoadMode = request.loadMode;
573
- const requireOpfsLoad = requestedLoadMode === 'opfs';
574
- if (requireOpfsLoad && useOpfsCache === false) {
575
- throw new Error('browser command: loadMode=opfs requires OPFS cache support (remove --no-opfs-cache).');
576
- }
577
- if (!requestedLoadMode && request.modelUrl && useOpfsCache === false) {
578
- request = {
579
- ...request,
580
- loadMode: 'http',
581
- };
582
- }
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
+ }
583
663
 
584
664
  let browser = null;
585
665
  let context = null;
586
666
  try {
587
- if (useOpfsCache) {
588
- // Persistent context: OPFS data survives between runs.
589
- // launchPersistentContext returns a BrowserContext directly (no separate Browser).
590
- try {
591
- context = await launchPersistentBrowser(chromium, userDataDir, launchOptions, {
592
- explicitChannel: Boolean(options.channel),
593
- explicitExecutablePath: Boolean(options.executablePath),
594
- });
595
- } catch (error) {
596
- if (!isRecoverablePersistentLaunchError(error)) {
597
- throw error;
598
- }
599
- if (typeof options.onConsole === 'function') {
600
- options.onConsole({
601
- type: 'warning',
602
- text: '[browser] Persistent browser launch failed; retrying with a clean OPFS profile.',
603
- });
604
- }
605
- 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).
606
670
  try {
607
671
  context = await launchPersistentBrowser(chromium, userDataDir, launchOptions, {
608
672
  explicitChannel: Boolean(options.channel),
609
673
  explicitExecutablePath: Boolean(options.executablePath),
610
674
  });
611
- } catch (retryError) {
612
- if (!isRecoverablePersistentLaunchError(retryError)) {
613
- throw retryError;
675
+ } catch (error) {
676
+ if (!isRecoverablePersistentLaunchError(error)) {
677
+ throw error;
614
678
  }
615
679
  if (typeof options.onConsole === 'function') {
616
680
  options.onConsole({
617
681
  type: 'warning',
618
- text: '[browser] Persistent launch still failing; falling back to non-persistent mode.',
682
+ text: '[browser] Persistent browser launch failed; retrying with a clean OPFS profile.',
619
683
  });
620
684
  }
621
- if (requireOpfsLoad) {
622
- throw new Error(
623
- 'browser command: loadMode=opfs requires persistent browser context; persistent launch failed.'
624
- );
625
- }
626
- useOpfsCache = false;
627
- if (request.loadMode === 'opfs') {
628
- request = {
629
- ...request,
630
- loadMode: 'http',
631
- };
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);
632
696
  }
633
- browser = await launchBrowser(chromium, launchOptions, {
634
- explicitChannel: Boolean(options.channel),
635
- explicitExecutablePath: Boolean(options.executablePath),
636
- });
637
- context = await browser.newContext();
638
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();
639
704
  }
640
- } else {
641
- browser = await launchBrowser(chromium, launchOptions, {
642
- explicitChannel: Boolean(options.channel),
643
- explicitExecutablePath: Boolean(options.executablePath),
644
- });
645
- context = await browser.newContext();
646
- }
647
705
 
648
- const page = await context.newPage();
649
- page.setDefaultTimeout(timeoutMs);
650
- const pageDiagnostics = [];
706
+ const page = await context.newPage();
707
+ page.setDefaultTimeout(timeoutMs);
708
+ const pageDiagnostics = [];
651
709
 
652
- if (typeof options.onConsole === 'function') {
653
- page.on('console', (message) => {
654
- options.onConsole({
655
- type: message.type(),
656
- text: message.text(),
710
+ if (typeof options.onConsole === 'function') {
711
+ page.on('console', (message) => {
712
+ options.onConsole({
713
+ type: message.type(),
714
+ text: message.text(),
715
+ });
657
716
  });
658
- });
659
- }
660
-
661
- page.on('pageerror', (error) => {
662
- pageDiagnostics.push(`pageerror: ${error?.message || String(error)}`);
663
- });
664
- page.on('requestfailed', (request) => {
665
- const failure = request.failure();
666
- pageDiagnostics.push(
667
- `requestfailed: ${request.url()} (${failure?.errorText || 'unknown error'})`
668
- );
669
- });
717
+ }
670
718
 
671
- const runnerUrl = new URL(runnerPath, resolvedBaseUrl);
672
- runnerUrl.searchParams.set('_dopplerRunner', String(Date.now()));
673
- await page.goto(runnerUrl.toString(), { waitUntil: 'load' });
674
- try {
675
- await page.waitForFunction(() => globalThis.__dopplerRunnerReady === true, null, {
676
- timeout: timeoutMs,
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
+ );
677
727
  });
678
- } catch (error) {
679
- const diagnostics = pageDiagnostics.length
680
- ? pageDiagnostics.slice(0, 10).join(' | ')
681
- : 'no page diagnostics captured';
682
- throw new Error(
683
- `browser command: runner did not become ready within ${timeoutMs}ms (${diagnostics}).`
684
- );
685
- }
686
728
 
687
- // OPFS cache: ensure model is cached before running the command.
688
- // On cache hit, strip modelUrl so the harness takes the fast OPFS path.
689
- if (useOpfsCache && request.modelId && request.modelUrl) {
729
+ const runnerUrl = new URL(runnerPath, resolvedBaseUrl);
730
+ runnerUrl.searchParams.set('_dopplerRunner', String(Date.now()));
731
+ await page.goto(runnerUrl.toString(), { waitUntil: 'load' });
690
732
  try {
691
- const cacheResult = await page.evaluate(async (payload) => {
692
- if (typeof globalThis.__dopplerEnsureCached !== 'function') {
693
- return { cached: false, error: '__dopplerEnsureCached not available' };
694
- }
695
- return globalThis.__dopplerEnsureCached(payload.modelId, payload.modelBaseUrl);
696
- }, {
697
- modelId: request.modelId,
698
- modelBaseUrl: request.modelUrl,
733
+ await page.waitForFunction(() => globalThis.__dopplerRunnerReady === true, null, {
734
+ timeout: timeoutMs,
699
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
+ });
700
757
 
701
- if (cacheResult.cached) {
702
- // Remove modelUrl so the harness loads from OPFS instead of HTTP.
703
- request = { ...request };
704
- delete request.modelUrl;
705
- request.loadMode = 'opfs';
706
- } else {
707
- if (requireOpfsLoad) {
758
+ if (cacheResult.cached) {
759
+ relayRequest = { ...relayRequest };
760
+ delete relayRequest.modelUrl;
761
+ } else {
708
762
  const cacheError = cacheResult?.error || 'model not cached';
709
763
  throw new Error(
710
- `[opfs-cache] loadMode=opfs requested but cache is unavailable for "${request.modelId || 'unknown-model'}": ${cacheError}`
764
+ `[opfs-cache] model cache is unavailable for "${relayRequest.modelId || 'unknown-model'}": ${cacheError}.`
711
765
  );
712
766
  }
713
- if (!requestedLoadMode) {
714
- request = { ...request, loadMode: 'http' };
715
- }
716
- if (cacheResult.error) {
717
- if (typeof options.onConsole === 'function') {
718
- options.onConsole({
719
- type: 'warning',
720
- text: `[opfs-cache] Cache check failed (${cacheResult.error}), falling back to HTTP`,
721
- });
722
- }
723
- }
724
- }
725
- } catch (error) {
726
- if (requireOpfsLoad) {
767
+ } catch (error) {
727
768
  throw new Error(
728
- `[opfs-cache] loadMode=opfs requested but cache priming failed: ${error?.message || error}`
769
+ `[opfs-cache] cache priming failed: ${error?.message || error}.`
729
770
  );
730
771
  }
731
- if (!requestedLoadMode && request.modelUrl) {
732
- request = { ...request, loadMode: 'http' };
733
- }
734
-
735
- // OPFS cache is best-effort; fall back to HTTP on any error.
736
- if (typeof options.onConsole === 'function') {
737
- options.onConsole({
738
- type: 'warning',
739
- text: `[opfs-cache] Error (${error?.message || error}), falling back to HTTP`,
740
- });
741
- }
742
- }
743
- }
744
-
745
- const response = await page.evaluate(async (payload) => {
746
- if (typeof globalThis.__dopplerRunBrowserCommand !== 'function') {
747
- throw new Error('browser command runner is missing globalThis.__dopplerRunBrowserCommand');
748
772
  }
749
- return globalThis.__dopplerRunBrowserCommand(payload.request, payload.options || {});
750
- }, {
751
- request,
752
- options: {
753
- runtimeLoadOptions: options.runtimeLoadOptions || {},
754
- },
755
- });
756
773
 
757
- const result = response?.result;
758
- if (result && typeof result === 'object' && !Array.isArray(result)) {
759
- const cpuInfo = typeof os.cpus === 'function' ? os.cpus() : null;
760
- const hostEnvironment = {
761
- platform: process.platform,
762
- arch: process.arch,
763
- nodeVersion: process.version,
764
- osRelease: typeof os.release === 'function' ? os.release() : null,
765
- cpuModel: Array.isArray(cpuInfo) && cpuInfo.length > 0 ? asNonEmptyString(cpuInfo[0]?.model) : null,
766
- };
767
- const webgpuBackend = inferWebgpuBackendFromArgs(launchOptions.args, hostEnvironment.platform);
768
- const env = result.env && typeof result.env === 'object' ? result.env : {};
769
- const deviceInfo = result.deviceInfo && typeof result.deviceInfo === 'object'
770
- ? result.deviceInfo
771
- : {};
772
- result.env = {
773
- ...env,
774
- webgpuBackend: normalizeWebgpuBackend(env.webgpuBackend)
775
- || normalizeWebgpuBackend(env.gpuBackend)
776
- || normalizeWebgpuBackend(env.graphicsBackend)
777
- || webgpuBackend,
778
- };
779
- const existingEnvironment = result.environment && typeof result.environment === 'object'
780
- ? result.environment
781
- : {};
782
- result.environment = {
783
- ...existingEnvironment,
784
- host: {
785
- ...(existingEnvironment.host && typeof existingEnvironment.host === 'object' ? existingEnvironment.host : {}),
786
- platform: asNonEmptyString(existingEnvironment?.host?.platform) || hostEnvironment.platform,
787
- arch: asNonEmptyString(existingEnvironment?.host?.arch) || hostEnvironment.arch,
788
- nodeVersion: asNonEmptyString(existingEnvironment?.host?.nodeVersion) || hostEnvironment.nodeVersion,
789
- osRelease: asNonEmptyString(existingEnvironment?.host?.osRelease) || hostEnvironment.osRelease,
790
- cpuModel: asNonEmptyString(existingEnvironment?.host?.cpuModel) || hostEnvironment.cpuModel,
791
- },
792
- browser: {
793
- ...(existingEnvironment.browser && typeof existingEnvironment.browser === 'object' ? existingEnvironment.browser : {}),
794
- userAgent: asNonEmptyString(existingEnvironment?.browser?.userAgent) || asNonEmptyString(env.browserUserAgent),
795
- platform: asNonEmptyString(existingEnvironment?.browser?.platform) || asNonEmptyString(env.browserPlatform),
796
- language: asNonEmptyString(existingEnvironment?.browser?.language) || asNonEmptyString(env.browserLanguage),
797
- vendor: asNonEmptyString(existingEnvironment?.browser?.vendor) || asNonEmptyString(env.browserVendor),
798
- executable: asNonEmptyString(existingEnvironment?.browser?.executable) || asNonEmptyString(options.executablePath),
799
- channel: asNonEmptyString(existingEnvironment?.browser?.channel) || asNonEmptyString(options.channel),
800
- },
801
- gpu: {
802
- ...(existingEnvironment.gpu && typeof existingEnvironment.gpu === 'object' ? existingEnvironment.gpu : {}),
803
- api: asNonEmptyString(existingEnvironment?.gpu?.api) || 'webgpu',
804
- backend: normalizeWebgpuBackend(existingEnvironment?.gpu?.backend)
805
- || normalizeWebgpuBackend(env.webgpuBackend)
806
- || webgpuBackend,
807
- vendor: asNonEmptyString(existingEnvironment?.gpu?.vendor) || asNonEmptyString(deviceInfo.vendor),
808
- architecture: asNonEmptyString(existingEnvironment?.gpu?.architecture) || asNonEmptyString(deviceInfo.architecture),
809
- device: asNonEmptyString(existingEnvironment?.gpu?.device) || asNonEmptyString(deviceInfo.device),
810
- description: asNonEmptyString(existingEnvironment?.gpu?.description) || asNonEmptyString(deviceInfo.description),
811
- hasF16: typeof existingEnvironment?.gpu?.hasF16 === 'boolean'
812
- ? existingEnvironment.gpu.hasF16
813
- : (typeof deviceInfo.hasF16 === 'boolean' ? deviceInfo.hasF16 : null),
814
- hasSubgroups: typeof existingEnvironment?.gpu?.hasSubgroups === 'boolean'
815
- ? existingEnvironment.gpu.hasSubgroups
816
- : (typeof deviceInfo.hasSubgroups === 'boolean' ? deviceInfo.hasSubgroups : null),
817
- hasTimestampQuery: typeof existingEnvironment?.gpu?.hasTimestampQuery === 'boolean'
818
- ? existingEnvironment.gpu.hasTimestampQuery
819
- : (typeof deviceInfo.hasTimestampQuery === 'boolean' ? deviceInfo.hasTimestampQuery : null),
820
- },
821
- runtime: {
822
- ...(existingEnvironment.runtime && typeof existingEnvironment.runtime === 'object' ? existingEnvironment.runtime : {}),
823
- library: asNonEmptyString(existingEnvironment?.runtime?.library) || asNonEmptyString(env.library) || 'doppler',
824
- version: asNonEmptyString(existingEnvironment?.runtime?.version) || asNonEmptyString(env.version),
825
- surface: asNonEmptyString(existingEnvironment?.runtime?.surface) || asNonEmptyString(env.runtime) || 'browser',
826
- device: asNonEmptyString(existingEnvironment?.runtime?.device) || asNonEmptyString(env.device),
827
- dtype: asNonEmptyString(existingEnvironment?.runtime?.dtype) || asNonEmptyString(env.dtype),
828
- requestedDtype: asNonEmptyString(existingEnvironment?.runtime?.requestedDtype) || asNonEmptyString(env.requestedDtype),
829
- executionProviderMode: asNonEmptyString(existingEnvironment?.runtime?.executionProviderMode)
830
- || asNonEmptyString(env.executionProviderMode),
831
- cacheMode: asNonEmptyString(existingEnvironment?.runtime?.cacheMode)
832
- || asNonEmptyString(result.cacheMode)
833
- || asNonEmptyString(result?.timing?.cacheMode),
834
- loadMode: asNonEmptyString(existingEnvironment?.runtime?.loadMode)
835
- || asNonEmptyString(result.loadMode)
836
- || 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 || {},
837
783
  },
838
- };
839
- }
784
+ });
840
785
 
841
- return response;
786
+ return finalizeBrowserRelayResponse(response, sourceRequest);
842
787
  } catch (error) {
843
788
  throw normalizeToToolingCommandError(error, {
844
789
  surface: 'browser',
845
- request,
790
+ request: sourceRequest,
846
791
  });
847
792
  } finally {
848
793
  if (context) {
@@ -858,7 +803,7 @@ export async function runBrowserCommandInNode(commandRequest, options = {}) {
858
803
  } catch (error) {
859
804
  throw normalizeToToolingCommandError(error, {
860
805
  surface: 'browser',
861
- request,
806
+ request: sourceRequest,
862
807
  });
863
808
  }
864
809
  }