webbrain-one commited on
Commit
3632834
·
verified ·
1 Parent(s): b45b2b1

Release Compass Tiny v2.1: tested v24 q4f16 WebGPU, validation and caveats

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +3 -0
  2. ARTIFACTS.json +261 -0
  3. ATTRIBUTIONS.md +17 -0
  4. PROVENANCE.json +270 -0
  5. README.md +153 -0
  6. chat_template.jinja +176 -0
  7. config.json +43 -0
  8. generation_config.json +11 -0
  9. integration/minicpm5-tool-parser.mjs +101 -0
  10. integration/parser.test.mjs +40 -0
  11. onnx/model_q4f16.onnx +3 -0
  12. onnx/model_q4f16.onnx_data +3 -0
  13. onnx/model_q4f16.onnx_data_1 +3 -0
  14. runtime/inference-worker.js +1539 -0
  15. runtime/tool-call-parser.js +512 -0
  16. runtime/vendor/LICENSE.onnxruntime.txt +21 -0
  17. runtime/vendor/LICENSE.transformers.txt +202 -0
  18. runtime/vendor/README.md +162 -0
  19. runtime/vendor/ThirdPartyNotices.onnxruntime.txt +0 -0
  20. runtime/vendor/onnxruntime-common/backend-impl.js +142 -0
  21. runtime/vendor/onnxruntime-common/backend.js +4 -0
  22. runtime/vendor/onnxruntime-common/env-impl.js +25 -0
  23. runtime/vendor/onnxruntime-common/env.js +8 -0
  24. runtime/vendor/onnxruntime-common/index.js +27 -0
  25. runtime/vendor/onnxruntime-common/inference-session-impl.js +208 -0
  26. runtime/vendor/onnxruntime-common/inference-session.js +6 -0
  27. runtime/vendor/onnxruntime-common/onnx-model.js +4 -0
  28. runtime/vendor/onnxruntime-common/onnx-value.js +4 -0
  29. runtime/vendor/onnxruntime-common/tensor-conversion-impl.js +195 -0
  30. runtime/vendor/onnxruntime-common/tensor-conversion.js +4 -0
  31. runtime/vendor/onnxruntime-common/tensor-factory-impl.js +265 -0
  32. runtime/vendor/onnxruntime-common/tensor-factory.js +4 -0
  33. runtime/vendor/onnxruntime-common/tensor-impl-type-mapping.js +58 -0
  34. runtime/vendor/onnxruntime-common/tensor-impl.js +366 -0
  35. runtime/vendor/onnxruntime-common/tensor-utils-impl.js +62 -0
  36. runtime/vendor/onnxruntime-common/tensor-utils.js +4 -0
  37. runtime/vendor/onnxruntime-common/tensor.js +6 -0
  38. runtime/vendor/onnxruntime-common/trace.js +69 -0
  39. runtime/vendor/onnxruntime-common/type-helper.js +4 -0
  40. runtime/vendor/onnxruntime-common/version.js +6 -0
  41. runtime/vendor/ort-wasm-simd-threaded.asyncify.mjs +112 -0
  42. runtime/vendor/ort-wasm-simd-threaded.asyncify.wasm +3 -0
  43. runtime/vendor/ort.webgpu.mjs +0 -0
  44. runtime/vendor/transformers.web.js +0 -0
  45. tokenizer.json +3 -0
  46. tokenizer_config.json +18 -0
  47. validation/cold-cache-results.json +29 -0
  48. validation/context-results.json +31 -0
  49. validation/numerical-results.json +319 -0
  50. validation/parser-results.json +92 -0
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ onnx/model_q4f16.onnx_data filter=lfs diff=lfs merge=lfs -text
37
+ onnx/model_q4f16.onnx_data_1 filter=lfs diff=lfs merge=lfs -text
38
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
ARTIFACTS.json ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "format": "exact-v24-webgpu-q4f16-plus-explicit-integration-helper",
3
+ "manifestExcludesItself": true,
4
+ "files": [
5
+ {
6
+ "path": "ATTRIBUTIONS.md",
7
+ "bytes": 964,
8
+ "sha256": "3d83117ca74892774eedf2c3288ba864de05e4f0fe242bb4d09885e8634100a6"
9
+ },
10
+ {
11
+ "path": "chat_template.jinja",
12
+ "bytes": 8986,
13
+ "sha256": "23ac861c8370f36819f4f6034c4439ff631e3c381855749451a02be85d19b2d6"
14
+ },
15
+ {
16
+ "path": "config.json",
17
+ "bytes": 951,
18
+ "sha256": "504e5e43d11c25189f59896801302a8c40b3caf14cedc9dd9dc050c6430dcc01"
19
+ },
20
+ {
21
+ "path": "generation_config.json",
22
+ "bytes": 186,
23
+ "sha256": "6f66aa5aaf68145027c3f3c384abfd837530fe4d1efb582e0aa919eecd4c9a8e"
24
+ },
25
+ {
26
+ "path": "integration/minicpm5-tool-parser.mjs",
27
+ "bytes": 5307,
28
+ "sha256": "0f1bf7265771ec5cf73147c299c52490086579bb2d2c9885cbddda7e067322e3"
29
+ },
30
+ {
31
+ "path": "integration/parser.test.mjs",
32
+ "bytes": 2376,
33
+ "sha256": "ca3a366878b648e4daeba1674aead2c9b2a11f21c6308ed3a416c986632521ba"
34
+ },
35
+ {
36
+ "path": "onnx/model_q4f16.onnx",
37
+ "bytes": 362892,
38
+ "sha256": "ab10bc051ca80ad6dffe5045acac030d0d6b75c52d90db363d044f1f214fccfa"
39
+ },
40
+ {
41
+ "path": "onnx/model_q4f16.onnx_data",
42
+ "bytes": 1596739584,
43
+ "sha256": "2abad6aebb250a04619df22e8b2cab9998ee92ba083ec5426c97ae1beb8476f7"
44
+ },
45
+ {
46
+ "path": "onnx/model_q4f16.onnx_data_1",
47
+ "bytes": 272252928,
48
+ "sha256": "715887fa2ce24f2f1d5f5f8adaebc0caac4bad02e7d4d90e081f85decefec244"
49
+ },
50
+ {
51
+ "path": "PROVENANCE.json",
52
+ "bytes": 11378,
53
+ "sha256": "f6cd6d8e5f4947c2b09308731dd58352c7c4a4897dedfeed1e81baf2837e97d9"
54
+ },
55
+ {
56
+ "path": "README.md",
57
+ "bytes": 8044,
58
+ "sha256": "e601af5ecf40c128ba3510f1c1a89edc1d63013901490b30e569311ffae3235c"
59
+ },
60
+ {
61
+ "path": "runtime/inference-worker.js",
62
+ "bytes": 58199,
63
+ "sha256": "6e8863a72f022fa8d12207d4029f8f33f339f3c2c066497618065cf642da85dd"
64
+ },
65
+ {
66
+ "path": "runtime/tool-call-parser.js",
67
+ "bytes": 18197,
68
+ "sha256": "7a33bbcd31cc3910a169e735541c4d5189f85480237577f262de3154ce91f12d"
69
+ },
70
+ {
71
+ "path": "runtime/vendor/LICENSE.onnxruntime.txt",
72
+ "bytes": 1073,
73
+ "sha256": "2f07c72751aed99790b8a4869cf2311df85a860b22ded05fa22803587a48922c"
74
+ },
75
+ {
76
+ "path": "runtime/vendor/LICENSE.transformers.txt",
77
+ "bytes": 11358,
78
+ "sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"
79
+ },
80
+ {
81
+ "path": "runtime/vendor/onnxruntime-common/backend-impl.js",
82
+ "bytes": 5339,
83
+ "sha256": "97dd81b055a06f61120f09468fe59f046a9262b7b87bf5d05bceb085bf4f5558"
84
+ },
85
+ {
86
+ "path": "runtime/vendor/onnxruntime-common/backend.js",
87
+ "bytes": 184,
88
+ "sha256": "017ce2ec9dc2021ebc1ef2c4f04e1bc74276c65921b0297e7ed8cc16e640495b"
89
+ },
90
+ {
91
+ "path": "runtime/vendor/onnxruntime-common/env-impl.js",
92
+ "bytes": 848,
93
+ "sha256": "0da4c562a49fe147e19320a64cbb456184eb0e6242b1fe69c65c8368c55f3d97"
94
+ },
95
+ {
96
+ "path": "runtime/vendor/onnxruntime-common/env.js",
97
+ "bytes": 262,
98
+ "sha256": "b2bbd3d3b00a95765713c57cd0f5ef73718ae5b8322e07cb47a43e2accb21f15"
99
+ },
100
+ {
101
+ "path": "runtime/vendor/onnxruntime-common/index.js",
102
+ "bytes": 1036,
103
+ "sha256": "ad23f6d6ce2c99c449031f24a5181f259516c64a9a67428c98a8539b857729bd"
104
+ },
105
+ {
106
+ "path": "runtime/vendor/onnxruntime-common/inference-session-impl.js",
107
+ "bytes": 8703,
108
+ "sha256": "0446814a392c147b5c38ba242d9911ee54002b5d01a8e19fb4895fb8f8a0cdd3"
109
+ },
110
+ {
111
+ "path": "runtime/vendor/onnxruntime-common/inference-session.js",
112
+ "bytes": 348,
113
+ "sha256": "3ad4271a88edb3d242dc1e0e7bbf66510f9a56eff874c433b3b37f900ac007b1"
114
+ },
115
+ {
116
+ "path": "runtime/vendor/onnxruntime-common/onnx-model.js",
117
+ "bytes": 145,
118
+ "sha256": "9e9dc8678be0ea04575a542894508db355a4f9f2155ac7f4c17a697c5f843382"
119
+ },
120
+ {
121
+ "path": "runtime/vendor/onnxruntime-common/onnx-value.js",
122
+ "bytes": 145,
123
+ "sha256": "33dc232498753fc9026dfa44c99440cb1a6d29f3e53e1ad2fb8b696705158f06"
124
+ },
125
+ {
126
+ "path": "runtime/vendor/onnxruntime-common/tensor-conversion-impl.js",
127
+ "bytes": 7852,
128
+ "sha256": "df6fa3cd1813c2d3e588cc07973cfe5ea2ab8647c957168200caa58e51440161"
129
+ },
130
+ {
131
+ "path": "runtime/vendor/onnxruntime-common/tensor-conversion.js",
132
+ "bytes": 152,
133
+ "sha256": "8c43ec1d061ab71032a1b28f45d85ef95f8d7bd2d4cd71d059e96d83e3a8f422"
134
+ },
135
+ {
136
+ "path": "runtime/vendor/onnxruntime-common/tensor-factory-impl.js",
137
+ "bytes": 11002,
138
+ "sha256": "cf5606fe23461e01bff59c2d72ac47d74e652548718f1e5fab07a22b441379b4"
139
+ },
140
+ {
141
+ "path": "runtime/vendor/onnxruntime-common/tensor-factory.js",
142
+ "bytes": 149,
143
+ "sha256": "68b9f4f4573f25012ce8a4f73c9a2cee3a28db91de8bbb0a20ddb08bcba33c4b"
144
+ },
145
+ {
146
+ "path": "runtime/vendor/onnxruntime-common/tensor-impl-type-mapping.js",
147
+ "bytes": 2706,
148
+ "sha256": "20bd5ddb858954cf071bace6c7a6628c8e311433798ec172f4c778bebd81d407"
149
+ },
150
+ {
151
+ "path": "runtime/vendor/onnxruntime-common/tensor-impl.js",
152
+ "bytes": 16091,
153
+ "sha256": "6a749be63055f9d21069566b1515412484a3a03448ca20427c8f8d29328a2843"
154
+ },
155
+ {
156
+ "path": "runtime/vendor/onnxruntime-common/tensor-utils-impl.js",
157
+ "bytes": 1920,
158
+ "sha256": "87d3e07c153616138c2f57846545eea9c47957c714d50c0264696bdc293e6732"
159
+ },
160
+ {
161
+ "path": "runtime/vendor/onnxruntime-common/tensor-utils.js",
162
+ "bytes": 147,
163
+ "sha256": "7acfdb2f8403ae84b7ae62d55d6eeb01519108be8874337b317b911ed60190cd"
164
+ },
165
+ {
166
+ "path": "runtime/vendor/onnxruntime-common/tensor.js",
167
+ "bytes": 286,
168
+ "sha256": "721d24a538de5333057bfc423bc00c3453a1c483f4495c857d0a6f9b8f2d330e"
169
+ },
170
+ {
171
+ "path": "runtime/vendor/onnxruntime-common/trace.js",
172
+ "bytes": 1891,
173
+ "sha256": "9b87e6309a1c502b03558aa94b0a65c8ea1e8eb59cb46090f54f6eacd191b841"
174
+ },
175
+ {
176
+ "path": "runtime/vendor/onnxruntime-common/type-helper.js",
177
+ "bytes": 146,
178
+ "sha256": "3e2588c6a5ee7b9755e199eef10c6aa2e777d0366ea47d27953657a5638deeae"
179
+ },
180
+ {
181
+ "path": "runtime/vendor/onnxruntime-common/version.js",
182
+ "bytes": 263,
183
+ "sha256": "00a18ffb867b29a3855fe00e965797dba270849134f1e0ab2929fb2761c0fe44"
184
+ },
185
+ {
186
+ "path": "runtime/vendor/ort-wasm-simd-threaded.asyncify.mjs",
187
+ "bytes": 47507,
188
+ "sha256": "7236653b8565da4046e459cd0e274123419a1d9f1f8f18fd36c28058346ca655"
189
+ },
190
+ {
191
+ "path": "runtime/vendor/ort-wasm-simd-threaded.asyncify.wasm",
192
+ "bytes": 24254953,
193
+ "sha256": "7e83cd6cee77e478bc96a7e91b198144fb5e4126287daf1f9b54bb195ebcd55a"
194
+ },
195
+ {
196
+ "path": "runtime/vendor/ort.webgpu.mjs",
197
+ "bytes": 679694,
198
+ "sha256": "177cf67f3e6c25e951a76a981a857e6af53b1571016685cc2480e1e6e47b1d46"
199
+ },
200
+ {
201
+ "path": "runtime/vendor/README.md",
202
+ "bytes": 8373,
203
+ "sha256": "a3b6d952b825a7382f16f89c1431e57090e6c3c2d1267ce1548069c802c103c7"
204
+ },
205
+ {
206
+ "path": "runtime/vendor/ThirdPartyNotices.onnxruntime.txt",
207
+ "bytes": 325054,
208
+ "sha256": "0e07b95f3a8d6230037707c5c4a2b554d12c4cb67369669ac255635528ffcee2"
209
+ },
210
+ {
211
+ "path": "runtime/vendor/transformers.web.js",
212
+ "bytes": 1099925,
213
+ "sha256": "83e3edd00c98fef962da9bdc22fe4b63b3858743a76c1b7e46bee8e483033644"
214
+ },
215
+ {
216
+ "path": "tokenizer.json",
217
+ "bytes": 10548223,
218
+ "sha256": "c7dc553ba83607a7d3cde4782e4055fcd15cb228f8f1cdc3fc149b1094580592"
219
+ },
220
+ {
221
+ "path": "tokenizer_config.json",
222
+ "bytes": 9706,
223
+ "sha256": "f8a13de1b9989de61731cf9ce5805a9c3da2aad822ded0cd5375cc3d1206e24c"
224
+ },
225
+ {
226
+ "path": "validation/cold-cache-results.json",
227
+ "bytes": 988,
228
+ "sha256": "ea1382b4cf397eed54510c2ac362f8c53c519d1a0bacd58a5266a44ceffb8972"
229
+ },
230
+ {
231
+ "path": "validation/context-results.json",
232
+ "bytes": 693,
233
+ "sha256": "c59e23f5038d5a6dd3d4e67f7ba90036d24af117a5c834221e30fc533a44e565"
234
+ },
235
+ {
236
+ "path": "validation/numerical-results.json",
237
+ "bytes": 8506,
238
+ "sha256": "d8d09941d44038efe040ad748d034d217f7e43227cd28ff5e47edfe061c80d4f"
239
+ },
240
+ {
241
+ "path": "validation/parser-results.json",
242
+ "bytes": 2257,
243
+ "sha256": "b50a394c4ddd5cfacf9208e8b8cbd39e3a048dbab5211bebc781ffb612f06bb1"
244
+ },
245
+ {
246
+ "path": "validation/parser-unit-tests.json",
247
+ "bytes": 804,
248
+ "sha256": "cdeef39b880727cf70b0578dd90dec8941edd007fa3eae0225cbbf6810bd4626"
249
+ },
250
+ {
251
+ "path": "validation/routing-results.json",
252
+ "bytes": 1316,
253
+ "sha256": "924be9a083ccd11206a528f6e63c64f743078e9a451a69fe968ba6f98cfcc2a4"
254
+ },
255
+ {
256
+ "path": "validation/smoke-results.json",
257
+ "bytes": 2009,
258
+ "sha256": "9f478ce6bd8c9f6ddb145e4468ae31f5290a8aaedfa4673a06483f1b0e7b66c9"
259
+ }
260
+ ]
261
+ }
ATTRIBUTIONS.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Source attributions and project restrictions
2
+
3
+ Base: OpenBMB MiniCPM5-2B, revision 3497c460c89e00520c3cfa2e73f49ab7647f1177,
4
+ Apache-2.0. https://huggingface.co/openbmb/MiniCPM5-2B
5
+ Modified by WebBrain through LoRA training and full-model merging.
6
+ Original authors do not endorse this derived release.
7
+
8
+ Training dataset: https://huggingface.co/datasets/webbrain-one/webbrain-compass-v2-dataset
9
+ Preserve the source attributions, manifests and restrictions in that private repository.
10
+ WebLINX: McGill-NLP contributors, CC BY-NC-SA 4.0.
11
+ https://huggingface.co/datasets/McGill-NLP/weblinx
12
+ https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.en
13
+
14
+ Project policy restricts this derived model to noncommercial research. Preserve
15
+ all source terms, attribution and applicable share-alike obligations. Private
16
+ hosting does not grant commercial rights. This is an experimental release with
17
+ a disclosed failed behavioral gate, not a claim of general safety or success.
PROVENANCE.json ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "releaseName": "WebBrain Compass Tiny v2.1",
3
+ "repository": "webbrain-one/webbrain-compass-tiny-v2.1",
4
+ "candidate": "v24",
5
+ "status": "private-experimental-release-authorized",
6
+ "authorization": "Owner requested release of v24 as webbrain-compass-tiny-v2.1 after receiving the numerical pass, parser caveat and three discouraged-action results. Private visibility retained.",
7
+ "numericalValidationPassed": true,
8
+ "numericalToleranceChanged": false,
9
+ "behavioralGatePassed": false,
10
+ "productionReleaseApproved": false,
11
+ "endToEndBrowserTaskSuccessMeasured": false,
12
+ "sourcePackageProofSha256": "dc91e19a96db79d51d77c15c1e03f755249021ae0fbf3403e4606ed877a664a1",
13
+ "sourceNumericalCompletionSha256": "d8d09941d44038efe040ad748d034d217f7e43227cd28ff5e47edfe061c80d4f",
14
+ "sourceBehavioralAuditSha256": "43e5a2d408995796994245d396a1b61522fe1185574b29fb07e47ff8de7a7365",
15
+ "historicalEvidenceUnchanged": true,
16
+ "change": "Same trained model; 211 MatMulNBits accuracy_level attributes 4 -> 2; no weight, tokenizer or runtime changes.",
17
+ "sourceBf16Repo": "webbrain-one/webbrain-compass-tiny-v2",
18
+ "sourceBf16Revision": "54bcab6731939137d0489ac01463c9404dff9da8",
19
+ "priorRelease": {
20
+ "repo": "webbrain-one/webbrain-compass-tiny-v2-onnx",
21
+ "revision": "efddf2666bced055e9b04e745197b4e7d56734e0",
22
+ "unchanged": true
23
+ },
24
+ "testedTransportAlias": "webbrain-one/webbrain-compass-tiny-v2-onnx",
25
+ "newRepoIsTransportRenameOnly": true,
26
+ "browser": "150.0.7871.187",
27
+ "hardware": "NVIDIA RTX 5090",
28
+ "runtime": {
29
+ "transformers.js": "4.2.0",
30
+ "onnxruntime-web": "1.27.0"
31
+ },
32
+ "testedBytes": [
33
+ {
34
+ "sourcePath": "bundle/chat_template.jinja",
35
+ "path": "chat_template.jinja",
36
+ "bytes": 8986,
37
+ "sha256": "23ac861c8370f36819f4f6034c4439ff631e3c381855749451a02be85d19b2d6"
38
+ },
39
+ {
40
+ "sourcePath": "bundle/config.json",
41
+ "path": "config.json",
42
+ "bytes": 951,
43
+ "sha256": "504e5e43d11c25189f59896801302a8c40b3caf14cedc9dd9dc050c6430dcc01"
44
+ },
45
+ {
46
+ "sourcePath": "bundle/generation_config.json",
47
+ "path": "generation_config.json",
48
+ "bytes": 186,
49
+ "sha256": "6f66aa5aaf68145027c3f3c384abfd837530fe4d1efb582e0aa919eecd4c9a8e"
50
+ },
51
+ {
52
+ "sourcePath": "bundle/onnx/model_q4f16.onnx",
53
+ "path": "onnx/model_q4f16.onnx",
54
+ "bytes": 362892,
55
+ "sha256": "ab10bc051ca80ad6dffe5045acac030d0d6b75c52d90db363d044f1f214fccfa"
56
+ },
57
+ {
58
+ "sourcePath": "bundle/onnx/model_q4f16.onnx_data",
59
+ "path": "onnx/model_q4f16.onnx_data",
60
+ "bytes": 1596739584,
61
+ "sha256": "2abad6aebb250a04619df22e8b2cab9998ee92ba083ec5426c97ae1beb8476f7"
62
+ },
63
+ {
64
+ "sourcePath": "bundle/onnx/model_q4f16.onnx_data_1",
65
+ "path": "onnx/model_q4f16.onnx_data_1",
66
+ "bytes": 272252928,
67
+ "sha256": "715887fa2ce24f2f1d5f5f8adaebc0caac4bad02e7d4d90e081f85decefec244"
68
+ },
69
+ {
70
+ "sourcePath": "bundle/tokenizer.json",
71
+ "path": "tokenizer.json",
72
+ "bytes": 10548223,
73
+ "sha256": "c7dc553ba83607a7d3cde4782e4055fcd15cb228f8f1cdc3fc149b1094580592"
74
+ },
75
+ {
76
+ "sourcePath": "bundle/tokenizer_config.json",
77
+ "path": "tokenizer_config.json",
78
+ "bytes": 9706,
79
+ "sha256": "f8a13de1b9989de61731cf9ce5805a9c3da2aad822ded0cd5375cc3d1206e24c"
80
+ },
81
+ {
82
+ "sourcePath": "runtime/inference-worker.js",
83
+ "path": "runtime/inference-worker.js",
84
+ "bytes": 58199,
85
+ "sha256": "6e8863a72f022fa8d12207d4029f8f33f339f3c2c066497618065cf642da85dd"
86
+ },
87
+ {
88
+ "sourcePath": "runtime/tool-call-parser.js",
89
+ "path": "runtime/tool-call-parser.js",
90
+ "bytes": 18197,
91
+ "sha256": "7a33bbcd31cc3910a169e735541c4d5189f85480237577f262de3154ce91f12d"
92
+ },
93
+ {
94
+ "sourcePath": "runtime/vendor/LICENSE.onnxruntime.txt",
95
+ "path": "runtime/vendor/LICENSE.onnxruntime.txt",
96
+ "bytes": 1073,
97
+ "sha256": "2f07c72751aed99790b8a4869cf2311df85a860b22ded05fa22803587a48922c"
98
+ },
99
+ {
100
+ "sourcePath": "runtime/vendor/LICENSE.transformers.txt",
101
+ "path": "runtime/vendor/LICENSE.transformers.txt",
102
+ "bytes": 11358,
103
+ "sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"
104
+ },
105
+ {
106
+ "sourcePath": "runtime/vendor/onnxruntime-common/backend-impl.js",
107
+ "path": "runtime/vendor/onnxruntime-common/backend-impl.js",
108
+ "bytes": 5339,
109
+ "sha256": "97dd81b055a06f61120f09468fe59f046a9262b7b87bf5d05bceb085bf4f5558"
110
+ },
111
+ {
112
+ "sourcePath": "runtime/vendor/onnxruntime-common/backend.js",
113
+ "path": "runtime/vendor/onnxruntime-common/backend.js",
114
+ "bytes": 184,
115
+ "sha256": "017ce2ec9dc2021ebc1ef2c4f04e1bc74276c65921b0297e7ed8cc16e640495b"
116
+ },
117
+ {
118
+ "sourcePath": "runtime/vendor/onnxruntime-common/env-impl.js",
119
+ "path": "runtime/vendor/onnxruntime-common/env-impl.js",
120
+ "bytes": 848,
121
+ "sha256": "0da4c562a49fe147e19320a64cbb456184eb0e6242b1fe69c65c8368c55f3d97"
122
+ },
123
+ {
124
+ "sourcePath": "runtime/vendor/onnxruntime-common/env.js",
125
+ "path": "runtime/vendor/onnxruntime-common/env.js",
126
+ "bytes": 262,
127
+ "sha256": "b2bbd3d3b00a95765713c57cd0f5ef73718ae5b8322e07cb47a43e2accb21f15"
128
+ },
129
+ {
130
+ "sourcePath": "runtime/vendor/onnxruntime-common/index.js",
131
+ "path": "runtime/vendor/onnxruntime-common/index.js",
132
+ "bytes": 1036,
133
+ "sha256": "ad23f6d6ce2c99c449031f24a5181f259516c64a9a67428c98a8539b857729bd"
134
+ },
135
+ {
136
+ "sourcePath": "runtime/vendor/onnxruntime-common/inference-session-impl.js",
137
+ "path": "runtime/vendor/onnxruntime-common/inference-session-impl.js",
138
+ "bytes": 8703,
139
+ "sha256": "0446814a392c147b5c38ba242d9911ee54002b5d01a8e19fb4895fb8f8a0cdd3"
140
+ },
141
+ {
142
+ "sourcePath": "runtime/vendor/onnxruntime-common/inference-session.js",
143
+ "path": "runtime/vendor/onnxruntime-common/inference-session.js",
144
+ "bytes": 348,
145
+ "sha256": "3ad4271a88edb3d242dc1e0e7bbf66510f9a56eff874c433b3b37f900ac007b1"
146
+ },
147
+ {
148
+ "sourcePath": "runtime/vendor/onnxruntime-common/onnx-model.js",
149
+ "path": "runtime/vendor/onnxruntime-common/onnx-model.js",
150
+ "bytes": 145,
151
+ "sha256": "9e9dc8678be0ea04575a542894508db355a4f9f2155ac7f4c17a697c5f843382"
152
+ },
153
+ {
154
+ "sourcePath": "runtime/vendor/onnxruntime-common/onnx-value.js",
155
+ "path": "runtime/vendor/onnxruntime-common/onnx-value.js",
156
+ "bytes": 145,
157
+ "sha256": "33dc232498753fc9026dfa44c99440cb1a6d29f3e53e1ad2fb8b696705158f06"
158
+ },
159
+ {
160
+ "sourcePath": "runtime/vendor/onnxruntime-common/tensor-conversion-impl.js",
161
+ "path": "runtime/vendor/onnxruntime-common/tensor-conversion-impl.js",
162
+ "bytes": 7852,
163
+ "sha256": "df6fa3cd1813c2d3e588cc07973cfe5ea2ab8647c957168200caa58e51440161"
164
+ },
165
+ {
166
+ "sourcePath": "runtime/vendor/onnxruntime-common/tensor-conversion.js",
167
+ "path": "runtime/vendor/onnxruntime-common/tensor-conversion.js",
168
+ "bytes": 152,
169
+ "sha256": "8c43ec1d061ab71032a1b28f45d85ef95f8d7bd2d4cd71d059e96d83e3a8f422"
170
+ },
171
+ {
172
+ "sourcePath": "runtime/vendor/onnxruntime-common/tensor-factory-impl.js",
173
+ "path": "runtime/vendor/onnxruntime-common/tensor-factory-impl.js",
174
+ "bytes": 11002,
175
+ "sha256": "cf5606fe23461e01bff59c2d72ac47d74e652548718f1e5fab07a22b441379b4"
176
+ },
177
+ {
178
+ "sourcePath": "runtime/vendor/onnxruntime-common/tensor-factory.js",
179
+ "path": "runtime/vendor/onnxruntime-common/tensor-factory.js",
180
+ "bytes": 149,
181
+ "sha256": "68b9f4f4573f25012ce8a4f73c9a2cee3a28db91de8bbb0a20ddb08bcba33c4b"
182
+ },
183
+ {
184
+ "sourcePath": "runtime/vendor/onnxruntime-common/tensor-impl-type-mapping.js",
185
+ "path": "runtime/vendor/onnxruntime-common/tensor-impl-type-mapping.js",
186
+ "bytes": 2706,
187
+ "sha256": "20bd5ddb858954cf071bace6c7a6628c8e311433798ec172f4c778bebd81d407"
188
+ },
189
+ {
190
+ "sourcePath": "runtime/vendor/onnxruntime-common/tensor-impl.js",
191
+ "path": "runtime/vendor/onnxruntime-common/tensor-impl.js",
192
+ "bytes": 16091,
193
+ "sha256": "6a749be63055f9d21069566b1515412484a3a03448ca20427c8f8d29328a2843"
194
+ },
195
+ {
196
+ "sourcePath": "runtime/vendor/onnxruntime-common/tensor-utils-impl.js",
197
+ "path": "runtime/vendor/onnxruntime-common/tensor-utils-impl.js",
198
+ "bytes": 1920,
199
+ "sha256": "87d3e07c153616138c2f57846545eea9c47957c714d50c0264696bdc293e6732"
200
+ },
201
+ {
202
+ "sourcePath": "runtime/vendor/onnxruntime-common/tensor-utils.js",
203
+ "path": "runtime/vendor/onnxruntime-common/tensor-utils.js",
204
+ "bytes": 147,
205
+ "sha256": "7acfdb2f8403ae84b7ae62d55d6eeb01519108be8874337b317b911ed60190cd"
206
+ },
207
+ {
208
+ "sourcePath": "runtime/vendor/onnxruntime-common/tensor.js",
209
+ "path": "runtime/vendor/onnxruntime-common/tensor.js",
210
+ "bytes": 286,
211
+ "sha256": "721d24a538de5333057bfc423bc00c3453a1c483f4495c857d0a6f9b8f2d330e"
212
+ },
213
+ {
214
+ "sourcePath": "runtime/vendor/onnxruntime-common/trace.js",
215
+ "path": "runtime/vendor/onnxruntime-common/trace.js",
216
+ "bytes": 1891,
217
+ "sha256": "9b87e6309a1c502b03558aa94b0a65c8ea1e8eb59cb46090f54f6eacd191b841"
218
+ },
219
+ {
220
+ "sourcePath": "runtime/vendor/onnxruntime-common/type-helper.js",
221
+ "path": "runtime/vendor/onnxruntime-common/type-helper.js",
222
+ "bytes": 146,
223
+ "sha256": "3e2588c6a5ee7b9755e199eef10c6aa2e777d0366ea47d27953657a5638deeae"
224
+ },
225
+ {
226
+ "sourcePath": "runtime/vendor/onnxruntime-common/version.js",
227
+ "path": "runtime/vendor/onnxruntime-common/version.js",
228
+ "bytes": 263,
229
+ "sha256": "00a18ffb867b29a3855fe00e965797dba270849134f1e0ab2929fb2761c0fe44"
230
+ },
231
+ {
232
+ "sourcePath": "runtime/vendor/ort-wasm-simd-threaded.asyncify.mjs",
233
+ "path": "runtime/vendor/ort-wasm-simd-threaded.asyncify.mjs",
234
+ "bytes": 47507,
235
+ "sha256": "7236653b8565da4046e459cd0e274123419a1d9f1f8f18fd36c28058346ca655"
236
+ },
237
+ {
238
+ "sourcePath": "runtime/vendor/ort-wasm-simd-threaded.asyncify.wasm",
239
+ "path": "runtime/vendor/ort-wasm-simd-threaded.asyncify.wasm",
240
+ "bytes": 24254953,
241
+ "sha256": "7e83cd6cee77e478bc96a7e91b198144fb5e4126287daf1f9b54bb195ebcd55a"
242
+ },
243
+ {
244
+ "sourcePath": "runtime/vendor/ort.webgpu.mjs",
245
+ "path": "runtime/vendor/ort.webgpu.mjs",
246
+ "bytes": 679694,
247
+ "sha256": "177cf67f3e6c25e951a76a981a857e6af53b1571016685cc2480e1e6e47b1d46"
248
+ },
249
+ {
250
+ "sourcePath": "runtime/vendor/README.md",
251
+ "path": "runtime/vendor/README.md",
252
+ "bytes": 8373,
253
+ "sha256": "a3b6d952b825a7382f16f89c1431e57090e6c3c2d1267ce1548069c802c103c7"
254
+ },
255
+ {
256
+ "sourcePath": "runtime/vendor/ThirdPartyNotices.onnxruntime.txt",
257
+ "path": "runtime/vendor/ThirdPartyNotices.onnxruntime.txt",
258
+ "bytes": 325054,
259
+ "sha256": "0e07b95f3a8d6230037707c5c4a2b554d12c4cb67369669ac255635528ffcee2"
260
+ },
261
+ {
262
+ "sourcePath": "runtime/vendor/transformers.web.js",
263
+ "path": "runtime/vendor/transformers.web.js",
264
+ "bytes": 1099925,
265
+ "sha256": "83e3edd00c98fef962da9bdc22fe4b63b3858743a76c1b7e46bee8e483033644"
266
+ }
267
+ ],
268
+ "optionalParserIncludedSeparately": true,
269
+ "bundledParserReplaced": false
270
+ }
README.md ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: other
3
+ license_name: webbrain-noncommercial-research-restrictions
4
+ license_link: https://huggingface.co/webbrain-one/webbrain-compass-tiny-v2.1/blob/main/ATTRIBUTIONS.md
5
+ library_name: transformers.js
6
+ base_model: webbrain-one/webbrain-compass-tiny-v2
7
+ pipeline_tag: text-generation
8
+ tags:
9
+ - onnx
10
+ - webgpu
11
+ - q4f16
12
+ - gptq
13
+ - tool-calling
14
+ - experimental
15
+ ---
16
+ # WebBrain Compass Tiny v2.1
17
+
18
+ **Private experimental q4f16/WebGPU release of the tested v24 candidate.**
19
+ Based on WebBrain Compass Tiny v2, a fine-tune of **OpenBMB MiniCPM5-2B (~2.6B parameters)**.
20
+ v2.1 is an **export/runtime revision**, not a new training run. The merged BF16 source remains
21
+ `webbrain-one/webbrain-compass-tiny-v2` at `54bcab6731939137d0489ac01463c9404dff9da8`.
22
+ The previous v23 release is separate and unchanged.
23
+
24
+ ## What improved
25
+
26
+ Only the ONNX graph's 211 `MatMulNBits` `accuracy_level` attributes changed from 4 to 2,
27
+ disabling the dynamic INT8-activation path used in the pinned WebGPU runtime. Quantized
28
+ weight shards, tokenizer, config and vendored runtime remain byte-identical to v23.
29
+
30
+ | Check | v23 | v2.1 / v24 |
31
+ |---|---:|---:|
32
+ | Quantized PyTorch vs WebGPU numerical checks | 2/12 pass | **12/12 pass** |
33
+ | Worst relative RMS logit difference; unchanged limit 2% | 6.5274% | **1.8334%** |
34
+ | Minimum cosine; unchanged limit 0.999 | 0.998065500 | **0.999833960** |
35
+
36
+ Generation smoke (8 cases), bounded **16,384-total-token** context/cache checks, and a
37
+ cold-browser restart with model downloads disabled passed. All three cold-cache native
38
+ tool-call outputs were unchanged. Numerical tests cover three prompts, prefill plus
39
+ three cached steps each, with identical token prefixes. They establish runtime parity
40
+ in those probes—not BF16 equivalence, universal numerical accuracy, or a percentage
41
+ loss in task accuracy. INT4 quality loss remains a separate issue; a Turkish smoke
42
+ response remained awkward despite passing structural checks.
43
+
44
+ ## Fixed next-response routing results
45
+
46
+ | Metric | BF16 reference | v2.1 / v24 WebGPU |
47
+ |---|---:|---:|
48
+ | First-turn structured calls | 94/100 | **98/100** |
49
+ | Strict exact action | 16/89 | **15/89** |
50
+ | Loose tool-family match | 41/89 | **43/89** |
51
+
52
+ This is **not end-to-end browser-agent task success or an Online-Mind2Web result**.
53
+ One run: 100 first-turn cases, 89 scored scenarios and 11 predetermined skips;
54
+ 194 raw responses including 5 smoke requests were retained locally. The 193 pinned
55
+ input files match the BF16 reference, but precision, runtime and JS/Torch random
56
+ generators differ. No output repair, model helper, fallback or result-based retry.
57
+
58
+ **The full predeclared behavioral gate failed:** 3 explicitly discouraged actions
59
+ versus a limit of 1. These were retrying an access-blocked URL, continuing a listing
60
+ before reporting available results, and inventing a pagination URL. Other declared
61
+ gates passed, with zero transport errors, zero native-decoder parser errors and zero
62
+ length-limited responses. The owner authorized this separate release with these
63
+ findings disclosed; it is not certified for unattended consequential actions.
64
+
65
+ ## Exact package
66
+
67
+ - GPTQ asymmetric INT4 weights, group size 32; FP16 scales/activations/KV cache.
68
+ - Default graph: `onnx/model_q4f16.onnx`. **Both** external data files are required:
69
+ `model_q4f16.onnx_data` and `model_q4f16.onnx_data_1` (1,868,992,512 bytes combined).
70
+ - The inherited BF16 dtype and 131072 architecture field in `config.json` do not
71
+ describe this quantized graph or establish a tested browser context limit.
72
+ - Exact tested runtime under `runtime/`: Transformers.js **4.2.0**, ONNX Runtime Web
73
+ **1.27.0**, WebBrain worker/parser and dependency licences. Tested with Chrome
74
+ **150.0.7871.187**, NVIDIA **RTX 5090**. Other devices/runtimes are not verified here.
75
+ - Generation-only, last-token logits; thinking disabled. No cloud or visual model.
76
+ - Tests used a local model transport alias; this repository name is the release
77
+ identifier. All 39 tested model/runtime files are mapped and hashed in provenance.
78
+ - `validation/` contains results and compact audit summaries. Complete original
79
+ local audit hashes are recorded; private training/calibration data, browser profiles,
80
+ credentials and raw benchmark requests are not included.
81
+
82
+ ## Integration
83
+
84
+ 1. Download the **pinned commit returned with the release**, using an HF account with
85
+ private-repository access:
86
+
87
+ ```sh
88
+ hf download webbrain-one/webbrain-compass-tiny-v2.1 --revision <PINNED_COMMIT> --local-dir ./compass-tiny-v2.1
89
+ ```
90
+
91
+ Keep `onnx/`, tokenizer/config files and both external shards together. Never embed
92
+ a personal/write token in an extension, webpage, source file or log. Use a trusted
93
+ authenticated asset proxy or a private local mirror pinned to that same commit.
94
+ Anonymous HF downloads cannot access this private release.
95
+
96
+ 2. In WebBrain, choose the custom model ID `webbrain-one/webbrain-compass-tiny-v2.1`,
97
+ **device `webgpu`, dtype `q4f16`**, default filename stem `model`. Use the supplied
98
+ runtime assets. The unchanged worker has no revision option: pin the asset proxy,
99
+ or explicitly pass `revision` at pipeline initialization in your integration.
100
+ The new model ID has a separate cache key; do not alias it onto v23's cached graph.
101
+
102
+ Equivalent library initialization, with executable runtime assets served locally:
103
+
104
+ ```js
105
+ import { env, pipeline } from './runtime/vendor/transformers.web.js';
106
+ import { parseMiniCpmToolCalls } from './integration/minicpm5-tool-parser.mjs';
107
+
108
+ env.backends.onnx.wasm.numThreads = 1;
109
+ env.backends.onnx.wasm.wasmPaths = {
110
+ mjs: new URL('./runtime/vendor/ort-wasm-simd-threaded.asyncify.mjs', import.meta.url).href,
111
+ wasm: new URL('./runtime/vendor/ort-wasm-simd-threaded.asyncify.wasm', import.meta.url).href,
112
+ };
113
+ // Configure your trusted authenticated asset proxy before accessing this private repo.
114
+ const generator = await pipeline('text-generation',
115
+ 'webbrain-one/webbrain-compass-tiny-v2.1', {
116
+ revision: '<PINNED_COMMIT>', device: 'webgpu', dtype: 'q4f16',
117
+ session_options: { extra: {
118
+ 'ep.webgpuexecutionprovider.storageBufferCacheMode': 'simple',
119
+ } },
120
+ });
121
+ try {
122
+ const result = await generator(messages, {
123
+ tools, do_sample: false, max_new_tokens: 256,
124
+ tokenizer_encode_kwargs: { enable_thinking: false },
125
+ });
126
+ const generated = result[0].generated_text;
127
+ const text = Array.isArray(generated) ? generated.at(-1).content : generated;
128
+ const calls = parseMiniCpmToolCalls(text, tools);
129
+ // Retain raw text. Validate argument schemas and user authorization before dispatch.
130
+ // An empty calls list is not automatically successful task completion.
131
+ } finally {
132
+ await generator.dispose();
133
+ }
134
+ ```
135
+
136
+ The native custom-model worker defaults to greedy generation and caps output at
137
+ 256 tokens. The routing benchmark used its documented test hook (4096-token budget,
138
+ temperature 0.15/0.3, seed 3407); do not claim those scores for the default UI settings.
139
+ Budget input plus output within the tested 16K limit and start with short tasks.
140
+
141
+ 3. **Parser integration is explicit, not silently changed.** The exact bundled parser
142
+ under `runtime/` matches 162/194 recorded native-decoder responses. It can leave
143
+ Python-style `False` and `['green', 'amber']` as strings. The optional, separately
144
+ audited `integration/minicpm5-tool-parser.mjs` matches **194/194** recorded responses
145
+ and passed its allowlist checks; it is not automatically wired into the worker.
146
+ Pass the full tool schemas so string-valued JSON bodies remain strings. The helper
147
+ does not execute literals, validate every schema constraint, or grant action approval.
148
+ Preserve downstream validation, confirmations and raw-output/error logging.
149
+
150
+ CPU-only helper checks: `node --test integration/parser.test.mjs`.
151
+
152
+ This release remains subject to `ATTRIBUTIONS.md`, including the project's
153
+ noncommercial-research restrictions. Private hosting does not expand usage rights.
chat_template.jinja ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {{- bos_token }}{%- if tools %}
2
+ {%- set tool_definitions %}
3
+ {{- "# Tools\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
4
+ {%- for tool in tools %}
5
+ {{- "\n" }}
6
+ {{- tool | tojson(ensure_ascii=False) }}
7
+ {%- endfor %}
8
+ {{- '\n</tools>\n\nTool usage guidelines:\n- You may call zero or more functions. If no function calls are needed, just answer normally and do not include any <function ... </function>.\n- When calling a function, return an XML object within <function ... </function> using:\n<function name="function-name"><param name="param-name">param-value</param></function>\n- param-value may be multi-line. If it contains <, & or newline characters, wrap it in a CDATA block: <param name="param-name"><![CDATA[...multi-line value...]]></param>' }}
9
+ {%- endset %}
10
+
11
+ {{- '<|im_start|>system\n' }}
12
+ {%- if messages[0].role == 'system' %}
13
+ {%- if '<tool_def_sep>' in messages[0].content %}
14
+ {{- messages[0].content.replace('<tool_def_sep>', tool_definitions) }}
15
+ {%- else %}
16
+ {{- messages[0].content + '\n\n' + tool_definitions }}
17
+ {%- endif %}
18
+ {%- else %}
19
+ {{- tool_definitions.lstrip() }}
20
+ {%- endif %}
21
+ {{- '<|im_end|>\n' }}
22
+ {%- else %}
23
+ {%- if messages[0].role == 'system' %}
24
+ {{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
25
+ {%- endif %}
26
+ {%- endif %}
27
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
28
+ {%- for message in messages[::-1] %}
29
+ {%- set index = (messages|length - 1) - loop.index0 %}
30
+ {%- if ns.multi_step_tool and message.role == "user" and message.content is string and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
31
+ {%- set ns.multi_step_tool = false %}
32
+ {%- set ns.last_query_index = index %}
33
+ {%- endif %}
34
+ {%- endfor %}
35
+ {%- for message in messages %}
36
+ {%- if message.content is string %}
37
+ {%- set content = message.content %}
38
+ {%- else %}
39
+ {%- set content = '' %}
40
+ {%- endif %}
41
+ {%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
42
+ {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
43
+ {%- elif message.role == "assistant" %}
44
+ {%- set reasoning_content = '' %}
45
+ {%- if message.reasoning_content is string %}
46
+ {%- set reasoning_content = message.reasoning_content %}
47
+ {%- else %}
48
+ {%- if '</think>' in content %}
49
+ {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
50
+ {%- set content = content.split('</think>')[-1].lstrip('\n') %}
51
+ {%- endif %}
52
+ {%- endif %}
53
+
54
+ {%- if message.tool_calls %}
55
+ {%- set content_parts = content.split('<tool_sep>') %}
56
+ {%- set processed_content = content_parts[0] %}
57
+ {%- set tool_calls_count = message.tool_calls|length %}
58
+ {%- set tool_sep_count = content_parts|length - 1 %}
59
+
60
+ {%- for i in range(1, content_parts|length) %}
61
+ {%- set tool_index = i - 1 %}
62
+ {%- if tool_index < tool_calls_count %}
63
+ {%- set tool_call = message.tool_calls[tool_index] %}
64
+ {%- if tool_call.function %}
65
+ {%- set tool_call = tool_call.function %}
66
+ {%- endif %}
67
+ {%- set single_tool_xml %}
68
+ {{- '<function name="' ~ tool_call.name ~ '">' }}
69
+ {%- if tool_call.arguments %}
70
+ {%- set args_dict = tool_call.arguments %}
71
+ {%- for param_name, param_value in args_dict.items() %}
72
+ {{- '<param name="' ~ param_name ~ '">' }}
73
+ {%- if param_value is string and ('<' in param_value or '&' in param_value or '\n' in param_value) %}
74
+ {{- '<![CDATA[' + param_value + ']]>' }}
75
+ {%- else %}
76
+ {{- param_value }}
77
+ {%- endif %}
78
+ {{- '</param>' }}
79
+ {%- endfor %}
80
+ {%- endif %}
81
+ {{- '</function>' }}
82
+ {%- endset %}
83
+ {%- set processed_content = processed_content + single_tool_xml + content_parts[i] %}
84
+ {%- else %}
85
+ {%- set processed_content = processed_content + content_parts[i] %}
86
+ {%- endif %}
87
+ {%- endfor %}
88
+
89
+ {%- if tool_calls_count > tool_sep_count %}
90
+ {%- for remaining_index in range(tool_sep_count, tool_calls_count) %}
91
+ {%- set tool_call = message.tool_calls[remaining_index] %}
92
+ {%- if tool_call.function %}
93
+ {%- set tool_call = tool_call.function %}
94
+ {%- endif %}
95
+ {%- set remaining_tool_xml %}
96
+ {{- '<function name="' ~ tool_call.name ~ '">' }}
97
+ {%- if tool_call.arguments %}
98
+ {%- set args_dict = tool_call.arguments %}
99
+ {%- for param_name, param_value in args_dict.items() %}
100
+ {{- '<param name="' ~ param_name ~ '">' }}
101
+ {%- if param_value is string and ('<' in param_value or '&' in param_value or '\n' in param_value) %}
102
+ {{- '<![CDATA[' + param_value + ']]>' }}
103
+ {%- else %}
104
+ {{- param_value }}
105
+ {%- endif %}
106
+ {{- '</param>' }}
107
+ {%- endfor %}
108
+ {%- endif %}
109
+ {{- '</function>' }}
110
+ {%- endset %}
111
+ {%- set processed_content = processed_content + remaining_tool_xml %}
112
+ {%- endfor %}
113
+ {%- endif %}
114
+
115
+ {%- set content = processed_content %}
116
+ {%- endif %}
117
+
118
+ {%- if reasoning_content %}
119
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
120
+ {%- elif '<think>' not in content and '</think>' not in content %}
121
+ {{- '<|im_start|>' + message.role + '\n<think>\n\n</think>\n\n' + content.lstrip('\n') }}
122
+ {%- else %}
123
+ {{- '<|im_start|>' + message.role + '\n' + content }}
124
+ {%- endif %}
125
+
126
+ {%- if message.tool_calls and not has_tool_sep %}
127
+ {%- for tool_call in message.tool_calls %}
128
+ {%- if (loop.first and content) or (not loop.first) %}
129
+ {{- '\n' }}
130
+ {%- endif %}
131
+ {%- if tool_call.function %}
132
+ {%- set tool_call = tool_call.function %}
133
+ {%- endif %}
134
+ {{- '<function name="' ~ tool_call.name ~ '">' }}
135
+ {%- if tool_call.arguments %}
136
+ {%- set args_dict = tool_call.arguments %}
137
+ {%- for param_name, param_value in args_dict.items() %}
138
+ {{- '<param name="' ~ param_name ~ '">' }}
139
+ {%- if param_value is string and ('<' in param_value or '&' in param_value or '\n' in param_value) %}
140
+ {{- '<![CDATA[' + param_value + ']]>' }}
141
+ {%- else %}
142
+ {{- param_value }}
143
+ {%- endif %}
144
+ {{- '</param>' }}
145
+ {%- endfor %}
146
+ {%- endif %}
147
+ {{- '</function>' }}
148
+ {%- endfor %}
149
+ {%- endif %}
150
+ {{- '<|im_end|>\n' }}
151
+ {%- elif message.role == "tool" %}
152
+ {%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
153
+ {{- '<|im_start|>user' }}
154
+ {%- endif %}
155
+ {{- '\n<tool_response>\n' }}
156
+ {%- if message.content is string %}
157
+ {{- content }}
158
+ {%- else %}
159
+ {{- message.content | tojson(ensure_ascii=False) }}
160
+ {%- endif %}
161
+ {{- '\n</tool_response>' }}
162
+ {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
163
+ {{- '<|im_end|>\n' }}
164
+ {%- endif %}
165
+ {%- endif %}
166
+ {%- endfor %}
167
+ {%- if add_generation_prompt %}
168
+ {{- '<|im_start|>assistant\n' }}
169
+ {%- if enable_thinking is defined %}
170
+ {%- if enable_thinking is false %}
171
+ {{- '<think>\n\n</think>\n\n' }}
172
+ {%- elif enable_thinking is true %}
173
+ {{- '<think>\n' }}
174
+ {%- endif %}
175
+ {%- endif %}
176
+ {%- endif %}
config.json ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "LlamaForCausalLM"
4
+ ],
5
+ "attention_bias": false,
6
+ "attention_dropout": 0.0,
7
+ "bos_token_id": 0,
8
+ "dtype": "bfloat16",
9
+ "eos_token_id": [
10
+ 1,
11
+ 130073
12
+ ],
13
+ "head_dim": 128,
14
+ "hidden_act": "silu",
15
+ "hidden_size": 2048,
16
+ "initializer_range": 0.02,
17
+ "intermediate_size": 6144,
18
+ "max_position_embeddings": 131072,
19
+ "mlp_bias": false,
20
+ "model_type": "llama",
21
+ "num_attention_heads": 16,
22
+ "num_hidden_layers": 42,
23
+ "num_key_value_heads": 2,
24
+ "pad_token_id": 1,
25
+ "pretraining_tp": 1,
26
+ "rms_norm_eps": 1e-06,
27
+ "rope_parameters": {
28
+ "rope_theta": 5000000,
29
+ "rope_type": "default"
30
+ },
31
+ "tie_word_embeddings": false,
32
+ "transformers_version": "5.16.1",
33
+ "use_cache": true,
34
+ "vocab_size": 130560,
35
+ "transformers.js_config": {
36
+ "kv_cache_dtype": {
37
+ "q4f16": "float16"
38
+ },
39
+ "use_external_data_format": {
40
+ "model_q4f16.onnx": 2
41
+ }
42
+ }
43
+ }
generation_config.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 0,
4
+ "eos_token_id": [
5
+ 1,
6
+ 130073
7
+ ],
8
+ "pad_token_id": 1,
9
+ "transformers_version": "5.16.1",
10
+ "use_cache": true
11
+ }
integration/minicpm5-tool-parser.mjs ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // MiniCPM5 native XML uses Python literals for typed parameters. No eval/Function.
2
+ export function parseMiniCpmParamValue(input, schema = {}) {
3
+ const raw = String(input ?? '');
4
+ const cdata = /^\s*<!\[CDATA\[([\s\S]*?)\]\]>\s*$/.exec(raw);
5
+ let decoded=cdata ? cdata[1] : raw;
6
+ if(!cdata) {
7
+ if(raw.includes('<')) throw Error('Malformed XML parameter: use CDATA for literal markup');
8
+ decoded=raw.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);/gi,(_,entity)=>{
9
+ if(entity.startsWith('#')) { const n=entity[1].toLowerCase()==='x'?parseInt(entity.slice(2),16):parseInt(entity.slice(1),10);return String.fromCodePoint(n); }
10
+ return {amp:'&',lt:'<',gt:'>',quot:'"',apos:"'"}[entity];
11
+ });
12
+ if(/&(?!(?:#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);)/i.test(raw)) throw Error('Unescaped XML ampersand');
13
+ }
14
+ if(schema.type==='string') return decoded;
15
+ const text = decoded.trim();
16
+ if (!text) return '';
17
+ let i = 0;
18
+ const ws = () => { while (i < text.length && /\s/u.test(text[i])) i++; };
19
+ const take = char => { ws(); if (text[i++] !== char) throw Error('Invalid literal'); };
20
+ function value(depth = 0) {
21
+ if (depth > 32) throw Error('Literal nesting limit');
22
+ ws();
23
+ const c = text[i];
24
+ if (c === "'" || c === '"') {
25
+ const quote = text[i++]; let result = '';
26
+ while (i < text.length) {
27
+ const ch = text[i++];
28
+ if (ch === quote) return result;
29
+ if (ch !== '\\') { result += ch; continue; }
30
+ const e = text[i++];
31
+ const simple = { '\\':'\\', "'":"'", '"':'"', n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', '/':'/' };
32
+ if (Object.hasOwn(simple,e)) { result += simple[e]; continue; }
33
+ const width = e === 'u' ? 4 : e === 'x' ? 2 : 0;
34
+ const hex = text.slice(i,i+width);
35
+ if (!width || !new RegExp(`^[a-fA-F0-9]{${width}}$`).test(hex)) throw Error('Invalid escape');
36
+ result += String.fromCharCode(parseInt(hex,16)); i += width;
37
+ }
38
+ throw Error('Unterminated string');
39
+ }
40
+ if (c === '[' || c === '{') {
41
+ i++; const array = c === '['; const end = array ? ']' : '}';
42
+ const result = array ? [] : Object.create(null);
43
+ ws(); if (text[i] === end) { i++; return array ? result : {...result}; }
44
+ while (i < text.length) {
45
+ if (array) result.push(value(depth+1));
46
+ else {
47
+ ws(); if (!['"',"'"].includes(text[i])) throw Error('Expected string key');
48
+ const key = value(depth+1);
49
+ if (Object.hasOwn(result,key)) throw Error('Duplicate key');
50
+ take(':'); result[key] = value(depth+1);
51
+ }
52
+ ws(); if (text[i] === end) { i++; return array ? result : {...result}; }
53
+ take(','); ws();
54
+ if (text[i] === end) { i++; return array ? result : {...result}; }
55
+ }
56
+ throw Error('Unterminated container');
57
+ }
58
+ const scalar = /^(True|False|None|true|false|null)(?![\w])/u.exec(text.slice(i));
59
+ if (scalar) { i += scalar[0].length; return {True:true,False:false,None:null,true:true,false:false,null:null}[scalar[0]]; }
60
+ const number = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/u.exec(text.slice(i));
61
+ if (number) { i += number[0].length; const result=Number(number[0]); if(Number.isFinite(result)) return result; }
62
+ throw Error('Not a literal');
63
+ }
64
+ try { const result=value(); ws(); if(i===text.length) return result; } catch { /* native strings are also unquoted */ }
65
+ return text;
66
+ }
67
+
68
+ // Strict standalone adapter. Keep downstream tool schema/approval validation in place.
69
+ export function parseMiniCpmToolCalls(text, toolsOrNames) {
70
+ const tools=Array.isArray(toolsOrNames)?toolsOrNames:[];
71
+ const allowedNames=Array.isArray(toolsOrNames)?new Set(tools.map(t=>t.function.name)):toolsOrNames;
72
+ const schemas=new Map(tools.map(t=>[t.function.name,t.function.parameters || {}]));
73
+ const source=String(text ?? '').trim();
74
+ if (!source || source.length>10000 || !(allowedNames instanceof Set)) return [];
75
+ // Native MiniCPM may explain an error before emitting the XML tool call.
76
+ // Accept that prose, but never skip over a malformed XML call or parameter.
77
+ const start=source.indexOf('<function');
78
+ if(start<0 || /<\/?(?:function|param)\b/.test(source.slice(0,start))) return [];
79
+ const calls=[]; let cursor=start;
80
+ const functions=/<function\s+name=["']([A-Za-z_]\w*)["']\s*>([\s\S]*?)<\/function>/gy;
81
+ const params=/<param\s+name=["']([A-Za-z_]\w*)["']\s*>([\s\S]*?)<\/param>/gy;
82
+ while(cursor<source.length) {
83
+ while (/\s/u.test(source[cursor] || '')) cursor++;
84
+ if(cursor===source.length) break;
85
+ functions.lastIndex=cursor; const fn=functions.exec(source);
86
+ if(!fn || !allowedNames.has(fn[1])) return [];
87
+ const args=Object.create(null); let pos=0;
88
+ while(pos<fn[2].length) {
89
+ while(/\s/u.test(fn[2][pos] || '')) pos++;
90
+ if(pos===fn[2].length) break;
91
+ params.lastIndex=pos; const param=params.exec(fn[2]);
92
+ if(!param || Object.hasOwn(args,param[1])) return [];
93
+ try { args[param[1]]=parseMiniCpmParamValue(param[2],schemas.get(fn[1])?.properties?.[param[1]]); }
94
+ catch { return []; }
95
+ pos=params.lastIndex;
96
+ }
97
+ calls.push({id:`compass_call_${Date.now()}_${calls.length}`,type:'function',function:{name:fn[1],arguments:JSON.stringify(args)}});
98
+ cursor=functions.lastIndex;
99
+ }
100
+ return calls;
101
+ }
integration/parser.test.mjs ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Portable CPU-only checks for the separately audited optional helper.
2
+ import test from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import {parseMiniCpmParamValue as parse, parseMiniCpmToolCalls as calls} from './minicpm5-tool-parser.mjs';
5
+
6
+ const tools = [{type: 'function', function: {name: 'save_labels', parameters: {
7
+ type: 'object', properties: {labels: {type: 'array', items: {type: 'string'}}, enabled: {type: 'boolean'}},
8
+ }}}];
9
+ const xml = '<function name="save_labels"><param name="labels">[\'green\', \'amber\']</param><param name="enabled">False</param></function>';
10
+
11
+ test('v24 native list and boolean keep their argument types', () => {
12
+ assert.deepEqual(JSON.parse(calls(xml, tools)[0].function.arguments), {labels: ['green', 'amber'], enabled: false});
13
+ });
14
+ test('CDATA URL and string-typed JSON stay strings', () => {
15
+ assert.equal(parse('<![CDATA[https://example.net/find?name=blue&sort=recent]]>', {type: 'string'}), 'https://example.net/find?name=blue&sort=recent');
16
+ assert.equal(parse('{"message":""}', {type: 'string'}), '{"message":""}');
17
+ });
18
+ test('unknown tool names and mixed unknown batches are rejected', () => {
19
+ const unknown = '<function name="unprovided_tool"></function>';
20
+ assert.deepEqual(calls(unknown, tools), []);
21
+ assert.deepEqual(calls(xml + unknown, tools), []);
22
+ assert.deepEqual(calls(xml, []), []);
23
+ });
24
+ test('malformed and duplicate native fields are not repaired', () => {
25
+ assert.deepEqual(calls(xml.replace('</function>', ''), tools), []);
26
+ assert.deepEqual(calls(xml.replace('</function>', '<param name="enabled">True</param></function>'), tools), []);
27
+ assert.deepEqual(calls(xml + 'garbage', tools), []);
28
+ });
29
+ test('nested literals do not evaluate code or pollute prototypes', () => {
30
+ assert.deepEqual(parse("{'flag': True, 'next': None, 'xs': [1, 2.5]}"), {flag: true, next: null, xs: [1, 2.5]});
31
+ assert.equal(parse("['x', dangerous()]"), "['x', dangerous()]");
32
+ assert.equal(parse('True + exec()'), 'True + exec()');
33
+ parse("{'__proto__': {'polluted': True}}");
34
+ assert.equal(Object.prototype.polluted, undefined);
35
+ });
36
+ test('zero-argument native functions remain valid', () => {
37
+ const result = calls('<function name="get_accessibility_tree"></function>', new Set(['get_accessibility_tree']));
38
+ assert.equal(result.length, 1);
39
+ assert.deepEqual(JSON.parse(result[0].function.arguments), {});
40
+ });
onnx/model_q4f16.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ab10bc051ca80ad6dffe5045acac030d0d6b75c52d90db363d044f1f214fccfa
3
+ size 362892
onnx/model_q4f16.onnx_data ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2abad6aebb250a04619df22e8b2cab9998ee92ba083ec5426c97ae1beb8476f7
3
+ size 1596739584
onnx/model_q4f16.onnx_data_1 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:715887fa2ce24f2f1d5f5f8adaebc0caac4bad02e7d4d90e081f85decefec244
3
+ size 272252928
runtime/inference-worker.js ADDED
@@ -0,0 +1,1539 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Dedicated WebGPU worker for endpoint-free local model inference.
3
+ *
4
+ * WebGPU is unavailable in the MV3 service worker, and large ONNX allocations
5
+ * are more reliable in a dedicated worker than on the offscreen document's
6
+ * main thread. The offscreen host owns this worker and proxies correlated
7
+ * request/response messages to it.
8
+ */
9
+
10
+ let libraryPromise = null;
11
+ let libraryVersion = null;
12
+ let workerConfig = null;
13
+ let visionRuntime = null;
14
+ let visionRuntimeKey = '';
15
+ let visionRuntimeModelKey = '';
16
+ let visionRuntimeOwner = '';
17
+ let visionRuntimeLoadPromise = null;
18
+ let visionRuntimeLoadKey = '';
19
+ let textRuntime = null;
20
+ let textRuntimeKey = '';
21
+ let textRuntimeModelKey = '';
22
+ let textRuntimeLoadPromise = null;
23
+ let textRuntimeLoadKey = '';
24
+ let modelOperationQueue = Promise.resolve();
25
+ const TRANSFORMERS_CACHE_NAME = 'transformers-cache';
26
+ const TEXT_DOWNLOAD_EVENT = 'text-download-state';
27
+ const WEBGPU_TEXT_MAX_NEW_TOKENS = 256;
28
+ const WEBGPU_LFM25_MODEL_ID = 'LiquidAI/LFM2.5-2.6B-ONNX';
29
+ const WEBGPU_LFM25_12B_INSTRUCT_MODEL_ID = 'LiquidAI/LFM2.5-1.2B-Instruct-ONNX';
30
+ const WEBGPU_LFM25_12B_THINKING_MODEL_ID = 'LiquidAI/LFM2.5-1.2B-Thinking-ONNX';
31
+ const WEBGPU_LFM25_VL_16B_MODEL_ID = 'LiquidAI/LFM2.5-VL-1.6B-ONNX';
32
+ const WEBGPU_LFM25_VL_3B_MODEL_ID = 'LiquidAI/LFM2.5-VL-3B-ONNX';
33
+ const WEBGPU_NANBEIGE42_3B_MODEL_ID = 'Michionlion/Nanbeige4.2-3B-ONNX-WebGPU';
34
+ const WEBGPU_MINICPM5_2B_MODEL_ID = 'RASMUS/MiniCPM5-2B-ONNX';
35
+ const WEBGPU_BONSAI27_MODEL_ID = 'prism-ml/Bonsai-27B-gguf';
36
+ const WEBGPU_LFM25_MAX_NEW_TOKENS = 2048;
37
+ const WEBGPU_LFM25_TEXT_MODEL_IDS = new Set([
38
+ WEBGPU_LFM25_MODEL_ID,
39
+ WEBGPU_LFM25_12B_INSTRUCT_MODEL_ID,
40
+ WEBGPU_LFM25_12B_THINKING_MODEL_ID,
41
+ ]);
42
+ const WEBGPU_LFM25_VL_MODEL_IDS = new Set([
43
+ WEBGPU_LFM25_VL_16B_MODEL_ID,
44
+ WEBGPU_LFM25_VL_3B_MODEL_ID,
45
+ ]);
46
+ // Presets whose reasoning is generated as hidden thinking rather than as part
47
+ // of the visible answer, so they need the long output budget.
48
+ const WEBGPU_REASONING_MODEL_IDS = new Set([
49
+ WEBGPU_LFM25_MODEL_ID,
50
+ WEBGPU_LFM25_12B_THINKING_MODEL_ID,
51
+ WEBGPU_NANBEIGE42_3B_MODEL_ID,
52
+ WEBGPU_MINICPM5_2B_MODEL_ID,
53
+ ]);
54
+ // Chat templates that emit the opening `<think>` themselves, so the runtime
55
+ // only returns the reasoning suffix.
56
+ const WEBGPU_OPEN_THINKING_MODEL_IDS = new Set([
57
+ WEBGPU_LFM25_MODEL_ID,
58
+ WEBGPU_NANBEIGE42_3B_MODEL_ID,
59
+ ]);
60
+ const WEBGPU_LONG_OUTPUT_MODEL_IDS = new Set([
61
+ ...WEBGPU_LFM25_TEXT_MODEL_IDS,
62
+ WEBGPU_NANBEIGE42_3B_MODEL_ID,
63
+ WEBGPU_MINICPM5_2B_MODEL_ID,
64
+ ]);
65
+ // Publisher-recommended decoding for the shipped reasoning presets. Custom
66
+ // repositories keep Transformers.js greedy defaults.
67
+ const WEBGPU_TEXT_SAMPLING = new Map([
68
+ [WEBGPU_LFM25_MODEL_ID, { temperature: 0.1, top_k: 50, repetition_penalty: 1.1 }],
69
+ [WEBGPU_LFM25_12B_THINKING_MODEL_ID, { temperature: 0.05, top_k: 50, repetition_penalty: 1.05 }],
70
+ // Nanbeige4.2-3B's own generation_config.json.
71
+ [WEBGPU_NANBEIGE42_3B_MODEL_ID, { temperature: 0.6, top_k: 20, top_p: 0.95 }],
72
+ // MiniCPM5-2B quickstart: temperature=1.0, top_p=0.95.
73
+ [WEBGPU_MINICPM5_2B_MODEL_ID, { temperature: 1.0, top_p: 0.95 }],
74
+ ]);
75
+ // Nanbeige ships a single WebGPU-fused graph under a non-default file name.
76
+ // Without this override Transformers.js looks for `onnx/model_q4f16.onnx`,
77
+ // which the repository does not publish.
78
+ const WEBGPU_TEXT_MODEL_FILE_NAMES = new Map([
79
+ [WEBGPU_NANBEIGE42_3B_MODEL_ID, 'model_webgpu_mlp'],
80
+ ]);
81
+ const WEBGPU_VISION_READY_MARKER_VERSION = 2;
82
+ const WEBGPU_VISION_READY_MARKER_PREFIX = 'https://webbrain.one/.well-known/webgpu-vision-ready/';
83
+ function createWebGpuTextSessionOptions() {
84
+ return {
85
+ extra: {
86
+ // ORT's default bucket cache can retain rounded-up transient buffers. That
87
+ // is especially costly for dynamic prefill/decode shapes on Metal.
88
+ 'ep.webgpuexecutionprovider.storageBufferCacheMode': 'simple',
89
+ },
90
+ };
91
+ }
92
+ const readyTextModelKeys = new Set();
93
+ const textDownloadFiles = new Map();
94
+ const nativeFetch = typeof globalThis.fetch === 'function' ? globalThis.fetch.bind(globalThis) : null;
95
+ let activeTextDownloadModelId = '';
96
+ let queuedTextDownload = null;
97
+ let textDownloadAbortController = null;
98
+ let textDownloadCancelMode = '';
99
+ let activeVisionDownloadRequest = null;
100
+ let queuedVisionDownload = null;
101
+ let visionDownloadAbortController = null;
102
+ const activeVisionGenerations = new Map();
103
+ const queuedVisionGenerations = new Set();
104
+ const cancelledVisionGenerations = new Set();
105
+ let lastTextProgressPostAt = 0;
106
+ let webGpuAdapterProbePromise = null;
107
+ let webGpuAdapterSummary = '';
108
+ let observedWebGpuDevice = null;
109
+ let lastWebGpuDeviceError = '';
110
+ let lastWebGpuDeviceLost = '';
111
+ let textDownloadState = {
112
+ status: 'not-downloaded',
113
+ ready: false,
114
+ modelId: '',
115
+ dtype: '',
116
+ file: '',
117
+ loaded: 0,
118
+ total: 0,
119
+ progress: 0,
120
+ error: '',
121
+ };
122
+
123
+ function textDtypeKey(dtype) {
124
+ if (!dtype || typeof dtype !== 'object' || Array.isArray(dtype)) return String(dtype || '').trim();
125
+ return JSON.stringify(Object.fromEntries(
126
+ Object.entries(dtype).sort(([left], [right]) => left.localeCompare(right)),
127
+ ));
128
+ }
129
+
130
+ function textModelKey(modelId, dtype) {
131
+ return `${String(modelId || '').trim()}|${textDtypeKey(dtype)}`;
132
+ }
133
+
134
+ function sameTextModel(leftModelId, leftDtype, rightModelId, rightDtype) {
135
+ return textModelKey(leftModelId, leftDtype) === textModelKey(rightModelId, rightDtype);
136
+ }
137
+
138
+ function assertOnnxTextModel(modelId) {
139
+ const normalized = String(modelId || '').trim();
140
+ if (!normalized) throw new Error('No text-generation model was specified.');
141
+ if (normalized === WEBGPU_BONSAI27_MODEL_ID || /\.gguf$/i.test(normalized)) {
142
+ throw new Error(`${normalized} is a GGUF checkpoint and cannot be loaded with Transformers.js. Use the Bonsai WebGPU runtime.`);
143
+ }
144
+ return normalized;
145
+ }
146
+
147
+ function isLfm25VlModel(modelId) {
148
+ return WEBGPU_LFM25_VL_MODEL_IDS.has(String(modelId || '').trim());
149
+ }
150
+
151
+ function lfm25VlProcessorOptions(modelId) {
152
+ if (!isLfm25VlModel(modelId)) return {};
153
+ return {
154
+ // LiquidAI's current VL ONNX repos use the Transformers v5 layout: image
155
+ // settings are nested in processor_config.json and the chat template is a
156
+ // separate Jinja file. The packaged Transformers.js 4.2 runtime supports
157
+ // both through WebBrain's documented browser-bundle compatibility hooks.
158
+ image_processor_config_file: 'processor_config.json',
159
+ chat_template_file: 'chat_template.jinja',
160
+ };
161
+ }
162
+
163
+ function assertTextDownloadCanStart(payload) {
164
+ const modelId = assertOnnxTextModel(payload?.modelId);
165
+ const dtype = payload?.dtype || 'q4f16';
166
+ const conflictsWithTransfer = textDownloadState.modelId
167
+ && !sameTextModel(textDownloadState.modelId, textDownloadState.dtype, modelId, dtype)
168
+ && ['downloading', 'paused', 'stopping'].includes(textDownloadState.status);
169
+ const conflictsWithQueued = queuedTextDownload
170
+ && !sameTextModel(queuedTextDownload.modelId, queuedTextDownload.dtype, modelId, dtype);
171
+ if (conflictsWithTransfer || conflictsWithQueued) {
172
+ const blockingModel = conflictsWithTransfer ? textDownloadState.modelId : queuedTextDownload.modelId;
173
+ throw new Error(`Finish or stop the ${blockingModel} download before downloading ${modelId}.`);
174
+ }
175
+ return { modelId, dtype, key: textModelKey(modelId, dtype) };
176
+ }
177
+
178
+ function textReadyMarkerUrl(modelId, dtype) {
179
+ const key = encodeURIComponent(textModelKey(modelId, dtype));
180
+ return `https://webbrain.one/.well-known/webgpu-model-ready/${key}`;
181
+ }
182
+
183
+ function safeDecodedUrl(value) {
184
+ try { return decodeURIComponent(String(value || '')); } catch { return String(value || ''); }
185
+ }
186
+
187
+ function fetchTargetsActiveTextModel(input) {
188
+ if (!activeTextDownloadModelId) return false;
189
+ const url = typeof input === 'string' || input instanceof URL ? String(input) : input?.url;
190
+ return safeDecodedUrl(url).includes(`/${activeTextDownloadModelId}/`);
191
+ }
192
+
193
+ function fetchTargetsActiveVisionModel(input) {
194
+ if (!activeVisionDownloadRequest?.modelId) return false;
195
+ const url = typeof input === 'string' || input instanceof URL ? String(input) : input?.url;
196
+ return safeDecodedUrl(url).includes(`/${activeVisionDownloadRequest.modelId}/`);
197
+ }
198
+
199
+ function visionReadyMarkerUrl(modelId) {
200
+ return `${WEBGPU_VISION_READY_MARKER_PREFIX}v${WEBGPU_VISION_READY_MARKER_VERSION}/${encodeURIComponent(String(modelId || '').trim())}`;
201
+ }
202
+
203
+ function isVisionReadyMarkerForModel(url, modelId) {
204
+ const candidate = String(url || '');
205
+ const encodedModelId = encodeURIComponent(String(modelId || '').trim());
206
+ return candidate.startsWith(WEBGPU_VISION_READY_MARKER_PREFIX)
207
+ && candidate.endsWith(`/${encodedModelId}`);
208
+ }
209
+
210
+ async function isVisionModelCached(modelId) {
211
+ const normalized = String(modelId || '').trim();
212
+ if (!normalized) return false;
213
+ if (typeof caches === 'undefined') return null;
214
+ const markerUrl = visionReadyMarkerUrl(normalized);
215
+ try {
216
+ for (const name of await caches.keys()) {
217
+ if (!/transformers/i.test(name)) continue;
218
+ const cache = await caches.open(name);
219
+ if (await cache.match(markerUrl)) return true;
220
+ }
221
+ } catch {
222
+ return null;
223
+ }
224
+ return false;
225
+ }
226
+
227
+ async function markVisionModelReady(modelId) {
228
+ const normalized = String(modelId || '').trim();
229
+ if (!normalized || typeof caches === 'undefined') return;
230
+ const cache = await caches.open(TRANSFORMERS_CACHE_NAME);
231
+ await cache.put(visionReadyMarkerUrl(normalized), new Response(JSON.stringify({
232
+ modelId: normalized,
233
+ markerVersion: WEBGPU_VISION_READY_MARKER_VERSION,
234
+ }), {
235
+ headers: { 'content-type': 'application/json' },
236
+ }));
237
+ }
238
+
239
+ async function controlledFetch(input, init = {}) {
240
+ if (!nativeFetch) throw new Error('Fetch is unavailable in the WebGPU worker.');
241
+ if (textDownloadAbortController && fetchTargetsActiveTextModel(input)) {
242
+ return nativeFetch(input, { ...init, signal: textDownloadAbortController.signal });
243
+ }
244
+ if (visionDownloadAbortController && fetchTargetsActiveVisionModel(input)) {
245
+ return nativeFetch(input, { ...init, signal: visionDownloadAbortController.signal });
246
+ }
247
+ return nativeFetch(input, init);
248
+ }
249
+
250
+ function textDownloadSnapshot() {
251
+ return { ...textDownloadState };
252
+ }
253
+
254
+ function postTextDownloadState({ force = false } = {}) {
255
+ const now = Date.now();
256
+ if (!force && now - lastTextProgressPostAt < 160) return;
257
+ lastTextProgressPostAt = now;
258
+ self.postMessage({ type: TEXT_DOWNLOAD_EVENT, state: textDownloadSnapshot() });
259
+ }
260
+
261
+ async function isTextModelReady(modelId, dtype) {
262
+ const key = textModelKey(modelId, dtype);
263
+ if (readyTextModelKeys.has(key)) return true;
264
+ if (typeof caches === 'undefined') return false;
265
+ try {
266
+ const cache = await caches.open(TRANSFORMERS_CACHE_NAME);
267
+ const marker = await cache.match(textReadyMarkerUrl(modelId, dtype));
268
+ if (!marker) return false;
269
+ readyTextModelKeys.add(key);
270
+ return true;
271
+ } catch {
272
+ return false;
273
+ }
274
+ }
275
+
276
+ async function markTextModelReady(modelId, dtype) {
277
+ const key = textModelKey(modelId, dtype);
278
+ if (typeof caches === 'undefined') {
279
+ readyTextModelKeys.add(key);
280
+ return;
281
+ }
282
+ const cache = await caches.open(TRANSFORMERS_CACHE_NAME);
283
+ await cache.put(textReadyMarkerUrl(modelId, dtype), new Response(JSON.stringify({ modelId, dtype }), {
284
+ headers: { 'content-type': 'application/json' },
285
+ }));
286
+ readyTextModelKeys.add(key);
287
+ }
288
+
289
+ async function loadLibrary() {
290
+ if (libraryPromise) return libraryPromise;
291
+ if (!workerConfig) throw new Error('WebGPU worker was not initialized.');
292
+ libraryPromise = (async () => {
293
+ let library;
294
+ try {
295
+ library = await import(workerConfig.transformersUrl);
296
+ } catch (error) {
297
+ libraryPromise = null;
298
+ throw new Error(`The packaged Transformers.js runtime could not be loaded: ${error?.message || error}`);
299
+ }
300
+ libraryVersion = library.env?.version || library.VERSION || 'unknown';
301
+ if (library.env) {
302
+ library.env.allowLocalModels = false;
303
+ library.env.allowRemoteModels = true;
304
+ library.env.useBrowserCache = true;
305
+ library.env.useWasmCache = false;
306
+ library.env.fetch = controlledFetch;
307
+ const wasm = library.env.backends?.onnx?.wasm;
308
+ if (wasm) {
309
+ wasm.numThreads = 1;
310
+ wasm.wasmPaths = {
311
+ mjs: workerConfig.wasmMjsUrl,
312
+ wasm: workerConfig.wasmUrl,
313
+ };
314
+ }
315
+ }
316
+ return library;
317
+ })();
318
+ return libraryPromise;
319
+ }
320
+
321
+ function compactAdapterInfo(adapter) {
322
+ if (!adapter) return '';
323
+ const info = adapter.info || {};
324
+ const identity = [info.vendor, info.architecture, info.device, info.description]
325
+ .map(value => String(value || '').trim())
326
+ .filter((value, index, values) => value && values.indexOf(value) === index)
327
+ .join(' / ');
328
+ const maxBufferSize = Number(adapter.limits?.maxBufferSize);
329
+ const maxStorageBinding = Number(adapter.limits?.maxStorageBufferBindingSize);
330
+ const limits = [
331
+ Number.isFinite(maxBufferSize) ? `maxBufferSize=${maxBufferSize}` : '',
332
+ Number.isFinite(maxStorageBinding) ? `maxStorageBufferBindingSize=${maxStorageBinding}` : '',
333
+ ].filter(Boolean).join(', ');
334
+ return [identity, limits].filter(Boolean).join('; ');
335
+ }
336
+
337
+ async function captureWebGpuAdapterSummary() {
338
+ if (webGpuAdapterProbePromise) return webGpuAdapterProbePromise;
339
+ webGpuAdapterProbePromise = (async () => {
340
+ if (typeof navigator === 'undefined' || !navigator.gpu) return '';
341
+ try {
342
+ const adapter = await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' });
343
+ webGpuAdapterSummary = compactAdapterInfo(adapter);
344
+ } catch {}
345
+ return webGpuAdapterSummary;
346
+ })();
347
+ return webGpuAdapterProbePromise;
348
+ }
349
+
350
+ function bindWebGpuDeviceDiagnostics(library) {
351
+ const device = library?.env?.backends?.onnx?.webgpu?.device;
352
+ if (!device || device === observedWebGpuDevice) return;
353
+ observedWebGpuDevice = device;
354
+ lastWebGpuDeviceError = '';
355
+ lastWebGpuDeviceLost = '';
356
+ device.addEventListener?.('uncapturederror', event => {
357
+ lastWebGpuDeviceError = String(event?.error?.message || event?.message || 'Unknown WebGPU validation error.');
358
+ console.error('[webgpu] uncaptured device error:', lastWebGpuDeviceError);
359
+ });
360
+ device.lost?.then(info => {
361
+ if (device !== observedWebGpuDevice) return;
362
+ lastWebGpuDeviceLost = String(info?.message || info?.reason || 'The WebGPU device was lost.');
363
+ console.error('[webgpu] device lost:', lastWebGpuDeviceLost);
364
+ }).catch(() => {});
365
+ }
366
+
367
+ function isWebGpuExecutionFailure(error) {
368
+ return /OrtRun|BufferManager::Download|mapAsync|GPUBuffer|device lost/i.test(error?.message || String(error));
369
+ }
370
+
371
+ async function enrichWebGpuExecutionError(error) {
372
+ // WebGPU uncaptured-error/device-lost events can arrive just after OrtRun's
373
+ // generic buffer readback exception. Give that event one task to land so the
374
+ // user sees the actionable root error instead of only "Invalid Buffer".
375
+ await new Promise(resolve => setTimeout(resolve, 0));
376
+ const details = [lastWebGpuDeviceError, lastWebGpuDeviceLost]
377
+ .map(value => String(value || '').trim())
378
+ .filter((value, index, values) => value && values.indexOf(value) === index);
379
+ const adapter = webGpuAdapterSummary || await captureWebGpuAdapterSummary();
380
+ const suffix = [
381
+ details.length ? `GPU detail: ${details.join(' ')}` : '',
382
+ adapter ? `Adapter: ${adapter}.` : '',
383
+ 'Close other GPU-heavy tabs/apps and retry with a short prompt. If it persists, this GPU/driver cannot execute this model with the current WebGPU runtime.',
384
+ ].filter(Boolean).join(' ');
385
+ return new Error(`${error?.message || String(error)} ${suffix}`);
386
+ }
387
+
388
+ function postProgress(modelId, event) {
389
+ if (modelId === activeTextDownloadModelId && !textDownloadCancelMode) {
390
+ const file = String(event?.file || event?.name || '');
391
+ if (file) {
392
+ const previous = textDownloadFiles.get(file) || { loaded: 0, total: 0, status: '' };
393
+ const total = Number(event?.total || previous.total || 0);
394
+ const loaded = event?.status === 'done' && total > 0
395
+ ? total
396
+ : Number(event?.loaded ?? previous.loaded ?? 0);
397
+ textDownloadFiles.set(file, {
398
+ status: event?.status || previous.status,
399
+ loaded: Math.max(0, loaded),
400
+ total: Math.max(0, total),
401
+ });
402
+ }
403
+ let loaded = 0;
404
+ let total = 0;
405
+ for (const item of textDownloadFiles.values()) {
406
+ if (item.total <= 0) continue;
407
+ loaded += Math.min(item.loaded, item.total);
408
+ total += item.total;
409
+ }
410
+ textDownloadState = {
411
+ ...textDownloadState,
412
+ status: 'downloading',
413
+ ready: false,
414
+ file,
415
+ loaded,
416
+ total,
417
+ progress: total > 0 ? Math.max(0, Math.min(100, loaded / total * 100)) : 0,
418
+ error: '',
419
+ };
420
+ postTextDownloadState({ force: event?.status === 'done' });
421
+ }
422
+ self.postMessage({
423
+ type: 'progress',
424
+ modelId,
425
+ status: event?.status || '',
426
+ file: event?.file || event?.name || '',
427
+ loaded: Number(event?.loaded || 0),
428
+ total: Number(event?.total || 0),
429
+ progress: Number(event?.progress || 0),
430
+ });
431
+ }
432
+
433
+ async function disposeRuntime(runtime) {
434
+ if (runtime?.pipeline?.dispose) {
435
+ try { await runtime.pipeline.dispose(); } catch {}
436
+ } else if (runtime?.model?.dispose) {
437
+ try { await runtime.model.dispose(); } catch {}
438
+ }
439
+ if (runtime?.processor?.dispose) {
440
+ try { await runtime.processor.dispose(); } catch {}
441
+ }
442
+ }
443
+
444
+ async function disposeVisionRuntime(expectedOwner = '') {
445
+ if (expectedOwner && visionRuntimeOwner && visionRuntimeOwner !== expectedOwner) return;
446
+ const runtime = visionRuntime;
447
+ visionRuntime = null;
448
+ visionRuntimeKey = '';
449
+ visionRuntimeModelKey = '';
450
+ visionRuntimeOwner = '';
451
+ await disposeRuntime(runtime);
452
+ }
453
+
454
+ async function disposeTextRuntime() {
455
+ const runtime = textRuntime;
456
+ textRuntime = null;
457
+ textRuntimeKey = '';
458
+ textRuntimeModelKey = '';
459
+ await disposeRuntime(runtime);
460
+ }
461
+
462
+ async function disposeAllRuntimes() {
463
+ await disposeVisionRuntime();
464
+ await disposeTextRuntime();
465
+ }
466
+
467
+ async function legacyLfm25VlConfig(library, modelId, progress_callback, localFilesOnly) {
468
+ if (modelId !== WEBGPU_LFM25_VL_16B_MODEL_ID) return null;
469
+ if (!library.AutoConfig) {
470
+ throw new Error('The packaged Transformers.js version cannot load the LFM2.5-VL-1.6B model config.');
471
+ }
472
+ const config = await library.AutoConfig.from_pretrained(modelId, {
473
+ progress_callback,
474
+ local_files_only: localFilesOnly,
475
+ });
476
+ config['transformers.js_config'] = {
477
+ ...(config['transformers.js_config'] || {}),
478
+ // LiquidAI's 1.6B ONNX export predates the standard Transformers.js
479
+ // ImageTextToText filenames used by the newer 3B package.
480
+ session_file_names: {
481
+ embed_tokens: 'embed_tokens',
482
+ vision_encoder: 'embed_images',
483
+ decoder_model_merged: 'decoder',
484
+ },
485
+ use_external_data_format: {
486
+ 'embed_tokens_fp16.onnx': 1,
487
+ 'embed_images_fp16.onnx': 1,
488
+ 'decoder_q4.onnx': 1,
489
+ },
490
+ };
491
+ return config;
492
+ }
493
+
494
+ async function getVisionRuntime(modelId, dtype, device, {
495
+ localFilesOnly = false,
496
+ owner = 'vision',
497
+ readiness = 'vision',
498
+ } = {}) {
499
+ const key = `vision|${modelId}|${device}|${JSON.stringify(dtype)}`;
500
+ if (visionRuntime && visionRuntimeKey === key) {
501
+ visionRuntimeOwner = owner;
502
+ return visionRuntime;
503
+ }
504
+ if (visionRuntimeLoadPromise) {
505
+ if (visionRuntimeLoadKey === key) return visionRuntimeLoadPromise;
506
+ await visionRuntimeLoadPromise.catch(() => {});
507
+ if (visionRuntime && visionRuntimeKey === key) {
508
+ visionRuntimeOwner = owner;
509
+ return visionRuntime;
510
+ }
511
+ }
512
+
513
+ const loadPromise = (async () => {
514
+ const locallyReady = readiness === 'text'
515
+ ? await isTextModelReady(modelId, dtype)
516
+ : await isVisionModelCached(modelId);
517
+ if (localFilesOnly && locallyReady === false) {
518
+ const error = new Error(`${modelId} is not cached locally.`);
519
+ error.code = readiness === 'text' ? 'text_model_not_downloaded' : 'vision_model_not_downloaded';
520
+ throw error;
521
+ }
522
+ const library = await loadLibrary();
523
+ const { AutoModelForImageTextToText, AutoProcessor } = library;
524
+ if (!AutoModelForImageTextToText || !AutoProcessor) {
525
+ throw new Error('The packaged Transformers.js version does not include image-text-to-text support.');
526
+ }
527
+ if (owner === 'text') await disposeTextRuntime();
528
+ await disposeVisionRuntime();
529
+ const progress_callback = event => postProgress(modelId, event);
530
+ const config = await legacyLfm25VlConfig(library, modelId, progress_callback, localFilesOnly);
531
+ const processorOptions = lfm25VlProcessorOptions(modelId);
532
+ const previousAllowLocalModels = library.env?.allowLocalModels;
533
+ if (localFilesOnly && library.env) library.env.allowLocalModels = true;
534
+ let processorResult;
535
+ let modelResult;
536
+ try {
537
+ [processorResult, modelResult] = await Promise.allSettled([
538
+ AutoProcessor.from_pretrained(modelId, {
539
+ ...processorOptions,
540
+ progress_callback,
541
+ local_files_only: localFilesOnly,
542
+ }),
543
+ AutoModelForImageTextToText.from_pretrained(modelId, {
544
+ device,
545
+ dtype,
546
+ ...(config ? { config } : {}),
547
+ progress_callback,
548
+ local_files_only: localFilesOnly,
549
+ }),
550
+ ]);
551
+ } finally {
552
+ if (localFilesOnly && library.env) library.env.allowLocalModels = previousAllowLocalModels;
553
+ }
554
+ if (processorResult.status === 'rejected' || modelResult.status === 'rejected') {
555
+ const loaded = [processorResult, modelResult]
556
+ .filter(result => result.status === 'fulfilled')
557
+ .map(result => result.value);
558
+ for (const resource of loaded) {
559
+ if (resource?.dispose) {
560
+ try { await resource.dispose(); } catch {}
561
+ }
562
+ }
563
+ throw processorResult.status === 'rejected'
564
+ ? processorResult.reason
565
+ : modelResult.reason;
566
+ }
567
+ const processor = processorResult.value;
568
+ const model = modelResult.value;
569
+ visionRuntime = { library, processor, model };
570
+ visionRuntimeKey = key;
571
+ visionRuntimeModelKey = textModelKey(modelId, dtype);
572
+ visionRuntimeOwner = owner;
573
+ return visionRuntime;
574
+ })();
575
+ visionRuntimeLoadPromise = loadPromise;
576
+ visionRuntimeLoadKey = key;
577
+ try {
578
+ return await loadPromise;
579
+ } finally {
580
+ if (visionRuntimeLoadPromise === loadPromise) {
581
+ visionRuntimeLoadPromise = null;
582
+ visionRuntimeLoadKey = '';
583
+ }
584
+ }
585
+ }
586
+
587
+ async function preloadRuntime(payload = {}) {
588
+ const modelId = String(payload.modelId || '').trim();
589
+ if (!modelId) throw new Error('No vision model was specified.');
590
+ const device = payload.device || 'webgpu';
591
+ const dtype = payload.dtype || {
592
+ embed_tokens: 'fp16',
593
+ vision_encoder: 'fp16',
594
+ decoder_model_merged: 'q4',
595
+ };
596
+ await getVisionRuntime(modelId, dtype, device);
597
+ await disposeVisionRuntime();
598
+ return modelId;
599
+ }
600
+
601
+ function visionDownloadState(request, status) {
602
+ return {
603
+ status,
604
+ ready: status === 'ready',
605
+ modelId: request?.modelId || '',
606
+ dtype: request?.dtype || '',
607
+ };
608
+ }
609
+
610
+ async function preloadVisionModel(payload, request) {
611
+ if (queuedVisionDownload !== request || request.cancelMode) {
612
+ return visionDownloadState(request, request.cancelMode === 'stop' ? 'not-downloaded' : 'paused');
613
+ }
614
+ queuedVisionDownload = null;
615
+ activeVisionDownloadRequest = request;
616
+ const controller = new AbortController();
617
+ visionDownloadAbortController = controller;
618
+ try {
619
+ await preloadRuntime(payload);
620
+ const status = request.cancelMode === 'stop'
621
+ ? 'not-downloaded'
622
+ : request.cancelMode === 'pause' ? 'paused' : 'ready';
623
+ if (status === 'ready') await markVisionModelReady(request.modelId);
624
+ return visionDownloadState(request, status);
625
+ } catch (error) {
626
+ if (request.cancelMode || controller.signal.aborted) {
627
+ return visionDownloadState(request, request.cancelMode === 'stop' ? 'not-downloaded' : 'paused');
628
+ }
629
+ throw error;
630
+ } finally {
631
+ if (visionDownloadAbortController === controller) visionDownloadAbortController = null;
632
+ if (activeVisionDownloadRequest === request) activeVisionDownloadRequest = null;
633
+ }
634
+ }
635
+
636
+ function pauseVisionDownload(modelId) {
637
+ const normalizedModelId = String(modelId || '').trim();
638
+ const queued = queuedVisionDownload;
639
+ const active = activeVisionDownloadRequest;
640
+ const targetQueued = queued && (!normalizedModelId || queued.modelId === normalizedModelId);
641
+ const targetActive = active && (!normalizedModelId || active.modelId === normalizedModelId);
642
+ if (targetQueued) {
643
+ queued.cancelMode = 'pause';
644
+ if (queuedVisionDownload === queued) queuedVisionDownload = null;
645
+ }
646
+ if (targetActive) {
647
+ active.cancelMode = 'pause';
648
+ visionDownloadAbortController?.abort();
649
+ }
650
+ const target = targetActive ? active : targetQueued ? queued : { modelId: normalizedModelId };
651
+ return {
652
+ ...visionDownloadState(target, 'paused'),
653
+ targetsQueued: Boolean(targetQueued),
654
+ targetsActive: Boolean(targetActive),
655
+ };
656
+ }
657
+
658
+ function stopVisionDownload(modelId) {
659
+ const normalizedModelId = String(modelId || '').trim();
660
+ const queued = queuedVisionDownload;
661
+ const active = activeVisionDownloadRequest;
662
+ const targetsQueued = Boolean(queued && (!normalizedModelId || queued.modelId === normalizedModelId));
663
+ const targetsActive = Boolean(active && (!normalizedModelId || active.modelId === normalizedModelId));
664
+ if (targetsQueued) {
665
+ queued.cancelMode = 'stop';
666
+ if (queuedVisionDownload === queued) queuedVisionDownload = null;
667
+ }
668
+ if (targetsActive) {
669
+ active.cancelMode = 'stop';
670
+ visionDownloadAbortController?.abort();
671
+ }
672
+ return { targetsQueued, targetsActive, hasActiveVision: Boolean(active) };
673
+ }
674
+
675
+ async function getTextRuntime(modelId, dtype, device, { localFilesOnly = false } = {}) {
676
+ assertOnnxTextModel(modelId);
677
+ const key = `text|${modelId}|${device}|${JSON.stringify(dtype)}`;
678
+ if (textRuntime && textRuntimeKey === key) return textRuntime;
679
+ if (textRuntimeLoadPromise) {
680
+ if (textRuntimeLoadKey === key) return textRuntimeLoadPromise;
681
+ await textRuntimeLoadPromise.catch(() => {});
682
+ if (textRuntime && textRuntimeKey === key) return textRuntime;
683
+ }
684
+
685
+ const loadPromise = (async () => {
686
+ const library = await loadLibrary();
687
+ if (!library.pipeline) {
688
+ throw new Error('The packaged Transformers.js version does not include text generation.');
689
+ }
690
+ await captureWebGpuAdapterSummary();
691
+ await disposeVisionRuntime('text');
692
+ await disposeTextRuntime();
693
+ const previousAllowLocalModels = library.env?.allowLocalModels;
694
+ if (localFilesOnly && library.env) library.env.allowLocalModels = true;
695
+ let pipeline;
696
+ try {
697
+ pipeline = await library.pipeline('text-generation', modelId, {
698
+ device,
699
+ dtype,
700
+ ...(WEBGPU_TEXT_MODEL_FILE_NAMES.has(modelId)
701
+ ? { model_file_name: WEBGPU_TEXT_MODEL_FILE_NAMES.get(modelId) }
702
+ : {}),
703
+ // ORT mutates this object while appending its default session config.
704
+ session_options: createWebGpuTextSessionOptions(),
705
+ local_files_only: localFilesOnly,
706
+ progress_callback: event => postProgress(modelId, event),
707
+ });
708
+ } finally {
709
+ if (localFilesOnly && library.env) library.env.allowLocalModels = previousAllowLocalModels;
710
+ }
711
+ bindWebGpuDeviceDiagnostics(library);
712
+ textRuntime = {
713
+ library,
714
+ pipeline,
715
+ model: pipeline.model,
716
+ tokenizer: pipeline.tokenizer,
717
+ };
718
+ textRuntimeKey = key;
719
+ textRuntimeModelKey = textModelKey(modelId, dtype);
720
+ return textRuntime;
721
+ })();
722
+ textRuntimeLoadPromise = loadPromise;
723
+ textRuntimeLoadKey = key;
724
+ try {
725
+ return await loadPromise;
726
+ } finally {
727
+ if (textRuntimeLoadPromise === loadPromise) {
728
+ textRuntimeLoadPromise = null;
729
+ textRuntimeLoadKey = '';
730
+ }
731
+ }
732
+ }
733
+
734
+ async function getDownloadedTextRuntime(modelId, dtype, device, { localFilesOnly = false } = {}) {
735
+ if (isLfm25VlModel(modelId)) {
736
+ return getVisionRuntime(modelId, dtype, device, {
737
+ localFilesOnly,
738
+ owner: 'text',
739
+ readiness: 'text',
740
+ });
741
+ }
742
+ return getTextRuntime(modelId, dtype, device, { localFilesOnly });
743
+ }
744
+
745
+ async function disposeDownloadedTextRuntime(modelId = '') {
746
+ if (!modelId || isLfm25VlModel(modelId)) await disposeVisionRuntime('text');
747
+ if (!modelId || !isLfm25VlModel(modelId)) await disposeTextRuntime();
748
+ }
749
+
750
+ function chatTemplateText(value) {
751
+ if (typeof value === 'string') return value;
752
+ if (Array.isArray(value)) return value.map(chatTemplateText).join('\n');
753
+ if (value && typeof value === 'object') return Object.values(value).map(chatTemplateText).join('\n');
754
+ return '';
755
+ }
756
+
757
+ export function tokenizerSupportsTools(tokenizer) {
758
+ const template = chatTemplateText(tokenizer?.chat_template ?? tokenizer?.chatTemplate);
759
+ return /\btools\b/.test(template);
760
+ }
761
+
762
+ function assertToolCapableTextRuntime(runtime, modelId) {
763
+ if (tokenizerSupportsTools(runtime?.tokenizer)) return;
764
+ throw new Error(`${modelId} is not compatible with WebBrain: custom repositories must provide a chat template that accepts tools.`);
765
+ }
766
+
767
+ async function getTextDownloadStatus(modelId, dtype) {
768
+ const ready = await isTextModelReady(modelId, dtype);
769
+ const sameModel = sameTextModel(textDownloadState.modelId, textDownloadState.dtype, modelId, dtype);
770
+ if (sameModel && ['downloading', 'paused', 'stopping'].includes(textDownloadState.status)) {
771
+ return textDownloadSnapshot();
772
+ }
773
+ if (ready) {
774
+ return {
775
+ status: 'ready',
776
+ ready: true,
777
+ modelId,
778
+ dtype,
779
+ file: sameModel ? textDownloadState.file : '',
780
+ loaded: sameModel ? textDownloadState.loaded : 0,
781
+ total: sameModel ? textDownloadState.total : 0,
782
+ progress: 100,
783
+ error: '',
784
+ };
785
+ }
786
+ if (sameModel && textDownloadState.status === 'error') return textDownloadSnapshot();
787
+ return {
788
+ status: 'not-downloaded',
789
+ ready: false,
790
+ modelId,
791
+ dtype,
792
+ file: '',
793
+ loaded: 0,
794
+ total: 0,
795
+ progress: 0,
796
+ error: '',
797
+ };
798
+ }
799
+
800
+ async function downloadTextModel(payload, { onStarted } = {}) {
801
+ const modelId = assertOnnxTextModel(payload?.modelId);
802
+ const device = payload?.device || 'webgpu';
803
+ const dtype = payload?.dtype || 'q4f16';
804
+ const tracksDifferentTransfer = textDownloadState.modelId
805
+ && !sameTextModel(textDownloadState.modelId, textDownloadState.dtype, modelId, dtype)
806
+ && ['downloading', 'paused', 'stopping'].includes(textDownloadState.status);
807
+ if (tracksDifferentTransfer) {
808
+ throw new Error(`Finish or stop the ${textDownloadState.modelId} download before downloading ${modelId}.`);
809
+ }
810
+ await clearLegacyLfm25VlWrongPrecisionCache(modelId);
811
+ if (await isTextModelReady(modelId, dtype)) {
812
+ if (payload?.requireTools === true) {
813
+ const runtime = await getDownloadedTextRuntime(modelId, dtype, device, { localFilesOnly: true });
814
+ assertToolCapableTextRuntime(runtime, modelId);
815
+ }
816
+ textDownloadState = {
817
+ ...textDownloadState,
818
+ status: 'ready',
819
+ ready: true,
820
+ modelId,
821
+ dtype,
822
+ progress: 100,
823
+ error: '',
824
+ };
825
+ postTextDownloadState({ force: true });
826
+ return textDownloadSnapshot();
827
+ }
828
+
829
+ const resuming = textDownloadState.status === 'paused'
830
+ && sameTextModel(textDownloadState.modelId, textDownloadState.dtype, modelId, dtype);
831
+ if (!resuming) textDownloadFiles.clear();
832
+ activeTextDownloadModelId = modelId;
833
+ textDownloadCancelMode = '';
834
+ const controller = new AbortController();
835
+ textDownloadAbortController = controller;
836
+ textDownloadState = {
837
+ status: 'downloading',
838
+ ready: false,
839
+ modelId,
840
+ dtype,
841
+ file: resuming ? textDownloadState.file : '',
842
+ loaded: resuming ? textDownloadState.loaded : 0,
843
+ total: resuming ? textDownloadState.total : 0,
844
+ progress: resuming ? textDownloadState.progress : 0,
845
+ error: '',
846
+ };
847
+ postTextDownloadState({ force: true });
848
+ onStarted?.(textDownloadSnapshot());
849
+
850
+ try {
851
+ const runtime = await getDownloadedTextRuntime(modelId, dtype, device);
852
+ if (payload?.requireTools === true) assertToolCapableTextRuntime(runtime, modelId);
853
+ if (textDownloadCancelMode) {
854
+ await disposeDownloadedTextRuntime(modelId);
855
+ return textDownloadSnapshot();
856
+ }
857
+ await markTextModelReady(modelId, dtype);
858
+ textDownloadState = {
859
+ ...textDownloadState,
860
+ status: 'ready',
861
+ ready: true,
862
+ progress: 100,
863
+ error: '',
864
+ };
865
+ postTextDownloadState({ force: true });
866
+ return textDownloadSnapshot();
867
+ } catch (error) {
868
+ if (textDownloadCancelMode === 'pause' || textDownloadCancelMode === 'stop' || controller.signal.aborted) {
869
+ textDownloadState = {
870
+ ...textDownloadState,
871
+ status: textDownloadCancelMode === 'stop' ? 'stopping' : 'paused',
872
+ ready: false,
873
+ error: '',
874
+ };
875
+ postTextDownloadState({ force: true });
876
+ return textDownloadSnapshot();
877
+ }
878
+ textDownloadState = {
879
+ ...textDownloadState,
880
+ status: 'error',
881
+ ready: false,
882
+ error: error?.message || String(error),
883
+ };
884
+ postTextDownloadState({ force: true });
885
+ throw error;
886
+ } finally {
887
+ if (textDownloadAbortController === controller) textDownloadAbortController = null;
888
+ activeTextDownloadModelId = '';
889
+ }
890
+ }
891
+
892
+ async function clearLegacyLfm25VlWrongPrecisionCache(modelId) {
893
+ if (modelId !== WEBGPU_LFM25_VL_16B_MODEL_ID || typeof caches === 'undefined') return 0;
894
+ const modelPath = `/${modelId}/`;
895
+ const wrongPrecisionFile = /\/onnx\/(?:decoder|embed_images)\.onnx(?:_data(?:_\d+)?)?(?:[?#]|$)/;
896
+ let deletedEntries = 0;
897
+ for (const name of await caches.keys()) {
898
+ if (!/transformers/i.test(name)) continue;
899
+ const cache = await caches.open(name);
900
+ for (const request of await cache.keys()) {
901
+ const url = safeDecodedUrl(request.url);
902
+ if (url.includes(modelPath) && wrongPrecisionFile.test(url) && await cache.delete(request)) {
903
+ deletedEntries++;
904
+ }
905
+ }
906
+ }
907
+ return deletedEntries;
908
+ }
909
+
910
+ function pauseTextDownload() {
911
+ if (textDownloadState.status !== 'downloading') return textDownloadSnapshot();
912
+ textDownloadCancelMode = 'pause';
913
+ textDownloadState = { ...textDownloadState, status: 'paused', ready: false, error: '' };
914
+ textDownloadAbortController?.abort();
915
+ postTextDownloadState({ force: true });
916
+ return textDownloadSnapshot();
917
+ }
918
+
919
+ async function clearTextModelCache(modelId, dtype) {
920
+ if (textRuntimeModelKey === textModelKey(modelId, dtype)) await disposeTextRuntime();
921
+ if (visionRuntimeOwner === 'text' && visionRuntimeModelKey === textModelKey(modelId, dtype)) {
922
+ await disposeVisionRuntime('text');
923
+ }
924
+ const modelPath = `/${modelId}/`;
925
+ const markerUrl = textReadyMarkerUrl(modelId, dtype);
926
+ if (typeof caches !== 'undefined') {
927
+ for (const name of await caches.keys()) {
928
+ if (!/transformers/i.test(name)) continue;
929
+ const cache = await caches.open(name);
930
+ for (const request of await cache.keys()) {
931
+ const url = safeDecodedUrl(request.url);
932
+ if (url.includes(modelPath) || request.url === markerUrl) await cache.delete(request);
933
+ }
934
+ await cache.delete(markerUrl);
935
+ }
936
+ }
937
+ readyTextModelKeys.delete(textModelKey(modelId, dtype));
938
+ textDownloadFiles.clear();
939
+ textDownloadCancelMode = '';
940
+ const clearedState = {
941
+ status: 'not-downloaded',
942
+ ready: false,
943
+ modelId,
944
+ dtype,
945
+ file: '',
946
+ loaded: 0,
947
+ total: 0,
948
+ progress: 0,
949
+ error: '',
950
+ };
951
+ const sameModel = !textDownloadState.modelId
952
+ || sameTextModel(textDownloadState.modelId, textDownloadState.dtype, modelId, dtype);
953
+ if (sameModel) {
954
+ textDownloadState = clearedState;
955
+ postTextDownloadState({ force: true });
956
+ }
957
+ return { ...clearedState };
958
+ }
959
+
960
+ function enqueueModelOperation(operation) {
961
+ const result = modelOperationQueue.then(operation, operation);
962
+ // Keep the queue usable after one request fails while preserving that
963
+ // failure for the caller awaiting `result`.
964
+ modelOperationQueue = result.catch(() => {});
965
+ return result;
966
+ }
967
+
968
+ function imageUrlFromBlock(block) {
969
+ if (block?.type === 'image_url') {
970
+ return typeof block.image_url === 'string'
971
+ ? block.image_url
972
+ : block.image_url?.url;
973
+ }
974
+ if (block?.type === 'image') {
975
+ return typeof block.image === 'string' ? block.image : block.url;
976
+ }
977
+ return '';
978
+ }
979
+
980
+ function prepareMultimodalMessages(messages) {
981
+ const imageUrls = [];
982
+ const prepared = [];
983
+ for (const message of Array.isArray(messages) ? messages : []) {
984
+ if (!message || typeof message !== 'object') continue;
985
+ const role = ['system', 'user', 'assistant', 'tool'].includes(message.role)
986
+ ? message.role
987
+ : 'user';
988
+ const imageBlocks = [];
989
+ const textBlocks = [];
990
+ if (Array.isArray(message.content)) {
991
+ for (const block of message.content) {
992
+ if (block?.type === 'text' && typeof block.text === 'string') {
993
+ textBlocks.push({ type: 'text', text: block.text });
994
+ continue;
995
+ }
996
+ const imageUrl = imageUrlFromBlock(block);
997
+ if (imageUrl) {
998
+ imageUrls.push(imageUrl);
999
+ imageBlocks.push({ type: 'image' });
1000
+ }
1001
+ }
1002
+ } else if (typeof message.content === 'string') {
1003
+ textBlocks.push({ type: 'text', text: message.content });
1004
+ }
1005
+ // LFM2.5-VL's published chat template places <image> before the question.
1006
+ // Normalize OpenAI-style messages (which often put text first) to that
1007
+ // model-specific contract without changing the provider-facing API.
1008
+ const blocks = [...imageBlocks, ...textBlocks];
1009
+ if (blocks.length || Array.isArray(message.tool_calls)) {
1010
+ prepared.push({
1011
+ role,
1012
+ content: blocks,
1013
+ ...(Array.isArray(message.tool_calls)
1014
+ ? { tool_calls: message.tool_calls.map(normalizeTextToolCall) }
1015
+ : {}),
1016
+ ...(message.reasoning_content ? { reasoning_content: String(message.reasoning_content) } : {}),
1017
+ });
1018
+ }
1019
+ }
1020
+ return { messages: prepared, imageUrls };
1021
+ }
1022
+
1023
+ function createVisionProbeImage(RawImage) {
1024
+ if (!RawImage) throw new Error('The packaged runtime does not expose RawImage.');
1025
+ // LFM2.5-VL-450M is much more dependable at coarse visual classification
1026
+ // than fine OCR. Use three large, unlabeled color panels so the connection
1027
+ // test still proves that pixels reached the model without asking it to read
1028
+ // tiny synthetic glyphs.
1029
+ const width = 480;
1030
+ const height = 320;
1031
+ const channels = 3;
1032
+ const colors = [
1033
+ [255, 255, 0],
1034
+ [0, 0, 255],
1035
+ [255, 0, 0],
1036
+ ];
1037
+ const data = new Uint8ClampedArray(width * height * channels);
1038
+ for (let y = 0; y < height; y++) {
1039
+ for (let x = 0; x < width; x++) {
1040
+ const targetOffset = (y * width + x) * channels;
1041
+ const color = colors[Math.min(colors.length - 1, Math.floor(x / (width / colors.length)))];
1042
+ for (let channel = 0; channel < channels; channel++) {
1043
+ data[targetOffset + channel] = color[channel];
1044
+ }
1045
+ }
1046
+ }
1047
+ return new RawImage(data, width, height, channels);
1048
+ }
1049
+
1050
+ async function runVision(payload, requestId) {
1051
+ const modelId = String(payload?.modelId || '').trim();
1052
+ if (!modelId) throw new Error('No vision model was specified.');
1053
+ const device = payload?.device || 'webgpu';
1054
+ const dtype = payload?.dtype || {
1055
+ embed_tokens: 'fp16',
1056
+ vision_encoder: 'fp16',
1057
+ decoder_model_merged: 'q4',
1058
+ };
1059
+ const library = await loadLibrary();
1060
+ const stoppingCriteria = library.InterruptableStoppingCriteria
1061
+ ? new library.InterruptableStoppingCriteria()
1062
+ : null;
1063
+ if (stoppingCriteria) activeVisionGenerations.set(requestId, stoppingCriteria);
1064
+ try {
1065
+ if (cancelledVisionGenerations.has(requestId)) {
1066
+ const error = new Error('Vision generation was cancelled.');
1067
+ error.name = 'AbortError';
1068
+ throw error;
1069
+ }
1070
+ const runtime = await getVisionRuntime(modelId, dtype, device, {
1071
+ localFilesOnly: true,
1072
+ owner: 'vision',
1073
+ readiness: 'vision',
1074
+ });
1075
+ const { messages, imageUrls } = prepareMultimodalMessages(payload?.messages);
1076
+ if (imageUrls.length !== 1) {
1077
+ throw new Error(`LFM2.5-VL requires exactly one screenshot; received ${imageUrls.length}.`);
1078
+ }
1079
+ const prompt = runtime.processor.apply_chat_template(messages, {
1080
+ add_generation_prompt: true,
1081
+ });
1082
+ const image = payload?.options?.visionProbe === true
1083
+ ? createVisionProbeImage(runtime.library.RawImage)
1084
+ : await runtime.library.load_image(imageUrls[0]);
1085
+ const inputs = await runtime.processor(image, prompt, { add_special_tokens: false });
1086
+ const requestedTokens = Number(payload?.options?.maxTokens);
1087
+ const maxNewTokens = Number.isFinite(requestedTokens)
1088
+ ? Math.max(1, Math.min(1600, Math.round(requestedTokens)))
1089
+ : 800;
1090
+ const outputs = await runtime.model.generate({
1091
+ ...inputs,
1092
+ do_sample: false,
1093
+ max_new_tokens: maxNewTokens,
1094
+ ...(stoppingCriteria ? { stopping_criteria: [stoppingCriteria] } : {}),
1095
+ });
1096
+ if (cancelledVisionGenerations.has(requestId)) {
1097
+ const error = new Error('Vision generation was cancelled.');
1098
+ error.name = 'AbortError';
1099
+ throw error;
1100
+ }
1101
+ const inputLength = inputs.input_ids.dims.at(-1);
1102
+ const generated = outputs.slice(null, [inputLength, null]);
1103
+ const decoded = runtime.processor.batch_decode(generated, { skip_special_tokens: true });
1104
+ return String(decoded?.[0] || '').trim();
1105
+ } finally {
1106
+ activeVisionGenerations.delete(requestId);
1107
+ cancelledVisionGenerations.delete(requestId);
1108
+ }
1109
+ }
1110
+
1111
+ function normalizeTextToolCall(toolCall) {
1112
+ if (!toolCall || typeof toolCall !== 'object') return toolCall;
1113
+ const usesFunctionWrapper = toolCall.function && typeof toolCall.function === 'object';
1114
+ const target = usesFunctionWrapper ? toolCall.function : toolCall;
1115
+ let parsedArguments = target.arguments;
1116
+ if (typeof parsedArguments === 'string') {
1117
+ try {
1118
+ parsedArguments = JSON.parse(parsedArguments);
1119
+ } catch {
1120
+ parsedArguments = {};
1121
+ }
1122
+ }
1123
+ if (!parsedArguments || typeof parsedArguments !== 'object' || Array.isArray(parsedArguments)) {
1124
+ // Ling's template calls .items() unconditionally, so malformed or omitted
1125
+ // historical arguments must still be represented by an object.
1126
+ parsedArguments = {};
1127
+ }
1128
+ if (parsedArguments === target.arguments) return toolCall;
1129
+
1130
+ if (usesFunctionWrapper) {
1131
+ return {
1132
+ ...toolCall,
1133
+ function: { ...target, arguments: parsedArguments },
1134
+ };
1135
+ }
1136
+ return { ...toolCall, arguments: parsedArguments };
1137
+ }
1138
+
1139
+ export function prepareTextMessages(messages) {
1140
+ return (Array.isArray(messages) ? messages : []).map(message => {
1141
+ if (!message || typeof message !== 'object') return { role: 'user', content: '' };
1142
+ const prepared = {
1143
+ ...message,
1144
+ ...(Array.isArray(message.tool_calls)
1145
+ ? { tool_calls: message.tool_calls.map(normalizeTextToolCall) }
1146
+ : {}),
1147
+ };
1148
+ if (Array.isArray(message.content)) {
1149
+ const text = message.content
1150
+ .filter(block => block?.type === 'text' && typeof block.text === 'string')
1151
+ .map(block => block.text)
1152
+ .join('\n');
1153
+ return { ...prepared, content: text };
1154
+ }
1155
+ return { ...prepared, content: String(message.content || '') };
1156
+ });
1157
+ }
1158
+
1159
+ export function splitThinking(content, { openingTagInPrompt = false } = {}) {
1160
+ const source = String(content || '').trim();
1161
+ const match = /^<think>\s*([\s\S]*?)\s*<\/think>\s*([\s\S]*)$/i.exec(source);
1162
+ if (match) {
1163
+ return {
1164
+ content: String(match[2] || '').trim(),
1165
+ reasoningContent: String(match[1] || '').trim() || null,
1166
+ incompleteReasoning: false,
1167
+ };
1168
+ }
1169
+ if (!openingTagInPrompt) {
1170
+ return { content: source, reasoningContent: null, incompleteReasoning: false };
1171
+ }
1172
+
1173
+ // LFM2.5's official template places `<think>` in the generation prompt.
1174
+ // Transformers.js therefore returns only the generated suffix: reasoning,
1175
+ // `</think>`, then the user-facing answer.
1176
+ const closingTag = /<\/think>/i.exec(source);
1177
+ if (!closingTag) {
1178
+ return {
1179
+ content: '',
1180
+ reasoningContent: source || null,
1181
+ incompleteReasoning: !!source,
1182
+ };
1183
+ }
1184
+ return {
1185
+ content: source.slice(closingTag.index + closingTag[0].length).trim(),
1186
+ reasoningContent: source.slice(0, closingTag.index).trim() || null,
1187
+ incompleteReasoning: false,
1188
+ };
1189
+ }
1190
+
1191
+ function addLegacyVlTools(messages, tools) {
1192
+ if (!tools.length) return messages;
1193
+ const toolText = `List of tools: [${tools.map(tool => JSON.stringify(tool)).join(', ')}]`;
1194
+ const prepared = messages.map(message => ({ ...message, content: [...(message.content || [])] }));
1195
+ if (prepared[0]?.role === 'system') {
1196
+ prepared[0].content.push({ type: 'text', text: `\n${toolText}` });
1197
+ } else {
1198
+ prepared.unshift({ role: 'system', content: [{ type: 'text', text: toolText }] });
1199
+ }
1200
+ return prepared;
1201
+ }
1202
+
1203
+ async function runMultimodalText(payload) {
1204
+ const modelId = assertOnnxTextModel(payload?.modelId);
1205
+ if (!isLfm25VlModel(modelId)) {
1206
+ throw new Error(`${modelId} is not a shipped WebGPU multimodal model.`);
1207
+ }
1208
+ const device = payload?.device || 'webgpu';
1209
+ const dtype = payload?.dtype || {
1210
+ embed_tokens: 'fp16',
1211
+ vision_encoder: 'fp16',
1212
+ decoder_model_merged: 'q4',
1213
+ };
1214
+ if (!await isTextModelReady(modelId, dtype)) {
1215
+ throw new Error(`${modelId} is not downloaded. Open Apocalypse Mode > WebGPU to download it before chatting.`);
1216
+ }
1217
+ const runtime = await getVisionRuntime(modelId, dtype, device, {
1218
+ localFilesOnly: true,
1219
+ owner: 'text',
1220
+ readiness: 'text',
1221
+ });
1222
+ const tools = Array.isArray(payload?.options?.tools) ? payload.options.tools : [];
1223
+ let { messages, imageUrls } = prepareMultimodalMessages(payload?.messages);
1224
+ if (modelId === WEBGPU_LFM25_VL_16B_MODEL_ID) messages = addLegacyVlTools(messages, tools);
1225
+ const prompt = runtime.processor.apply_chat_template(messages, {
1226
+ add_generation_prompt: true,
1227
+ tools: tools.length ? tools : undefined,
1228
+ });
1229
+ let inputs;
1230
+ if (imageUrls.length) {
1231
+ const images = await Promise.all(imageUrls.map(url => runtime.library.load_image(url)));
1232
+ inputs = await runtime.processor(images.length === 1 ? images[0] : images, prompt, {
1233
+ add_special_tokens: false,
1234
+ });
1235
+ } else {
1236
+ inputs = runtime.processor.tokenizer(prompt, { add_special_tokens: false });
1237
+ }
1238
+ const requestedTokens = Number(payload?.options?.maxTokens);
1239
+ const maxNewTokens = Number.isFinite(requestedTokens)
1240
+ ? Math.max(1, Math.min(1600, Math.round(requestedTokens)))
1241
+ : 800;
1242
+ lastWebGpuDeviceError = '';
1243
+ lastWebGpuDeviceLost = '';
1244
+ let outputs;
1245
+ try {
1246
+ outputs = await runtime.model.generate({
1247
+ ...inputs,
1248
+ do_sample: false,
1249
+ max_new_tokens: maxNewTokens,
1250
+ });
1251
+ } catch (error) {
1252
+ if (isWebGpuExecutionFailure(error)) throw await enrichWebGpuExecutionError(error);
1253
+ throw error;
1254
+ }
1255
+ const inputLength = inputs.input_ids.dims.at(-1);
1256
+ const generated = outputs.slice(null, [inputLength, null]);
1257
+ const decoded = runtime.processor.batch_decode(generated, { skip_special_tokens: true });
1258
+ const result = splitThinking(String(decoded?.[0] || '').trim());
1259
+ return { content: result.content, reasoningContent: result.reasoningContent };
1260
+ }
1261
+
1262
+ async function runText(payload) {
1263
+ const modelId = assertOnnxTextModel(payload?.modelId);
1264
+ const device = payload?.device || 'webgpu';
1265
+ const dtype = payload?.dtype || 'q4f16';
1266
+ const usesReasoningTemplate = WEBGPU_REASONING_MODEL_IDS.has(modelId);
1267
+ const opensThinkingInPrompt = WEBGPU_OPEN_THINKING_MODEL_IDS.has(modelId);
1268
+ const usesLongOutputBudget = WEBGPU_LONG_OUTPUT_MODEL_IDS.has(modelId);
1269
+ const sampling = WEBGPU_TEXT_SAMPLING.get(modelId);
1270
+ if (!await isTextModelReady(modelId, dtype)) {
1271
+ throw new Error(`${modelId} is not downloaded. Open Apocalypse Mode > WebGPU to download it before chatting.`);
1272
+ }
1273
+ const runtime = await getTextRuntime(modelId, dtype, device, { localFilesOnly: true });
1274
+ if (payload?.requireTools === true) assertToolCapableTextRuntime(runtime, modelId);
1275
+ const requestedTokens = Number(payload?.options?.maxTokens);
1276
+ const maxTokenLimit = usesLongOutputBudget
1277
+ ? WEBGPU_LFM25_MAX_NEW_TOKENS
1278
+ : WEBGPU_TEXT_MAX_NEW_TOKENS;
1279
+ const maxNewTokens = Number.isFinite(requestedTokens)
1280
+ ? Math.max(1, Math.min(maxTokenLimit, Math.round(requestedTokens)))
1281
+ : maxTokenLimit;
1282
+ const tools = Array.isArray(payload?.options?.tools) ? payload.options.tools : [];
1283
+ lastWebGpuDeviceError = '';
1284
+ lastWebGpuDeviceLost = '';
1285
+ let output;
1286
+ try {
1287
+ output = await runtime.pipeline(prepareTextMessages(payload?.messages), {
1288
+ do_sample: Boolean(sampling),
1289
+ ...(sampling || {}),
1290
+ max_new_tokens: maxNewTokens,
1291
+ tools: tools.length ? tools : undefined,
1292
+ // Reasoning templates re-open `<think>` for the pending turn and collapse
1293
+ // completed thinking in history; the rest suppress thinking outright.
1294
+ tokenizer_encode_kwargs: usesReasoningTemplate
1295
+ ? { preserve_thinking: false }
1296
+ : { enable_thinking: false },
1297
+ });
1298
+ } catch (error) {
1299
+ if (isWebGpuExecutionFailure(error)) throw await enrichWebGpuExecutionError(error);
1300
+ throw error;
1301
+ }
1302
+ const generated = output?.[0]?.generated_text;
1303
+ const content = Array.isArray(generated)
1304
+ ? generated.at(-1)?.content
1305
+ : generated;
1306
+ if (typeof content !== 'string') {
1307
+ throw new Error('The WebGPU model returned no generated text.');
1308
+ }
1309
+ const result = splitThinking(content, { openingTagInPrompt: opensThinkingInPrompt });
1310
+ if (result.incompleteReasoning) {
1311
+ throw new Error(`${modelId} used its generation budget before finishing reasoning. Retry with a shorter prompt.`);
1312
+ }
1313
+ return { content: result.content, reasoningContent: result.reasoningContent };
1314
+ }
1315
+
1316
+ async function probeRuntime() {
1317
+ await loadLibrary();
1318
+ const hasWebGPU = typeof navigator !== 'undefined' && !!navigator.gpu;
1319
+ let adapter = null;
1320
+ if (hasWebGPU) {
1321
+ try { adapter = await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' }); } catch {}
1322
+ }
1323
+ const isFallbackAdapter = !!(adapter?.isFallbackAdapter ?? adapter?.info?.isFallbackAdapter);
1324
+ return {
1325
+ libraryVersion,
1326
+ hasWebGPU: hasWebGPU && !!adapter,
1327
+ isFallbackAdapter,
1328
+ adapterFeatures: adapter ? [...adapter.features].slice(0, 12) : [],
1329
+ };
1330
+ }
1331
+
1332
+ export async function clearVisionModelCache(modelId) {
1333
+ const normalizedModelId = String(modelId || '').trim();
1334
+ if (!normalizedModelId) throw new Error('No vision model was specified.');
1335
+ await disposeVisionRuntime('vision');
1336
+ const modelPath = `/${normalizedModelId}/`;
1337
+ const markerUrl = visionReadyMarkerUrl(normalizedModelId);
1338
+ let deletedEntries = 0;
1339
+ if (typeof caches !== 'undefined') {
1340
+ for (const name of await caches.keys()) {
1341
+ if (!/transformers/i.test(name)) continue;
1342
+ const cache = await caches.open(name);
1343
+ for (const request of await cache.keys()) {
1344
+ const url = safeDecodedUrl(request.url);
1345
+ if (url.includes(modelPath) || isVisionReadyMarkerForModel(request.url, normalizedModelId)) {
1346
+ if (await cache.delete(request)) deletedEntries++;
1347
+ }
1348
+ }
1349
+ if (await cache.delete(markerUrl)) deletedEntries++;
1350
+ }
1351
+ }
1352
+ return { modelId: normalizedModelId, deletedEntries };
1353
+ }
1354
+
1355
+ self.addEventListener('message', async event => {
1356
+ const { id, type, payload } = event.data || {};
1357
+ try {
1358
+ if (type === 'cancel') {
1359
+ const requestId = Number(payload?.requestId);
1360
+ const queued = queuedVisionGenerations.has(requestId);
1361
+ const active = activeVisionGenerations.has(requestId);
1362
+ const cancellable = Number.isFinite(requestId) && (queued || active);
1363
+ if (cancellable) {
1364
+ cancelledVisionGenerations.add(requestId);
1365
+ activeVisionGenerations.get(requestId)?.interrupt?.();
1366
+ }
1367
+ self.postMessage({
1368
+ id,
1369
+ ok: true,
1370
+ cancelled: cancellable,
1371
+ requestId,
1372
+ queued: queued && !active,
1373
+ active,
1374
+ });
1375
+ return;
1376
+ }
1377
+ if (type === 'init') {
1378
+ workerConfig = payload;
1379
+ self.postMessage({ id, ok: true });
1380
+ return;
1381
+ }
1382
+ if (type === 'probe') {
1383
+ self.postMessage({ id, ok: true, ...(await probeRuntime()) });
1384
+ return;
1385
+ }
1386
+ if (type === 'text-download-status') {
1387
+ const modelId = String(payload?.modelId || '').trim();
1388
+ const dtype = payload?.dtype || 'q4f16';
1389
+ self.postMessage({ id, ok: true, ...(await getTextDownloadStatus(modelId, dtype)) });
1390
+ return;
1391
+ }
1392
+ if (type === 'download-text') {
1393
+ const state = await enqueueModelOperation(() => downloadTextModel(payload));
1394
+ self.postMessage({ id, ok: true, ...state });
1395
+ return;
1396
+ }
1397
+ if (type === 'start-download-text') {
1398
+ const request = assertTextDownloadCanStart(payload);
1399
+ queuedTextDownload = request;
1400
+ let acknowledged = false;
1401
+ const operation = enqueueModelOperation(() => {
1402
+ if (queuedTextDownload !== request) return getTextDownloadStatus(request.modelId, request.dtype);
1403
+ return downloadTextModel(payload, {
1404
+ onStarted(state) {
1405
+ acknowledged = true;
1406
+ self.postMessage({ id, ok: true, ...state });
1407
+ },
1408
+ });
1409
+ });
1410
+ void operation.then((state) => {
1411
+ if (!acknowledged) self.postMessage({ id, ok: true, ...state });
1412
+ }).catch((error) => {
1413
+ if (!acknowledged) self.postMessage({ id, ok: false, error: error?.message || String(error) });
1414
+ }).finally(() => {
1415
+ if (queuedTextDownload?.key === request.key) queuedTextDownload = null;
1416
+ });
1417
+ return;
1418
+ }
1419
+ if (type === 'pause-text-download') {
1420
+ self.postMessage({ id, ok: true, ...pauseTextDownload() });
1421
+ return;
1422
+ }
1423
+ if (type === 'stop-text-download') {
1424
+ const modelId = String(payload?.modelId || '').trim();
1425
+ const dtype = payload?.dtype || 'q4f16';
1426
+ const targetsQueuedTransfer = queuedTextDownload
1427
+ && sameTextModel(queuedTextDownload.modelId, queuedTextDownload.dtype, modelId, dtype);
1428
+ if (targetsQueuedTransfer) queuedTextDownload = null;
1429
+ const targetsTrackedTransfer = sameTextModel(textDownloadState.modelId, textDownloadState.dtype, modelId, dtype);
1430
+ if (targetsTrackedTransfer) {
1431
+ textDownloadCancelMode = 'stop';
1432
+ textDownloadState = { ...textDownloadState, status: 'stopping', ready: false, error: '' };
1433
+ if (activeTextDownloadModelId === modelId) textDownloadAbortController?.abort();
1434
+ postTextDownloadState({ force: true });
1435
+ }
1436
+ const state = await enqueueModelOperation(() => clearTextModelCache(modelId, dtype));
1437
+ self.postMessage({ id, ok: true, ...state });
1438
+ return;
1439
+ }
1440
+ if (type === 'pause-vision-download') {
1441
+ self.postMessage({ id, ok: true, ...pauseVisionDownload(payload?.modelId) });
1442
+ return;
1443
+ }
1444
+ if (type === 'stop-vision-download') {
1445
+ const modelId = String(payload?.modelId || '').trim();
1446
+ if (!modelId) throw new Error('No vision model was specified.');
1447
+ const stopped = stopVisionDownload(modelId);
1448
+ // A queued preload owns no live vision operation. Clear its model-specific
1449
+ // cache immediately so Stop is not trapped behind an unrelated text-model
1450
+ // transfer in the shared WebGPU operation queue. The host may also pass
1451
+ // targetsQueued after pause already dequeued that preload.
1452
+ const targetsQueued = stopped.targetsQueued || payload?.targetsQueued === true;
1453
+ const result = targetsQueued && !stopped.hasActiveVision
1454
+ ? await clearVisionModelCache(modelId)
1455
+ : await enqueueModelOperation(() => clearVisionModelCache(modelId));
1456
+ self.postMessage({
1457
+ id,
1458
+ ok: true,
1459
+ status: 'not-downloaded',
1460
+ ready: false,
1461
+ ...result,
1462
+ });
1463
+ return;
1464
+ }
1465
+ if (type === 'clear-cache') {
1466
+ const modelId = String(payload?.modelId || '').trim();
1467
+ const result = await enqueueModelOperation(() => clearVisionModelCache(modelId));
1468
+ self.postMessage({ id, ok: true, ...result });
1469
+ return;
1470
+ }
1471
+ if (type === 'dispose' || type === 'dispose-all') {
1472
+ await enqueueModelOperation(disposeAllRuntimes);
1473
+ self.postMessage({ id, ok: true, disposed: true });
1474
+ return;
1475
+ }
1476
+ if (type === 'dispose-vision') {
1477
+ await enqueueModelOperation(() => disposeVisionRuntime('vision'));
1478
+ self.postMessage({ id, ok: true, disposed: true });
1479
+ return;
1480
+ }
1481
+ if (type === 'dispose-text') {
1482
+ await enqueueModelOperation(() => disposeDownloadedTextRuntime());
1483
+ self.postMessage({ id, ok: true, disposed: true });
1484
+ return;
1485
+ }
1486
+ if (type === 'preload') {
1487
+ const modelId = String(payload?.modelId || '').trim();
1488
+ if (!modelId) throw new Error('No vision model was specified.');
1489
+ const request = {
1490
+ modelId,
1491
+ dtype: payload?.dtype || '',
1492
+ cancelMode: '',
1493
+ };
1494
+ queuedVisionDownload = request;
1495
+ self.postMessage({ type: 'vision-preload-state', modelId, status: 'queued' });
1496
+ const state = await enqueueModelOperation(() => {
1497
+ self.postMessage({ type: 'vision-preload-state', modelId, status: 'loading' });
1498
+ return preloadVisionModel(payload, request);
1499
+ });
1500
+ if (queuedVisionDownload === request) queuedVisionDownload = null;
1501
+ self.postMessage({ id, ok: true, ...state });
1502
+ return;
1503
+ }
1504
+ if (type === 'chat') {
1505
+ queuedVisionGenerations.add(id);
1506
+ try {
1507
+ const content = await enqueueModelOperation(() => runVision(payload, id));
1508
+ self.postMessage({ id, ok: true, content, raw: { model: payload?.modelId || '' } });
1509
+ } finally {
1510
+ queuedVisionGenerations.delete(id);
1511
+ cancelledVisionGenerations.delete(id);
1512
+ }
1513
+ return;
1514
+ }
1515
+ if (type === 'text-chat') {
1516
+ const result = await enqueueModelOperation(() => runText(payload));
1517
+ self.postMessage({
1518
+ id,
1519
+ ok: true,
1520
+ ...result,
1521
+ raw: { model: payload?.modelId || '' },
1522
+ });
1523
+ return;
1524
+ }
1525
+ if (type === 'multimodal-text-chat') {
1526
+ const result = await enqueueModelOperation(() => runMultimodalText(payload));
1527
+ self.postMessage({
1528
+ id,
1529
+ ok: true,
1530
+ ...result,
1531
+ raw: { model: payload?.modelId || '' },
1532
+ });
1533
+ return;
1534
+ }
1535
+ throw new Error(`Unknown WebGPU worker message: ${type || 'missing type'}`);
1536
+ } catch (error) {
1537
+ self.postMessage({ id, ok: false, error: error?.message || String(error) });
1538
+ }
1539
+ });
runtime/tool-call-parser.js ADDED
@@ -0,0 +1,512 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Browser-free fallback parser for local models that emit tool calls as text
2
+ // instead of using the provider's structured tool_calls field. This file is
3
+ // mirrored in the Firefox tree; keep both copies byte-identical.
4
+
5
+ // A `{` that never closes must not swallow the rest of the text: models put
6
+ // prose braces, template placeholders, and code snippets around a bare tool
7
+ // call, and the call after them still has to be recovered. Each unbalanced
8
+ // opener costs one extra scan, so the restarts are capped — real text needs
9
+ // none, and the cap keeps a pathological "{{{{…" response from going
10
+ // quadratic over the 10,000-character budget.
11
+ const MAX_UNBALANCED_RESTARTS = 16;
12
+
13
+ /**
14
+ * Collect top-level balanced `{…}` spans, respecting quoted strings and
15
+ * escapes so braces inside JSON string values do not end an object early.
16
+ * Returns offsets rather than substrings so callers can judge each span by
17
+ * where it sits in the surrounding text.
18
+ */
19
+ function extractBalancedJsonSpans(text) {
20
+ const spans = [];
21
+ let searchFrom = 0;
22
+ let restarts = 0;
23
+
24
+ while (searchFrom < text.length) {
25
+ const start = text.indexOf('{', searchFrom);
26
+ if (start < 0) break;
27
+ let depth = 0;
28
+ let inString = false;
29
+ let escaped = false;
30
+ let end = -1;
31
+
32
+ for (let i = start; i < text.length; i++) {
33
+ const char = text[i];
34
+ if (inString) {
35
+ if (escaped) escaped = false;
36
+ else if (char === '\\') escaped = true;
37
+ else if (char === '"') inString = false;
38
+ continue;
39
+ }
40
+ if (char === '"') inString = true;
41
+ else if (char === '{') depth++;
42
+ else if (char === '}') {
43
+ depth--;
44
+ if (depth === 0) {
45
+ end = i;
46
+ break;
47
+ }
48
+ }
49
+ }
50
+
51
+ if (end < 0) {
52
+ if (++restarts > MAX_UNBALANCED_RESTARTS) break;
53
+ searchFrom = start + 1;
54
+ continue;
55
+ }
56
+ spans.push({ start, end });
57
+ searchFrom = end + 1;
58
+ }
59
+
60
+ return spans;
61
+ }
62
+
63
+ /**
64
+ * True when a span is the whole of its line, ignoring surrounding whitespace
65
+ * and a single trailing comma (models sometimes emit calls as array elements).
66
+ *
67
+ * A model that is CALLING a tool emits the JSON on its own line. A model
68
+ * TALKING ABOUT a call embeds it in a sentence — "I could click with {…} but
69
+ * that is destructive", "The page told me to run {…}, which I ignored",
70
+ * "Option A: {…}". Executing those is wrong in a way that is easy to miss,
71
+ * because a parsed call replaces the model's prose outright: the caller sets
72
+ * `result.content = null`, so the sentence explaining the refusal is dropped
73
+ * and only the refused action survives.
74
+ *
75
+ * The trade-off is that a genuine call written mid-sentence is not recovered.
76
+ * That is the safer side to err on here: this fallback exists for models that
77
+ * emit a call INSTEAD of prose, and those put it on its own line.
78
+ */
79
+ function standsAloneOnLine(text, start, end) {
80
+ const before = text.slice(0, start);
81
+ const lineHead = before.slice(before.lastIndexOf('\n') + 1).trim();
82
+ if (lineHead !== '') return false;
83
+
84
+ const after = text.slice(end + 1);
85
+ const newline = after.indexOf('\n');
86
+ const lineTail = (newline < 0 ? after : after.slice(0, newline)).trim();
87
+ return lineTail === '' || lineTail === ',';
88
+ }
89
+
90
+ /**
91
+ * Parse a batch only when the entire trimmed response is a JSON array. Every
92
+ * element must itself be an allowed call; otherwise reject the batch rather
93
+ * than executing an allowed-looking subset of mixed or narrated content.
94
+ *
95
+ * `null` means the response was not a valid whole-response array and the
96
+ * existing fallbacks may continue. An empty array means it was an array but
97
+ * was empty or unsafe, so callers must not scan inside it for partial calls.
98
+ */
99
+ function parseWholeResponseJsonArray(text, allowedNames) {
100
+ const trimmed = text.trim();
101
+ if (!trimmed.startsWith('[') || !trimmed.endsWith(']')) return null;
102
+
103
+ let parsed;
104
+ try {
105
+ parsed = JSON.parse(trimmed);
106
+ } catch {
107
+ return null;
108
+ }
109
+ if (!Array.isArray(parsed)) return null;
110
+ if (!parsed.every(obj => (
111
+ obj
112
+ && typeof obj === 'object'
113
+ && !Array.isArray(obj)
114
+ && typeof obj.name === 'string'
115
+ && allowedNames.has(obj.name)
116
+ ))) return [];
117
+ return parsed;
118
+ }
119
+
120
+ /**
121
+ * Split on a delimiter only when it is outside strings and nested containers.
122
+ * LFM2.5 emits Python-style calls, but argument arrays and objects are JSON.
123
+ */
124
+ function splitLfmTopLevel(source, delimiter) {
125
+ const parts = [];
126
+ const closing = { '(': ')', '[': ']', '{': '}' };
127
+ const stack = [];
128
+ let quote = '';
129
+ let escaped = false;
130
+ let start = 0;
131
+
132
+ for (let i = 0; i < source.length; i++) {
133
+ const char = source[i];
134
+ if (quote) {
135
+ if (escaped) escaped = false;
136
+ else if (char === '\\') escaped = true;
137
+ else if (char === quote) quote = '';
138
+ continue;
139
+ }
140
+ if (char === '"' || char === "'") {
141
+ quote = char;
142
+ continue;
143
+ }
144
+ if (closing[char]) {
145
+ stack.push(closing[char]);
146
+ continue;
147
+ }
148
+ if (char === ')' || char === ']' || char === '}') {
149
+ if (stack.pop() !== char) return null;
150
+ continue;
151
+ }
152
+ if (char === delimiter && stack.length === 0) {
153
+ parts.push(source.slice(start, i));
154
+ start = i + 1;
155
+ }
156
+ }
157
+
158
+ if (quote || escaped || stack.length > 0) return null;
159
+ parts.push(source.slice(start));
160
+ return parts;
161
+ }
162
+
163
+ function parseLfmString(source) {
164
+ const quote = source[0];
165
+ if ((quote !== '"' && quote !== "'") || source.at(-1) !== quote) return null;
166
+ let output = '';
167
+ const escapes = {
168
+ '\\': '\\',
169
+ '"': '"',
170
+ "'": "'",
171
+ n: '\n',
172
+ r: '\r',
173
+ t: '\t',
174
+ b: '\b',
175
+ f: '\f',
176
+ };
177
+
178
+ for (let i = 1; i < source.length - 1; i++) {
179
+ const char = source[i];
180
+ if (char === quote) return null;
181
+ if (char !== '\\') {
182
+ if (char === '\n' || char === '\r') return null;
183
+ output += char;
184
+ continue;
185
+ }
186
+ if (++i >= source.length - 1) return null;
187
+ const escaped = source[i];
188
+ if (Object.hasOwn(escapes, escaped)) {
189
+ output += escapes[escaped];
190
+ continue;
191
+ }
192
+ const width = escaped === 'u' ? 4 : escaped === 'x' ? 2 : 0;
193
+ const hex = width ? source.slice(i + 1, i + 1 + width) : '';
194
+ if (!width || !new RegExp(`^[0-9a-fA-F]{${width}}$`).test(hex)) return null;
195
+ output += String.fromCodePoint(Number.parseInt(hex, 16));
196
+ i += width;
197
+ }
198
+ return output;
199
+ }
200
+
201
+ function parseLfmValue(source) {
202
+ const value = source.trim();
203
+ if (!value) return { ok: false };
204
+ if (value[0] === '"' || value[0] === "'") {
205
+ const parsed = parseLfmString(value);
206
+ return parsed === null ? { ok: false } : { ok: true, value: parsed };
207
+ }
208
+ if (value[0] === '[' || value[0] === '{') {
209
+ try {
210
+ return { ok: true, value: JSON.parse(value) };
211
+ } catch {
212
+ return { ok: false };
213
+ }
214
+ }
215
+ if (value === 'True' || value === 'true') return { ok: true, value: true };
216
+ if (value === 'False' || value === 'false') return { ok: true, value: false };
217
+ if (value === 'None' || value === 'null') return { ok: true, value: null };
218
+ if (/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(value)) {
219
+ const number = Number(value);
220
+ return Number.isFinite(number) ? { ok: true, value: number } : { ok: false };
221
+ }
222
+ return { ok: false };
223
+ }
224
+
225
+ const LFM_DIRECTIONAL_SCROLL_ALIASES = Object.freeze({
226
+ scrollup: 'up',
227
+ scrolldown: 'down',
228
+ scrolltop: 'top',
229
+ scrollbottom: 'bottom',
230
+ });
231
+
232
+ /**
233
+ * Parse LFM2/LFM2.5's documented native format:
234
+ * <|tool_call_start|>[tool_name(key='value', flag=False)]<|tool_call_end|>
235
+ *
236
+ * The wrapper must occupy the whole response, and every call must be valid and
237
+ * allowlisted. A recognized but unsafe block returns an empty atomic batch so
238
+ * later generic scanners cannot execute JSON fragments embedded inside it.
239
+ */
240
+ function parseLfmToolCalls(text, allowedNames) {
241
+ const startToken = '<|tool_call_start|>';
242
+ const endToken = '<|tool_call_end|>';
243
+ const source = text.trim();
244
+ if (!source.includes(startToken) && !source.includes(endToken)) return null;
245
+ if (!source.startsWith(startToken) || !source.endsWith(endToken)) return [];
246
+ const inner = source.slice(startToken.length, -endToken.length).trim();
247
+ if (inner.includes(startToken) || inner.includes(endToken)) return [];
248
+ if (!inner.startsWith('[') || !inner.endsWith(']')) return [];
249
+
250
+ const callParts = splitLfmTopLevel(inner.slice(1, -1), ',');
251
+ if (!callParts || callParts.length === 0 || callParts.some(part => !part.trim())) return [];
252
+ const calls = [];
253
+ for (const part of callParts) {
254
+ const match = /^([A-Za-z_]\w*)\s*\(([\s\S]*)\)$/.exec(part.trim());
255
+ if (!match) return [];
256
+ const aliasDirection = LFM_DIRECTIONAL_SCROLL_ALIASES[match[1]] || '';
257
+ const toolName = aliasDirection ? 'scroll' : match[1];
258
+ if (!allowedNames.has(toolName)) return [];
259
+ const args = Object.create(null);
260
+ if (match[2].trim()) {
261
+ const argParts = splitLfmTopLevel(match[2], ',');
262
+ if (!argParts || argParts.some(arg => !arg.trim())) return [];
263
+ for (const arg of argParts) {
264
+ const assignment = splitLfmTopLevel(arg, '=');
265
+ if (!assignment || assignment.length !== 2) return [];
266
+ const key = assignment[0].trim();
267
+ if (!/^[A-Za-z_]\w*$/.test(key) || Object.hasOwn(args, key)) return [];
268
+ const parsed = parseLfmValue(assignment[1]);
269
+ if (!parsed.ok) return [];
270
+ args[key] = parsed.value;
271
+ }
272
+ }
273
+ if (aliasDirection) {
274
+ if (Object.hasOwn(args, 'direction') && args.direction !== aliasDirection) return [];
275
+ args.direction = aliasDirection;
276
+ }
277
+ calls.push({ name: toolName, arguments: args });
278
+ }
279
+ return calls;
280
+ }
281
+
282
+ /**
283
+ * Quote relaxed `key:` tokens only when they occur outside JSON strings and
284
+ * after an object boundary. A regular-expression replacement corrupts string
285
+ * values such as "Keep, status: pending" before JSON.parse sees them.
286
+ */
287
+ function quoteBareJsonKeys(body) {
288
+ const source = String(body || '');
289
+ let output = '';
290
+ let inString = false;
291
+ let escaped = false;
292
+
293
+ for (let i = 0; i < source.length;) {
294
+ const char = source[i];
295
+ if (inString) {
296
+ output += char;
297
+ if (escaped) escaped = false;
298
+ else if (char === '\\') escaped = true;
299
+ else if (char === '"') inString = false;
300
+ i++;
301
+ continue;
302
+ }
303
+ if (char === '"') {
304
+ inString = true;
305
+ output += char;
306
+ i++;
307
+ continue;
308
+ }
309
+ if (/\w/.test(char)) {
310
+ let previous = i - 1;
311
+ while (previous >= 0 && /\s/.test(source[previous])) previous--;
312
+ if (previous < 0 || source[previous] === '{' || source[previous] === ',') {
313
+ let keyEnd = i + 1;
314
+ while (keyEnd < source.length && /\w/.test(source[keyEnd])) keyEnd++;
315
+ let colon = keyEnd;
316
+ while (colon < source.length && /\s/.test(source[colon])) colon++;
317
+ if (source[colon] === ':') {
318
+ output += `"${source.slice(i, keyEnd)}"${source.slice(keyEnd, colon + 1)}`;
319
+ i = colon + 1;
320
+ continue;
321
+ }
322
+ }
323
+ }
324
+ output += char;
325
+ i++;
326
+ }
327
+ return output;
328
+ }
329
+
330
+ function toFallbackToolCalls(objects) {
331
+ return objects.map((obj, index) => ({
332
+ id: `fallback_call_${Date.now()}_${index}`,
333
+ type: 'function',
334
+ function: {
335
+ name: obj.name,
336
+ arguments: typeof obj.arguments === 'string'
337
+ ? obj.arguments
338
+ : JSON.stringify(obj.arguments || obj.parameters || {}),
339
+ },
340
+ }));
341
+ }
342
+
343
+ /**
344
+ * Parse common text tool-call formats into OpenAI-style tool call objects.
345
+ * Only names in allowedNames are accepted.
346
+ */
347
+ export function parseToolCallsFromText(text, allowedNames) {
348
+ if (!text || text.length > 10000) return [];
349
+
350
+ const lfmCalls = parseLfmToolCalls(text, allowedNames);
351
+ if (lfmCalls !== null) return toFallbackToolCalls(lfmCalls);
352
+
353
+ const wholeResponseArray = parseWholeResponseJsonArray(text, allowedNames);
354
+ if (wholeResponseArray !== null) {
355
+ return toFallbackToolCalls(wholeResponseArray);
356
+ }
357
+
358
+ const results = [];
359
+ const parseXmlParamValue = (value) => {
360
+ const raw = String(value || '');
361
+ // MiniCPM5 wraps values containing <, &, or newlines in CDATA. Extract
362
+ // the literal content first so the tag strip below does not eat it.
363
+ const cdataMatch = /^\s*<!\[CDATA\[([\s\S]*?)\]\]>\s*$/.exec(raw);
364
+ if (cdataMatch) return cdataMatch[1];
365
+ const cleaned = raw
366
+ .replace(/<[^>]+>/g, '')
367
+ .trim();
368
+ if (!cleaned) return '';
369
+ try {
370
+ if (/^(?:"|'.*'|\{|\[|-?\d|true\b|false\b|null\b)/i.test(cleaned)) {
371
+ return JSON.parse(cleaned.replace(/^'([\s\S]*)'$/, '"$1"'));
372
+ }
373
+ } catch { /* fall through to string cleanup */ }
374
+ return cleaned.replace(/^["']+|["']+$/g, '');
375
+ };
376
+ const parseBailingToolCall = (inner) => {
377
+ const nameMatch = /^([A-Za-z_]\w*)/.exec(inner);
378
+ if (!nameMatch || !allowedNames.has(nameMatch[1])) return null;
379
+ let cursor = nameMatch[0].length;
380
+ const args = {};
381
+ const pairRe = /<arg_key>\s*([A-Za-z_]\w*)\s*<\/arg_key>\s*<arg_value>\s*([\s\S]*?)\s*<\/arg_value>/giy;
382
+ while (cursor < inner.length) {
383
+ while (cursor < inner.length && /\s/.test(inner[cursor])) cursor++;
384
+ if (cursor >= inner.length) break;
385
+ pairRe.lastIndex = cursor;
386
+ const pair = pairRe.exec(inner);
387
+ if (!pair || pair.index !== cursor) return null;
388
+ args[pair[1]] = parseXmlParamValue(pair[2]);
389
+ cursor = pairRe.lastIndex;
390
+ }
391
+ return { name: nameMatch[1], arguments: args };
392
+ };
393
+
394
+ const patterns = [
395
+ /<tool_call>\s*([\s\S]*?)\s*<\/tool_call>/gi,
396
+ /<\|tool_call\|?>\s*([\s\S]*?)\s*<\|?\/?tool_call\|?>/gi,
397
+ /<functioncall>\s*([\s\S]*?)\s*<\/functioncall>/gi,
398
+ ];
399
+
400
+ for (const re of patterns) {
401
+ let match;
402
+ while ((match = re.exec(text)) !== null) {
403
+ const inner = match[1].trim();
404
+ const wrappedArray = parseWholeResponseJsonArray(inner, allowedNames);
405
+ if (wrappedArray !== null) {
406
+ results.push(...wrappedArray);
407
+ continue;
408
+ }
409
+ try {
410
+ const obj = JSON.parse(inner);
411
+ if (obj && obj.name && allowedNames.has(obj.name)) {
412
+ results.push(obj);
413
+ continue;
414
+ }
415
+ } catch { /* not JSON — try call:name{} format below */ }
416
+
417
+ // Ling/Bailing V3 native tool format:
418
+ // <tool_call>click_ax\n<arg_key>ref_id</arg_key>\n<arg_value>ref_7</arg_value></tool_call>
419
+ const bailingCall = parseBailingToolCall(inner);
420
+ if (bailingCall) {
421
+ results.push(bailingCall);
422
+ continue;
423
+ }
424
+
425
+ const callMatch = /^call:(\w+)\s*\{([\s\S]*)\}$/.exec(inner);
426
+ if (callMatch && allowedNames.has(callMatch[1])) {
427
+ const toolName = callMatch[1];
428
+ let argsBody = callMatch[2]
429
+ .replace(/<\|"\|>/g, '"')
430
+ .replace(/<\|'\\?\|>/g, "'");
431
+ argsBody = quoteBareJsonKeys(argsBody);
432
+ try {
433
+ const args = JSON.parse(`{${argsBody}}`);
434
+ results.push({ name: toolName, arguments: args });
435
+ } catch { /* malformed arguments must never dispatch */ }
436
+ }
437
+ }
438
+ }
439
+
440
+ // XML-ish tool-call format used by some local/chat-template models:
441
+ // <tool_call><function=click_ax><parameter=ref_id>ref_6</parameter>...
442
+ const xmlToolRe = /<tool_call>\s*<function(?:\s*=\s*["']?([A-Za-z_]\w*)["']?|\s+name\s*=\s*["']?([A-Za-z_]\w*)["']?)\s*>\s*([\s\S]*?)\s*<\/function>\s*<\/tool_call>/gi;
443
+ const xmlToolSpans = [];
444
+ let xmlMatch;
445
+ while ((xmlMatch = xmlToolRe.exec(text)) !== null) {
446
+ xmlToolSpans.push({ start: xmlMatch.index, end: xmlMatch.index + xmlMatch[0].length });
447
+ const toolName = xmlMatch[1] || xmlMatch[2];
448
+ if (!allowedNames.has(toolName)) continue;
449
+ const body = xmlMatch[3] || '';
450
+ const args = {};
451
+ const paramRe = /<(?:param|parameter)(?:\s*=\s*["']?([A-Za-z_]\w*)["']?|\s+name\s*=\s*["']?([A-Za-z_]\w*)["']?)\s*>\s*([\s\S]*?)\s*<\/(?:param|parameter)>/gi;
452
+ let paramMatch;
453
+ while ((paramMatch = paramRe.exec(body)) !== null) {
454
+ const key = paramMatch[1] || paramMatch[2];
455
+ if (!key) continue;
456
+ args[key] = parseXmlParamValue(paramMatch[3]);
457
+ }
458
+ results.push({ name: toolName, arguments: args });
459
+ }
460
+
461
+ // MiniCPM5-2B native tool format (no outer <tool_call> wrapper):
462
+ // <function name="click"><param name="ref_id">ref_6</param>...</function>
463
+ const minicpmFunctionRe = /<function(?:\s+name\s*=\s*["']([A-Za-z_]\w*)["']|\s*=\s*["']?([A-Za-z_]\w*)["']?)\s*>\s*([\s\S]*?)\s*<\/function>/gi;
464
+ let minicpmMatch;
465
+ while ((minicpmMatch = minicpmFunctionRe.exec(text)) !== null) {
466
+ // Skip functions already consumed inside a <tool_call> wrapper above.
467
+ if (xmlToolSpans.some(span => minicpmMatch.index >= span.start && minicpmMatch.index < span.end)) continue;
468
+ const toolName = minicpmMatch[1] || minicpmMatch[2];
469
+ if (!allowedNames.has(toolName)) continue;
470
+ const body = minicpmMatch[3] || '';
471
+ const args = {};
472
+ const paramRe = /<(?:param|parameter)(?:\s*=\s*["']?([A-Za-z_]\w*)["']?|\s+name\s*=\s*["']?([A-Za-z_]\w*)["']?)\s*>\s*([\s\S]*?)\s*<\/(?:param|parameter)>/gi;
473
+ let paramMatch;
474
+ while ((paramMatch = paramRe.exec(body)) !== null) {
475
+ const key = paramMatch[1] || paramMatch[2];
476
+ if (!key) continue;
477
+ args[key] = parseXmlParamValue(paramMatch[3]);
478
+ }
479
+ results.push({ name: toolName, arguments: args });
480
+ }
481
+
482
+ if (results.length === 0) {
483
+ for (const { start, end } of extractBalancedJsonSpans(text)) {
484
+ if (!standsAloneOnLine(text, start, end)) continue;
485
+ try {
486
+ const obj = JSON.parse(text.slice(start, end + 1));
487
+ if (obj && obj.name && allowedNames.has(obj.name)) {
488
+ results.push(obj);
489
+ }
490
+ } catch { /* skip */ }
491
+ }
492
+ }
493
+
494
+ if (results.length === 0) {
495
+ const callRe = /call:(\w+)\s*\{([\s\S]*?)\}/g;
496
+ let match;
497
+ while ((match = callRe.exec(text)) !== null) {
498
+ if (!allowedNames.has(match[1])) continue;
499
+ const toolName = match[1];
500
+ let argsBody = match[2]
501
+ .replace(/<\|"\|>/g, '"')
502
+ .replace(/<\|'\\?\|>/g, "'");
503
+ argsBody = quoteBareJsonKeys(argsBody);
504
+ try {
505
+ const args = JSON.parse(`{${argsBody}}`);
506
+ results.push({ name: toolName, arguments: args });
507
+ } catch { /* malformed arguments must never dispatch */ }
508
+ }
509
+ }
510
+
511
+ return toFallbackToolCalls(results);
512
+ }
runtime/vendor/LICENSE.onnxruntime.txt ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) Microsoft Corporation
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
runtime/vendor/LICENSE.transformers.txt ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
runtime/vendor/README.md ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Vendored Transformers.js WebGPU runtime
2
+
3
+ This directory packages the JavaScript and WASM runtime used by two local
4
+ WebGPU paths in Chrome and by offline RAG's CPU/WASM semantic reranker:
5
+
6
+ - **Apocalypse Mode -> local WebGPU chat** downloads the selected LFM2.5 text
7
+ or vision-language preset, or the Nanbeige4.2-3B ONNX export, used by the
8
+ standalone-chat nuclear override. An opt-in Bonsai 27B preset uses a separate
9
+ vendored bitgpu worker, not this Transformers.js runtime; see
10
+ `src/chrome/vendor/bitgpu/README.webbrain.md`.
11
+ - **Settings -> Multimodal -> Vision -> LFM2.5-VL local fallback** runs
12
+ `LiquidAI/LFM2.5-VL-450M-ONNX` as the dedicated screenshot sidecar.
13
+ - **Apocalypse Mode -> Offline RAG** runs the explicitly downloaded, pinned
14
+ `Xenova/multilingual-e5-small` q8 model in a separate CPU/WASM worker. Model
15
+ weights remain optional and are never bundled or downloaded by a question.
16
+
17
+ Model weights are not bundled. Transformers.js downloads each selected WebGPU
18
+ model on first use and stores it in the browser cache. The shipped ONNX chat
19
+ presets are LFM2.5 2.6B, 1.2B Instruct, 1.2B Thinking, VL 1.6B, VL 3B, and
20
+ Nanbeige4.2-3B.
21
+ They are available through the nuclear control in standalone chat and can also
22
+ be selected as the normal provider after download. The reasoning presets keep
23
+ completed thinking out of visible answers. Current LFM2.5-VL layouts use:
24
+
25
+ - `embed_tokens`: FP16
26
+ - `vision_encoder`: FP16
27
+ - `decoder_model_merged`: Q4
28
+
29
+ `Michionlion/Nanbeige4.2-3B-ONNX-WebGPU` needs no bundle patch, but it does
30
+ need two things from this runtime. It publishes one WebGPU-fused graph named
31
+ `onnx/model_webgpu_mlp_q4f16.onnx` instead of the default `model_q4f16.onnx`,
32
+ so the worker passes `model_file_name: 'model_webgpu_mlp'`; the two external
33
+ data shards are declared by the repository's own
34
+ `transformers.js_config.use_external_data_format`. Its graph also uses the
35
+ `com.microsoft::MatMulNBitsMlp` WebGPU kernel, which is compiled into the
36
+ vendored `ort-wasm-simd-threaded.asyncify.wasm`. Do not replace the ONNX
37
+ Runtime artifacts with a build that drops that kernel; a test asserts it is
38
+ present because its absence only shows up as an opaque session-creation
39
+ failure at runtime.
40
+
41
+ The dedicated 450M vision sidecar uses the same component layout and is
42
+ approximately 810 MB. The VL 1.6B chat export instead names its vision and
43
+ decoder components `embed_images` and `decoder`; the compatibility patch below
44
+ maps those files into the standard image-text session names.
45
+
46
+ ## Packaged files
47
+
48
+ | File / directory | Source | Purpose |
49
+ | --- | --- | --- |
50
+ | `transformers.web.js` | `@huggingface/transformers` 4.2.0 | Browser ESM model/processor APIs |
51
+ | `ort.webgpu.mjs` | `onnxruntime-web` 1.27.0 | WebGPU execution provider |
52
+ | `onnxruntime-common/` | matching `onnxruntime-common` dependency | Tensor and session types |
53
+ | `ort-wasm-simd-threaded.asyncify.*` | `onnxruntime-web` 1.27.0 | The only WASM bridge; carries the WebGPU/JSEP runtime |
54
+ | `LICENSE.transformers.txt` | `@huggingface/transformers` 4.2.0 | Apache-2.0 license |
55
+ | `LICENSE.onnxruntime.txt` | ONNX Runtime 1.27.0 | MIT license |
56
+ | `ThirdPartyNotices.onnxruntime.txt` | ONNX Runtime 1.27.0 | Notices for incorporated third-party software |
57
+
58
+ The readable, unminified browser builds are committed so a fresh checkout is a
59
+ complete, Chrome Web Store-reviewable extension. Remote executable code is not
60
+ allowed by Manifest V3 CSP; only model/config/tokenizer data is fetched from
61
+ Hugging Face.
62
+
63
+ Only the asyncify variant is vendored. `ort.webgpu.mjs` resolves its WASM
64
+ filename from flags fixed at ONNX Runtime build time, and in this build the jsep
65
+ and jspi branches are compiled out, so `ort-wasm-simd-threaded.asyncify.*` is the
66
+ only bridge that can ever load. The WebGPU/JSEP execution provider lives inside
67
+ that artifact; it is not a separate download. Copying the `jsep` or `jspi` pair
68
+ adds tens of megabytes of unreachable binary to the reviewed package, so do not
69
+ restore them without first checking that filename branch.
70
+
71
+ The ONNX Runtime files are intentionally newer than the version pinned by
72
+ Transformers.js 4.2.0. Stable 1.27.0 contains WebGPU buffer-pool and
73
+ Qwen3/QMoE correctness fixes needed by Ling while retaining the same public
74
+ JavaScript session API used by this Transformers.js release.
75
+
76
+ ## Browser bundle patches
77
+
78
+ The upstream browser bundle contains two bare module specifiers that an
79
+ unbundled extension cannot resolve. After copying a new release, rewrite them:
80
+
81
+ ```bash
82
+ sed -i 's|"onnxruntime-web/webgpu"|"./ort.webgpu.mjs"|' \
83
+ src/chrome/vendor/transformers/transformers.web.js
84
+ sed -i 's|"onnxruntime-common"|"./onnxruntime-common/index.js"|' \
85
+ src/chrome/vendor/transformers/transformers.web.js
86
+ ```
87
+
88
+ Verify that no executable bare imports remain:
89
+
90
+ ```bash
91
+ grep -E '(import|export)[^"]*from\s+"[a-zA-Z@]' \
92
+ src/chrome/vendor/transformers/transformers.web.js \
93
+ | grep -v '^\s*//' | grep -v '^\s*\*'
94
+ ```
95
+
96
+ The LFM2.5-VL ONNX repositories need two compatibility hooks. The 1.6B export
97
+ predates the standard ImageTextToText component filenames, so keep the small
98
+ `session_file_names` alias hook in
99
+ `MODEL_SESSION_CONFIG[MODEL_TYPES.ImageTextToText]`; the worker supplies aliases
100
+ through `config["transformers.js_config"]`. Keep the corresponding `getSession`
101
+ logic resolving device and dtype by logical `session_name`, while using the
102
+ aliased filename only to fetch the physical ONNX graph. Both current VL exports
103
+ also use the Transformers v5 processor layout: image metadata is nested in
104
+ `processor_config.json`, and the chat template lives in `chat_template.jinja`.
105
+ Keep `loadImageProcessorConfig`, `image_processor_config_file`, and
106
+ `chat_template_file` support so the worker can opt into that layout without
107
+ changing older models. Reapply these patches after replacing
108
+ `transformers.web.js`, and mirror the resulting browser bundle into Firefox so
109
+ the packaged vendor files remain byte-identical.
110
+
111
+ ## Updating
112
+
113
+ Use a temporary dependency install; WebBrain does not need a runtime npm
114
+ dependency because the reviewed browser assets are committed directly:
115
+
116
+ ```bash
117
+ npm install --no-save @huggingface/transformers@latest
118
+ cp node_modules/@huggingface/transformers/dist/transformers.web.js \
119
+ src/chrome/vendor/transformers/
120
+ cp node_modules/onnxruntime-web/dist/ort.webgpu.mjs \
121
+ src/chrome/vendor/transformers/
122
+ cp node_modules/onnxruntime-web/dist/ort-wasm-simd-threaded.asyncify.{mjs,wasm} \
123
+ src/chrome/vendor/transformers/
124
+ rm -rf src/chrome/vendor/transformers/onnxruntime-common
125
+ mkdir src/chrome/vendor/transformers/onnxruntime-common
126
+ cp node_modules/onnxruntime-common/dist/esm/*.js \
127
+ src/chrome/vendor/transformers/onnxruntime-common/
128
+ ```
129
+
130
+ Copy the Transformers.js license and the ONNX Runtime license plus
131
+ `ThirdPartyNotices.txt` into this directory whenever the runtime is updated.
132
+ Reapply the specifier and ImageTextToText session-alias patches, update the
133
+ version table above, then verify:
134
+
135
+ 1. `node --check` passes for the provider, host, and worker.
136
+ 2. **Use local fallback** enables the option without downloading weights.
137
+ 3. **Test Connection** reads `WB7` from the packaged vision probe image.
138
+ 4. The second test reuses browser-cached model files.
139
+
140
+ ## Runtime architecture
141
+
142
+ ```text
143
+ ProviderManager._createProvider('webgpu') / getVisionProvider()
144
+ -> WebGPUProvider.chat() / WebGPUVisionProvider.chat()
145
+ -> MV3 offscreen document
146
+ -> dedicated module Worker
147
+ -> text-generation pipeline / AutoProcessor + AutoModelForImageTextToText
148
+ -> selected LFM2.5 or Nanbeige ONNX repo / LFM2.5-VL-450M-ONNX over WebGPU
149
+ ```
150
+
151
+ Keep inference in the Worker. The MV3 service worker has no WebGPU, while the
152
+ offscreen document's main thread has shown tighter WASM allocation limits for
153
+ large ONNX runs. Do not set `preferredOutputLocation: 'gpu-buffer'` on this
154
+ generation path: Transformers.js decodes the generated tensor on the CPU and
155
+ must be allowed to download that output normally.
156
+
157
+ LFM2.5-VL expects the image placeholder before the user's text in its chat
158
+ template. The Worker normalizes incoming OpenAI-style multimodal messages to
159
+ that order. For the connection test, it replaces the packaged generic OCR image
160
+ with three large, unlabeled color panels. This proves the local model received
161
+ pixels without relying on fine OCR, which is brittle for a 450M model. Normal
162
+ screenshots are never replaced by this probe-only path.
runtime/vendor/ThirdPartyNotices.onnxruntime.txt ADDED
The diff for this file is too large to render. See raw diff
 
runtime/vendor/onnxruntime-common/backend-impl.js ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ const backends = new Map();
4
+ const backendsSortedByPriority = [];
5
+ /**
6
+ * Register a backend.
7
+ *
8
+ * @param name - the name as a key to lookup as an execution provider.
9
+ * @param backend - the backend object.
10
+ * @param priority - an integer indicating the priority of the backend. Higher number means higher priority. if priority
11
+ * < 0, it will be considered as a 'beta' version and will not be used as a fallback backend by default.
12
+ *
13
+ * @ignore
14
+ */
15
+ export const registerBackend = (name, backend, priority) => {
16
+ if (backend && typeof backend.init === 'function' && typeof backend.createInferenceSessionHandler === 'function') {
17
+ const currentBackend = backends.get(name);
18
+ if (currentBackend === undefined) {
19
+ backends.set(name, { backend, priority });
20
+ }
21
+ else if (currentBackend.priority > priority) {
22
+ // same name is already registered with a higher priority. skip registeration.
23
+ return;
24
+ }
25
+ else if (currentBackend.priority === priority) {
26
+ if (currentBackend.backend !== backend) {
27
+ throw new Error(`cannot register backend "${name}" using priority ${priority}`);
28
+ }
29
+ }
30
+ if (priority >= 0) {
31
+ const i = backendsSortedByPriority.indexOf(name);
32
+ if (i !== -1) {
33
+ backendsSortedByPriority.splice(i, 1);
34
+ }
35
+ for (let i = 0; i < backendsSortedByPriority.length; i++) {
36
+ if (backends.get(backendsSortedByPriority[i]).priority <= priority) {
37
+ backendsSortedByPriority.splice(i, 0, name);
38
+ return;
39
+ }
40
+ }
41
+ backendsSortedByPriority.push(name);
42
+ }
43
+ return;
44
+ }
45
+ throw new TypeError('not a valid backend');
46
+ };
47
+ /**
48
+ * Try to resolve and initialize a backend.
49
+ *
50
+ * @param backendName - the name of the backend.
51
+ * @returns the backend instance if resolved and initialized successfully, or an error message if failed.
52
+ */
53
+ const tryResolveAndInitializeBackend = async (backendName) => {
54
+ const backendInfo = backends.get(backendName);
55
+ if (!backendInfo) {
56
+ return 'backend not found.';
57
+ }
58
+ if (backendInfo.initialized) {
59
+ return backendInfo.backend;
60
+ }
61
+ else if (backendInfo.aborted) {
62
+ return backendInfo.error;
63
+ }
64
+ else {
65
+ const isInitializing = !!backendInfo.initPromise;
66
+ try {
67
+ if (!isInitializing) {
68
+ backendInfo.initPromise = backendInfo.backend.init(backendName);
69
+ }
70
+ await backendInfo.initPromise;
71
+ backendInfo.initialized = true;
72
+ return backendInfo.backend;
73
+ }
74
+ catch (e) {
75
+ if (!isInitializing) {
76
+ backendInfo.error = `${e}`;
77
+ backendInfo.aborted = true;
78
+ }
79
+ return backendInfo.error;
80
+ }
81
+ finally {
82
+ delete backendInfo.initPromise;
83
+ }
84
+ }
85
+ };
86
+ /**
87
+ * Resolve execution providers from the specific session options.
88
+ *
89
+ * @param options - the session options object.
90
+ * @returns a promise that resolves to a tuple of an initialized backend instance and a session options object with
91
+ * filtered EP list.
92
+ *
93
+ * @ignore
94
+ */
95
+ export const resolveBackendAndExecutionProviders = async (options) => {
96
+ // extract backend hints from session options
97
+ const eps = options.executionProviders || [];
98
+ const backendHints = eps.map((i) => (typeof i === 'string' ? i : i.name));
99
+ const backendNames = backendHints.length === 0 ? backendsSortedByPriority : backendHints;
100
+ // try to resolve and initialize all requested backends
101
+ let backend;
102
+ const errors = [];
103
+ const availableBackendNames = new Set();
104
+ for (const backendName of backendNames) {
105
+ const resolveResult = await tryResolveAndInitializeBackend(backendName);
106
+ if (typeof resolveResult === 'string') {
107
+ errors.push({ name: backendName, err: resolveResult });
108
+ }
109
+ else {
110
+ if (!backend) {
111
+ backend = resolveResult;
112
+ }
113
+ if (backend === resolveResult) {
114
+ availableBackendNames.add(backendName);
115
+ }
116
+ }
117
+ }
118
+ // if no backend is available, throw error.
119
+ if (!backend) {
120
+ throw new Error(`no available backend found. ERR: ${errors.map((e) => `[${e.name}] ${e.err}`).join(', ')}`);
121
+ }
122
+ // for each explicitly requested backend, if it's not available, output warning message.
123
+ for (const { name, err } of errors) {
124
+ if (backendHints.includes(name)) {
125
+ // eslint-disable-next-line no-console
126
+ console.warn(`removing requested execution provider "${name}" from session options because it is not available: ${err}`);
127
+ }
128
+ }
129
+ const filteredEps = eps.filter((i) => availableBackendNames.has(typeof i === 'string' ? i : i.name));
130
+ return [
131
+ backend,
132
+ new Proxy(options, {
133
+ get: (target, prop) => {
134
+ if (prop === 'executionProviders') {
135
+ return filteredEps;
136
+ }
137
+ return Reflect.get(target, prop);
138
+ },
139
+ }),
140
+ ];
141
+ };
142
+ //# sourceMappingURL=backend-impl.js.map
runtime/vendor/onnxruntime-common/backend.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ export { registerBackend } from './backend-impl.js';
4
+ //# sourceMappingURL=backend.js.map
runtime/vendor/onnxruntime-common/env-impl.js ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ import { version } from './version.js';
4
+ let logLevelValue = 'warning';
5
+ export const env = {
6
+ wasm: {},
7
+ webgl: {},
8
+ webgpu: {},
9
+ versions: { common: version },
10
+ set logLevel(value) {
11
+ if (value === undefined) {
12
+ return;
13
+ }
14
+ if (typeof value !== 'string' || ['verbose', 'info', 'warning', 'error', 'fatal'].indexOf(value) === -1) {
15
+ throw new Error(`Unsupported logging level: ${value}`);
16
+ }
17
+ logLevelValue = value;
18
+ },
19
+ get logLevel() {
20
+ return logLevelValue;
21
+ },
22
+ };
23
+ // set property 'logLevel' so that they can be correctly transferred to worker by `postMessage()`.
24
+ Object.defineProperty(env, 'logLevel', { enumerable: true });
25
+ //# sourceMappingURL=env-impl.js.map
runtime/vendor/onnxruntime-common/env.js ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ import { env as envImpl } from './env-impl.js';
4
+ /**
5
+ * Represent a set of flags as a global singleton.
6
+ */
7
+ export const env = envImpl;
8
+ //# sourceMappingURL=env.js.map
runtime/vendor/onnxruntime-common/index.js ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ /**
4
+ * # ONNX Runtime JavaScript API
5
+ *
6
+ * ONNX Runtime JavaScript API is a unified API for all JavaScript usages, including the following NPM packages:
7
+ *
8
+ * - [onnxruntime-node](https://www.npmjs.com/package/onnxruntime-node)
9
+ * - [onnxruntime-web](https://www.npmjs.com/package/onnxruntime-web)
10
+ * - [onnxruntime-react-native](https://www.npmjs.com/package/onnxruntime-react-native)
11
+ *
12
+ * See also:
13
+ * - [Get Started](https://onnxruntime.ai/docs/get-started/with-javascript/)
14
+ * - [Inference examples](https://github.com/microsoft/onnxruntime-inference-examples/tree/main/js)
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+ export * from './backend.js';
19
+ export * from './env.js';
20
+ export * from './inference-session.js';
21
+ export * from './tensor.js';
22
+ export * from './tensor-conversion.js';
23
+ export * from './tensor-factory.js';
24
+ export * from './trace.js';
25
+ export * from './onnx-model.js';
26
+ export * from './onnx-value.js';
27
+ //# sourceMappingURL=index.js.map
runtime/vendor/onnxruntime-common/inference-session-impl.js ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ import { resolveBackendAndExecutionProviders } from './backend-impl.js';
4
+ import { Tensor } from './tensor.js';
5
+ import { TRACE_FUNC_BEGIN, TRACE_FUNC_END, TRACE_EVENT_BEGIN, TRACE_EVENT_END } from './trace.js';
6
+ export class InferenceSession {
7
+ constructor(handler) {
8
+ this.handler = handler;
9
+ }
10
+ async run(feeds, arg1, arg2) {
11
+ TRACE_FUNC_BEGIN();
12
+ TRACE_EVENT_BEGIN('InferenceSession.run');
13
+ const fetches = {};
14
+ let options = {};
15
+ // check inputs
16
+ if (typeof feeds !== 'object' || feeds === null || feeds instanceof Tensor || Array.isArray(feeds)) {
17
+ throw new TypeError("'feeds' must be an object that use input names as keys and OnnxValue as corresponding values.");
18
+ }
19
+ let isFetchesEmpty = true;
20
+ // determine which override is being used
21
+ if (typeof arg1 === 'object') {
22
+ if (arg1 === null) {
23
+ throw new TypeError('Unexpected argument[1]: cannot be null.');
24
+ }
25
+ if (arg1 instanceof Tensor) {
26
+ throw new TypeError("'fetches' cannot be a Tensor");
27
+ }
28
+ if (Array.isArray(arg1)) {
29
+ if (arg1.length === 0) {
30
+ throw new TypeError("'fetches' cannot be an empty array.");
31
+ }
32
+ isFetchesEmpty = false;
33
+ // output names
34
+ for (const name of arg1) {
35
+ if (typeof name !== 'string') {
36
+ throw new TypeError("'fetches' must be a string array or an object.");
37
+ }
38
+ if (this.outputNames.indexOf(name) === -1) {
39
+ throw new RangeError(`'fetches' contains invalid output name: ${name}.`);
40
+ }
41
+ fetches[name] = null;
42
+ }
43
+ if (typeof arg2 === 'object' && arg2 !== null) {
44
+ options = arg2;
45
+ }
46
+ else if (typeof arg2 !== 'undefined') {
47
+ throw new TypeError("'options' must be an object.");
48
+ }
49
+ }
50
+ else {
51
+ // decide whether arg1 is fetches or options
52
+ // if any output name is present and its value is valid OnnxValue, we consider it fetches
53
+ let isFetches = false;
54
+ const arg1Keys = Object.getOwnPropertyNames(arg1);
55
+ for (const name of this.outputNames) {
56
+ if (arg1Keys.indexOf(name) !== -1) {
57
+ const v = arg1[name];
58
+ if (v === null || v instanceof Tensor) {
59
+ isFetches = true;
60
+ isFetchesEmpty = false;
61
+ fetches[name] = v;
62
+ }
63
+ }
64
+ }
65
+ if (isFetches) {
66
+ if (typeof arg2 === 'object' && arg2 !== null) {
67
+ options = arg2;
68
+ }
69
+ else if (typeof arg2 !== 'undefined') {
70
+ throw new TypeError("'options' must be an object.");
71
+ }
72
+ }
73
+ else {
74
+ options = arg1;
75
+ }
76
+ }
77
+ }
78
+ else if (typeof arg1 !== 'undefined') {
79
+ throw new TypeError("Unexpected argument[1]: must be 'fetches' or 'options'.");
80
+ }
81
+ // check if all inputs are in feed
82
+ for (const name of this.inputNames) {
83
+ if (typeof feeds[name] === 'undefined') {
84
+ throw new Error(`input '${name}' is missing in 'feeds'.`);
85
+ }
86
+ }
87
+ // if no fetches is specified, we use the full output names list
88
+ if (isFetchesEmpty) {
89
+ for (const name of this.outputNames) {
90
+ fetches[name] = null;
91
+ }
92
+ }
93
+ // feeds, fetches and options are prepared
94
+ const results = await this.handler.run(feeds, fetches, options);
95
+ const returnValue = {};
96
+ for (const key in results) {
97
+ if (Object.hasOwnProperty.call(results, key)) {
98
+ const result = results[key];
99
+ if (result instanceof Tensor) {
100
+ returnValue[key] = result;
101
+ }
102
+ else {
103
+ returnValue[key] = new Tensor(result.type, result.data, result.dims);
104
+ }
105
+ }
106
+ }
107
+ TRACE_EVENT_END('InferenceSession.run');
108
+ TRACE_FUNC_END();
109
+ return returnValue;
110
+ }
111
+ async release() {
112
+ return this.handler.dispose();
113
+ }
114
+ static async create(arg0, arg1, arg2, arg3) {
115
+ TRACE_FUNC_BEGIN();
116
+ TRACE_EVENT_BEGIN('InferenceSession.create');
117
+ // either load from a file or buffer
118
+ let filePathOrUint8Array;
119
+ let options = {};
120
+ if (typeof arg0 === 'string') {
121
+ filePathOrUint8Array = arg0;
122
+ if (typeof arg1 === 'object' && arg1 !== null) {
123
+ options = arg1;
124
+ }
125
+ else if (typeof arg1 !== 'undefined') {
126
+ throw new TypeError("'options' must be an object.");
127
+ }
128
+ }
129
+ else if (arg0 instanceof Uint8Array) {
130
+ filePathOrUint8Array = arg0;
131
+ if (typeof arg1 === 'object' && arg1 !== null) {
132
+ options = arg1;
133
+ }
134
+ else if (typeof arg1 !== 'undefined') {
135
+ throw new TypeError("'options' must be an object.");
136
+ }
137
+ }
138
+ else if (arg0 instanceof ArrayBuffer ||
139
+ (typeof SharedArrayBuffer !== 'undefined' && arg0 instanceof SharedArrayBuffer)) {
140
+ const buffer = arg0;
141
+ let byteOffset = 0;
142
+ let byteLength = arg0.byteLength;
143
+ if (typeof arg1 === 'object' && arg1 !== null) {
144
+ options = arg1;
145
+ }
146
+ else if (typeof arg1 === 'number') {
147
+ byteOffset = arg1;
148
+ if (!Number.isSafeInteger(byteOffset)) {
149
+ throw new RangeError("'byteOffset' must be an integer.");
150
+ }
151
+ if (byteOffset < 0 || byteOffset >= buffer.byteLength) {
152
+ throw new RangeError(`'byteOffset' is out of range [0, ${buffer.byteLength}).`);
153
+ }
154
+ byteLength = arg0.byteLength - byteOffset;
155
+ if (typeof arg2 === 'number') {
156
+ byteLength = arg2;
157
+ if (!Number.isSafeInteger(byteLength)) {
158
+ throw new RangeError("'byteLength' must be an integer.");
159
+ }
160
+ if (byteLength <= 0 || byteOffset + byteLength > buffer.byteLength) {
161
+ throw new RangeError(`'byteLength' is out of range (0, ${buffer.byteLength - byteOffset}].`);
162
+ }
163
+ if (typeof arg3 === 'object' && arg3 !== null) {
164
+ options = arg3;
165
+ }
166
+ else if (typeof arg3 !== 'undefined') {
167
+ throw new TypeError("'options' must be an object.");
168
+ }
169
+ }
170
+ else if (typeof arg2 !== 'undefined') {
171
+ throw new TypeError("'byteLength' must be a number.");
172
+ }
173
+ }
174
+ else if (typeof arg1 !== 'undefined') {
175
+ throw new TypeError("'options' must be an object.");
176
+ }
177
+ filePathOrUint8Array = new Uint8Array(buffer, byteOffset, byteLength);
178
+ }
179
+ else {
180
+ throw new TypeError("Unexpected argument[0]: must be 'path' or 'buffer'.");
181
+ }
182
+ // resolve backend, update session options with validated EPs, and create session handler
183
+ const [backend, optionsWithValidatedEPs] = await resolveBackendAndExecutionProviders(options);
184
+ const handler = await backend.createInferenceSessionHandler(filePathOrUint8Array, optionsWithValidatedEPs);
185
+ TRACE_EVENT_END('InferenceSession.create');
186
+ TRACE_FUNC_END();
187
+ return new InferenceSession(handler);
188
+ }
189
+ startProfiling() {
190
+ this.handler.startProfiling();
191
+ }
192
+ endProfiling() {
193
+ this.handler.endProfiling();
194
+ }
195
+ get inputNames() {
196
+ return this.handler.inputNames;
197
+ }
198
+ get outputNames() {
199
+ return this.handler.outputNames;
200
+ }
201
+ get inputMetadata() {
202
+ return this.handler.inputMetadata;
203
+ }
204
+ get outputMetadata() {
205
+ return this.handler.outputMetadata;
206
+ }
207
+ }
208
+ //# sourceMappingURL=inference-session-impl.js.map
runtime/vendor/onnxruntime-common/inference-session.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ import { InferenceSession as InferenceSessionImpl } from './inference-session-impl.js';
4
+ // eslint-disable-next-line @typescript-eslint/naming-convention
5
+ export const InferenceSession = InferenceSessionImpl;
6
+ //# sourceMappingURL=inference-session.js.map
runtime/vendor/onnxruntime-common/onnx-model.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ export {};
4
+ //# sourceMappingURL=onnx-model.js.map
runtime/vendor/onnxruntime-common/onnx-value.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ export {};
4
+ //# sourceMappingURL=onnx-value.js.map
runtime/vendor/onnxruntime-common/tensor-conversion-impl.js ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ /**
4
+ * implementation of Tensor.toDataURL()
5
+ */
6
+ export const tensorToDataURL = (tensor, options) => {
7
+ const canvas = typeof document !== 'undefined' ? document.createElement('canvas') : new OffscreenCanvas(1, 1);
8
+ canvas.width = tensor.dims[3];
9
+ canvas.height = tensor.dims[2];
10
+ const pixels2DContext = canvas.getContext('2d');
11
+ if (pixels2DContext != null) {
12
+ // Default values for height and width & format
13
+ let width;
14
+ let height;
15
+ if (options?.tensorLayout !== undefined && options.tensorLayout === 'NHWC') {
16
+ width = tensor.dims[2];
17
+ height = tensor.dims[3];
18
+ }
19
+ else {
20
+ // Default layout is NCWH
21
+ width = tensor.dims[3];
22
+ height = tensor.dims[2];
23
+ }
24
+ const inputformat = options?.format !== undefined ? options.format : 'RGB';
25
+ const norm = options?.norm;
26
+ let normMean;
27
+ let normBias;
28
+ if (norm === undefined || norm.mean === undefined) {
29
+ normMean = [255, 255, 255, 255];
30
+ }
31
+ else {
32
+ if (typeof norm.mean === 'number') {
33
+ normMean = [norm.mean, norm.mean, norm.mean, norm.mean];
34
+ }
35
+ else {
36
+ normMean = [norm.mean[0], norm.mean[1], norm.mean[2], 0];
37
+ if (norm.mean[3] !== undefined) {
38
+ normMean[3] = norm.mean[3];
39
+ }
40
+ }
41
+ }
42
+ if (norm === undefined || norm.bias === undefined) {
43
+ normBias = [0, 0, 0, 0];
44
+ }
45
+ else {
46
+ if (typeof norm.bias === 'number') {
47
+ normBias = [norm.bias, norm.bias, norm.bias, norm.bias];
48
+ }
49
+ else {
50
+ normBias = [norm.bias[0], norm.bias[1], norm.bias[2], 0];
51
+ if (norm.bias[3] !== undefined) {
52
+ normBias[3] = norm.bias[3];
53
+ }
54
+ }
55
+ }
56
+ const stride = height * width;
57
+ // Default pointer assignments
58
+ let rTensorPointer = 0, gTensorPointer = stride, bTensorPointer = stride * 2, aTensorPointer = -1;
59
+ // Updating the pointer assignments based on the input image format
60
+ if (inputformat === 'RGBA') {
61
+ rTensorPointer = 0;
62
+ gTensorPointer = stride;
63
+ bTensorPointer = stride * 2;
64
+ aTensorPointer = stride * 3;
65
+ }
66
+ else if (inputformat === 'RGB') {
67
+ rTensorPointer = 0;
68
+ gTensorPointer = stride;
69
+ bTensorPointer = stride * 2;
70
+ }
71
+ else if (inputformat === 'RBG') {
72
+ rTensorPointer = 0;
73
+ bTensorPointer = stride;
74
+ gTensorPointer = stride * 2;
75
+ }
76
+ for (let i = 0; i < height; i++) {
77
+ for (let j = 0; j < width; j++) {
78
+ const R = (tensor.data[rTensorPointer++] - normBias[0]) * normMean[0]; // R value
79
+ const G = (tensor.data[gTensorPointer++] - normBias[1]) * normMean[1]; // G value
80
+ const B = (tensor.data[bTensorPointer++] - normBias[2]) * normMean[2]; // B value
81
+ const A = aTensorPointer === -1 ? 255 : (tensor.data[aTensorPointer++] - normBias[3]) * normMean[3]; // A value
82
+ pixels2DContext.fillStyle = 'rgba(' + R + ',' + G + ',' + B + ',' + A + ')';
83
+ pixels2DContext.fillRect(j, i, 1, 1);
84
+ }
85
+ }
86
+ if ('toDataURL' in canvas) {
87
+ return canvas.toDataURL();
88
+ }
89
+ else {
90
+ throw new Error('toDataURL is not supported');
91
+ }
92
+ }
93
+ else {
94
+ throw new Error('Can not access image data');
95
+ }
96
+ };
97
+ /**
98
+ * implementation of Tensor.toImageData()
99
+ */
100
+ export const tensorToImageData = (tensor, options) => {
101
+ const pixels2DContext = typeof document !== 'undefined'
102
+ ? document.createElement('canvas').getContext('2d')
103
+ : new OffscreenCanvas(1, 1).getContext('2d');
104
+ let image;
105
+ if (pixels2DContext != null) {
106
+ // Default values for height and width & format
107
+ let width;
108
+ let height;
109
+ let channels;
110
+ if (options?.tensorLayout !== undefined && options.tensorLayout === 'NHWC') {
111
+ width = tensor.dims[2];
112
+ height = tensor.dims[1];
113
+ channels = tensor.dims[3];
114
+ }
115
+ else {
116
+ // Default layout is NCWH
117
+ width = tensor.dims[3];
118
+ height = tensor.dims[2];
119
+ channels = tensor.dims[1];
120
+ }
121
+ const inputformat = options !== undefined ? (options.format !== undefined ? options.format : 'RGB') : 'RGB';
122
+ const norm = options?.norm;
123
+ let normMean;
124
+ let normBias;
125
+ if (norm === undefined || norm.mean === undefined) {
126
+ normMean = [255, 255, 255, 255];
127
+ }
128
+ else {
129
+ if (typeof norm.mean === 'number') {
130
+ normMean = [norm.mean, norm.mean, norm.mean, norm.mean];
131
+ }
132
+ else {
133
+ normMean = [norm.mean[0], norm.mean[1], norm.mean[2], 255];
134
+ if (norm.mean[3] !== undefined) {
135
+ normMean[3] = norm.mean[3];
136
+ }
137
+ }
138
+ }
139
+ if (norm === undefined || norm.bias === undefined) {
140
+ normBias = [0, 0, 0, 0];
141
+ }
142
+ else {
143
+ if (typeof norm.bias === 'number') {
144
+ normBias = [norm.bias, norm.bias, norm.bias, norm.bias];
145
+ }
146
+ else {
147
+ normBias = [norm.bias[0], norm.bias[1], norm.bias[2], 0];
148
+ if (norm.bias[3] !== undefined) {
149
+ normBias[3] = norm.bias[3];
150
+ }
151
+ }
152
+ }
153
+ const stride = height * width;
154
+ if (options !== undefined) {
155
+ if ((options.format !== undefined && channels === 4 && options.format !== 'RGBA') ||
156
+ (channels === 3 && options.format !== 'RGB' && options.format !== 'BGR')) {
157
+ throw new Error("Tensor format doesn't match input tensor dims");
158
+ }
159
+ }
160
+ // Default pointer assignments
161
+ const step = 4;
162
+ let rImagePointer = 0, gImagePointer = 1, bImagePointer = 2, aImagePointer = 3;
163
+ let rTensorPointer = 0, gTensorPointer = stride, bTensorPointer = stride * 2, aTensorPointer = -1;
164
+ // Updating the pointer assignments based on the input image format
165
+ if (inputformat === 'RGBA') {
166
+ rTensorPointer = 0;
167
+ gTensorPointer = stride;
168
+ bTensorPointer = stride * 2;
169
+ aTensorPointer = stride * 3;
170
+ }
171
+ else if (inputformat === 'RGB') {
172
+ rTensorPointer = 0;
173
+ gTensorPointer = stride;
174
+ bTensorPointer = stride * 2;
175
+ }
176
+ else if (inputformat === 'RBG') {
177
+ rTensorPointer = 0;
178
+ bTensorPointer = stride;
179
+ gTensorPointer = stride * 2;
180
+ }
181
+ image = pixels2DContext.createImageData(width, height);
182
+ for (let i = 0; i < height * width; rImagePointer += step, gImagePointer += step, bImagePointer += step, aImagePointer += step, i++) {
183
+ image.data[rImagePointer] = (tensor.data[rTensorPointer++] - normBias[0]) * normMean[0]; // R value
184
+ image.data[gImagePointer] = (tensor.data[gTensorPointer++] - normBias[1]) * normMean[1]; // G value
185
+ image.data[bImagePointer] = (tensor.data[bTensorPointer++] - normBias[2]) * normMean[2]; // B value
186
+ image.data[aImagePointer] =
187
+ aTensorPointer === -1 ? 255 : (tensor.data[aTensorPointer++] - normBias[3]) * normMean[3]; // A value
188
+ }
189
+ }
190
+ else {
191
+ throw new Error('Can not access image data');
192
+ }
193
+ return image;
194
+ };
195
+ //# sourceMappingURL=tensor-conversion-impl.js.map
runtime/vendor/onnxruntime-common/tensor-conversion.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ export {};
4
+ //# sourceMappingURL=tensor-conversion.js.map
runtime/vendor/onnxruntime-common/tensor-factory-impl.js ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ import { Tensor } from './tensor-impl.js';
4
+ /**
5
+ * Create a new tensor object from image object
6
+ *
7
+ * @param buffer - Extracted image buffer data - assuming RGBA format
8
+ * @param imageFormat - input image configuration - required configurations height, width, format
9
+ * @param tensorFormat - output tensor configuration - Default is RGB format
10
+ */
11
+ export const bufferToTensor = (buffer, options) => {
12
+ if (buffer === undefined) {
13
+ throw new Error('Image buffer must be defined');
14
+ }
15
+ if (options.height === undefined || options.width === undefined) {
16
+ throw new Error('Image height and width must be defined');
17
+ }
18
+ if (options.tensorLayout === 'NHWC') {
19
+ throw new Error('NHWC Tensor layout is not supported yet');
20
+ }
21
+ const { height, width } = options;
22
+ const norm = options.norm ?? { mean: 255, bias: 0 };
23
+ let normMean;
24
+ let normBias;
25
+ if (typeof norm.mean === 'number') {
26
+ normMean = [norm.mean, norm.mean, norm.mean, norm.mean];
27
+ }
28
+ else {
29
+ normMean = [norm.mean[0], norm.mean[1], norm.mean[2], norm.mean[3] ?? 255];
30
+ }
31
+ if (typeof norm.bias === 'number') {
32
+ normBias = [norm.bias, norm.bias, norm.bias, norm.bias];
33
+ }
34
+ else {
35
+ normBias = [norm.bias[0], norm.bias[1], norm.bias[2], norm.bias[3] ?? 0];
36
+ }
37
+ const inputformat = options.format !== undefined ? options.format : 'RGBA';
38
+ // default value is RGBA since imagedata and HTMLImageElement uses it
39
+ const outputformat = options.tensorFormat !== undefined ? (options.tensorFormat !== undefined ? options.tensorFormat : 'RGB') : 'RGB';
40
+ const stride = height * width;
41
+ const float32Data = outputformat === 'RGBA' ? new Float32Array(stride * 4) : new Float32Array(stride * 3);
42
+ // Default pointer assignments
43
+ let step = 4, rImagePointer = 0, gImagePointer = 1, bImagePointer = 2, aImagePointer = 3;
44
+ let rTensorPointer = 0, gTensorPointer = stride, bTensorPointer = stride * 2, aTensorPointer = -1;
45
+ // Updating the pointer assignments based on the input image format
46
+ if (inputformat === 'RGB') {
47
+ step = 3;
48
+ rImagePointer = 0;
49
+ gImagePointer = 1;
50
+ bImagePointer = 2;
51
+ aImagePointer = -1;
52
+ }
53
+ // Updating the pointer assignments based on the output tensor format
54
+ if (outputformat === 'RGBA') {
55
+ aTensorPointer = stride * 3;
56
+ }
57
+ else if (outputformat === 'RBG') {
58
+ rTensorPointer = 0;
59
+ bTensorPointer = stride;
60
+ gTensorPointer = stride * 2;
61
+ }
62
+ else if (outputformat === 'BGR') {
63
+ bTensorPointer = 0;
64
+ gTensorPointer = stride;
65
+ rTensorPointer = stride * 2;
66
+ }
67
+ for (let i = 0; i < stride; i++, rImagePointer += step, bImagePointer += step, gImagePointer += step, aImagePointer += step) {
68
+ float32Data[rTensorPointer++] = (buffer[rImagePointer] + normBias[0]) / normMean[0];
69
+ float32Data[gTensorPointer++] = (buffer[gImagePointer] + normBias[1]) / normMean[1];
70
+ float32Data[bTensorPointer++] = (buffer[bImagePointer] + normBias[2]) / normMean[2];
71
+ if (aTensorPointer !== -1 && aImagePointer !== -1) {
72
+ float32Data[aTensorPointer++] = (buffer[aImagePointer] + normBias[3]) / normMean[3];
73
+ }
74
+ }
75
+ // Float32Array -> ort.Tensor
76
+ const outputTensor = outputformat === 'RGBA'
77
+ ? new Tensor('float32', float32Data, [1, 4, height, width])
78
+ : new Tensor('float32', float32Data, [1, 3, height, width]);
79
+ return outputTensor;
80
+ };
81
+ /**
82
+ * implementation of Tensor.fromImage().
83
+ */
84
+ export const tensorFromImage = async (image, options) => {
85
+ // checking the type of image object
86
+ const isHTMLImageEle = typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement;
87
+ const isImageDataEle = typeof ImageData !== 'undefined' && image instanceof ImageData;
88
+ const isImageBitmap = typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap;
89
+ const isString = typeof image === 'string';
90
+ let data;
91
+ let bufferToTensorOptions = options ?? {};
92
+ const createCanvas = () => {
93
+ if (typeof document !== 'undefined') {
94
+ return document.createElement('canvas');
95
+ }
96
+ else if (typeof OffscreenCanvas !== 'undefined') {
97
+ return new OffscreenCanvas(1, 1);
98
+ }
99
+ else {
100
+ throw new Error('Canvas is not supported');
101
+ }
102
+ };
103
+ const createCanvasContext = (canvas) => {
104
+ if (typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement) {
105
+ return canvas.getContext('2d');
106
+ }
107
+ else if (canvas instanceof OffscreenCanvas) {
108
+ return canvas.getContext('2d');
109
+ }
110
+ else {
111
+ return null;
112
+ }
113
+ };
114
+ // filling and checking image configuration options
115
+ if (isHTMLImageEle) {
116
+ // HTMLImageElement - image object - format is RGBA by default
117
+ const canvas = createCanvas();
118
+ canvas.width = image.width;
119
+ canvas.height = image.height;
120
+ const pixels2DContext = createCanvasContext(canvas);
121
+ if (pixels2DContext != null) {
122
+ let height = image.height;
123
+ let width = image.width;
124
+ if (options !== undefined && options.resizedHeight !== undefined && options.resizedWidth !== undefined) {
125
+ height = options.resizedHeight;
126
+ width = options.resizedWidth;
127
+ }
128
+ if (options !== undefined) {
129
+ bufferToTensorOptions = options;
130
+ if (options.tensorFormat !== undefined) {
131
+ throw new Error('Image input config format must be RGBA for HTMLImageElement');
132
+ }
133
+ else {
134
+ bufferToTensorOptions.tensorFormat = 'RGBA';
135
+ }
136
+ bufferToTensorOptions.height = height;
137
+ bufferToTensorOptions.width = width;
138
+ }
139
+ else {
140
+ bufferToTensorOptions.tensorFormat = 'RGBA';
141
+ bufferToTensorOptions.height = height;
142
+ bufferToTensorOptions.width = width;
143
+ }
144
+ pixels2DContext.drawImage(image, 0, 0);
145
+ data = pixels2DContext.getImageData(0, 0, width, height).data;
146
+ }
147
+ else {
148
+ throw new Error('Can not access image data');
149
+ }
150
+ }
151
+ else if (isImageDataEle) {
152
+ let height;
153
+ let width;
154
+ if (options !== undefined && options.resizedWidth !== undefined && options.resizedHeight !== undefined) {
155
+ height = options.resizedHeight;
156
+ width = options.resizedWidth;
157
+ }
158
+ else {
159
+ height = image.height;
160
+ width = image.width;
161
+ }
162
+ if (options !== undefined) {
163
+ bufferToTensorOptions = options;
164
+ }
165
+ bufferToTensorOptions.format = 'RGBA';
166
+ bufferToTensorOptions.height = height;
167
+ bufferToTensorOptions.width = width;
168
+ if (options !== undefined) {
169
+ const tempCanvas = createCanvas();
170
+ tempCanvas.width = width;
171
+ tempCanvas.height = height;
172
+ const pixels2DContext = createCanvasContext(tempCanvas);
173
+ if (pixels2DContext != null) {
174
+ pixels2DContext.putImageData(image, 0, 0);
175
+ data = pixels2DContext.getImageData(0, 0, width, height).data;
176
+ }
177
+ else {
178
+ throw new Error('Can not access image data');
179
+ }
180
+ }
181
+ else {
182
+ data = image.data;
183
+ }
184
+ }
185
+ else if (isImageBitmap) {
186
+ // ImageBitmap - image object - format must be provided by user
187
+ if (options === undefined) {
188
+ throw new Error('Please provide image config with format for Imagebitmap');
189
+ }
190
+ const canvas = createCanvas();
191
+ canvas.width = image.width;
192
+ canvas.height = image.height;
193
+ const pixels2DContext = createCanvasContext(canvas);
194
+ if (pixels2DContext != null) {
195
+ const height = image.height;
196
+ const width = image.width;
197
+ pixels2DContext.drawImage(image, 0, 0, width, height);
198
+ data = pixels2DContext.getImageData(0, 0, width, height).data;
199
+ bufferToTensorOptions.height = height;
200
+ bufferToTensorOptions.width = width;
201
+ return bufferToTensor(data, bufferToTensorOptions);
202
+ }
203
+ else {
204
+ throw new Error('Can not access image data');
205
+ }
206
+ }
207
+ else if (isString) {
208
+ return new Promise((resolve, reject) => {
209
+ const canvas = createCanvas();
210
+ const context = createCanvasContext(canvas);
211
+ if (!image || !context) {
212
+ return reject();
213
+ }
214
+ const newImage = new Image();
215
+ newImage.crossOrigin = 'Anonymous';
216
+ newImage.src = image;
217
+ newImage.onload = () => {
218
+ canvas.width = newImage.width;
219
+ canvas.height = newImage.height;
220
+ context.drawImage(newImage, 0, 0, canvas.width, canvas.height);
221
+ const img = context.getImageData(0, 0, canvas.width, canvas.height);
222
+ bufferToTensorOptions.height = canvas.height;
223
+ bufferToTensorOptions.width = canvas.width;
224
+ resolve(bufferToTensor(img.data, bufferToTensorOptions));
225
+ };
226
+ });
227
+ }
228
+ else {
229
+ throw new Error('Input data provided is not supported - aborted tensor creation');
230
+ }
231
+ if (data !== undefined) {
232
+ return bufferToTensor(data, bufferToTensorOptions);
233
+ }
234
+ else {
235
+ throw new Error('Input data provided is not supported - aborted tensor creation');
236
+ }
237
+ };
238
+ /**
239
+ * implementation of Tensor.fromTexture().
240
+ */
241
+ export const tensorFromTexture = (texture, options) => {
242
+ const { width, height, download, dispose } = options;
243
+ // Always assume RGBAF32. TODO: support different texture format
244
+ const dims = [1, height, width, 4];
245
+ return new Tensor({ location: 'texture', type: 'float32', texture, dims, download, dispose });
246
+ };
247
+ /**
248
+ * implementation of Tensor.fromGpuBuffer().
249
+ */
250
+ export const tensorFromGpuBuffer = (gpuBuffer, options) => {
251
+ const { dataType, dims, download, dispose } = options;
252
+ return new Tensor({ location: 'gpu-buffer', type: dataType ?? 'float32', gpuBuffer, dims, download, dispose });
253
+ };
254
+ /**
255
+ * implementation of Tensor.fromMLTensor().
256
+ */
257
+ export const tensorFromMLTensor = (mlTensor, options) => {
258
+ const { dataType, dims, download, dispose } = options;
259
+ return new Tensor({ location: 'ml-tensor', type: dataType ?? 'float32', mlTensor, dims, download, dispose });
260
+ };
261
+ /**
262
+ * implementation of Tensor.fromPinnedBuffer().
263
+ */
264
+ export const tensorFromPinnedBuffer = (type, buffer, dims) => new Tensor({ location: 'cpu-pinned', type, data: buffer, dims: dims ?? [buffer.length] });
265
+ //# sourceMappingURL=tensor-factory-impl.js.map
runtime/vendor/onnxruntime-common/tensor-factory.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ export {};
4
+ //# sourceMappingURL=tensor-factory.js.map
runtime/vendor/onnxruntime-common/tensor-impl-type-mapping.js ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ // a runtime map that maps type string to TypedArray constructor. Should match Tensor.DataTypeMap.
4
+ export const NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP = new Map([
5
+ ['float32', Float32Array],
6
+ ['uint8', Uint8Array],
7
+ ['int8', Int8Array],
8
+ ['uint16', Uint16Array],
9
+ ['int16', Int16Array],
10
+ ['int32', Int32Array],
11
+ ['bool', Uint8Array],
12
+ ['float64', Float64Array],
13
+ ['uint32', Uint32Array],
14
+ ['int4', Uint8Array],
15
+ ['uint4', Uint8Array],
16
+ ]);
17
+ // a runtime map that maps type string to TypedArray constructor. Should match Tensor.DataTypeMap.
18
+ export const NUMERIC_TENSOR_TYPEDARRAY_TO_TYPE_MAP = new Map([
19
+ [Float32Array, 'float32'],
20
+ [Uint8Array, 'uint8'],
21
+ [Int8Array, 'int8'],
22
+ [Uint16Array, 'uint16'],
23
+ [Int16Array, 'int16'],
24
+ [Int32Array, 'int32'],
25
+ [Float64Array, 'float64'],
26
+ [Uint32Array, 'uint32'],
27
+ ]);
28
+ // the following code allows delaying execution of BigInt/Float16Array checking. This allows lazy initialization for
29
+ // NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP and NUMERIC_TENSOR_TYPEDARRAY_TO_TYPE_MAP, which allows BigInt/Float16Array
30
+ // polyfill if available.
31
+ let isTypedArrayChecked = false;
32
+ export const checkTypedArray = () => {
33
+ if (!isTypedArrayChecked) {
34
+ isTypedArrayChecked = true;
35
+ const isBigInt64ArrayAvailable = typeof BigInt64Array !== 'undefined' && BigInt64Array.from;
36
+ const isBigUint64ArrayAvailable = typeof BigUint64Array !== 'undefined' && BigUint64Array.from;
37
+ // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any
38
+ const Float16Array = globalThis.Float16Array;
39
+ const isFloat16ArrayAvailable = typeof Float16Array !== 'undefined' && Float16Array.from;
40
+ if (isBigInt64ArrayAvailable) {
41
+ NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP.set('int64', BigInt64Array);
42
+ NUMERIC_TENSOR_TYPEDARRAY_TO_TYPE_MAP.set(BigInt64Array, 'int64');
43
+ }
44
+ if (isBigUint64ArrayAvailable) {
45
+ NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP.set('uint64', BigUint64Array);
46
+ NUMERIC_TENSOR_TYPEDARRAY_TO_TYPE_MAP.set(BigUint64Array, 'uint64');
47
+ }
48
+ if (isFloat16ArrayAvailable) {
49
+ NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP.set('float16', Float16Array);
50
+ NUMERIC_TENSOR_TYPEDARRAY_TO_TYPE_MAP.set(Float16Array, 'float16');
51
+ }
52
+ else {
53
+ // if Float16Array is not available, use 'Uint16Array' to store the data.
54
+ NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP.set('float16', Uint16Array);
55
+ }
56
+ }
57
+ };
58
+ //# sourceMappingURL=tensor-impl-type-mapping.js.map
runtime/vendor/onnxruntime-common/tensor-impl.js ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ import { tensorToDataURL, tensorToImageData } from './tensor-conversion-impl.js';
4
+ import { tensorFromGpuBuffer, tensorFromImage, tensorFromMLTensor, tensorFromPinnedBuffer, tensorFromTexture, } from './tensor-factory-impl.js';
5
+ import { checkTypedArray, NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP, NUMERIC_TENSOR_TYPEDARRAY_TO_TYPE_MAP, } from './tensor-impl-type-mapping.js';
6
+ import { calculateSize, tensorReshape } from './tensor-utils-impl.js';
7
+ /**
8
+ * the implementation of Tensor interface.
9
+ *
10
+ * @ignore
11
+ */
12
+ export class Tensor {
13
+ /**
14
+ * implementation.
15
+ */
16
+ constructor(arg0, arg1, arg2) {
17
+ // perform one-time check for BigInt/Float16Array support
18
+ checkTypedArray();
19
+ let type;
20
+ let dims;
21
+ if (typeof arg0 === 'object' && 'location' in arg0) {
22
+ //
23
+ // constructing tensor from specific location
24
+ //
25
+ this.dataLocation = arg0.location;
26
+ type = arg0.type;
27
+ dims = arg0.dims;
28
+ switch (arg0.location) {
29
+ case 'cpu-pinned': {
30
+ const expectedTypedArrayConstructor = NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP.get(type);
31
+ if (!expectedTypedArrayConstructor) {
32
+ throw new TypeError(`unsupported type "${type}" to create tensor from pinned buffer`);
33
+ }
34
+ if (!(arg0.data instanceof expectedTypedArrayConstructor)) {
35
+ throw new TypeError(`buffer should be of type ${expectedTypedArrayConstructor.name}`);
36
+ }
37
+ this.cpuData = arg0.data;
38
+ break;
39
+ }
40
+ case 'texture': {
41
+ if (type !== 'float32') {
42
+ throw new TypeError(`unsupported type "${type}" to create tensor from texture`);
43
+ }
44
+ this.gpuTextureData = arg0.texture;
45
+ this.downloader = arg0.download;
46
+ this.disposer = arg0.dispose;
47
+ break;
48
+ }
49
+ case 'gpu-buffer': {
50
+ if (type !== 'float32' &&
51
+ type !== 'float16' &&
52
+ type !== 'int32' &&
53
+ type !== 'int64' &&
54
+ type !== 'uint32' &&
55
+ type !== 'uint8' &&
56
+ type !== 'bool' &&
57
+ type !== 'uint4' &&
58
+ type !== 'int4') {
59
+ throw new TypeError(`unsupported type "${type}" to create tensor from gpu buffer`);
60
+ }
61
+ this.gpuBufferData = arg0.gpuBuffer;
62
+ this.downloader = arg0.download;
63
+ this.disposer = arg0.dispose;
64
+ break;
65
+ }
66
+ case 'ml-tensor': {
67
+ if (type !== 'float32' &&
68
+ type !== 'float16' &&
69
+ type !== 'int32' &&
70
+ type !== 'int64' &&
71
+ type !== 'uint32' &&
72
+ type !== 'uint64' &&
73
+ type !== 'int8' &&
74
+ type !== 'uint8' &&
75
+ type !== 'bool' &&
76
+ type !== 'uint4' &&
77
+ type !== 'int4') {
78
+ throw new TypeError(`unsupported type "${type}" to create tensor from MLTensor`);
79
+ }
80
+ this.mlTensorData = arg0.mlTensor;
81
+ this.downloader = arg0.download;
82
+ this.disposer = arg0.dispose;
83
+ break;
84
+ }
85
+ default:
86
+ throw new Error(`Tensor constructor: unsupported location '${this.dataLocation}'`);
87
+ }
88
+ }
89
+ else {
90
+ //
91
+ // constructing tensor of location 'cpu'
92
+ //
93
+ let data;
94
+ let maybeDims;
95
+ // check whether arg0 is type or data
96
+ if (typeof arg0 === 'string') {
97
+ //
98
+ // Override: constructor(type, data, ...)
99
+ //
100
+ type = arg0;
101
+ maybeDims = arg2;
102
+ if (arg0 === 'string') {
103
+ // string tensor
104
+ if (!Array.isArray(arg1)) {
105
+ throw new TypeError("A string tensor's data must be a string array.");
106
+ }
107
+ // we don't check whether every element in the array is string; this is too slow. we assume it's correct and
108
+ // error will be populated at inference
109
+ data = arg1;
110
+ }
111
+ else {
112
+ // numeric tensor
113
+ const typedArrayConstructor = NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP.get(arg0);
114
+ if (typedArrayConstructor === undefined) {
115
+ throw new TypeError(`Unsupported tensor type: ${arg0}.`);
116
+ }
117
+ if (Array.isArray(arg1)) {
118
+ if ((arg0 === 'float16' && typedArrayConstructor === Uint16Array) || arg0 === 'uint4' || arg0 === 'int4') {
119
+ // - 'float16':
120
+ // When no Float16Array polyfill is used, we cannot create 'float16' tensor from number array.
121
+ //
122
+ // Throw error here because when user try to use number array as data,
123
+ // e.g. new Tensor('float16', [1, 2, 3, 4], dims)), it will actually call
124
+ // Uint16Array.from(arg1) which generates wrong data.
125
+ //
126
+ // - 'uint4' and 'int4':
127
+ // Uint8Array.from(arg1) will generate wrong data for 'uint4' and 'int4' tensor.
128
+ //
129
+ throw new TypeError(`Creating a ${arg0} tensor from number array is not supported. Please use ${typedArrayConstructor.name} as data.`);
130
+ }
131
+ else if (arg0 === 'uint64' || arg0 === 'int64') {
132
+ // use 'as any' here because:
133
+ // 1. TypeScript's check on type of 'Array.isArray()' does not work with readonly arrays.
134
+ // see https://github.com/microsoft/TypeScript/issues/17002
135
+ // 2. TypeScript's check on union type of '(BigInt64ArrayConstructor|BigUint64ArrayConstructor).from()'
136
+ // does not accept parameter mapFn.
137
+ // 3. parameters of 'SupportedTypedArrayConstructors.from()' does not match the requirement of the union
138
+ // type.
139
+ // assume 'arg1' is of type "readonly number[]|readonly bigint[]" here.
140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
+ data = typedArrayConstructor.from(arg1, BigInt);
142
+ }
143
+ else {
144
+ // assume 'arg1' is of type "readonly number[]" here.
145
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
146
+ data = typedArrayConstructor.from(arg1);
147
+ }
148
+ }
149
+ else if (arg1 instanceof typedArrayConstructor) {
150
+ data = arg1;
151
+ }
152
+ else if (arg1 instanceof Uint8ClampedArray) {
153
+ if (arg0 === 'uint8') {
154
+ data = Uint8Array.from(arg1);
155
+ }
156
+ else {
157
+ throw new TypeError(`A Uint8ClampedArray tensor's data must be type of uint8`);
158
+ }
159
+ }
160
+ else if (arg0 === 'float16' && arg1 instanceof Uint16Array && typedArrayConstructor !== Uint16Array) {
161
+ // when Float16Array is available and data is of type Uint16Array.
162
+ // We allow Uint16Array to be passed in as data for 'float16' tensor until Float16Array is generally
163
+ // supported in JavaScript environment.
164
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
165
+ data = new globalThis.Float16Array(arg1.buffer, arg1.byteOffset, arg1.length);
166
+ }
167
+ else {
168
+ throw new TypeError(`A ${type} tensor's data must be type of ${typedArrayConstructor}`);
169
+ }
170
+ }
171
+ }
172
+ else {
173
+ //
174
+ // Override: constructor(data, ...)
175
+ //
176
+ maybeDims = arg1;
177
+ if (Array.isArray(arg0)) {
178
+ // only boolean[] and string[] is supported
179
+ if (arg0.length === 0) {
180
+ throw new TypeError('Tensor type cannot be inferred from an empty array.');
181
+ }
182
+ const firstElementType = typeof arg0[0];
183
+ if (firstElementType === 'string') {
184
+ type = 'string';
185
+ data = arg0;
186
+ }
187
+ else if (firstElementType === 'boolean') {
188
+ type = 'bool';
189
+ // 'arg0' is of type 'boolean[]'. Uint8Array.from(boolean[]) actually works, but typescript thinks this is
190
+ // wrong type. We use 'as any' to make it happy.
191
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
192
+ data = Uint8Array.from(arg0);
193
+ }
194
+ else {
195
+ throw new TypeError(`Invalid element type of data array: ${firstElementType}.`);
196
+ }
197
+ }
198
+ else if (arg0 instanceof Uint8ClampedArray) {
199
+ type = 'uint8';
200
+ data = Uint8Array.from(arg0);
201
+ }
202
+ else {
203
+ // get tensor type from TypedArray
204
+ const mappedType = NUMERIC_TENSOR_TYPEDARRAY_TO_TYPE_MAP.get(arg0.constructor);
205
+ if (mappedType === undefined) {
206
+ throw new TypeError(`Unsupported type for tensor data: ${arg0.constructor}.`);
207
+ }
208
+ type = mappedType;
209
+ data = arg0;
210
+ }
211
+ }
212
+ // type and data is processed, now processing dims
213
+ if (maybeDims === undefined) {
214
+ // assume 1-D tensor if dims omitted
215
+ maybeDims = [data.length];
216
+ }
217
+ else if (!Array.isArray(maybeDims)) {
218
+ throw new TypeError("A tensor's dims must be a number array");
219
+ }
220
+ dims = maybeDims;
221
+ this.cpuData = data;
222
+ this.dataLocation = 'cpu';
223
+ }
224
+ // perform check on dims
225
+ const size = calculateSize(dims);
226
+ // if data is on CPU, check whether data length matches tensor size
227
+ if (this.cpuData && size !== this.cpuData.length) {
228
+ if ((type === 'uint4' || type === 'int4') && Math.ceil(size / 2) === this.cpuData.length) {
229
+ // for (u)int4, the data length is half of the tensor size. So we check this special case when size is odd.
230
+ }
231
+ else {
232
+ throw new Error(`Tensor's size(${size}) does not match data length(${this.cpuData.length}).`);
233
+ }
234
+ }
235
+ this.type = type;
236
+ this.dims = dims;
237
+ this.size = size;
238
+ }
239
+ // #endregion
240
+ // #region factory
241
+ static async fromImage(image, options) {
242
+ return tensorFromImage(image, options);
243
+ }
244
+ static fromTexture(texture, options) {
245
+ return tensorFromTexture(texture, options);
246
+ }
247
+ static fromGpuBuffer(gpuBuffer, options) {
248
+ return tensorFromGpuBuffer(gpuBuffer, options);
249
+ }
250
+ static fromMLTensor(mlTensor, options) {
251
+ return tensorFromMLTensor(mlTensor, options);
252
+ }
253
+ static fromPinnedBuffer(type, buffer, dims) {
254
+ return tensorFromPinnedBuffer(type, buffer, dims);
255
+ }
256
+ // #endregion
257
+ // #region conversions
258
+ toDataURL(options) {
259
+ return tensorToDataURL(this, options);
260
+ }
261
+ toImageData(options) {
262
+ return tensorToImageData(this, options);
263
+ }
264
+ // #endregion
265
+ // #region properties
266
+ get data() {
267
+ this.ensureValid();
268
+ if (!this.cpuData) {
269
+ throw new Error('The data is not on CPU. Use `getData()` to download GPU data to CPU, ' +
270
+ 'or use `texture` or `gpuBuffer` property to access the GPU data directly.');
271
+ }
272
+ return this.cpuData;
273
+ }
274
+ get location() {
275
+ return this.dataLocation;
276
+ }
277
+ get texture() {
278
+ this.ensureValid();
279
+ if (!this.gpuTextureData) {
280
+ throw new Error('The data is not stored as a WebGL texture.');
281
+ }
282
+ return this.gpuTextureData;
283
+ }
284
+ get gpuBuffer() {
285
+ this.ensureValid();
286
+ if (!this.gpuBufferData) {
287
+ throw new Error('The data is not stored as a WebGPU buffer.');
288
+ }
289
+ return this.gpuBufferData;
290
+ }
291
+ get mlTensor() {
292
+ this.ensureValid();
293
+ if (!this.mlTensorData) {
294
+ throw new Error('The data is not stored as a WebNN MLTensor.');
295
+ }
296
+ return this.mlTensorData;
297
+ }
298
+ // #endregion
299
+ // #region methods
300
+ async getData(releaseData) {
301
+ this.ensureValid();
302
+ switch (this.dataLocation) {
303
+ case 'cpu':
304
+ case 'cpu-pinned':
305
+ return this.data;
306
+ case 'texture':
307
+ case 'gpu-buffer':
308
+ case 'ml-tensor': {
309
+ if (!this.downloader) {
310
+ throw new Error('The current tensor is not created with a specified data downloader.');
311
+ }
312
+ if (this.isDownloading) {
313
+ throw new Error('The current tensor is being downloaded.');
314
+ }
315
+ try {
316
+ this.isDownloading = true;
317
+ const data = await this.downloader();
318
+ this.downloader = undefined;
319
+ this.dataLocation = 'cpu';
320
+ this.cpuData = data;
321
+ if (releaseData && this.disposer) {
322
+ this.disposer();
323
+ this.disposer = undefined;
324
+ }
325
+ return data;
326
+ }
327
+ finally {
328
+ this.isDownloading = false;
329
+ }
330
+ }
331
+ default:
332
+ throw new Error(`cannot get data from location: ${this.dataLocation}`);
333
+ }
334
+ }
335
+ dispose() {
336
+ if (this.isDownloading) {
337
+ throw new Error('The current tensor is being downloaded.');
338
+ }
339
+ if (this.disposer) {
340
+ this.disposer();
341
+ this.disposer = undefined;
342
+ }
343
+ this.cpuData = undefined;
344
+ this.gpuTextureData = undefined;
345
+ this.gpuBufferData = undefined;
346
+ this.mlTensorData = undefined;
347
+ this.downloader = undefined;
348
+ this.isDownloading = undefined;
349
+ this.dataLocation = 'none';
350
+ }
351
+ // #endregion
352
+ // #region tensor utilities
353
+ ensureValid() {
354
+ if (this.dataLocation === 'none') {
355
+ throw new Error('The tensor is disposed.');
356
+ }
357
+ }
358
+ reshape(dims) {
359
+ this.ensureValid();
360
+ if (this.downloader || this.disposer) {
361
+ throw new Error('Cannot reshape a tensor that owns GPU resource.');
362
+ }
363
+ return tensorReshape(this, dims);
364
+ }
365
+ }
366
+ //# sourceMappingURL=tensor-impl.js.map
runtime/vendor/onnxruntime-common/tensor-utils-impl.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ import { Tensor } from './tensor-impl.js';
4
+ /**
5
+ * calculate size from dims.
6
+ *
7
+ * @param dims the dims array. May be an illegal input.
8
+ */
9
+ export const calculateSize = (dims) => {
10
+ let size = 1;
11
+ for (let i = 0; i < dims.length; i++) {
12
+ const dim = dims[i];
13
+ if (typeof dim !== 'number' || !Number.isSafeInteger(dim)) {
14
+ throw new TypeError(`dims[${i}] must be an integer, got: ${dim}`);
15
+ }
16
+ if (dim < 0) {
17
+ throw new RangeError(`dims[${i}] must be a non-negative integer, got: ${dim}`);
18
+ }
19
+ size *= dim;
20
+ }
21
+ return size;
22
+ };
23
+ /**
24
+ * implementation of Tensor.reshape()
25
+ */
26
+ export const tensorReshape = (tensor, dims) => {
27
+ switch (tensor.location) {
28
+ case 'cpu':
29
+ return new Tensor(tensor.type, tensor.data, dims);
30
+ case 'cpu-pinned':
31
+ return new Tensor({
32
+ location: 'cpu-pinned',
33
+ data: tensor.data,
34
+ type: tensor.type,
35
+ dims,
36
+ });
37
+ case 'texture':
38
+ return new Tensor({
39
+ location: 'texture',
40
+ texture: tensor.texture,
41
+ type: tensor.type,
42
+ dims,
43
+ });
44
+ case 'gpu-buffer':
45
+ return new Tensor({
46
+ location: 'gpu-buffer',
47
+ gpuBuffer: tensor.gpuBuffer,
48
+ type: tensor.type,
49
+ dims,
50
+ });
51
+ case 'ml-tensor':
52
+ return new Tensor({
53
+ location: 'ml-tensor',
54
+ mlTensor: tensor.mlTensor,
55
+ type: tensor.type,
56
+ dims,
57
+ });
58
+ default:
59
+ throw new Error(`tensorReshape: tensor location ${tensor.location} is not supported`);
60
+ }
61
+ };
62
+ //# sourceMappingURL=tensor-utils-impl.js.map
runtime/vendor/onnxruntime-common/tensor-utils.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ export {};
4
+ //# sourceMappingURL=tensor-utils.js.map
runtime/vendor/onnxruntime-common/tensor.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ import { Tensor as TensorImpl } from './tensor-impl.js';
4
+ // eslint-disable-next-line @typescript-eslint/naming-convention
5
+ export const Tensor = TensorImpl;
6
+ //# sourceMappingURL=tensor.js.map
runtime/vendor/onnxruntime-common/trace.js ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ import { env } from './env-impl.js';
4
+ /**
5
+ * @ignore
6
+ */
7
+ export const TRACE = (deviceType, label) => {
8
+ if (typeof env.trace === 'undefined' ? !env.wasm.trace : !env.trace) {
9
+ return;
10
+ }
11
+ // eslint-disable-next-line no-console
12
+ console.timeStamp(`${deviceType}::ORT::${label}`);
13
+ };
14
+ const TRACE_FUNC = (msg, extraMsg) => {
15
+ const stack = new Error().stack?.split(/\r\n|\r|\n/g) || [];
16
+ let hasTraceFunc = false;
17
+ for (let i = 0; i < stack.length; i++) {
18
+ if (hasTraceFunc && !stack[i].includes('TRACE_FUNC')) {
19
+ let label = `FUNC_${msg}::${stack[i].trim().split(' ')[1]}`;
20
+ if (extraMsg) {
21
+ label += `::${extraMsg}`;
22
+ }
23
+ TRACE('CPU', label);
24
+ return;
25
+ }
26
+ if (stack[i].includes('TRACE_FUNC')) {
27
+ hasTraceFunc = true;
28
+ }
29
+ }
30
+ };
31
+ /**
32
+ * @ignore
33
+ */
34
+ export const TRACE_FUNC_BEGIN = (extraMsg) => {
35
+ if (typeof env.trace === 'undefined' ? !env.wasm.trace : !env.trace) {
36
+ return;
37
+ }
38
+ TRACE_FUNC('BEGIN', extraMsg);
39
+ };
40
+ /**
41
+ * @ignore
42
+ */
43
+ export const TRACE_FUNC_END = (extraMsg) => {
44
+ if (typeof env.trace === 'undefined' ? !env.wasm.trace : !env.trace) {
45
+ return;
46
+ }
47
+ TRACE_FUNC('END', extraMsg);
48
+ };
49
+ /**
50
+ * @ignore
51
+ */
52
+ export const TRACE_EVENT_BEGIN = (extraMsg) => {
53
+ if (typeof env.trace === 'undefined' ? !env.wasm.trace : !env.trace) {
54
+ return;
55
+ }
56
+ // eslint-disable-next-line no-console
57
+ console.time(`ORT::${extraMsg}`);
58
+ };
59
+ /**
60
+ * @ignore
61
+ */
62
+ export const TRACE_EVENT_END = (extraMsg) => {
63
+ if (typeof env.trace === 'undefined' ? !env.wasm.trace : !env.trace) {
64
+ return;
65
+ }
66
+ // eslint-disable-next-line no-console
67
+ console.timeEnd(`ORT::${extraMsg}`);
68
+ };
69
+ //# sourceMappingURL=trace.js.map
runtime/vendor/onnxruntime-common/type-helper.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ export {};
4
+ //# sourceMappingURL=type-helper.js.map
runtime/vendor/onnxruntime-common/version.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ // This file is generated by /js/scripts/update-version.ts
4
+ // Do not modify file content manually.
5
+ export const version = '1.27.0';
6
+ //# sourceMappingURL=version.js.map
runtime/vendor/ort-wasm-simd-threaded.asyncify.mjs ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ async function ortWasmThreaded(moduleArg={}){var moduleRtn;var g=moduleArg,aa=!!globalThis.window,ba=!!globalThis.WorkerGlobalScope,l=globalThis.process?.versions?.node&&"renderer"!=globalThis.process?.type,n=ba&&self.name?.startsWith("em-pthread");if(l){const {createRequire:a}=await import("module");var require=a(import.meta.url),ca=require("worker_threads");global.Worker=ca.Worker;n=(ba=!ca.oe)&&"em-pthread"==ca.workerData}g.mountExternalData=(a,b)=>{a.startsWith("./")&&(a=a.substring(2));(g.Vc||(g.Vc=new Map)).set(a,b)};
2
+ g.unmountExternalData=()=>{delete g.Vc};var SharedArrayBuffer=globalThis.SharedArrayBuffer??(new WebAssembly.Memory({initial:0,maximum:0,shared:!0})).buffer.constructor;
3
+ let ea=()=>{const a=b=>(...c)=>{const d=q;c=b(...c);return q!=d?da():c};(b=>{for(const c of b)g[c]=a(g[c])})(["_OrtAppendExecutionProvider","_OrtCreateSession","_OrtRun","_OrtRunWithBinding","_OrtBindInput"]);"undefined"!==typeof jsepRunAsync&&(g._OrtRun=jsepRunAsync(g._OrtRun),g._OrtRunWithBinding=jsepRunAsync(g._OrtRunWithBinding));ea=void 0};g.asyncInit=()=>{ea?.()};var fa="./this.program",ha=(a,b)=>{throw b;},ia=import.meta.url,ja="",ka,la;
4
+ if(l){var fs=require("fs");ia.startsWith("file:")&&(ja=require("path").dirname(require("url").fileURLToPath(ia))+"/");la=a=>{a=ma(a)?new URL(a):a;return fs.readFileSync(a)};ka=async a=>{a=ma(a)?new URL(a):a;return fs.readFileSync(a,void 0)};1<process.argv.length&&(fa=process.argv[1].replace(/\\/g,"/"));process.argv.slice(2);ha=(a,b)=>{process.exitCode=a;throw b;}}else if(aa||ba){try{ja=(new URL(".",ia)).href}catch{}l||(ba&&(la=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";
5
+ b.send(null);return new Uint8Array(b.response)}),ka=async a=>{if(ma(a))return new Promise((c,d)=>{var e=new XMLHttpRequest;e.open("GET",a,!0);e.responseType="arraybuffer";e.onload=()=>{200==e.status||0==e.status&&e.response?c(e.response):d(e.status)};e.onerror=d;e.send(null)});var b=await fetch(a,{credentials:"same-origin"});if(b.ok)return b.arrayBuffer();throw Error(b.status+" : "+b.url);})}var na=console.log.bind(console),oa=console.error.bind(console);
6
+ if(l){var pa=require("util"),qa=a=>"object"==typeof a?pa.inspect(a):a;na=(...a)=>fs.writeSync(1,a.map(qa).join(" ")+"\n");oa=(...a)=>fs.writeSync(2,a.map(qa).join(" ")+"\n")}var ra=na,t=oa,sa,ta,ua=!1,va,ma=a=>a.startsWith("file://");function u(){v.buffer!=w.buffer&&wa()}var xa,ya;
7
+ if(l&&n){var Aa=ca.parentPort;Aa.on("message",a=>global.onmessage?.({data:a}));Object.assign(globalThis,{self:global,postMessage:a=>Aa.postMessage(a)});process.on("uncaughtException",a=>{postMessage({Pc:"uncaughtException",error:a});process.exit(1)})}var Ba;
8
+ if(n){var Ca=!1;self.onunhandledrejection=b=>{throw b.reason||b;};function a(b){try{var c=b.data,d=c.Pc;if("load"===d){let e=[];self.onmessage=f=>e.push(f);Ba=()=>{postMessage({Pc:"loaded"});for(let f of e)a(f);self.onmessage=a};for(const f of c.ee)if(!g[f]||g[f].proxy)g[f]=(...h)=>{postMessage({Pc:"callHandler",de:f,args:h})},"print"==f&&(ra=g[f]),"printErr"==f&&(t=g[f]);v=c.ke;wa();ta=c.le;Da();Ea()}else if("run"===d){Fa(c.Oc);Ga(c.Oc,0,0,1,0,0);Ha();Ia(c.Oc);Ca||(Ja(),Ca=!0);try{Ka(c.ie,c.Xc)}catch(e){if("unwind"!=
9
+ e)throw e;}}else"setimmediate"!==c.target&&("checkMailbox"===d?Ca&&La():d&&(t(`worker: received unknown command ${d}`),t(c)))}catch(e){throw Ma(),e;}}self.onmessage=a}var w,x,Na,Oa,A,B,Pa,E,F,Qa,Ra=!1;function wa(){var a=v.buffer;g.HEAP8=w=new Int8Array(a);Na=new Int16Array(a);g.HEAPU8=x=new Uint8Array(a);Oa=new Uint16Array(a);g.HEAP32=A=new Int32Array(a);g.HEAPU32=B=new Uint32Array(a);Pa=new Float32Array(a);E=new Float64Array(a);F=new BigInt64Array(a);Qa=new BigUint64Array(a)}
10
+ function Sa(){Ra=!0;n?Ba():G.$b()}function Ta(a){a="Aborted("+a+")";t(a);ua=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");ya?.(a);throw a;}var Ua;async function Va(a){if(!sa)try{var b=await ka(a);return new Uint8Array(b)}catch{}if(a==Ua&&sa)a=new Uint8Array(sa);else if(la)a=la(a);else throw"both async and sync fetching of the wasm failed";return a}
11
+ async function Wa(a,b){try{var c=await Va(a);return await WebAssembly.instantiate(c,b)}catch(d){t(`failed to asynchronously prepare wasm: ${d}`),Ta(d)}}async function Xa(a){var b=Ua;if(!sa&&!ma(b)&&!l)try{var c=fetch(b,{credentials:"same-origin"});return await WebAssembly.instantiateStreaming(c,a)}catch(d){t(`wasm streaming compile failed: ${d}`),t("falling back to ArrayBuffer instantiation")}return Wa(b,a)}
12
+ function Ya(){Za={f:$a,J:ab,k:bb,p:cb,l:db,sa:eb,b:fb,ca:gb,Ja:hb,q:ib,da:jb,Za:kb,Fa:lb,Ha:mb,_a:nb,Xa:ob,Qa:pb,Wa:qb,oa:rb,Ga:sb,Yb:tb,Ya:ub,Zb:vb,db:wb,Da:xb,Tb:zb,Rb:Ab,Ca:Cb,M:Db,I:Eb,Sb:Fb,ja:Gb,Ub:Hb,Ta:Ib,Wb:Jb,Ka:Kb,Pb:Lb,ka:Mb,Sa:Ia,ab:Nb,U:Ob,n:Pb,c:Qb,rb:Rb,w:Sb,L:Tb,z:Ub,j:Vb,o:Wb,sb:Xb,G:Yb,T:Zb,h:$b,u:ac,m:bc,i:cc,Na:dc,Oa:ec,Pa:fc,La:gc,Ma:hc,Qb:ic,eb:jc,cb:kc,Y:lc,qb:mc,la:nc,bb:oc,fb:pc,$a:qc,Xb:rc,N:sc,gb:tc,X:uc,Vb:vc,nb:wc,C:xc,ra:yc,qa:zc,pb:Ac,W:Bc,v:Cc,mb:Dc,lb:Ec,kb:Fc,ob:Gc,
13
+ jb:Hc,ib:Ic,hb:Jc,Ua:Kc,Va:Lc,Ia:Mc,V:Nc,na:Oc,Ra:Pc,ma:Qc,Cb:Rc,xa:Sc,Db:Tc,ya:Uc,F:Vc,e:Wc,s:Xc,x:Yc,B:Zc,Fb:$c,ba:ad,D:bd,za:cd,$:dd,ga:ed,Gb:fd,Hb:gd,Ba:hd,Aa:jd,Ib:kd,wa:ld,aa:md,d:nd,A:od,r:pd,Bb:qd,t:rd,y:sd,H:td,E:ud,K:vd,R:wd,ia:xd,_:yd,Kb:zd,Lb:Ad,Jb:Bd,g:Cd,a:v,Ob:Dd,Eb:Ed,ha:Fd,O:Gd,pa:Hd,Mb:Id,ta:Jd,Q:Kd,yb:Ld,zb:Md,ua:Nd,ea:Od,P:Pd,Ea:Qd,va:Rd,Z:Sd,wb:Td,_b:Ud,S:Vd,Ab:Wd,tb:Xd,ub:Yd,vb:Zd,fa:$d,xb:ae,Nb:be};return{a:Za}}
14
+ async function Da(){function a(d,e){var f=G=d.exports;d={};for(let [h,k]of Object.entries(f))"function"==typeof k?(f=ce(k),d[h]=f):d[h]=k;G=d;G=de();ee.push(G.jd);d=G;fe=d.ac;Ja=d.bc;g._OrtInit=d.cc;g._OrtGetLastError=d.dc;g._OrtCreateSessionOptions=d.ec;g._OrtAppendExecutionProvider=d.fc;g._OrtAddFreeDimensionOverride=d.gc;g._OrtAddSessionConfigEntry=d.hc;g._OrtReleaseSessionOptions=d.ic;g._OrtCreateSession=d.jc;g._OrtReleaseSession=d.kc;g._OrtGetInputOutputCount=d.lc;g._OrtGetInputOutputMetadata=
15
+ d.mc;g._OrtFree=d.nc;g._OrtCreateTensor=d.oc;g._OrtGetTensorData=d.pc;g._OrtReleaseTensor=d.qc;g._OrtCreateRunOptions=d.rc;g._OrtAddRunConfigEntry=d.sc;g._OrtReleaseRunOptions=d.tc;g._OrtCreateBinding=d.uc;g._OrtBindInput=d.vc;g._OrtBindOutput=d.wc;g._OrtClearBoundOutputs=d.xc;g._OrtReleaseBinding=d.yc;g._OrtRunWithBinding=d.zc;g._OrtRun=d.Ac;g._OrtEndProfiling=d.Bc;ge=g._OrtGetWebGpuDevice=d.Cc;he=d.Dc;H=g._free=d.Ec;ie=g._malloc=d.Fc;je=g._wgpuBufferRelease=d.Gc;ke=g._wgpuCreateInstance=d.Hc;le=
16
+ d.Ic;me=d.Jc;ne=d.Kc;oe=d.Lc;pe=d.Mc;qe=d.Qc;re=d._c;se=d.$c;te=d.ad;ue=d.cd;ve=d.dd;we=d.ed;xe=d.fd;ye=d.gd;ze=d.hd;Ae=d.id;Ga=d.ld;Ma=d.md;Be=d.nd;Ce=d.od;De=d.pd;Ee=d.qd;Fe=d.rd;Ge=d.sd;I=d.td;He=d.ud;Ie=d.vd;J=d.wd;Je=d.xd;K=d.yd;Ke=d.zd;Le=d.Ad;Me=d.Bd;Ne=d.Cd;dynCall_vii=d.Dd;Oe=d.Ed;dynCall_v=d.Fd;Pe=d.Gd;Qe=d.Hd;Re=d.Id;dynCall_iii=d.Jd;Se=d.Kd;Te=d.Ld;Ue=d.Md;dynCall_vi=d.Nd;Ve=d.Od;We=d.Pd;Xe=d.Qd;Ye=d.Rd;Ze=d.Sd;$e=d.Ud;af=d.Vd;bf=d.Wd;cf=d.Xd;df=d.Zd;ef=d._d;ff=d.$d;gf=d.ae;hf=d.be;jf=
17
+ d.ce;kf=d.qe;lf=d.re;mf=d.se;nf=d.te;of=d.ue;pf=d.ve;qf=d.we;rf=d.xe;sf=d.ye;tf=d.ze;uf=d.Ae;vf=d.$e;wf=d.af;xf=d.bf;yf=d.cf;ta=e;return G}var b=Ya();if(g.instantiateWasm)return new Promise(d=>{g.instantiateWasm(b,(e,f)=>{d(a(e,f))})});if(n){var c=new WebAssembly.Instance(ta,Ya());return a(c,ta)}Ua??=g.locateFile?g.locateFile?g.locateFile("ort-wasm-simd-threaded.asyncify.wasm",ja):ja+"ort-wasm-simd-threaded.asyncify.wasm":(new URL("ort-wasm-simd-threaded.asyncify.wasm",import.meta.url)).href;
18
+ return function(d){return a(d.instance,d.module)}(await Xa(b))}class zf{name="ExitStatus";constructor(a){this.message=`Program terminated with exit(${a})`;this.status=a}}
19
+ var Af=a=>{a.terminate();a.onmessage=()=>{}},Bf=[],Cf=0,Df=null,Jf=a=>{0==Ef.length&&(Ff(),Gf(Ef[0]));var b=Ef.pop();if(!b)return 6;Hf.push(b);If[a.Oc]=b;b.Oc=a.Oc;var c={Pc:"run",ie:a.he,Xc:a.Xc,Oc:a.Oc};l&&b.unref();b.postMessage(c,a.Zc);return 0},L=0,M=(a,b,...c)=>{var d=16*c.length,e=K(),f=Je(d),h=f>>>3,k;for(k of c)"bigint"==typeof k?((u(),F)[h++>>>0]=1n,(u(),F)[h++>>>0]=k):((u(),F)[h++>>>0]=0n,(u(),E)[h++>>>0]=k);a=Be(a,0,d,f,b);J(e);return a};
20
+ function Dd(a){if(n)return M(0,1,a);va=a;if(!(0<L)){for(var b of Hf)Af(b);for(b of Ef)Af(b);Ef=[];Hf=[];If={};ua=!0}ha(a,new zf(a))}function Kf(a){if(n)return M(1,0,a);Mc(a)}var Mc=a=>{va=a;if(n)throw Kf(a),"unwind";Dd(a)},Ef=[],Hf=[],ee=[],If={};function Lf(){for(var a=g.numThreads-1;a--;)Ff();Bf.push(async()=>{var b=Mf();Cf++;await b;Cf--;0==Cf&&Df&&(b=Df,Df=null,b())})}var Nf=a=>{var b=a.Oc;delete If[b];Ef.push(a);Hf.splice(Hf.indexOf(a),1);a.Oc=0;Ce(b)};function Ha(){ee.forEach(a=>a())}
21
+ var Gf=a=>new Promise(b=>{a.onmessage=f=>{var h=f.data;f=h.Pc;if(h.Wc&&h.Wc!=he()){var k=If[h.Wc];k?k.postMessage(h,h.Zc):t(`Internal error! Worker sent a message "${f}" to target pthread ${h.Wc}, but that thread no longer exists!`)}else if("checkMailbox"===f)La();else if("spawnThread"===f)Jf(h);else if("cleanupThread"===f)N(()=>{Nf(If[h.je])});else if("loaded"===f)a.loaded=!0,l&&!a.Oc&&a.unref(),b(a);else if("setimmediate"===h.target)a.postMessage(h);else if("uncaughtException"===f)a.onerror(h.error);
22
+ else if("callHandler"===f)g[h.de](...h.args);else f&&t(`worker sent an unknown command ${f}`)};a.onerror=f=>{t(`${"worker sent an error!"} ${f.filename}:${f.lineno}: ${f.message}`);throw f;};l&&(a.on("message",f=>a.onmessage({data:f})),a.on("error",f=>a.onerror(f)));var c=[],d=[],e;for(e of d)g.propertyIsEnumerable(e)&&c.push(e);a.postMessage({Pc:"load",ee:c,ke:v,le:ta})});async function Mf(){if(!n)return Promise.all(Ef.map(Gf))}
23
+ function Ff(){var a=new Worker(new URL(import.meta.url),{type:"module",workerData:"em-pthread",name:"em-pthread"});Ef.push(a)}function Fa(a){var b=(u(),B)[a+52>>>2>>>0];a=(u(),B)[a+56>>>2>>>0];Ie(b,b-a);J(b)}var Ka=(a,b)=>{L=0;a=Oe(a,b);0<L?va=a:De(a)},v,Of=[],Pf=0,O=a=>-9007199254740992>a||9007199254740992<a?NaN:Number(a);function $a(a){a>>>=0;var b=new Qf(a);0==(u(),w)[b.Rc+12>>>0]&&(Rf(b,!0),Pf--);Sf(b,!1);Of.push(b);return Ne(a)}
24
+ var Tf=0,ab=()=>{I(0,0);var a=Of.pop();Ke(a.Yc);Tf=0};function Rf(a,b){b=b?1:0;(u(),w)[a.Rc+12>>>0]=b}function Sf(a,b){b=b?1:0;(u(),w)[a.Rc+13>>>0]=b}class Qf{constructor(a){this.Yc=a;this.Rc=a-24}}var Uf=a=>{var b=Tf;if(!b)return He(0),0;var c=new Qf(b);(u(),B)[c.Rc+16>>>2>>>0]=b;var d=(u(),B)[c.Rc+4>>>2>>>0];if(!d)return He(0),b;for(var e of a){if(0===e||e===d)break;if(Me(e,d,c.Rc+16))return He(e),b}He(d);return b};function bb(){return Uf([])}function cb(a){return Uf([a>>>0])}
25
+ function db(a,b,c,d){return Uf([a>>>0,b>>>0,c>>>0,d>>>0])}var eb=()=>{var a=Of.pop();a||Ta("no exception to throw");var b=a.Yc;0==(u(),w)[a.Rc+13>>>0]&&(Of.push(a),Sf(a,!0),Rf(a,!1),Pf++);Le(b);Tf=b;throw Tf;};function fb(a,b,c){a>>>=0;var d=new Qf(a);b>>>=0;c>>>=0;(u(),B)[d.Rc+16>>>2>>>0]=0;(u(),B)[d.Rc+4>>>2>>>0]=b;(u(),B)[d.Rc+8>>>2>>>0]=c;Le(a);Tf=a;Pf++;throw Tf;}var gb=()=>Pf;function Vf(a,b,c,d){return n?M(2,1,a,b,c,d):hb(a,b,c,d)}
26
+ function hb(a,b,c,d){a>>>=0;b>>>=0;c>>>=0;d>>>=0;if(!globalThis.SharedArrayBuffer)return 6;var e=[];if(n&&0===e.length)return Vf(a,b,c,d);a={he:c,Oc:a,Xc:d,Zc:e};return n?(a.Pc="spawnThread",postMessage(a,e),0):Jf(a)}function ib(a){Tf||=a>>>0;throw Tf;}
27
+ var Wf=globalThis.TextDecoder&&new TextDecoder,Xf=(a,b,c,d)=>{c=b+c;if(d)return c;for(;a[b]&&!(b>=c);)++b;return b},Yf=(a,b=0,c,d)=>{b>>>=0;c=Xf(a,b,c,d);if(16<c-b&&a.buffer&&Wf)return Wf.decode(a.buffer instanceof ArrayBuffer?a.subarray(b,c):a.slice(b,c));for(d="";b<c;){var e=a[b++];if(e&128){var f=a[b++]&63;if(192==(e&224))d+=String.fromCharCode((e&31)<<6|f);else{var h=a[b++]&63;e=224==(e&240)?(e&15)<<12|f<<6|h:(e&7)<<18|f<<12|h<<6|a[b++]&63;65536>e?d+=String.fromCharCode(e):(e-=65536,d+=String.fromCharCode(55296|
28
+ e>>10,56320|e&1023))}}else d+=String.fromCharCode(e)}return d},Zf=(a,b,c)=>(a>>>=0)?Yf((u(),x),a,b,c):"";function jb(a,b,c){return n?M(3,1,a,b,c):0}function kb(a,b){if(n)return M(4,1,a,b)}function lb(a,b){if(n)return M(5,1,a,b)}function mb(a,b,c){if(n)return M(6,1,a,b,c)}function nb(a,b,c){return n?M(7,1,a,b,c):0}function ob(a,b){if(n)return M(8,1,a,b)}function pb(a,b,c){if(n)return M(9,1,a,b,c)}function qb(a,b,c,d){if(n)return M(10,1,a,b,c,d)}function rb(a,b,c,d){if(n)return M(11,1,a,b,c,d)}
29
+ function sb(a,b,c,d){if(n)return M(12,1,a,b,c,d)}function tb(a){if(n)return M(13,1,a)}function ub(a,b){if(n)return M(14,1,a,b)}function vb(a,b,c){if(n)return M(15,1,a,b,c)}var wb=()=>Ta(""),P=a=>{a>>>=0;for(var b="";;){var c=(u(),x)[a++>>>0];if(!c)return b;b+=String.fromCharCode(c)}},$f={},ag={},bg={},cg=class extends Error{constructor(a){super(a);this.name="BindingError"}};
30
+ function dg(a,b,c={}){var d=b.name;if(!a)throw new cg(`type "${d}" must have a positive integer typeid pointer`);if(ag.hasOwnProperty(a)){if(c.fe)return;throw new cg(`Cannot register type '${d}' twice`);}ag[a]=b;delete bg[a];$f.hasOwnProperty(a)&&(b=$f[a],delete $f[a],b.forEach(e=>e()))}function Q(a,b,c={}){return dg(a,b,c)}
31
+ var eg=(a,b,c)=>{switch(b){case 1:return c?d=>(u(),w)[d>>>0]:d=>(u(),x)[d>>>0];case 2:return c?d=>(u(),Na)[d>>>1>>>0]:d=>(u(),Oa)[d>>>1>>>0];case 4:return c?d=>(u(),A)[d>>>2>>>0]:d=>(u(),B)[d>>>2>>>0];case 8:return c?d=>(u(),F)[d>>>3>>>0]:d=>(u(),Qa)[d>>>3>>>0];default:throw new TypeError(`invalid integer width (${b}): ${a}`);}};
32
+ function xb(a,b,c,d,e){a>>>=0;c>>>=0;b=P(b>>>0);d=0n===d;let f=h=>h;if(d){const h=8*c;f=k=>BigInt.asUintN(h,k);e=f(e)}Q(a,{name:b,Nc:f,Tc:(h,k)=>{"number"==typeof k&&(k=BigInt(k));return k},Sc:eg(b,c,!d),Uc:null})}function zb(a,b,c,d){a>>>=0;b=P(b>>>0);Q(a,{name:b,Nc:function(e){return!!e},Tc:function(e,f){return f?c:d},Sc:function(e){return this.Nc((u(),x)[e>>>0])},Uc:null})}var fg=[],gg=[0,1,,1,null,1,!0,1,!1,1];function Qb(a){a>>>=0;9<a&&0===--gg[a+1]&&(gg[a]=void 0,fg.push(a))}
33
+ var R=a=>{if(!a)throw new cg(`Cannot use deleted val. handle = ${a}`);return gg[a]},S=a=>{switch(a){case void 0:return 2;case null:return 4;case !0:return 6;case !1:return 8;default:const b=fg.pop()||gg.length;gg[b]=a;gg[b+1]=1;return b}};function hg(a){return this.Nc((u(),B)[a>>>2>>>0])}var ig={name:"emscripten::val",Nc:a=>{var b=R(a);Qb(a);return b},Tc:(a,b)=>S(b),Sc:hg,Uc:null};function Ab(a){return Q(a>>>0,ig)}
34
+ var jg=(a,b)=>{switch(b){case 4:return function(c){return this.Nc((u(),Pa)[c>>>2>>>0])};case 8:return function(c){return this.Nc((u(),E)[c>>>3>>>0])};default:throw new TypeError(`invalid float width (${b}): ${a}`);}};function Cb(a,b,c){a>>>=0;c>>>=0;b=P(b>>>0);Q(a,{name:b,Nc:d=>d,Tc:(d,e)=>e,Sc:jg(b,c),Uc:null})}function Db(a,b,c,d,e){a>>>=0;c>>>=0;b=P(b>>>0);let f=k=>k;if(0===d){var h=32-8*c;f=k=>k<<h>>>h;e=f(e)}Q(a,{name:b,Nc:f,Tc:(k,m)=>m,Sc:eg(b,c,0!==d),Uc:null})}
35
+ function Eb(a,b,c){function d(f){var h=(u(),B)[f>>>2>>>0];f=(u(),B)[f+4>>>2>>>0];return new e((u(),w).buffer,f,h)}a>>>=0;var e=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array][b];c=P(c>>>0);Q(a,{name:c,Nc:d,Sc:d},{fe:!0})}
36
+ var T=(a,b,c)=>{var d=(u(),x);b>>>=0;if(0<c){var e=b;c=b+c-1;for(var f=0;f<a.length;++f){var h=a.codePointAt(f);if(127>=h){if(b>=c)break;d[b++>>>0]=h}else if(2047>=h){if(b+1>=c)break;d[b++>>>0]=192|h>>6;d[b++>>>0]=128|h&63}else if(65535>=h){if(b+2>=c)break;d[b++>>>0]=224|h>>12;d[b++>>>0]=128|h>>6&63;d[b++>>>0]=128|h&63}else{if(b+3>=c)break;d[b++>>>0]=240|h>>18;d[b++>>>0]=128|h>>12&63;d[b++>>>0]=128|h>>6&63;d[b++>>>0]=128|h&63;f++}}d[b>>>0]=0;a=b-e}else a=0;return a},U=a=>{for(var b=0,c=0;c<a.length;++c){var d=
37
+ a.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=d?(b+=4,++c):b+=3}return b};
38
+ function Fb(a,b){a>>>=0;b=P(b>>>0);Q(a,{name:b,Nc(c){var d=(u(),B)[c>>>2>>>0];d=Zf(c+4,d,!0);H(c);return d},Tc(c,d){d instanceof ArrayBuffer&&(d=new Uint8Array(d));var e="string"==typeof d;if(!(e||ArrayBuffer.isView(d)&&1==d.BYTES_PER_ELEMENT))throw new cg("Cannot pass non-string to std::string");var f=e?U(d):d.length;var h=ie(4+f+1),k=h+4;(u(),B)[h>>>2>>>0]=f;e?T(d,k,f+1):(u(),x).set(d,k>>>0);null!==c&&c.push(H,h);return h},Sc:hg,Uc(c){H(c)}})}
39
+ var kg=globalThis.TextDecoder?new TextDecoder("utf-16le"):void 0,lg=(a,b,c)=>{a>>>=1;b=Xf((u(),Oa),a,b/2,c);if(16<b-a&&kg)return kg.decode((u(),Oa).slice(a,b));for(c="";a<b;++a){var d=(u(),Oa)[a>>>0];c+=String.fromCharCode(d)}return c},mg=(a,b,c)=>{c??=2147483647;if(2>c)return 0;c-=2;var d=b;c=c<2*a.length?c/2:a.length;for(var e=0;e<c;++e){var f=a.charCodeAt(e);(u(),Na)[b>>>1>>>0]=f;b+=2}(u(),Na)[b>>>1>>>0]=0;return b-d},ng=a=>2*a.length,og=(a,b,c)=>{var d="";a>>>=2;for(var e=0;!(e>=b/4);e++){var f=
40
+ (u(),B)[a+e>>>0];if(!f&&!c)break;d+=String.fromCodePoint(f)}return d},pg=(a,b,c)=>{b>>>=0;c??=2147483647;if(4>c)return 0;var d=b;c=d+c-4;for(var e=0;e<a.length;++e){var f=a.codePointAt(e);65535<f&&e++;(u(),A)[b>>>2>>>0]=f;b+=4;if(b+4>c)break}(u(),A)[b>>>2>>>0]=0;return b-d},qg=a=>{for(var b=0,c=0;c<a.length;++c)65535<a.codePointAt(c)&&c++,b+=4;return b};
41
+ function Gb(a,b,c){a>>>=0;b>>>=0;c>>>=0;c=P(c);if(2===b){var d=lg;var e=mg;var f=ng}else d=og,e=pg,f=qg;Q(a,{name:c,Nc:h=>{var k=(u(),B)[h>>>2>>>0];k=d(h+4,k*b,!0);H(h);return k},Tc:(h,k)=>{if("string"!=typeof k)throw new cg(`Cannot pass non-string to C++ string type ${c}`);var m=f(k),p=ie(4+m+b);(u(),B)[p>>>2>>>0]=m/b;e(k,p+4,m+b);null!==h&&h.push(H,p);return p},Sc:hg,Uc(h){H(h)}})}function Hb(a,b){a>>>=0;b=P(b>>>0);Q(a,{ge:!0,name:b,Nc:()=>{},Tc:()=>{}})}
42
+ function Ib(a){Ga(a>>>0,!ba,1,!aa,131072,!1);Ha()}var N=a=>{if(!ua)try{if(a(),!(0<L))try{n?he()&&De(va):Mc(va)}catch(b){b instanceof zf||"unwind"==b||ha(1,b)}}catch(b){b instanceof zf||"unwind"==b||ha(1,b)}},rg=!Atomics.waitAsync||globalThis.navigator?.userAgent&&91>Number((navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)||[])[2]);function Ia(a){a>>>=0;rg||(Atomics.waitAsync((u(),A),a>>>2,a).value.then(La),a+=128,Atomics.store((u(),A),a>>>2,1))}var La=()=>N(()=>{var a=he();a&&(Ia(a),Fe())});
43
+ function Jb(a,b){a>>>=0;a==b>>>0?setTimeout(La):n?postMessage({Wc:a,Pc:"checkMailbox"}):(a=If[a])&&a.postMessage({Pc:"checkMailbox"})}var sg=[];function Kb(a,b,c,d,e){b>>>=0;e>>>=0;sg.length=0;c=e>>>3;for(d=e+d>>>3;c<d;){var f;(u(),F)[c++>>>0]?f=(u(),F)[c++>>>0]:f=(u(),E)[c++>>>0];sg.push(f)}return(b?tg[b]:ug[a])(...sg)}var Lb=()=>{L=0};function Mb(a){a>>>=0;n?postMessage({Pc:"cleanupThread",je:a}):Nf(If[a])}function Nb(a){l&&If[a>>>0].ref()}var vg=a=>{try{a()}catch(b){Ta(b)}};
44
+ function ce(a){var b=(...c)=>{wg.push(a);try{return a(...c)}finally{ua||(wg.pop(),q&&1===xg&&0===wg.length&&(xg=0,L+=1,vg(wf),"undefined"!=typeof Fibers&&Fibers.De()))}};yg.set(a,b);return b}var xg=0,q=null,zg=0,wg=[],Ag=new Map,Bg=new Map,yg=new Map,Cg=0,Dg=null,Eg=[];function da(){return new Promise((a,b)=>{Dg={resolve:a,reject:b}})}
45
+ function Fg(){var a=ie(65548),b=a+12;(u(),B)[a>>>2>>>0]=b;(u(),B)[a+4>>>2>>>0]=b+65536;b=wg[0];if(!Ag.has(b)){var c=Cg++;Ag.set(b,c);Bg.set(c,b)}b=Ag.get(b);(u(),A)[a+8>>>2>>>0]=b;return a}function Gg(){var a=(u(),A)[q+8>>>2>>>0];a=Bg.get(a);a=yg.get(a);--L;return a()}
46
+ function Hg(a){if(!ua){if(0===xg){var b=!1,c=!1;a((d=0)=>{if(!ua&&(zg=d,b=!0,c)){xg=2;vg(()=>xf(q));"undefined"!=typeof MainLoop&&MainLoop.Yd&&MainLoop.resume();d=!1;try{var e=Gg()}catch(k){e=k,d=!0}var f=!1;if(!q){var h=Dg;h&&(Dg=null,(d?h.reject:h.resolve)(e),f=!0)}if(d&&!f)throw e;}});c=!0;b||(xg=1,q=Fg(),"undefined"!=typeof MainLoop&&MainLoop.Yd&&MainLoop.pause(),vg(()=>vf(q)))}else 2===xg?(xg=0,vg(yf),H(q),q=null,Eg.forEach(N)):Ta(`invalid state: ${xg}`);return zg}}var Ig=a=>Hg(b=>{a().then(b)});
47
+ function Ob(a){a>>>=0;return Ig(async()=>{var b=await R(a);return S(b)})}var Jg=[],Kg=a=>{var b=Jg.length;Jg.push(a);return b},Lg=(a,b)=>{for(var c=Array(a),d=0;d<a;++d){var e=d,f=(u(),B)[b+4*d>>>2>>>0],h=ag[f];if(void 0===h)throw a=`parameter ${d}`,f=fe(f),b=P(f),H(f),new cg(`${a} has unknown type ${b}`);c[e]=h}return c},Mg=(a,b,c)=>{var d=[];a=a(d,c);d.length&&((u(),B)[b>>>2>>>0]=S(d));return a},Ng={},Og=a=>{var b=Ng[a];return void 0===b?P(a):b};
48
+ function Pb(a,b,c){var [d,...e]=Lg(a,b>>>0);b=d.Tc.bind(d);var f=e.map(m=>m.Sc.bind(m));a--;var h={toValue:R};a=f.map((m,p)=>{var C=`argFromPtr${p}`;h[C]=m;return`${C}(args${p?"+"+8*p:""})`});switch(c){case 0:var k="toValue(handle)";break;case 2:k="new (toValue(handle))";break;case 3:k="";break;case 1:h.getStringOrSymbol=Og,k="toValue(handle)[getStringOrSymbol(methodName)]"}k+=`(${a})`;d.ge||(h.toReturnWire=b,h.emval_returnValue=Mg,k=`return emval_returnValue(toReturnWire, destructorsRef, ${k})`);
49
+ k=`return function (handle, methodName, destructorsRef, args) {\n ${k}\n }`;c=(new Function(Object.keys(h),k))(...Object.values(h));k=`methodCaller<(${e.map(m=>m.name)}) => ${d.name}>`;return Kg(Object.defineProperty(c,"name",{value:k}))}function Rb(a,b){b>>>=0;a=R(a>>>0);b=R(b);return a==b}function Sb(a){a>>>=0;if(!a)return S(globalThis);a=Og(a);return S(globalThis[a])}function Tb(a){a=Og(a>>>0);return S(g[a])}function Ub(a,b){b>>>=0;a=R(a>>>0);b=R(b);return S(a[b])}
50
+ function Vb(a){a>>>=0;9<a&&(gg[a+1]+=1)}function Wb(a,b,c,d,e){return Jg[a>>>0](b>>>0,c>>>0,d>>>0,e>>>0)}function Xb(a,b,c,d,e){return Wb(a>>>0,b>>>0,c>>>0,d>>>0,e>>>0)}function Yb(){return S([])}function Zb(a){a=R(a>>>0);for(var b=Array(a.length),c=0;c<a.length;c++)b[c]=a[c];return S(b)}function $b(a){return S(Og(a>>>0))}function ac(){return S({})}function bc(a){a>>>=0;for(var b=R(a);b.length;){var c=b.pop();b.pop()(c)}Qb(a)}function cc(a,b,c){b>>>=0;c>>>=0;a=R(a>>>0);b=R(b);c=R(c);a[b]=c}
51
+ function dc(a,b){a=O(a);b>>>=0;a=new Date(1E3*a);(u(),A)[b>>>2>>>0]=a.getUTCSeconds();(u(),A)[b+4>>>2>>>0]=a.getUTCMinutes();(u(),A)[b+8>>>2>>>0]=a.getUTCHours();(u(),A)[b+12>>>2>>>0]=a.getUTCDate();(u(),A)[b+16>>>2>>>0]=a.getUTCMonth();(u(),A)[b+20>>>2>>>0]=a.getUTCFullYear()-1900;(u(),A)[b+24>>>2>>>0]=a.getUTCDay();a=(a.getTime()-Date.UTC(a.getUTCFullYear(),0,1,0,0,0,0))/864E5|0;(u(),A)[b+28>>>2>>>0]=a}
52
+ var Pg=a=>0===a%4&&(0!==a%100||0===a%400),Qg=[0,31,60,91,121,152,182,213,244,274,305,335],Rg=[0,31,59,90,120,151,181,212,243,273,304,334];
53
+ function ec(a,b){a=O(a);b>>>=0;a=new Date(1E3*a);(u(),A)[b>>>2>>>0]=a.getSeconds();(u(),A)[b+4>>>2>>>0]=a.getMinutes();(u(),A)[b+8>>>2>>>0]=a.getHours();(u(),A)[b+12>>>2>>>0]=a.getDate();(u(),A)[b+16>>>2>>>0]=a.getMonth();(u(),A)[b+20>>>2>>>0]=a.getFullYear()-1900;(u(),A)[b+24>>>2>>>0]=a.getDay();var c=(Pg(a.getFullYear())?Qg:Rg)[a.getMonth()]+a.getDate()-1|0;(u(),A)[b+28>>>2>>>0]=c;(u(),A)[b+36>>>2>>>0]=-(60*a.getTimezoneOffset());c=(new Date(a.getFullYear(),6,1)).getTimezoneOffset();var d=(new Date(a.getFullYear(),
54
+ 0,1)).getTimezoneOffset();a=(c!=d&&a.getTimezoneOffset()==Math.min(d,c))|0;(u(),A)[b+32>>>2>>>0]=a}
55
+ function fc(a){a>>>=0;var b=new Date((u(),A)[a+20>>>2>>>0]+1900,(u(),A)[a+16>>>2>>>0],(u(),A)[a+12>>>2>>>0],(u(),A)[a+8>>>2>>>0],(u(),A)[a+4>>>2>>>0],(u(),A)[a>>>2>>>0],0),c=(u(),A)[a+32>>>2>>>0],d=b.getTimezoneOffset(),e=(new Date(b.getFullYear(),6,1)).getTimezoneOffset(),f=(new Date(b.getFullYear(),0,1)).getTimezoneOffset(),h=Math.min(f,e);0>c?(u(),A)[a+32>>>2>>>0]=Number(e!=f&&h==d):0<c!=(h==d)&&(e=Math.max(f,e),b.setTime(b.getTime()+6E4*((0<c?h:e)-d)));(u(),A)[a+24>>>2>>>0]=b.getDay();c=(Pg(b.getFullYear())?
56
+ Qg:Rg)[b.getMonth()]+b.getDate()-1|0;(u(),A)[a+28>>>2>>>0]=c;(u(),A)[a>>>2>>>0]=b.getSeconds();(u(),A)[a+4>>>2>>>0]=b.getMinutes();(u(),A)[a+8>>>2>>>0]=b.getHours();(u(),A)[a+12>>>2>>>0]=b.getDate();(u(),A)[a+16>>>2>>>0]=b.getMonth();(u(),A)[a+20>>>2>>>0]=b.getYear();a=b.getTime();return BigInt(isNaN(a)?-1:a/1E3)}function gc(a,b,c,d,e,f,h){return n?M(16,1,a,b,c,d,e,f,h):-52}function hc(a,b,c,d,e,f){if(n)return M(17,1,a,b,c,d,e,f)}var Sg={},sc=()=>performance.timeOrigin+performance.now();
57
+ function ic(a,b){if(n)return M(18,1,a,b);Sg[a]&&(clearTimeout(Sg[a].id),delete Sg[a]);if(!b)return 0;var c=setTimeout(()=>{delete Sg[a];N(()=>Ee(a,performance.timeOrigin+performance.now()))},b);Sg[a]={id:c,Ce:b};return 0}
58
+ function jc(a,b,c,d){a>>>=0;b>>>=0;c>>>=0;d>>>=0;var e=(new Date).getFullYear(),f=(new Date(e,0,1)).getTimezoneOffset();e=(new Date(e,6,1)).getTimezoneOffset();var h=Math.max(f,e);(u(),B)[a>>>2>>>0]=60*h;(u(),A)[b>>>2>>>0]=Number(f!=e);b=k=>{var m=Math.abs(k);return`UTC${0<=k?"-":"+"}${String(Math.floor(m/60)).padStart(2,"0")}${String(m%60).padStart(2,"0")}`};a=b(f);b=b(e);e<f?(T(a,c,17),T(b,d,17)):(T(a,d,17),T(b,c,17))}var oc=()=>Date.now(),Tg=1;
59
+ function kc(a,b,c){c>>>=0;if(!(0<=a&&3>=a))return 28;if(0===a)a=Date.now();else if(Tg)a=performance.timeOrigin+performance.now();else return 52;a=Math.round(1E6*a);(u(),F)[c>>>3>>>0]=BigInt(a);return 0}var Ug=[],Vg=(a,b)=>{Ug.length=0;for(var c;c=(u(),x)[a++>>>0];){var d=105!=c;d&=112!=c;b+=d&&b%8?4:0;Ug.push(112==c?(u(),B)[b>>>2>>>0]:106==c?(u(),F)[b>>>3>>>0]:105==c?(u(),A)[b>>>2>>>0]:(u(),E)[b>>>3>>>0]);b+=d?8:4}return Ug};function lc(a,b,c){a>>>=0;b=Vg(b>>>0,c>>>0);return tg[a](...b)}
60
+ function mc(a,b,c){a>>>=0;b=Vg(b>>>0,c>>>0);return tg[a](...b)}var nc=()=>{};function pc(a,b){return t(Zf(a>>>0,b>>>0))}var qc=()=>{L+=1;throw"unwind";};function rc(){return 4294901760}var tc=()=>1,uc=()=>l?require("os").cpus().length:navigator.hardwareConcurrency;
61
+ function vc(a){a>>>=0;var b=(u(),x).length;if(a<=b||4294901760<a)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);a:{d=(Math.min(4294901760,65536*Math.ceil(Math.max(a,d)/65536))-v.buffer.byteLength+65535)/65536|0;try{v.grow(d);wa();var e=1;break a}catch(f){}e=void 0}if(e)return!0}return!1}
62
+ var V=a=>{var b=U(a)+1,c=Je(b);T(a,c,b);return c},Wg=(a,b)=>{(u(),B)[a>>>2>>>0]=b;var c=(u(),B)[a>>>2>>>0];(u(),B)[a+4>>>2>>>0]=(b-c)/4294967296},Xg=a=>(u(),B)[a>>>2>>>0]+4294967296*(u(),A)[a+4>>>2>>>0],W=[],Yg=(a,b)=>{W[a>>>0]=b},X=[],Zg=[],$g=(a,b)=>{Zg[a]=new Promise(c=>b.finally(()=>c(a)))},Y=a=>{if(a)return W[a>>>0]},ah=(a,b=0)=>{"unmapped"===a.mapState||Ta();b=re(b);W[b>>>0]=a;return b},bh=(a,b=0)=>{var c=te(b);b=se(b,c);W[c>>>0]=a.queue;W[b>>>0]=a;return b},ch=(a,b)=>{for(a=(u(),B)[a>>>2>>>
63
+ 0];a;a=(u(),B)[a>>>2>>>0]){var c=(u(),A)[a+4>>>2>>>0];b[c](a)}},dh=(a,b,c)=>{(u(),B)[a>>>2>>>0]=b;(u(),B)[a+4>>>2>>>0]=c},eh=a=>{var b=(u(),B)[a>>>2>>>0];a=(u(),B)[a+4>>>2>>>0];return Zf(b,a)},Z=a=>{var b=(u(),B)[a>>>2>>>0];a=(u(),B)[a+4>>>2>>>0];if(b)return Zf(b,a);if(0===a)return""},fh=a=>{var b=Z(a+4);var c=(c=(u(),B)[a+12>>>2>>>0])?Y(c):"auto";if(a+=16){var d=Y((u(),B)[a+4>>>2>>>0]);var e=(u(),B)[a+16>>>2>>>0];var f=(u(),B)[a+20>>>2>>>0];if(e){for(var h={},k=0;k<e;++k){var m=f+24*k,p=eh(m+4);
64
+ h[p]=(u(),E)[m+16>>>3>>>0]}e=h}else e=void 0;a={module:d,constants:e,entryPoint:Z(a+8)}}else a=void 0;return{label:b,layout:c,compute:a}},gh=(a,b)=>{function c(d,e){d=a[d];(u(),B)[b+e>>>2>>>0]=d}c("maxTextureDimension1D",4);c("maxTextureDimension2D",8);c("maxTextureDimension3D",12);c("maxTextureArrayLayers",16);c("maxBindGroups",20);c("maxBindGroupsPlusVertexBuffers",24);c("maxBindingsPerBindGroup",28);c("maxDynamicUniformBuffersPerPipelineLayout",32);c("maxDynamicStorageBuffersPerPipelineLayout",
65
+ 36);c("maxSampledTexturesPerShaderStage",40);c("maxSamplersPerShaderStage",44);c("maxStorageBuffersPerShaderStage",48);c("maxStorageTexturesPerShaderStage",52);c("maxUniformBuffersPerShaderStage",56);c("minUniformBufferOffsetAlignment",80);c("minStorageBufferOffsetAlignment",84);Wg(b+64,a.maxUniformBufferBindingSize);Wg(b+72,a.maxStorageBufferBindingSize);c("maxVertexBuffers",88);Wg(b+96,a.maxBufferSize);c("maxVertexAttributes",104);c("maxVertexBufferArrayStride",108);c("maxInterStageShaderVariables",
66
+ 112);c("maxColorAttachments",116);c("maxColorAttachmentBytesPerSample",120);c("maxComputeWorkgroupStorageSize",124);c("maxComputeInvocationsPerWorkgroup",128);c("maxComputeWorkgroupSizeX",132);c("maxComputeWorkgroupSizeY",136);c("maxComputeWorkgroupSizeZ",140);c("maxComputeWorkgroupsPerDimension",144);void 0!==a.Be&&c("maxImmediateSize",148)},hh=[,"validation","out-of-memory","internal"],ih=[,"compatibility","core"],jh={1:"core-features-and-limits",2:"depth-clip-control",3:"depth32float-stencil8",
67
+ 4:"texture-compression-bc",5:"texture-compression-bc-sliced-3d",6:"texture-compression-etc2",7:"texture-compression-astc",8:"texture-compression-astc-sliced-3d",9:"timestamp-query",10:"indirect-first-instance",11:"shader-f16",12:"rg11b10ufloat-renderable",13:"bgra8unorm-storage",14:"float32-filterable",15:"float32-blendable",16:"clip-distances",17:"dual-source-blending",18:"subgroups",19:"texture-formats-tier1",20:"texture-formats-tier2",21:"primitive-index",22:"texture-component-swizzle",327692:"chromium-experimental-unorm16-texture-formats",
68
+ 327729:"chromium-experimental-multi-draw-indirect"},kh=[,"low-power","high-performance"],lh=[,"occlusion","timestamp"],mh={undefined:1,unknown:1,destroyed:2};
69
+ function wc(a,b,c,d,e,f){b=O(b);c=O(c);d>>>=0;e>>>=0;f>>>=0;var h=Y(a>>>0);a={};if(f){var k=(u(),B)[f+12>>>2>>>0];if(k){var m=(u(),B)[f+16>>>2>>>0];a.requiredFeatures=Array.from((u(),B).subarray(m>>>2>>>0,m+4*k>>>2>>>0),r=>jh[r])}var p=(u(),B)[f+20>>>2>>>0];if(p){var C={};function r(z,D,za=!1){D=p+D;D=(u(),B)[D>>>2>>>0];4294967295==D||za&&0==D||(C[z]=D)}function y(z,D){D=p+D;var za=(u(),B)[D>>>2>>>0],yb=(u(),B)[D+4>>>2>>>0];if(4294967295!=za||4294967295!=yb)C[z]=Xg(D)}r("maxTextureDimension1D",4);
70
+ r("maxTextureDimension2D",8);r("maxTextureDimension3D",12);r("maxTextureArrayLayers",16);r("maxBindGroups",20);r("maxBindGroupsPlusVertexBuffers",24);r("maxDynamicUniformBuffersPerPipelineLayout",32);r("maxDynamicStorageBuffersPerPipelineLayout",36);r("maxSampledTexturesPerShaderStage",40);r("maxSamplersPerShaderStage",44);r("maxStorageBuffersPerShaderStage",48);r("maxStorageTexturesPerShaderStage",52);r("maxUniformBuffersPerShaderStage",56);r("minUniformBufferOffsetAlignment",80);r("minStorageBufferOffsetAlignment",
71
+ 84);y("maxUniformBufferBindingSize",64);y("maxStorageBufferBindingSize",72);r("maxVertexBuffers",88);y("maxBufferSize",96);r("maxVertexAttributes",104);r("maxVertexBufferArrayStride",108);r("maxInterStageShaderVariables",112);r("maxColorAttachments",116);r("maxColorAttachmentBytesPerSample",120);r("maxComputeWorkgroupStorageSize",124);r("maxComputeInvocationsPerWorkgroup",128);r("maxComputeWorkgroupSizeX",132);r("maxComputeWorkgroupSizeY",136);r("maxComputeWorkgroupSizeZ",140);r("maxComputeWorkgroupsPerDimension",
72
+ 144);r("maxImmediateSize",148,!0);a.requiredLimits=C}if(k=(u(),B)[f+24>>>2>>>0])k={label:Z(k+4)},a.defaultQueue=k;a.label=Z(f+4)}L+=1;$g(b,h.requestDevice(a).then(r=>{--L;N(()=>{W[e>>>0]=r.queue;W[d>>>0]=r;L+=1;$g(c,r.lost.then(y=>{N(()=>{r.onuncapturederror=()=>{};var z=K(),D=V(y.message);ve(c,mh[y.reason],D);J(z)});--L}));r.onuncapturederror=y=>{var z=5;y.error instanceof GPUValidationError?z=2:y.error instanceof GPUOutOfMemoryError?z=3:y.error instanceof GPUInternalError&&(z=4);var D=K();y=V(y.error.message);
73
+ Ae(d,z,y);J(D)};"adapterInfo"in r||(r.adapterInfo=h.info);ze(b,1,d,0)})},r=>{--L;N(()=>{var y=K(),z=V(r.message);ze(b,3,d,z);c&&ve(c,4,z);J(y)})}))}function xc(a){a>>>=0;var b=Y(a),c=X[a];if(c){for(var d=0;d<c.length;++d)c[d]();delete X[a]}b.destroy()}function yc(a,b,c){a>>>=0;c>>>=0;var d=Y(a);4294967295==c&&(c=void 0);try{var e=d.getMappedRange(b>>>0,c)}catch(h){return 0}var f=Ge(16,e.byteLength);(u(),x).set(new Uint8Array(e),f>>>0);X[a].push(()=>H(f));return f}
74
+ function zc(a,b,c){a>>>=0;c>>>=0;var d=Y(a);4294967295==c&&(c=void 0);try{var e=d.getMappedRange(b>>>0,c)}catch(h){return 0}var f=Ge(16,e.byteLength);(u(),x).fill(0,f,e.byteLength);X[a].push(()=>{(new Uint8Array(e)).set((u(),x).subarray(f>>>0,f+e.byteLength>>>0));H(f)});return f}
75
+ function Ac(a,b,c,d,e){a>>>=0;b=O(b);c=O(c);e>>>=0;var f=Y(a);X[a]=[];4294967295==e&&(e=void 0);L+=1;$g(b,f.mapAsync(c,d>>>0,e).then(()=>{--L;N(()=>{we(b,1,0)})},h=>{--L;N(()=>{K();var k=V(h.message);we(b,"AbortError"===h.name?4:"OperationError"===h.name?3:0,k);delete X[a]})}))}function Bc(a){a>>>=0;var b=Y(a),c=X[a];if(c){for(var d=0;d<c.length;++d)c[d]();delete X[a];b.unmap()}}function Cc(a){delete W[a>>>0]}
76
+ function Dc(a,b,c){a>>>=0;b>>>=0;c>>>=0;var d=!!(u(),B)[b+32>>>2>>>0];b={label:Z(b+4),usage:(u(),B)[b+16>>>2>>>0],size:Xg(b+24),mappedAtCreation:d};a=Y(a);try{var e=a.createBuffer(b)}catch(f){return!1}W[c>>>0]=e;d&&(X[c]=[]);return!0}function Ec(a,b,c,d){a>>>=0;b=O(b);d>>>=0;c=fh(c>>>0);a=Y(a);L+=1;$g(b,a.createComputePipelineAsync(c).then(e=>{--L;N(()=>{W[d>>>0]=e;ue(b,1,d,0)})},e=>{--L;N(()=>{var f=K(),h=V(e.message);ue(b,"validation"===e.reason?3:"internal"===e.reason?4:0,d,h);J(f)})}))}
77
+ function Fc(a,b,c){a>>>=0;b>>>=0;c>>>=0;var d=(u(),B)[b>>>2>>>0],e=(u(),A)[d+4>>>2>>>0];b={label:Z(b+4),code:""};switch(e){case 2:b.code=eh(d+8)}a=Y(a).createShaderModule(b);W[c>>>0]=a}var Gc=a=>{a=Y(a);a.onuncapturederror=null;a.destroy()};
78
+ function Hc(a,b){b=O(b);a=Y(a>>>0);L+=1;$g(b,a.popErrorScope().then(c=>{--L;N(()=>{var d=5;c?c instanceof GPUValidationError?d=2:c instanceof GPUOutOfMemoryError?d=3:c instanceof GPUInternalError&&(d=4):d=1;var e=K(),f=c?V(c.message):0;xe(b,1,d,f);J(e)})},c=>{--L;N(()=>{var d=K(),e=V(c.message);xe(b,1,5,e);J(d)})}))}
79
+ function Ic(a,b,c,d){b=O(b);c>>>=0;d>>>=0;if(c){var e={featureLevel:ih[(u(),A)[c+4>>>2>>>0]],powerPreference:kh[(u(),A)[c+8>>>2>>>0]],forceFallbackAdapter:!!(u(),B)[c+12>>>2>>>0]};a=(u(),B)[c>>>2>>>0];0!==a&&(u(),e.Fe=!!(u(),B)[a+8>>>2>>>0])}"gpu"in navigator?(L+=1,$g(b,navigator.gpu.requestAdapter(e).then(f=>{--L;N(()=>{if(f)W[d>>>0]=f,ye(b,1,d,0);else{var h=K(),k=V("WebGPU not available on this browser (requestAdapter returned null)");ye(b,3,d,k);J(h)}})},f=>{--L;N(()=>{var h=K(),k=V(f.message);
80
+ ye(b,4,d,k);J(h)})}))):(e=K(),a=V("WebGPU not available on this browser (navigator.gpu is not available)"),ye(b,3,d,a),J(e))}function Jc(a,b,c){a>>>=0;b>>>=0;c>>>=0;return Ig(async()=>{var d=[];if(c){var e=(u(),A)[c>>>2>>>0];d.length=b+1;d[b]=new Promise(k=>setTimeout(k,e,0))}else d.length=b;for(var f=0;f<b;++f){var h=Xg(a+8*f);if(!(h in Zg))return h;d[f]=Zg[h]}d=await Promise.race(d);delete Zg[d];return d})}
81
+ var nh={},ph=()=>{if(!oh){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8",_:fa||"./this.program"},b;for(b in nh)void 0===nh[b]?delete a[b]:a[b]=nh[b];var c=[];for(b in a)c.push(`${b}=${a[b]}`);oh=c}return oh},oh;function Kc(a,b){if(n)return M(19,1,a,b);a>>>=0;b>>>=0;var c=0,d=0,e;for(e of ph()){var f=b+c;(u(),B)[a+d>>>2>>>0]=f;c+=T(e,f,Infinity)+1;d+=4}return 0}
82
+ function Lc(a,b){if(n)return M(20,1,a,b);a>>>=0;b>>>=0;var c=ph();(u(),B)[a>>>2>>>0]=c.length;a=0;for(var d of c)a+=U(d)+1;(u(),B)[b>>>2>>>0]=a;return 0}function Nc(a){return n?M(21,1,a):52}function Oc(a,b,c,d){return n?M(22,1,a,b,c,d):52}function Pc(a,b,c,d){return n?M(23,1,a,b,c,d):70}var qh=[null,[],[]];
83
+ function Qc(a,b,c,d){if(n)return M(24,1,a,b,c,d);b>>>=0;c>>>=0;d>>>=0;for(var e=0,f=0;f<c;f++){var h=(u(),B)[b>>>2>>>0],k=(u(),B)[b+4>>>2>>>0];b+=8;for(var m=0;m<k;m++){var p=a,C=(u(),x)[h+m>>>0],r=qh[p];0===C||10===C?((1===p?ra:t)(Yf(r)),r.length=0):r.push(C)}e+=k}(u(),B)[d>>>2>>>0]=e;return 0}function Cd(a){return a>>>0}function Ed(a,b){gh(Y(a>>>0).limits,b>>>0);return 1}function Fd(a,b){return Y(a>>>0).features.has(jh[b])}function Gd(a){return BigInt(Y(a>>>0).size)}
84
+ function Hd(a){return BigInt(Y(a>>>0).usage)}function Id(a,b){a>>>=0;b>>>=0;if(b){var c=Z(b+4);b=(u(),B)[b+12>>>2>>>0];b=0!==b?{querySet:Y((u(),B)[b+4>>>2>>>0]),beginningOfPassWriteIndex:(u(),B)[b+8>>>2>>>0],endOfPassWriteIndex:(u(),B)[b+12>>>2>>>0]}:void 0;c={label:c,timestampWrites:b}}b=Y(a);a=pe(0);c=b.beginComputePass(c);W[a>>>0]=c;return a}function Jd(a,b,c,d){c=O(c);d=O(d);a=Y(a>>>0);-1==d&&(d=void 0);a.clearBuffer(Y(b>>>0),c,d)}
85
+ function Kd(a,b,c,d,e,f){c=O(c);e=O(e);f=O(f);Y(a>>>0).copyBufferToBuffer(Y(b>>>0),c,Y(d>>>0),e,f)}function Ld(a){var b=Y(a>>>0);a=ne(0);b=b.finish();W[a>>>0]=b;return a}function Md(a,b,c,d,e,f){f=O(f);Y(a>>>0).resolveQuerySet(Y(b>>>0),c,d,Y(e>>>0),f)}function Nd(a,b,c,d){Y(a>>>0).dispatchWorkgroups(b,c,d)}function Od(a,b,c){c=O(c);Y(a>>>0).dispatchWorkgroupsIndirect(Y(b>>>0),c)}function Pd(a){Y(a>>>0).end()}
86
+ function Qd(a,b,c,d,e){d>>>=0;e>>>=0;a=Y(a>>>0);c=Y(c>>>0);0==d?a.setBindGroup(b,c):a.setBindGroup(b,c,(u(),B),e>>>2,d)}function Rd(a,b){Y(a>>>0).setPipeline(Y(b>>>0))}function Sd(a,b,c){Y(a>>>0).Ee(Y(b>>>0),c)}function Td(a,b){var c=Y(a>>>0);a=me(0);b=c.getBindGroupLayout(b);W[a>>>0]=b;return a}
87
+ function Ud(a,b){function c(e){var f=(u(),B)[e+8>>>2>>>0],h=(u(),B)[e+32>>>2>>>0],k=(u(),B)[e+36>>>2>>>0],m=0;ch(e,{327681:p=>{m=(u(),B)[p+8>>>2>>>0]}});f?(h=Xg(e+24),-1==h&&(h=void 0),f={buffer:Y(f),offset:Xg(e+16),size:h}):f=Y(h||k||m);return{binding:(u(),B)[e+4>>>2>>>0],resource:f}}a>>>=0;b>>>=0;b={label:Z(b+4),layout:Y((u(),B)[b+12>>>2>>>0]),entries:function(e,f){for(var h=[],k=0;k<e;++k)h.push(c(f+40*k));return h}((u(),B)[b+16>>>2>>>0],(u(),B)[b+20>>>2>>>0])};a=Y(a);var d=le(0);Yg(d,a.createBindGroup(b));
88
+ return d}function Vd(a,b){a>>>=0;b>>>=0;var c;b&&(c={label:Z(b+4)});b=Y(a);a=oe(0);c=b.createCommandEncoder(c);W[a>>>0]=c;return a}function Wd(a,b){a>>>=0;b>>>=0;b={type:lh[(u(),A)[b+12>>>2>>>0]],count:(u(),B)[b+16>>>2>>>0]};var c=Y(a);a=qe(0);b=c.createQuerySet(b);W[a>>>0]=b;return a}
89
+ function Xd(a,b){a=Y(a>>>0).adapterInfo;b>>>=0;(u(),B)[b+52>>>2>>>0]=a.subgroupMinSize;(u(),B)[b+56>>>2>>>0]=a.subgroupMaxSize;var c=a.vendor+a.architecture+a.device+a.description;var d=U(c)+1,e=ie(d);e&&T(c,e,d);c=e;d=U(a.vendor);dh(b+4,c,d);c+=d;d=U(a.architecture);dh(b+12,c,d);c+=d;d=U(a.device);dh(b+20,c,d);dh(b+28,c+d,U(a.description));(u(),A)[b+36>>>2>>>0]=2;a=a.isFallbackAdapter?3:4;(u(),A)[b+40>>>2>>>0]=a;(u(),B)[b+44>>>2>>>0]=0;(u(),B)[b+48>>>2>>>0]=0;return 1}
90
+ var rh={"core-features-and-limits":1,"depth-clip-control":2,"depth32float-stencil8":3,"texture-compression-bc":4,"texture-compression-bc-sliced-3d":5,"texture-compression-etc2":6,"texture-compression-astc":7,"texture-compression-astc-sliced-3d":8,"timestamp-query":9,"indirect-first-instance":10,"shader-f16":11,"rg11b10ufloat-renderable":12,"bgra8unorm-storage":13,"float32-filterable":14,"float32-blendable":15,"clip-distances":16,"dual-source-blending":17,subgroups:18,"texture-formats-tier1":19,"texture-formats-tier2":20,
91
+ "primitive-index":21,"texture-component-swizzle":22,"chromium-experimental-unorm16-texture-formats":327692,"chromium-experimental-multi-draw-indirect":327729};function Yd(a,b){b>>>=0;var c=Y(a>>>0);a=ie(4*c.features.size);var d=0,e=0;for(const f of c.features)c=rh[f],0<=c&&((u(),A)[a+d>>>2>>>0]=c,d+=4,e++);(u(),B)[b+4>>>2>>>0]=a;(u(),B)[b>>>2>>>0]=e}function Zd(a,b){gh(Y(a>>>0).limits,b>>>0);return 1}function $d(a,b){Y(a>>>0).pushErrorScope(hh[b])}
92
+ function ae(a,b,c){b>>>=0;c>>>=0;a=Y(a>>>0);b=Array.from((u(),A).subarray(c>>>2>>>0,c+4*b>>>2>>>0),d=>Y(d));a.submit(b)}function be(a,b,c,d,e){c=O(c);d>>>=0;e>>>=0;a=Y(a>>>0);b=Y(b>>>0);d=(u(),x).subarray(d>>>0,d+e>>>0);a.writeBuffer(b,c,d,0,e)}n||Lf();n||(v=new WebAssembly.Memory({initial:256,maximum:65536,shared:!0}),wa());g.wasmBinary&&(sa=g.wasmBinary);g.stackSave=()=>K();g.stackRestore=a=>J(a);g.stackAlloc=a=>Je(a);
93
+ g.setValue=function(a,b,c="i8"){c.endsWith("*")&&(c="*");switch(c){case "i1":(u(),w)[a>>>0]=b;break;case "i8":(u(),w)[a>>>0]=b;break;case "i16":(u(),Na)[a>>>1>>>0]=b;break;case "i32":(u(),A)[a>>>2>>>0]=b;break;case "i64":(u(),F)[a>>>3>>>0]=BigInt(b);break;case "float":(u(),Pa)[a>>>2>>>0]=b;break;case "double":(u(),E)[a>>>3>>>0]=b;break;case "*":(u(),B)[a>>>2>>>0]=b;break;default:Ta(`invalid type for setValue: ${c}`)}};
94
+ g.getValue=function(a,b="i8"){b.endsWith("*")&&(b="*");switch(b){case "i1":return(u(),w)[a>>>0];case "i8":return(u(),w)[a>>>0];case "i16":return(u(),Na)[a>>>1>>>0];case "i32":return(u(),A)[a>>>2>>>0];case "i64":return(u(),F)[a>>>3>>>0];case "float":return(u(),Pa)[a>>>2>>>0];case "double":return(u(),E)[a>>>3>>>0];case "*":return(u(),B)[a>>>2>>>0];default:Ta(`invalid type for getValue: ${b}`)}};g.UTF8ToString=Zf;g.stringToUTF8=T;g.lengthBytesUTF8=U;
95
+ var ug=[Dd,Kf,Vf,jb,kb,lb,mb,nb,ob,pb,qb,rb,sb,tb,ub,vb,gc,hc,ic,Kc,Lc,Nc,Oc,Pc,Qc],tg={1028716:(a,b,c,d,e)=>{if("undefined"==typeof g||!g.Vc)return 1;a=Zf(Number(a>>>0));a.startsWith("./")&&(a=a.substring(2));a=g.Vc.get(a);if(!a)return 2;b=Number(b>>>0);c=Number(c>>>0);d=Number(d>>>0);if(b+c>a.byteLength)return 3;try{const f=a.subarray(b,b+c);switch(e){case 0:(u(),x).set(f,d>>>0);break;case 1:g.bd?g.bd(d,f):g.pe(d,f);break;default:return 4}return 0}catch{return 4}},1029540:(a,b,c)=>{g.Td(a,(u(),
96
+ x).subarray(b>>>0,b+c>>>0))},1029604:()=>g.ne(),1029646:a=>{g.kd(a)},1029683:()=>"undefined"!==typeof wasmOffsetConverter},fe,Ja,ge,he,H,ie,je,ke,le,me,ne,oe,pe,qe,re,se,te,ue,ve,we,xe,ye,ze,Ae,Ga,Ma,Be,Ce,De,Ee,Fe,Ge,I,He,Ie,J,Je,K,Ke,Le,Me,Ne,dynCall_vii,Oe,dynCall_v,Pe,Qe,Re,dynCall_iii,Se,Te,Ue,dynCall_vi,Ve,We,Xe,Ye,Ze,$e,af,bf,cf,df,ef,ff,gf,hf,jf,kf,lf,mf,nf,of,pf,qf,rf,sf,tf,uf,vf,wf,xf,yf,Za;function Yc(a,b,c,d){var e=K();try{return Ue(a,b,c,d)}catch(f){J(e);if(f!==f+0)throw f;I(1,0)}}
97
+ function Xc(a,b,c){var d=K();try{return dynCall_iii(a,b,c)}catch(e){J(d);if(e!==e+0)throw e;I(1,0)}}function nd(a){var b=K();try{dynCall_v(a)}catch(c){J(b);if(c!==c+0)throw c;I(1,0)}}function Wc(a,b){var c=K();try{return Oe(a,b)}catch(d){J(c);if(d!==d+0)throw d;I(1,0)}}function pd(a,b,c){var d=K();try{dynCall_vii(a,b,c)}catch(e){J(d);if(e!==e+0)throw e;I(1,0)}}function od(a,b){var c=K();try{dynCall_vi(a,b)}catch(d){J(c);if(d!==d+0)throw d;I(1,0)}}
98
+ function bd(a,b,c,d,e,f,h){var k=K();try{return Re(a,b,c,d,e,f,h)}catch(m){J(k);if(m!==m+0)throw m;I(1,0)}}function td(a,b,c,d,e,f){var h=K();try{Pe(a,b,c,d,e,f)}catch(k){J(h);if(k!==k+0)throw k;I(1,0)}}function rd(a,b,c,d){var e=K();try{Te(a,b,c,d)}catch(f){J(e);if(f!==f+0)throw f;I(1,0)}}function ud(a,b,c,d,e,f,h){var k=K();try{We(a,b,c,d,e,f,h)}catch(m){J(k);if(m!==m+0)throw m;I(1,0)}}function Ad(a,b,c,d,e,f,h){var k=K();try{Xe(a,b,c,d,e,f,h)}catch(m){J(k);if(m!==m+0)throw m;I(1,0)}}
99
+ function zd(a,b,c,d,e,f,h,k){var m=K();try{ff(a,b,c,d,e,f,h,k)}catch(p){J(m);if(p!==p+0)throw p;I(1,0)}}function xd(a,b,c,d,e,f,h,k,m,p,C,r){var y=K();try{Ye(a,b,c,d,e,f,h,k,m,p,C,r)}catch(z){J(y);if(z!==z+0)throw z;I(1,0)}}function Zc(a,b,c,d,e){var f=K();try{return Ve(a,b,c,d,e)}catch(h){J(f);if(h!==h+0)throw h;I(1,0)}}function Bd(a,b,c,d,e,f){var h=K();try{gf(a,b,c,d,e,f)}catch(k){J(h);if(k!==k+0)throw k;I(1,0)}}
100
+ function sd(a,b,c,d,e){var f=K();try{Qe(a,b,c,d,e)}catch(h){J(f);if(h!==h+0)throw h;I(1,0)}}function vd(a,b,c,d,e,f,h,k){var m=K();try{Se(a,b,c,d,e,f,h,k)}catch(p){J(m);if(p!==p+0)throw p;I(1,0)}}function Vc(a){var b=K();try{return hf(a)}catch(c){J(b);if(c!==c+0)throw c;I(1,0)}}function hd(a,b,c){var d=K();try{return jf(a,b,c)}catch(e){J(d);if(e!==e+0)throw e;I(1,0)}}function kd(a,b){var c=K();try{return uf(a,b)}catch(d){J(c);if(d!==d+0)throw d;I(1,0);return 0n}}
101
+ function jd(a){var b=K();try{return Ze(a)}catch(c){J(b);if(c!==c+0)throw c;I(1,0);return 0n}}function gd(a,b,c,d){var e=K();try{return kf(a,b,c,d)}catch(f){J(e);if(f!==f+0)throw f;I(1,0)}}function fd(a,b,c,d,e){var f=K();try{return lf(a,b,c,d,e)}catch(h){J(f);if(h!==h+0)throw h;I(1,0)}}function ed(a,b,c,d,e,f){var h=K();try{return mf(a,b,c,d,e,f)}catch(k){J(h);if(k!==k+0)throw k;I(1,0)}}function ad(a,b,c,d,e,f){var h=K();try{return df(a,b,c,d,e,f)}catch(k){J(h);if(k!==k+0)throw k;I(1,0)}}
102
+ function $c(a,b,c,d,e,f){var h=K();try{return nf(a,b,c,d,e,f)}catch(k){J(h);if(k!==k+0)throw k;I(1,0)}}function cd(a,b,c,d,e,f,h,k){var m=K();try{return ef(a,b,c,d,e,f,h,k)}catch(p){J(m);if(p!==p+0)throw p;I(1,0)}}function md(a,b,c,d,e){var f=K();try{return of(a,b,c,d,e)}catch(h){J(f);if(h!==h+0)throw h;I(1,0);return 0n}}function Uc(a,b,c,d){var e=K();try{return pf(a,b,c,d)}catch(f){J(e);if(f!==f+0)throw f;I(1,0)}}
103
+ function Sc(a,b,c,d){var e=K();try{return qf(a,b,c,d)}catch(f){J(e);if(f!==f+0)throw f;I(1,0)}}function dd(a,b,c,d,e,f,h,k,m,p,C,r){var y=K();try{return rf(a,b,c,d,e,f,h,k,m,p,C,r)}catch(z){J(y);if(z!==z+0)throw z;I(1,0)}}function wd(a,b,c,d,e,f,h,k,m,p,C){var r=K();try{sf(a,b,c,d,e,f,h,k,m,p,C)}catch(y){J(r);if(y!==y+0)throw y;I(1,0)}}function yd(a,b,c,d,e,f,h,k,m,p,C,r,y,z,D,za){var yb=K();try{tf(a,b,c,d,e,f,h,k,m,p,C,r,y,z,D,za)}catch(Bb){J(yb);if(Bb!==Bb+0)throw Bb;I(1,0)}}
104
+ function ld(a,b,c){var d=K();try{return af(a,b,c)}catch(e){J(d);if(e!==e+0)throw e;I(1,0);return 0n}}function Tc(a,b,c){var d=K();try{return $e(a,b,c)}catch(e){J(d);if(e!==e+0)throw e;I(1,0)}}function Rc(a,b,c){var d=K();try{return bf(a,b,c)}catch(e){J(d);if(e!==e+0)throw e;I(1,0)}}function qd(a,b,c,d){var e=K();try{cf(a,b,c,d)}catch(f){J(e);if(f!==f+0)throw f;I(1,0)}}
105
+ function de(){var a=G;a=Object.assign({},a);var b=d=>e=>d(e)>>>0,c=d=>()=>d()>>>0;a.ac=b(a.ac);a.Dc=c(a.Dc);a.Fc=b(a.Fc);a.sd=(d=>(e,f)=>d(e,f)>>>0)(a.sd);a.xd=b(a.xd);a.yd=c(a.yd);a.Cd=b(a.Cd);return a}function Ea(){if(0<Cf)Df=Ea;else if(n)xa?.(g),Sa();else{for(var a=Bf;0<a.length;)a.shift()(g);0<Cf?Df=Ea:(g.calledRun=!0,ua||(Sa(),xa?.(g)))}}var G;n||(G=await (Da()),Ea());g.PTR_SIZE=4;
106
+ g.webgpuInit=a=>{const b=new WeakMap;let c=1,d=void 0,e=void 0;g.webgpuRegisterDevice=k=>{if(void 0!==e)throw Error("another WebGPU EP inference session is being created.");if(k){var m=b.get(k);if(!m){m=ke(0);const p=bh(k,m);m=[c++,m,p];b.set(k,m)}d=k;e=m[0];return m}d=void 0;e=0};const f=new Map;g.webgpuOnCreateSession=k=>{if(void 0!==e){var m=e;e=void 0;if(k){const p=ge(m);f.set(k,p);0===m&&a(d??Y(p))}d=void 0}};g.webgpuOnReleaseSession=k=>{f.delete(k)};const h=Symbol("gpuBufferMetadata");g.webgpuRegisterBuffer=
107
+ (k,m,p)=>{if(p)return k[h]=[p,NaN],p;if(p=k[h])return p[1]++,p[0];m=f.get(m);if(void 0===m)throw Error("Invalid session handle passed to webgpuRegisterBuffer");m=ah(k,m);k[h]=[m,1];return m};g.webgpuUnregisterBuffer=k=>{const m=k[h];if(!m)throw Error("Buffer is not registered");m[1]--;0===m[1]&&(je(m[0]),delete k[h])};g.webgpuGetBuffer=k=>Y(k);g.webgpuCreateDownloader=(k,m,p)=>{p=f.get(p);if(void 0===p)throw Error("Invalid session handle passed to webgpuRegisterBuffer");const C=Y(p),r=16*Math.ceil(Number(m)/
108
+ 16);return async()=>{const y=C.createBuffer({size:r,usage:9});try{const z=C.createCommandEncoder();z.copyBufferToBuffer(k,0,y,0,r);C.queue.submit([z.finish()]);await y.mapAsync(GPUMapMode.READ);return y.getMappedRange().slice(0,m)}finally{y.destroy()}}};g.bd=(k,m)=>{var p=m.buffer;const C=m.byteOffset,r=m.byteLength;m=16*Math.ceil(Number(r)/16);k=Y(k);if(!d){var y=ge(e);d=Y(y)}y=d.createBuffer({mappedAtCreation:!0,size:m,usage:6});const z=y.getMappedRange();(new Uint8Array(z)).set(new Uint8Array(p,
109
+ C,r));y.unmap();p=d.createCommandEncoder();p.copyBufferToBuffer(y,0,k,0,m);d.queue.submit([p.finish()]);y.destroy()}};
110
+ g.webnnInit=a=>{const b=a[0];[g.ne,g.kd,g.webnnEnsureTensor,g.Td,g.webnnDownloadTensor,g.me,g.webnnEnableTraceEvent]=a.slice(1);g.webnnReleaseTensorId=g.kd;g.webnnUploadTensor=g.Td;g.webnnRegisterMLContext=g.me;g.webnnOnRunStart=c=>b.onRunStart(c);g.webnnOnRunEnd=b.onRunEnd.bind(b);g.webnnOnReleaseSession=c=>{b.onReleaseSession(c)};g.webnnCreateMLTensorDownloader=(c,d)=>b.createMLTensorDownloader(c,d);g.webnnRegisterMLTensor=(c,d,e,f)=>b.registerMLTensor(c,d,e,f);g.webnnCreateMLContext=c=>b.createMLContext(c);
111
+ g.webnnRegisterMLConstant=(c,d,e,f,h,k)=>b.registerMLConstant(c,d,e,f,h,g.Vc,k);g.webnnRegisterGraphInput=b.registerGraphInput.bind(b);g.webnnIsGraphInput=b.isGraphInput.bind(b);g.webnnRegisterGraphOutput=b.registerGraphOutput.bind(b);g.webnnIsGraphOutput=b.isGraphOutput.bind(b);g.webnnCreateTemporaryTensor=b.createTemporaryTensor.bind(b);g.webnnIsGraphInputOutputTypeSupported=b.isGraphInputOutputTypeSupported.bind(b)};Ra?moduleRtn=g:moduleRtn=new Promise((a,b)=>{xa=a;ya=b});
112
+ ;return moduleRtn}export default ortWasmThreaded;var isPthread=globalThis.self?.name?.startsWith("em-pthread");var isNode=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(isNode)isPthread=(await import("worker_threads")).workerData==="em-pthread";isPthread&&ortWasmThreaded();
runtime/vendor/ort-wasm-simd-threaded.asyncify.wasm ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7e83cd6cee77e478bc96a7e91b198144fb5e4126287daf1f9b54bb195ebcd55a
3
+ size 24254953
runtime/vendor/ort.webgpu.mjs ADDED
The diff for this file is too large to render. See raw diff
 
runtime/vendor/transformers.web.js ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c7dc553ba83607a7d3cde4782e4055fcd15cb228f8f1cdc3fc149b1094580592
3
+ size 10548223
tokenizer_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": null,
3
+ "backend": "tokenizers",
4
+ "bos_token": "<s>",
5
+ "clean_up_tokenization_spaces": false,
6
+ "eos_token": "</s>",
7
+ "is_local": true,
8
+ "legacy": true,
9
+ "local_files_only": true,
10
+ "model_max_length": 131072,
11
+ "pad_token": "</s>",
12
+ "sp_model_kwargs": {},
13
+ "spaces_between_special_tokens": false,
14
+ "tokenizer_class": "TokenizersBackend",
15
+ "unk_token": "<unk>",
16
+ "use_default_system_prompt": false,
17
+ "chat_template": "{{- bos_token }}{%- if tools %}\n {%- set tool_definitions %}\n {{- \"# Tools\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson(ensure_ascii=False) }}\n {%- endfor %}\n {{- '\\n</tools>\\n\\nTool usage guidelines:\\n- You may call zero or more functions. If no function calls are needed, just answer normally and do not include any <function ... </function>.\\n- When calling a function, return an XML object within <function ... </function> using:\\n<function name=\"function-name\"><param name=\"param-name\">param-value</param></function>\\n- param-value may be multi-line. If it contains <, & or newline characters, wrap it in a CDATA block: <param name=\"param-name\"><![CDATA[...multi-line value...]]></param>' }}\n {%- endset %}\n \n {{- '<|im_start|>system\\n' }}\n {%- if messages[0].role == 'system' %}\n {%- if '<tool_def_sep>' in messages[0].content %}\n {{- messages[0].content.replace('<tool_def_sep>', tool_definitions) }}\n {%- else %}\n {{- messages[0].content + '\\n\\n' + tool_definitions }}\n {%- endif %}\n {%- else %}\n {{- tool_definitions.lstrip() }}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n{%- else %}\n {%- if messages[0].role == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0].content + '<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}\n{%- for message in messages[::-1] %}\n {%- set index = (messages|length - 1) - loop.index0 %}\n {%- if ns.multi_step_tool and message.role == \"user\" and message.content is string and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}\n {%- set ns.multi_step_tool = false %}\n {%- set ns.last_query_index = index %}\n {%- endif %}\n{%- endfor %}\n{%- for message in messages %}\n {%- if message.content is string %}\n {%- set content = message.content %}\n {%- else %}\n {%- set content = '' %}\n {%- endif %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) %}\n {{- '<|im_start|>' + message.role + '\\n' + content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {%- set reasoning_content = '' %}\n {%- if message.reasoning_content is string %}\n {%- set reasoning_content = message.reasoning_content %}\n {%- else %}\n {%- if '</think>' in content %}\n {%- set reasoning_content = content.split('</think>')[0].rstrip('\\n').split('<think>')[-1].lstrip('\\n') %}\n {%- set content = content.split('</think>')[-1].lstrip('\\n') %}\n {%- endif %}\n {%- endif %}\n \n {%- if message.tool_calls %}\n {%- set content_parts = content.split('<tool_sep>') %}\n {%- set processed_content = content_parts[0] %}\n {%- set tool_calls_count = message.tool_calls|length %}\n {%- set tool_sep_count = content_parts|length - 1 %}\n \n {%- for i in range(1, content_parts|length) %}\n {%- set tool_index = i - 1 %}\n {%- if tool_index < tool_calls_count %}\n {%- set tool_call = message.tool_calls[tool_index] %}\n {%- if tool_call.function %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {%- set single_tool_xml %}\n {{- '<function name=\"' ~ tool_call.name ~ '\">' }}\n {%- if tool_call.arguments %}\n {%- set args_dict = tool_call.arguments %}\n {%- for param_name, param_value in args_dict.items() %}\n {{- '<param name=\"' ~ param_name ~ '\">' }}\n {%- if param_value is string and ('<' in param_value or '&' in param_value or '\\n' in param_value) %}\n {{- '<![CDATA[' + param_value + ']]>' }}\n {%- else %}\n {{- param_value }}\n {%- endif %}\n {{- '</param>' }}\n {%- endfor %}\n {%- endif %}\n {{- '</function>' }}\n {%- endset %}\n {%- set processed_content = processed_content + single_tool_xml + content_parts[i] %}\n {%- else %}\n {%- set processed_content = processed_content + content_parts[i] %}\n {%- endif %}\n {%- endfor %}\n \n {%- if tool_calls_count > tool_sep_count %}\n {%- for remaining_index in range(tool_sep_count, tool_calls_count) %}\n {%- set tool_call = message.tool_calls[remaining_index] %}\n {%- if tool_call.function %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {%- set remaining_tool_xml %}\n {{- '<function name=\"' ~ tool_call.name ~ '\">' }}\n {%- if tool_call.arguments %}\n {%- set args_dict = tool_call.arguments %}\n {%- for param_name, param_value in args_dict.items() %}\n {{- '<param name=\"' ~ param_name ~ '\">' }}\n {%- if param_value is string and ('<' in param_value or '&' in param_value or '\\n' in param_value) %}\n {{- '<![CDATA[' + param_value + ']]>' }}\n {%- else %}\n {{- param_value }}\n {%- endif %}\n {{- '</param>' }}\n {%- endfor %}\n {%- endif %}\n {{- '</function>' }}\n {%- endset %}\n {%- set processed_content = processed_content + remaining_tool_xml %}\n {%- endfor %}\n {%- endif %}\n \n {%- set content = processed_content %}\n {%- endif %}\n \n {%- if reasoning_content %}\n {{- '<|im_start|>' + message.role + '\\n<think>\\n' + reasoning_content.strip('\\n') + '\\n</think>\\n\\n' + content.lstrip('\\n') }}\n {%- elif '<think>' not in content and '</think>' not in content %}\n {{- '<|im_start|>' + message.role + '\\n<think>\\n\\n</think>\\n\\n' + content.lstrip('\\n') }}\n {%- else %}\n {{- '<|im_start|>' + message.role + '\\n' + content }}\n {%- endif %}\n \n {%- if message.tool_calls and not has_tool_sep %}\n {%- for tool_call in message.tool_calls %}\n {%- if (loop.first and content) or (not loop.first) %}\n {{- '\\n' }}\n {%- endif %}\n {%- if tool_call.function %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '<function name=\"' ~ tool_call.name ~ '\">' }}\n {%- if tool_call.arguments %}\n {%- set args_dict = tool_call.arguments %}\n {%- for param_name, param_value in args_dict.items() %}\n {{- '<param name=\"' ~ param_name ~ '\">' }}\n {%- if param_value is string and ('<' in param_value or '&' in param_value or '\\n' in param_value) %}\n {{- '<![CDATA[' + param_value + ']]>' }}\n {%- else %}\n {{- param_value }}\n {%- endif %}\n {{- '</param>' }}\n {%- endfor %}\n {%- endif %}\n {{- '</function>' }}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if loop.first or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {%- if message.content is string %}\n {{- content }}\n {%- else %}\n {{- message.content | tojson(ensure_ascii=False) }}\n {%- endif %}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n {%- if enable_thinking is defined %}\n {%- if enable_thinking is false %}\n {{- '<think>\\n\\n</think>\\n\\n' }}\n {%- elif enable_thinking is true %}\n {{- '<think>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endif %}\n"
18
+ }
validation/cold-cache-results.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "status": "passed",
3
+ "modelNetworkDisabled": true,
4
+ "nativeToolCallExact": true,
5
+ "coldBrowserRestart": true,
6
+ "cacheBytes": 1879921408,
7
+ "browser": "150.0.7871.187",
8
+ "cases": [
9
+ {
10
+ "case": "single_address",
11
+ "contentSha256": "6fb7d867dc3ffc68a48c3d8ba62326085e54207791b1fe596ed1ccefe57520ce",
12
+ "unchanged": true
13
+ },
14
+ {
15
+ "case": "escaped_address",
16
+ "contentSha256": "bbca493756a904976f235188b871396fe327e0257118ca15b9a428b06fb30e52",
17
+ "unchanged": true
18
+ },
19
+ {
20
+ "case": "typed_labels",
21
+ "contentSha256": "400631740d1f4d911f6fb52e748a92e2700e4cbda20e7aae7c2a68f39530dcbb",
22
+ "unchanged": true
23
+ }
24
+ ],
25
+ "harnessSha256": "140c191d0f0b9cd50a6376deb2ca8e0042340a3f964915e192cff91f2fbef652",
26
+ "readySha256": "659375dbc84327f36a0de545aa1f769771f386a01e4a4d083707f9261530f395",
27
+ "eventsSha256": "6ed824b7f684f4f154b08805b2a191bfb66d0eccd7a91c54bfdb43874faa91d7",
28
+ "time": 1789231468.5665188
29
+ }
validation/context-results.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "status": "passed",
3
+ "contextWindowExercised": 16384,
4
+ "results": [
5
+ {
6
+ "inputTokens": 4088,
7
+ "generatedTokens": 8,
8
+ "finite": true,
9
+ "seconds": 1.199800000190735
10
+ },
11
+ {
12
+ "inputTokens": 8184,
13
+ "generatedTokens": 8,
14
+ "finite": true,
15
+ "seconds": 2.257800000190735
16
+ },
17
+ {
18
+ "inputTokens": 16376,
19
+ "generatedTokens": 8,
20
+ "finite": true,
21
+ "seconds": 5.950199999809265
22
+ },
23
+ {
24
+ "inputTokens": 16383,
25
+ "generatedTokens": 1,
26
+ "finite": true,
27
+ "seconds": 5.6705
28
+ }
29
+ ],
30
+ "implementationSha256": "6502ce8d72aa9f9a68d9fa8aa646543d1d6bcfa089db92658e4da3af3c2abd80"
31
+ }
validation/numerical-results.json ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "passed": true,
3
+ "checks": [
4
+ {
5
+ "case": "single_address",
6
+ "step": 0,
7
+ "sourceBf16ToFp16": {
8
+ "maxAbs": 0.248046875,
9
+ "relativeRms": 0.012782927668845466,
10
+ "cosine": 0.9999188460342593,
11
+ "top1Equal": true,
12
+ "finite": true
13
+ },
14
+ "fp16ToGptq": {
15
+ "maxAbs": 2.92236328125,
16
+ "relativeRms": 0.1725033061543679,
17
+ "cosine": 0.9851563643330277,
18
+ "top1Equal": true,
19
+ "finite": true
20
+ },
21
+ "gptqToWebgpu": {
22
+ "maxAbs": 0.0673828125,
23
+ "relativeRms": 0.0042797916585819926,
24
+ "cosine": 0.9999915840946518,
25
+ "top1Equal": true,
26
+ "finite": true
27
+ },
28
+ "passed": true
29
+ },
30
+ {
31
+ "case": "single_address",
32
+ "step": 1,
33
+ "sourceBf16ToFp16": {
34
+ "maxAbs": 0.392578125,
35
+ "relativeRms": 0.03175030043037405,
36
+ "cosine": 0.9995014526875671,
37
+ "top1Equal": true,
38
+ "finite": true
39
+ },
40
+ "fp16ToGptq": {
41
+ "maxAbs": 5.8984375,
42
+ "relativeRms": 0.4321884810039682,
43
+ "cosine": 0.9178090500009005,
44
+ "top1Equal": true,
45
+ "finite": true
46
+ },
47
+ "gptqToWebgpu": {
48
+ "maxAbs": 0.1185302734375,
49
+ "relativeRms": 0.009224104859662774,
50
+ "cosine": 0.9999598279641384,
51
+ "top1Equal": true,
52
+ "finite": true
53
+ },
54
+ "passed": true
55
+ },
56
+ {
57
+ "case": "single_address",
58
+ "step": 2,
59
+ "sourceBf16ToFp16": {
60
+ "maxAbs": 0.40625,
61
+ "relativeRms": 0.024433671533829995,
62
+ "cosine": 0.9998189972088237,
63
+ "top1Equal": true,
64
+ "finite": true
65
+ },
66
+ "fp16ToGptq": {
67
+ "maxAbs": 4.21875,
68
+ "relativeRms": 0.15488227828082832,
69
+ "cosine": 0.9880322921705068,
70
+ "top1Equal": true,
71
+ "finite": true
72
+ },
73
+ "gptqToWebgpu": {
74
+ "maxAbs": 0.125,
75
+ "relativeRms": 0.00558969882051299,
76
+ "cosine": 0.9999846162524721,
77
+ "top1Equal": true,
78
+ "finite": true
79
+ },
80
+ "passed": true
81
+ },
82
+ {
83
+ "case": "single_address",
84
+ "step": 3,
85
+ "sourceBf16ToFp16": {
86
+ "maxAbs": 0.244140625,
87
+ "relativeRms": 0.02230636952290329,
88
+ "cosine": 0.9997514739004472,
89
+ "top1Equal": true,
90
+ "finite": true
91
+ },
92
+ "fp16ToGptq": {
93
+ "maxAbs": 2.0703125,
94
+ "relativeRms": 0.15789749529090216,
95
+ "cosine": 0.9876037120121298,
96
+ "top1Equal": true,
97
+ "finite": true
98
+ },
99
+ "gptqToWebgpu": {
100
+ "maxAbs": 0.0888671875,
101
+ "relativeRms": 0.00605211109920661,
102
+ "cosine": 0.9999817012220191,
103
+ "top1Equal": true,
104
+ "finite": true
105
+ },
106
+ "passed": true
107
+ },
108
+ {
109
+ "case": "typed_labels",
110
+ "step": 0,
111
+ "sourceBf16ToFp16": {
112
+ "maxAbs": 0.38671875,
113
+ "relativeRms": 0.025063363075193648,
114
+ "cosine": 0.9997845808098521,
115
+ "top1Equal": true,
116
+ "finite": true
117
+ },
118
+ "fp16ToGptq": {
119
+ "maxAbs": 2.6171875,
120
+ "relativeRms": 0.1662042144350609,
121
+ "cosine": 0.9868052467412282,
122
+ "top1Equal": true,
123
+ "finite": true
124
+ },
125
+ "gptqToWebgpu": {
126
+ "maxAbs": 0.078125,
127
+ "relativeRms": 0.005452968305617882,
128
+ "cosine": 0.9999875200694265,
129
+ "top1Equal": true,
130
+ "finite": true
131
+ },
132
+ "passed": true
133
+ },
134
+ {
135
+ "case": "typed_labels",
136
+ "step": 1,
137
+ "sourceBf16ToFp16": {
138
+ "maxAbs": 0.380859375,
139
+ "relativeRms": 0.0250109210315334,
140
+ "cosine": 0.9997064286834567,
141
+ "top1Equal": true,
142
+ "finite": true
143
+ },
144
+ "fp16ToGptq": {
145
+ "maxAbs": 7.640625,
146
+ "relativeRms": 0.57221657649197,
147
+ "cosine": 0.8201217343475685,
148
+ "top1Equal": true,
149
+ "finite": true
150
+ },
151
+ "gptqToWebgpu": {
152
+ "maxAbs": 0.212890625,
153
+ "relativeRms": 0.018333812515897996,
154
+ "cosine": 0.999833960131897,
155
+ "top1Equal": true,
156
+ "finite": true
157
+ },
158
+ "passed": true
159
+ },
160
+ {
161
+ "case": "typed_labels",
162
+ "step": 2,
163
+ "sourceBf16ToFp16": {
164
+ "maxAbs": 0.345703125,
165
+ "relativeRms": 0.018745048201074564,
166
+ "cosine": 0.9998726263349046,
167
+ "top1Equal": true,
168
+ "finite": true
169
+ },
170
+ "fp16ToGptq": {
171
+ "maxAbs": 4.439453125,
172
+ "relativeRms": 0.19829943940799244,
173
+ "cosine": 0.9824647962089508,
174
+ "top1Equal": true,
175
+ "finite": true
176
+ },
177
+ "gptqToWebgpu": {
178
+ "maxAbs": 0.1796875,
179
+ "relativeRms": 0.006915722281827882,
180
+ "cosine": 0.9999793827494786,
181
+ "top1Equal": true,
182
+ "finite": true
183
+ },
184
+ "passed": true
185
+ },
186
+ {
187
+ "case": "typed_labels",
188
+ "step": 3,
189
+ "sourceBf16ToFp16": {
190
+ "maxAbs": 0.234375,
191
+ "relativeRms": 0.018754406808742356,
192
+ "cosine": 0.999827896719525,
193
+ "top1Equal": true,
194
+ "finite": true
195
+ },
196
+ "fp16ToGptq": {
197
+ "maxAbs": 2.617431640625,
198
+ "relativeRms": 0.186232702205544,
199
+ "cosine": 0.9827491247292427,
200
+ "top1Equal": true,
201
+ "finite": true
202
+ },
203
+ "gptqToWebgpu": {
204
+ "maxAbs": 0.076171875,
205
+ "relativeRms": 0.005799131539776698,
206
+ "cosine": 0.9999832046335031,
207
+ "top1Equal": true,
208
+ "finite": true
209
+ },
210
+ "passed": true
211
+ },
212
+ {
213
+ "case": "tool_history",
214
+ "step": 0,
215
+ "sourceBf16ToFp16": {
216
+ "maxAbs": 0.1943359375,
217
+ "relativeRms": 0.011942814015739078,
218
+ "cosine": 0.9999351512834989,
219
+ "top1Equal": true,
220
+ "finite": true
221
+ },
222
+ "fp16ToGptq": {
223
+ "maxAbs": 3.724609375,
224
+ "relativeRms": 0.1542023887030577,
225
+ "cosine": 0.9880457221982332,
226
+ "top1Equal": true,
227
+ "finite": true
228
+ },
229
+ "gptqToWebgpu": {
230
+ "maxAbs": 0.07421875,
231
+ "relativeRms": 0.004467858699488606,
232
+ "cosine": 0.9999902288828103,
233
+ "top1Equal": true,
234
+ "finite": true
235
+ },
236
+ "passed": true
237
+ },
238
+ {
239
+ "case": "tool_history",
240
+ "step": 1,
241
+ "sourceBf16ToFp16": {
242
+ "maxAbs": 0.55859375,
243
+ "relativeRms": 0.028931119415106666,
244
+ "cosine": 0.9995831520949008,
245
+ "top1Equal": true,
246
+ "finite": true
247
+ },
248
+ "fp16ToGptq": {
249
+ "maxAbs": 5.9765625,
250
+ "relativeRms": 0.3487054531078975,
251
+ "cosine": 0.9377851831391082,
252
+ "top1Equal": true,
253
+ "finite": true
254
+ },
255
+ "gptqToWebgpu": {
256
+ "maxAbs": 0.14453125,
257
+ "relativeRms": 0.01074293845406932,
258
+ "cosine": 0.9999440250098686,
259
+ "top1Equal": true,
260
+ "finite": true
261
+ },
262
+ "passed": true
263
+ },
264
+ {
265
+ "case": "tool_history",
266
+ "step": 2,
267
+ "sourceBf16ToFp16": {
268
+ "maxAbs": 0.3828125,
269
+ "relativeRms": 0.016834366518147053,
270
+ "cosine": 0.9998583938821983,
271
+ "top1Equal": true,
272
+ "finite": true
273
+ },
274
+ "fp16ToGptq": {
275
+ "maxAbs": 5.638671875,
276
+ "relativeRms": 0.3736643910497181,
277
+ "cosine": 0.9318102942626478,
278
+ "top1Equal": true,
279
+ "finite": true
280
+ },
281
+ "gptqToWebgpu": {
282
+ "maxAbs": 0.1328125,
283
+ "relativeRms": 0.007678886224809575,
284
+ "cosine": 0.9999712093288873,
285
+ "top1Equal": true,
286
+ "finite": true
287
+ },
288
+ "passed": true
289
+ },
290
+ {
291
+ "case": "tool_history",
292
+ "step": 3,
293
+ "sourceBf16ToFp16": {
294
+ "maxAbs": 0.328125,
295
+ "relativeRms": 0.030876288162470202,
296
+ "cosine": 0.9995263219302111,
297
+ "top1Equal": true,
298
+ "finite": true
299
+ },
300
+ "fp16ToGptq": {
301
+ "maxAbs": 2.5712890625,
302
+ "relativeRms": 0.2003064180768053,
303
+ "cosine": 0.9805788883889465,
304
+ "top1Equal": true,
305
+ "finite": true
306
+ },
307
+ "gptqToWebgpu": {
308
+ "maxAbs": 0.09375,
309
+ "relativeRms": 0.006090321309410876,
310
+ "cosine": 0.9999814728684856,
311
+ "top1Equal": true,
312
+ "finite": true
313
+ },
314
+ "passed": true
315
+ }
316
+ ],
317
+ "packagedProofSha256": "dc91e19a96db79d51d77c15c1e03f755249021ae0fbf3403e4606ed877a664a1",
318
+ "uploadAllowed": false
319
+ }
validation/parser-results.json ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "responseCount": 194,
3
+ "packageParserSha256": "7a33bbcd31cc3910a169e735541c4d5189f85480237577f262de3154ce91f12d",
4
+ "optionalHelperSha256": "0f1bf7265771ec5cf73147c299c52490086579bb2d2c9885cbddda7e067322e3",
5
+ "bundledMatches": 162,
6
+ "bundledMismatchCount": 32,
7
+ "optionalHelperMatches": 194,
8
+ "optionalHelperMismatchCount": 0,
9
+ "optionalHelperAllowlistFailures": 0,
10
+ "smoke": [
11
+ {
12
+ "name": "single_address",
13
+ "sameTypedArguments": true,
14
+ "bundled": {
15
+ "calls": [
16
+ {
17
+ "name": "open_address",
18
+ "args": {
19
+ "address": "https://example.net/guide"
20
+ }
21
+ }
22
+ ]
23
+ },
24
+ "helper": {
25
+ "calls": [
26
+ {
27
+ "name": "open_address",
28
+ "args": {
29
+ "address": "https://example.net/guide"
30
+ }
31
+ }
32
+ ]
33
+ }
34
+ },
35
+ {
36
+ "name": "escaped_address",
37
+ "sameTypedArguments": true,
38
+ "bundled": {
39
+ "calls": [
40
+ {
41
+ "name": "open_address",
42
+ "args": {
43
+ "address": "https://example.net/find?name=blue&sort=recent"
44
+ }
45
+ }
46
+ ]
47
+ },
48
+ "helper": {
49
+ "calls": [
50
+ {
51
+ "name": "open_address",
52
+ "args": {
53
+ "address": "https://example.net/find?name=blue&sort=recent"
54
+ }
55
+ }
56
+ ]
57
+ }
58
+ },
59
+ {
60
+ "name": "typed_labels",
61
+ "sameTypedArguments": false,
62
+ "bundled": {
63
+ "calls": [
64
+ {
65
+ "name": "save_labels",
66
+ "args": {
67
+ "labels": "['green', 'amber']",
68
+ "enabled": "False"
69
+ }
70
+ }
71
+ ]
72
+ },
73
+ "helper": {
74
+ "calls": [
75
+ {
76
+ "name": "save_labels",
77
+ "args": {
78
+ "labels": [
79
+ "green",
80
+ "amber"
81
+ ],
82
+ "enabled": false
83
+ }
84
+ }
85
+ ]
86
+ }
87
+ }
88
+ ],
89
+ "originalAuditSha256": "14e167d6a11fb7b87b93dbfcd1d94585a4ab79479bb2e058fa9ec5d5ebfe3030",
90
+ "optionalHelperAutomaticallyIntegrated": false,
91
+ "noToolsExecuted": true
92
+ }