nico-martin HF Staff commited on
Commit
ec2237a
Β·
1 Parent(s): 3624cee

added pythonic tool calls

Browse files
dist/assets/{index-DxhcZCe7.js β†’ index-DU4-Hcde.js} RENAMED
The diff for this file is too large to render. See raw diff
 
dist/assets/{textGenerationWorker-ns1jR_Qy.js β†’ textGenerationWorker-CYbgGXFl.js} RENAMED
The diff for this file is too large to render. See raw diff
 
dist/index.html CHANGED
@@ -11,7 +11,7 @@
11
  rel="stylesheet"
12
  />
13
  <title>Transformers.js TextGeneration</title>
14
- <script type="module" crossorigin src="/assets/index-DxhcZCe7.js"></script>
15
  <link rel="stylesheet" crossorigin href="/assets/index-CYRNQXs7.css">
16
  </head>
17
  <body>
 
11
  rel="stylesheet"
12
  />
13
  <title>Transformers.js TextGeneration</title>
14
+ <script type="module" crossorigin src="/assets/index-DU4-Hcde.js"></script>
15
  <link rel="stylesheet" crossorigin href="/assets/index-CYRNQXs7.css">
16
  </head>
17
  <body>
package-lock.json CHANGED
@@ -40,7 +40,8 @@
40
  "typescript-eslint": "^8.44.0",
41
  "vite": "^7.1.7",
42
  "vite-plugin-html-config": "^2.0.2",
43
- "vite-tsconfig-paths": "^5.1.4"
 
44
  }
45
  },
46
  "../../transformers.js": {
@@ -75,10 +76,8 @@
75
  "license": "Apache-2.0",
76
  "dependencies": {
77
  "@huggingface/jinja": "^0.5.1",
78
- "esbuild": "^0.27.0",
79
  "onnxruntime-node": "1.24.0-dev.20251104-75d35474d5",
80
  "onnxruntime-web": "1.24.0-dev.20251104-75d35474d5",
81
- "rimraf": "^6.1.2",
82
  "sharp": "^0.34.3"
83
  },
84
  "devDependencies": {
@@ -90,7 +89,10 @@
90
  "jsdoc-to-markdown": "^9.1.1",
91
  "prettier": "3.4.2",
92
  "typescript": "^5.8.3",
93
- "wavefile": "11.0.0"
 
 
 
94
  }
95
  },
96
  "node_modules/@babel/code-frame": {
@@ -1490,6 +1492,13 @@
1490
  "win32"
1491
  ]
1492
  },
 
 
 
 
 
 
 
1493
  "node_modules/@tailwindcss/node": {
1494
  "version": "4.1.13",
1495
  "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz",
@@ -1870,6 +1879,24 @@
1870
  "@babel/types": "^7.28.2"
1871
  }
1872
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1873
  "node_modules/@types/estree": {
1874
  "version": "1.0.8",
1875
  "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -2213,6 +2240,117 @@
2213
  "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
2214
  }
2215
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2216
  "node_modules/acorn": {
2217
  "version": "8.15.0",
2218
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -2276,6 +2414,16 @@
2276
  "dev": true,
2277
  "license": "Python-2.0"
2278
  },
 
 
 
 
 
 
 
 
 
 
2279
  "node_modules/autoprefixer": {
2280
  "version": "10.4.21",
2281
  "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
@@ -2420,6 +2568,16 @@
2420
  ],
2421
  "license": "CC-BY-4.0"
2422
  },
 
 
 
 
 
 
 
 
 
 
2423
  "node_modules/chalk": {
2424
  "version": "4.1.2",
2425
  "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -2593,6 +2751,13 @@
2593
  "node": ">=10.13.0"
2594
  }
2595
  },
 
 
 
 
 
 
 
2596
  "node_modules/esbuild": {
2597
  "version": "0.25.10",
2598
  "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz",
@@ -2826,6 +2991,16 @@
2826
  "node": ">=4.0"
2827
  }
2828
  },
 
 
 
 
 
 
 
 
 
 
2829
  "node_modules/esutils": {
2830
  "version": "2.0.3",
2831
  "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -2836,6 +3011,16 @@
2836
  "node": ">=0.10.0"
2837
  }
2838
  },
 
 
 
 
 
 
 
 
 
 
2839
  "node_modules/fast-deep-equal": {
2840
  "version": "3.1.3",
2841
  "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -3556,9 +3741,9 @@
3556
  }
3557
  },
3558
  "node_modules/magic-string": {
3559
- "version": "0.30.19",
3560
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
3561
- "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
3562
  "dev": true,
3563
  "license": "MIT",
3564
  "dependencies": {
@@ -3675,6 +3860,17 @@
3675
  "node": ">=0.10.0"
3676
  }
3677
  },
 
 
 
 
 
 
 
 
 
 
 
3678
  "node_modules/optionator": {
3679
  "version": "0.9.4",
3680
  "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -3758,6 +3954,13 @@
3758
  "node": ">=8"
3759
  }
3760
  },
 
 
 
 
 
 
 
3761
  "node_modules/picocolors": {
3762
  "version": "1.1.1",
3763
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -4160,6 +4363,13 @@
4160
  "url": "https://www.paypal.me/tiviesantos"
4161
  }
4162
  },
 
 
 
 
 
 
 
4163
  "node_modules/source-map-js": {
4164
  "version": "1.2.1",
4165
  "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -4170,6 +4380,20 @@
4170
  "node": ">=0.10.0"
4171
  }
4172
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4173
  "node_modules/strip-json-comments": {
4174
  "version": "3.1.1",
4175
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -4244,6 +4468,23 @@
4244
  "node": ">=18"
4245
  }
4246
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4247
  "node_modules/tinyglobby": {
4248
  "version": "0.2.15",
4249
  "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -4274,6 +4515,16 @@
4274
  "url": "https://github.com/sponsors/jonschlinkert"
4275
  }
4276
  },
 
 
 
 
 
 
 
 
 
 
4277
  "node_modules/to-regex-range": {
4278
  "version": "5.0.1",
4279
  "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -4555,6 +4806,97 @@
4555
  "url": "https://github.com/sponsors/jonschlinkert"
4556
  }
4557
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4558
  "node_modules/which": {
4559
  "version": "2.0.2",
4560
  "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -4571,6 +4913,23 @@
4571
  "node": ">= 8"
4572
  }
4573
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4574
  "node_modules/word-wrap": {
4575
  "version": "1.2.5",
4576
  "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
 
40
  "typescript-eslint": "^8.44.0",
41
  "vite": "^7.1.7",
42
  "vite-plugin-html-config": "^2.0.2",
43
+ "vite-tsconfig-paths": "^5.1.4",
44
+ "vitest": "^4.0.15"
45
  }
46
  },
47
  "../../transformers.js": {
 
76
  "license": "Apache-2.0",
77
  "dependencies": {
78
  "@huggingface/jinja": "^0.5.1",
 
79
  "onnxruntime-node": "1.24.0-dev.20251104-75d35474d5",
80
  "onnxruntime-web": "1.24.0-dev.20251104-75d35474d5",
 
81
  "sharp": "^0.34.3"
82
  },
83
  "devDependencies": {
 
89
  "jsdoc-to-markdown": "^9.1.1",
90
  "prettier": "3.4.2",
91
  "typescript": "^5.8.3",
92
+ "wavefile": "11.0.0",
93
+ "webpack": "^5.99.9",
94
+ "webpack-cli": "^6.0.1",
95
+ "webpack-dev-server": "^5.2.2"
96
  }
97
  },
98
  "node_modules/@babel/code-frame": {
 
1492
  "win32"
1493
  ]
1494
  },
1495
+ "node_modules/@standard-schema/spec": {
1496
+ "version": "1.0.0",
1497
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
1498
+ "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
1499
+ "dev": true,
1500
+ "license": "MIT"
1501
+ },
1502
  "node_modules/@tailwindcss/node": {
1503
  "version": "4.1.13",
1504
  "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz",
 
1879
  "@babel/types": "^7.28.2"
1880
  }
1881
  },
1882
+ "node_modules/@types/chai": {
1883
+ "version": "5.2.3",
1884
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
1885
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
1886
+ "dev": true,
1887
+ "license": "MIT",
1888
+ "dependencies": {
1889
+ "@types/deep-eql": "*",
1890
+ "assertion-error": "^2.0.1"
1891
+ }
1892
+ },
1893
+ "node_modules/@types/deep-eql": {
1894
+ "version": "4.0.2",
1895
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
1896
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
1897
+ "dev": true,
1898
+ "license": "MIT"
1899
+ },
1900
  "node_modules/@types/estree": {
1901
  "version": "1.0.8",
1902
  "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
 
2240
  "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
2241
  }
2242
  },
2243
+ "node_modules/@vitest/expect": {
2244
+ "version": "4.0.15",
2245
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.15.tgz",
2246
+ "integrity": "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==",
2247
+ "dev": true,
2248
+ "license": "MIT",
2249
+ "dependencies": {
2250
+ "@standard-schema/spec": "^1.0.0",
2251
+ "@types/chai": "^5.2.2",
2252
+ "@vitest/spy": "4.0.15",
2253
+ "@vitest/utils": "4.0.15",
2254
+ "chai": "^6.2.1",
2255
+ "tinyrainbow": "^3.0.3"
2256
+ },
2257
+ "funding": {
2258
+ "url": "https://opencollective.com/vitest"
2259
+ }
2260
+ },
2261
+ "node_modules/@vitest/mocker": {
2262
+ "version": "4.0.15",
2263
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.15.tgz",
2264
+ "integrity": "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==",
2265
+ "dev": true,
2266
+ "license": "MIT",
2267
+ "dependencies": {
2268
+ "@vitest/spy": "4.0.15",
2269
+ "estree-walker": "^3.0.3",
2270
+ "magic-string": "^0.30.21"
2271
+ },
2272
+ "funding": {
2273
+ "url": "https://opencollective.com/vitest"
2274
+ },
2275
+ "peerDependencies": {
2276
+ "msw": "^2.4.9",
2277
+ "vite": "^6.0.0 || ^7.0.0-0"
2278
+ },
2279
+ "peerDependenciesMeta": {
2280
+ "msw": {
2281
+ "optional": true
2282
+ },
2283
+ "vite": {
2284
+ "optional": true
2285
+ }
2286
+ }
2287
+ },
2288
+ "node_modules/@vitest/pretty-format": {
2289
+ "version": "4.0.15",
2290
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.15.tgz",
2291
+ "integrity": "sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==",
2292
+ "dev": true,
2293
+ "license": "MIT",
2294
+ "dependencies": {
2295
+ "tinyrainbow": "^3.0.3"
2296
+ },
2297
+ "funding": {
2298
+ "url": "https://opencollective.com/vitest"
2299
+ }
2300
+ },
2301
+ "node_modules/@vitest/runner": {
2302
+ "version": "4.0.15",
2303
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.15.tgz",
2304
+ "integrity": "sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==",
2305
+ "dev": true,
2306
+ "license": "MIT",
2307
+ "dependencies": {
2308
+ "@vitest/utils": "4.0.15",
2309
+ "pathe": "^2.0.3"
2310
+ },
2311
+ "funding": {
2312
+ "url": "https://opencollective.com/vitest"
2313
+ }
2314
+ },
2315
+ "node_modules/@vitest/snapshot": {
2316
+ "version": "4.0.15",
2317
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.15.tgz",
2318
+ "integrity": "sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==",
2319
+ "dev": true,
2320
+ "license": "MIT",
2321
+ "dependencies": {
2322
+ "@vitest/pretty-format": "4.0.15",
2323
+ "magic-string": "^0.30.21",
2324
+ "pathe": "^2.0.3"
2325
+ },
2326
+ "funding": {
2327
+ "url": "https://opencollective.com/vitest"
2328
+ }
2329
+ },
2330
+ "node_modules/@vitest/spy": {
2331
+ "version": "4.0.15",
2332
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.15.tgz",
2333
+ "integrity": "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==",
2334
+ "dev": true,
2335
+ "license": "MIT",
2336
+ "funding": {
2337
+ "url": "https://opencollective.com/vitest"
2338
+ }
2339
+ },
2340
+ "node_modules/@vitest/utils": {
2341
+ "version": "4.0.15",
2342
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.15.tgz",
2343
+ "integrity": "sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==",
2344
+ "dev": true,
2345
+ "license": "MIT",
2346
+ "dependencies": {
2347
+ "@vitest/pretty-format": "4.0.15",
2348
+ "tinyrainbow": "^3.0.3"
2349
+ },
2350
+ "funding": {
2351
+ "url": "https://opencollective.com/vitest"
2352
+ }
2353
+ },
2354
  "node_modules/acorn": {
2355
  "version": "8.15.0",
2356
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
 
2414
  "dev": true,
2415
  "license": "Python-2.0"
2416
  },
2417
+ "node_modules/assertion-error": {
2418
+ "version": "2.0.1",
2419
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
2420
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
2421
+ "dev": true,
2422
+ "license": "MIT",
2423
+ "engines": {
2424
+ "node": ">=12"
2425
+ }
2426
+ },
2427
  "node_modules/autoprefixer": {
2428
  "version": "10.4.21",
2429
  "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
 
2568
  ],
2569
  "license": "CC-BY-4.0"
2570
  },
2571
+ "node_modules/chai": {
2572
+ "version": "6.2.1",
2573
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz",
2574
+ "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==",
2575
+ "dev": true,
2576
+ "license": "MIT",
2577
+ "engines": {
2578
+ "node": ">=18"
2579
+ }
2580
+ },
2581
  "node_modules/chalk": {
2582
  "version": "4.1.2",
2583
  "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
 
2751
  "node": ">=10.13.0"
2752
  }
2753
  },
2754
+ "node_modules/es-module-lexer": {
2755
+ "version": "1.7.0",
2756
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
2757
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
2758
+ "dev": true,
2759
+ "license": "MIT"
2760
+ },
2761
  "node_modules/esbuild": {
2762
  "version": "0.25.10",
2763
  "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz",
 
2991
  "node": ">=4.0"
2992
  }
2993
  },
2994
+ "node_modules/estree-walker": {
2995
+ "version": "3.0.3",
2996
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
2997
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
2998
+ "dev": true,
2999
+ "license": "MIT",
3000
+ "dependencies": {
3001
+ "@types/estree": "^1.0.0"
3002
+ }
3003
+ },
3004
  "node_modules/esutils": {
3005
  "version": "2.0.3",
3006
  "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
 
3011
  "node": ">=0.10.0"
3012
  }
3013
  },
3014
+ "node_modules/expect-type": {
3015
+ "version": "1.2.2",
3016
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz",
3017
+ "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==",
3018
+ "dev": true,
3019
+ "license": "Apache-2.0",
3020
+ "engines": {
3021
+ "node": ">=12.0.0"
3022
+ }
3023
+ },
3024
  "node_modules/fast-deep-equal": {
3025
  "version": "3.1.3",
3026
  "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
 
3741
  }
3742
  },
3743
  "node_modules/magic-string": {
3744
+ "version": "0.30.21",
3745
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
3746
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
3747
  "dev": true,
3748
  "license": "MIT",
3749
  "dependencies": {
 
3860
  "node": ">=0.10.0"
3861
  }
3862
  },
3863
+ "node_modules/obug": {
3864
+ "version": "2.1.1",
3865
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
3866
+ "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
3867
+ "dev": true,
3868
+ "funding": [
3869
+ "https://github.com/sponsors/sxzz",
3870
+ "https://opencollective.com/debug"
3871
+ ],
3872
+ "license": "MIT"
3873
+ },
3874
  "node_modules/optionator": {
3875
  "version": "0.9.4",
3876
  "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
 
3954
  "node": ">=8"
3955
  }
3956
  },
3957
+ "node_modules/pathe": {
3958
+ "version": "2.0.3",
3959
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
3960
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
3961
+ "dev": true,
3962
+ "license": "MIT"
3963
+ },
3964
  "node_modules/picocolors": {
3965
  "version": "1.1.1",
3966
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
 
4363
  "url": "https://www.paypal.me/tiviesantos"
4364
  }
4365
  },
4366
+ "node_modules/siginfo": {
4367
+ "version": "2.0.0",
4368
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
4369
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
4370
+ "dev": true,
4371
+ "license": "ISC"
4372
+ },
4373
  "node_modules/source-map-js": {
4374
  "version": "1.2.1",
4375
  "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
 
4380
  "node": ">=0.10.0"
4381
  }
4382
  },
4383
+ "node_modules/stackback": {
4384
+ "version": "0.0.2",
4385
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
4386
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
4387
+ "dev": true,
4388
+ "license": "MIT"
4389
+ },
4390
+ "node_modules/std-env": {
4391
+ "version": "3.10.0",
4392
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
4393
+ "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
4394
+ "dev": true,
4395
+ "license": "MIT"
4396
+ },
4397
  "node_modules/strip-json-comments": {
4398
  "version": "3.1.1",
4399
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
 
4468
  "node": ">=18"
4469
  }
4470
  },
4471
+ "node_modules/tinybench": {
4472
+ "version": "2.9.0",
4473
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
4474
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
4475
+ "dev": true,
4476
+ "license": "MIT"
4477
+ },
4478
+ "node_modules/tinyexec": {
4479
+ "version": "1.0.2",
4480
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
4481
+ "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
4482
+ "dev": true,
4483
+ "license": "MIT",
4484
+ "engines": {
4485
+ "node": ">=18"
4486
+ }
4487
+ },
4488
  "node_modules/tinyglobby": {
4489
  "version": "0.2.15",
4490
  "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
 
4515
  "url": "https://github.com/sponsors/jonschlinkert"
4516
  }
4517
  },
4518
+ "node_modules/tinyrainbow": {
4519
+ "version": "3.0.3",
4520
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
4521
+ "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==",
4522
+ "dev": true,
4523
+ "license": "MIT",
4524
+ "engines": {
4525
+ "node": ">=14.0.0"
4526
+ }
4527
+ },
4528
  "node_modules/to-regex-range": {
4529
  "version": "5.0.1",
4530
  "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
 
4806
  "url": "https://github.com/sponsors/jonschlinkert"
4807
  }
4808
  },
4809
+ "node_modules/vitest": {
4810
+ "version": "4.0.15",
4811
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.15.tgz",
4812
+ "integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==",
4813
+ "dev": true,
4814
+ "license": "MIT",
4815
+ "dependencies": {
4816
+ "@vitest/expect": "4.0.15",
4817
+ "@vitest/mocker": "4.0.15",
4818
+ "@vitest/pretty-format": "4.0.15",
4819
+ "@vitest/runner": "4.0.15",
4820
+ "@vitest/snapshot": "4.0.15",
4821
+ "@vitest/spy": "4.0.15",
4822
+ "@vitest/utils": "4.0.15",
4823
+ "es-module-lexer": "^1.7.0",
4824
+ "expect-type": "^1.2.2",
4825
+ "magic-string": "^0.30.21",
4826
+ "obug": "^2.1.1",
4827
+ "pathe": "^2.0.3",
4828
+ "picomatch": "^4.0.3",
4829
+ "std-env": "^3.10.0",
4830
+ "tinybench": "^2.9.0",
4831
+ "tinyexec": "^1.0.2",
4832
+ "tinyglobby": "^0.2.15",
4833
+ "tinyrainbow": "^3.0.3",
4834
+ "vite": "^6.0.0 || ^7.0.0",
4835
+ "why-is-node-running": "^2.3.0"
4836
+ },
4837
+ "bin": {
4838
+ "vitest": "vitest.mjs"
4839
+ },
4840
+ "engines": {
4841
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
4842
+ },
4843
+ "funding": {
4844
+ "url": "https://opencollective.com/vitest"
4845
+ },
4846
+ "peerDependencies": {
4847
+ "@edge-runtime/vm": "*",
4848
+ "@opentelemetry/api": "^1.9.0",
4849
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
4850
+ "@vitest/browser-playwright": "4.0.15",
4851
+ "@vitest/browser-preview": "4.0.15",
4852
+ "@vitest/browser-webdriverio": "4.0.15",
4853
+ "@vitest/ui": "4.0.15",
4854
+ "happy-dom": "*",
4855
+ "jsdom": "*"
4856
+ },
4857
+ "peerDependenciesMeta": {
4858
+ "@edge-runtime/vm": {
4859
+ "optional": true
4860
+ },
4861
+ "@opentelemetry/api": {
4862
+ "optional": true
4863
+ },
4864
+ "@types/node": {
4865
+ "optional": true
4866
+ },
4867
+ "@vitest/browser-playwright": {
4868
+ "optional": true
4869
+ },
4870
+ "@vitest/browser-preview": {
4871
+ "optional": true
4872
+ },
4873
+ "@vitest/browser-webdriverio": {
4874
+ "optional": true
4875
+ },
4876
+ "@vitest/ui": {
4877
+ "optional": true
4878
+ },
4879
+ "happy-dom": {
4880
+ "optional": true
4881
+ },
4882
+ "jsdom": {
4883
+ "optional": true
4884
+ }
4885
+ }
4886
+ },
4887
+ "node_modules/vitest/node_modules/picomatch": {
4888
+ "version": "4.0.3",
4889
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
4890
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
4891
+ "dev": true,
4892
+ "license": "MIT",
4893
+ "engines": {
4894
+ "node": ">=12"
4895
+ },
4896
+ "funding": {
4897
+ "url": "https://github.com/sponsors/jonschlinkert"
4898
+ }
4899
+ },
4900
  "node_modules/which": {
4901
  "version": "2.0.2",
4902
  "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
 
4913
  "node": ">= 8"
4914
  }
4915
  },
4916
+ "node_modules/why-is-node-running": {
4917
+ "version": "2.3.0",
4918
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
4919
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
4920
+ "dev": true,
4921
+ "license": "MIT",
4922
+ "dependencies": {
4923
+ "siginfo": "^2.0.0",
4924
+ "stackback": "0.0.2"
4925
+ },
4926
+ "bin": {
4927
+ "why-is-node-running": "cli.js"
4928
+ },
4929
+ "engines": {
4930
+ "node": ">=8"
4931
+ }
4932
+ },
4933
  "node_modules/word-wrap": {
4934
  "version": "1.2.5",
4935
  "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
package.json CHANGED
@@ -7,7 +7,8 @@
7
  "dev": "vite",
8
  "build": "tsc -b && vite build",
9
  "lint": "eslint .",
10
- "preview": "vite preview"
 
11
  },
12
  "dependencies": {
13
  "@huggingface/transformers": "../transformers.js",
@@ -42,6 +43,7 @@
42
  "typescript-eslint": "^8.44.0",
43
  "vite": "^7.1.7",
44
  "vite-plugin-html-config": "^2.0.2",
45
- "vite-tsconfig-paths": "^5.1.4"
 
46
  }
47
  }
 
7
  "dev": "vite",
8
  "build": "tsc -b && vite build",
9
  "lint": "eslint .",
10
+ "preview": "vite preview",
11
+ "test": "vitest"
12
  },
13
  "dependencies": {
14
  "@huggingface/transformers": "../transformers.js",
 
43
  "typescript-eslint": "^8.44.0",
44
  "vite": "^7.1.7",
45
  "vite-plugin-html-config": "^2.0.2",
46
+ "vite-tsconfig-paths": "^5.1.4",
47
+ "vitest": "^4.0.15"
48
  }
49
  }
src/chat/ChatForm.tsx CHANGED
@@ -22,7 +22,7 @@ export default function ChatForm({
22
  }) {
23
  const { control, handleSubmit, reset } = useForm<FormParams>({
24
  defaultValues: {
25
- input: "",
26
  },
27
  });
28
 
 
22
  }) {
23
  const { control, handleSubmit, reset } = useForm<FormParams>({
24
  defaultValues: {
25
+ input: "Whats the current time?",
26
  },
27
  });
28
 
src/textGeneration/TextGeneration.ts CHANGED
@@ -1,11 +1,11 @@
1
  import { type Message } from "@huggingface/transformers";
2
-
3
  import {
4
- type WebMCPTool,
5
  executeToolCall,
6
  splitResponse,
7
  webMCPToolToChatTemplateTool,
8
- } from "../utils/webMcp.ts";
 
 
9
  import {
10
  type ChatMessage,
11
  type ChatMessageAssistant,
 
1
  import { type Message } from "@huggingface/transformers";
 
2
  import {
 
3
  executeToolCall,
4
  splitResponse,
5
  webMCPToolToChatTemplateTool,
6
+ } from "@utils/webMcp";
7
+ import type { WebMCPTool } from "@utils/webMcp/types.ts";
8
+
9
  import {
10
  type ChatMessage,
11
  type ChatMessageAssistant,
src/textGeneration/types.ts CHANGED
@@ -1,7 +1,10 @@
1
  import { type Message } from "@huggingface/transformers";
2
 
3
  import type { MODELS } from "../utils/models.ts";
4
- import type { ChatTemplateTool, ToolCallPayload } from "../utils/webMcp.ts";
 
 
 
5
 
6
  export enum RequestType {
7
  INITIALIZE_MODEL,
 
1
  import { type Message } from "@huggingface/transformers";
2
 
3
  import type { MODELS } from "../utils/models.ts";
4
+ import type {
5
+ ChatTemplateTool,
6
+ ToolCallPayload,
7
+ } from "../utils/webMcp/types.ts";
8
 
9
  export enum RequestType {
10
  INITIALIZE_MODEL,
src/textGeneration/worker/textGenerationWorker.ts CHANGED
@@ -121,6 +121,9 @@ self.onmessage = async ({ data }: MessageEvent<Request>) => {
121
  let numTokens = 0;
122
  let tps: number = 0;
123
 
 
 
 
124
  const tokenCallbackFunction = () => {
125
  firstTokenTime ??= performance.now();
126
  if (numTokens++ > 0) {
@@ -128,16 +131,17 @@ self.onmessage = async ({ data }: MessageEvent<Request>) => {
128
  }
129
  };
130
 
131
- const callbackFunction = (chunk: string) =>
132
  postMessage({
133
  type: ResponseType.GENERATE_TEXT_CHUNK,
134
- chunk,
135
  requestId,
136
  });
 
137
 
138
  const streamer = new TextStreamer(tokenizer, {
139
  skip_prompt: true,
140
- skip_special_tokens: true,
141
  token_callback_function: tokenCallbackFunction,
142
  callback_function: callbackFunction,
143
  });
@@ -157,16 +161,18 @@ self.onmessage = async ({ data }: MessageEvent<Request>) => {
157
  const ended = performance.now();
158
 
159
  const lengthOfInput = input.input_ids.dims[1];
160
- const response = tokenizer.batch_decode(
161
- /**
162
- * First argument (null): Don't slice dimension 0 (the batch dimension) - keep all batches
163
- * Second argument ([lengthOfInput, Number.MAX_SAFE_INTEGER]): For dimension 1 (the sequence/token dimension), slice from index lengthOfInput to the end
164
- */
165
- sequences.slice(null, [lengthOfInput, Number.MAX_SAFE_INTEGER]),
166
- {
167
- skip_special_tokens: true, // removes the <|end_of_text|>
168
- }
169
- )[0];
 
 
170
 
171
  const template = tokenizer.batch_decode(sequences, {
172
  skip_special_tokens: false,
 
121
  let numTokens = 0;
122
  let tps: number = 0;
123
 
124
+ const removeEosToken = (content: string): string =>
125
+ content.replace(tokenizer.eos_token, "");
126
+
127
  const tokenCallbackFunction = () => {
128
  firstTokenTime ??= performance.now();
129
  if (numTokens++ > 0) {
 
131
  }
132
  };
133
 
134
+ const callbackFunction = (chunk: string) => {
135
  postMessage({
136
  type: ResponseType.GENERATE_TEXT_CHUNK,
137
+ chunk: removeEosToken(chunk),
138
  requestId,
139
  });
140
+ };
141
 
142
  const streamer = new TextStreamer(tokenizer, {
143
  skip_prompt: true,
144
+ skip_special_tokens: false,
145
  token_callback_function: tokenCallbackFunction,
146
  callback_function: callbackFunction,
147
  });
 
161
  const ended = performance.now();
162
 
163
  const lengthOfInput = input.input_ids.dims[1];
164
+ const response = removeEosToken(
165
+ tokenizer.batch_decode(
166
+ /**
167
+ * First argument (null): Don't slice dimension 0 (the batch dimension) - keep all batches
168
+ * Second argument ([lengthOfInput, Number.MAX_SAFE_INTEGER]): For dimension 1 (the sequence/token dimension), slice from index lengthOfInput to the end
169
+ */
170
+ sequences.slice(null, [lengthOfInput, Number.MAX_SAFE_INTEGER]),
171
+ {
172
+ skip_special_tokens: false,
173
+ }
174
+ )[0]
175
+ );
176
 
177
  const template = tokenizer.batch_decode(sequences, {
178
  skip_special_tokens: false,
src/utils/calculateDownloadProgress.ts CHANGED
@@ -62,6 +62,13 @@ export const calculateDownloadProgress = (
62
  const percentage =
63
  totalSize > 0 ? round((totalLoaded / totalSize) * 100, 2) : 0;
64
 
 
 
 
 
 
 
 
65
  callback({
66
  percentage,
67
  total: totalSize,
 
62
  const percentage =
63
  totalSize > 0 ? round((totalLoaded / totalSize) * 100, 2) : 0;
64
 
65
+ /*if (percentage === 100) {
66
+ console.log("totalSize");
67
+ console.log(totalSize);
68
+ console.log("filesRecord");
69
+ console.log(filesRecord);
70
+ }*/
71
+
72
  callback({
73
  percentage,
74
  total: totalSize,
src/utils/context/chatSettings/ChatSettingsModalForm.tsx CHANGED
@@ -34,10 +34,42 @@ export default function ChatSettingsModalForm({
34
  defaultValues,
35
  });
36
 
37
- const modelOptions = Object.entries(MODELS).map(([key, model]) => ({
38
- value: key,
39
- label: `${model.title} - ${formatBytes(model.size)}${downloadedModels.includes(key) ? " *" : ""}`,
40
- }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  const toolOptions = TOOLS.map((tool) => ({
43
  name: tool.name,
 
34
  defaultValues,
35
  });
36
 
37
+ const modelOptions = Object.values(
38
+ Object.entries(MODELS).reduce(
39
+ (groups, [key, model]) => {
40
+ const groupName = model.group;
41
+
42
+ if (!groups[groupName]) {
43
+ groups[groupName] = {
44
+ label: groupName.charAt(0).toUpperCase() + groupName.slice(1),
45
+ options: [],
46
+ };
47
+ }
48
+
49
+ groups[groupName].options.push({
50
+ value: key,
51
+ label: `${model.title} - ${formatBytes(model.size)}${downloadedModels.includes(key) ? " *" : ""}`,
52
+ title: model.title,
53
+ });
54
+
55
+ return groups;
56
+ },
57
+ {} as Record<
58
+ string,
59
+ {
60
+ label: string;
61
+ options: Array<{ value: string; label: string; title: string }>;
62
+ }
63
+ >
64
+ )
65
+ )
66
+ .sort((a, b) => a.label.localeCompare(b.label))
67
+ .map((group) => ({
68
+ ...group,
69
+ options: group.options
70
+ .sort((a, b) => a.title.localeCompare(b.title))
71
+ .map(({ value, label }) => ({ value, label })),
72
+ }));
73
 
74
  const toolOptions = TOOLS.map((tool) => ({
75
  name: tool.name,
src/utils/models.ts CHANGED
@@ -8,9 +8,26 @@ export interface Model {
8
  device: "webgpu";
9
  size: number;
10
  files: Record<string, number>;
 
11
  }
12
 
13
  export const MODELS: Record<string, Model> = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  apertus8BInstruct2509q4f16: {
15
  modelId: "onnx-community/Apertus-8B-Instruct-2509-ONNX",
16
  title: "Apertus 8B Instruct 2509 (q4f16)",
@@ -25,21 +42,28 @@ export const MODELS: Record<string, Model> = {
25
  "onnx/model_q4f16.onnx_data_2": 487_391_232,
26
  "onnx/model_q4f16.onnx_data": 2_093_498_368,
27
  },
 
28
  },
29
- apertus8BInstruct2509q4: {
30
  modelId: "onnx-community/Apertus-8B-Instruct-2509-ONNX",
31
- title: "Apertus 8B Instruct 2509 (q4)",
32
- dtype: "q4",
33
  device: "webgpu",
34
- size: 5_194_104_473,
35
  files: {
36
  "config.json": 1_193,
37
  "generation_config.json": 141,
38
- "onnx/model_q4.onnx_data_2": 1_015_676_928,
39
- "onnx/model_q4.onnx": 489_827,
40
- "onnx/model_q4.onnx_data": 2_082_095_104,
41
- "onnx/model_q4.onnx_data_1": 2_095_841_280,
 
 
 
 
 
42
  },
 
43
  },
44
  gemma3270m: {
45
  modelId: "onnx-community/gemma-3-270m-it-ONNX",
@@ -55,6 +79,7 @@ export const MODELS: Record<string, Model> = {
55
  "onnx/model.onnx": 201_742,
56
  "onnx/model.onnx_data": 1_139_501_568,
57
  },
 
58
  },
59
  granite350m: {
60
  modelId: "onnx-community/granite-4.0-350m-ONNX-web",
@@ -70,6 +95,7 @@ export const MODELS: Record<string, Model> = {
70
  "onnx/model_fp16.onnx": 202_945,
71
  "onnx/model_fp16.onnx_data": 708_954_112,
72
  },
 
73
  },
74
  granite1B: {
75
  modelId: "onnx-community/granite-4.0-1b-ONNX-web",
@@ -85,6 +111,7 @@ export const MODELS: Record<string, Model> = {
85
  "generation_config.json": 147,
86
  "onnx/model_q4.onnx_data": 1_781_145_600,
87
  },
 
88
  },
89
  granite3B: {
90
  modelId: "onnx-community/granite-4.0-micro-ONNX-web",
@@ -101,6 +128,7 @@ export const MODELS: Record<string, Model> = {
101
  "onnx/model_q4f16.onnx_data_1": 212_336_640,
102
  "generation_config.json": 152,
103
  },
 
104
  },
105
  qwen34B: {
106
  modelId: "onnx-community/Qwen3-4B-ONNX",
@@ -117,6 +145,7 @@ export const MODELS: Record<string, Model> = {
117
  "onnx/model_q4f16.onnx_data_1": 677_150_720,
118
  "onnx/model_q4f16.onnx_data": 2_096_005_120,
119
  },
 
120
  },
121
  smolLM33B: {
122
  modelId: "HuggingFaceTB/SmolLM3-3B-ONNX",
@@ -132,6 +161,49 @@ export const MODELS: Record<string, Model> = {
132
  "generation_config.json": 182,
133
  "onnx/model_q4f16.onnx_data": 2_124_320_768,
134
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  },
136
  } as const;
137
 
 
8
  device: "webgpu";
9
  size: number;
10
  files: Record<string, number>;
11
+ group: string;
12
  }
13
 
14
  export const MODELS: Record<string, Model> = {
15
+ apertus8BInstruct2509q4: {
16
+ modelId: "onnx-community/Apertus-8B-Instruct-2509-ONNX",
17
+ title: "Apertus 8B Instruct 2509 (q4)",
18
+ dtype: "q4",
19
+ device: "webgpu",
20
+ size: 5_194_104_473,
21
+ files: {
22
+ "config.json": 1_193,
23
+ "generation_config.json": 141,
24
+ "onnx/model_q4.onnx_data_2": 1_015_676_928,
25
+ "onnx/model_q4.onnx": 489_827,
26
+ "onnx/model_q4.onnx_data": 2_082_095_104,
27
+ "onnx/model_q4.onnx_data_1": 2_095_841_280,
28
+ },
29
+ group: "Apertus",
30
+ },
31
  apertus8BInstruct2509q4f16: {
32
  modelId: "onnx-community/Apertus-8B-Instruct-2509-ONNX",
33
  title: "Apertus 8B Instruct 2509 (q4f16)",
 
42
  "onnx/model_q4f16.onnx_data_2": 487_391_232,
43
  "onnx/model_q4f16.onnx_data": 2_093_498_368,
44
  },
45
+ group: "Apertus",
46
  },
47
+ apertus8BInstruct2509fp16: {
48
  modelId: "onnx-community/Apertus-8B-Instruct-2509-ONNX",
49
+ title: "Apertus 8B Instruct 2509 (fp16)",
50
+ dtype: "fp16",
51
  device: "webgpu",
52
+ size: 14_119_488_832,
53
  files: {
54
  "config.json": 1_193,
55
  "generation_config.json": 141,
56
+ "onnx/model_fp16.onnx": 559_114,
57
+ "onnx/model_fp16.onnx_data": 2_046_918_656,
58
+ "onnx/model_fp16.onnx_data_1": 1_921_122_304,
59
+ "onnx/model_fp16.onnx_data_2": 2_005_041_152,
60
+ "onnx/model_fp16.onnx_data_3": 1_921_122_304,
61
+ "onnx/model_fp16.onnx_data_4": 2_005_041_152,
62
+ "onnx/model_fp16.onnx_data_5": 1_921_122_304,
63
+ "onnx/model_fp16.onnx_data_7": 1_224_818_688,
64
+ "onnx/model_fp16.onnx_data_8": 1_073_741_824,
65
  },
66
+ group: "Apertus",
67
  },
68
  gemma3270m: {
69
  modelId: "onnx-community/gemma-3-270m-it-ONNX",
 
79
  "onnx/model.onnx": 201_742,
80
  "onnx/model.onnx_data": 1_139_501_568,
81
  },
82
+ group: "Gemma",
83
  },
84
  granite350m: {
85
  modelId: "onnx-community/granite-4.0-350m-ONNX-web",
 
95
  "onnx/model_fp16.onnx": 202_945,
96
  "onnx/model_fp16.onnx_data": 708_954_112,
97
  },
98
+ group: "Granite",
99
  },
100
  granite1B: {
101
  modelId: "onnx-community/granite-4.0-1b-ONNX-web",
 
111
  "generation_config.json": 147,
112
  "onnx/model_q4.onnx_data": 1_781_145_600,
113
  },
114
+ group: "Granite",
115
  },
116
  granite3B: {
117
  modelId: "onnx-community/granite-4.0-micro-ONNX-web",
 
128
  "onnx/model_q4f16.onnx_data_1": 212_336_640,
129
  "generation_config.json": 152,
130
  },
131
+ group: "Granite",
132
  },
133
  qwen34B: {
134
  modelId: "onnx-community/Qwen3-4B-ONNX",
 
145
  "onnx/model_q4f16.onnx_data_1": 677_150_720,
146
  "onnx/model_q4f16.onnx_data": 2_096_005_120,
147
  },
148
+ group: "Qwen",
149
  },
150
  smolLM33B: {
151
  modelId: "HuggingFaceTB/SmolLM3-3B-ONNX",
 
161
  "generation_config.json": 182,
162
  "onnx/model_q4f16.onnx_data": 2_124_320_768,
163
  },
164
+ group: "SmolLM3",
165
+ },
166
+ lfm2350: {
167
+ modelId: "onnx-community/LFM2-350M-ONNX",
168
+ title: "LFM2-350M (q4f16)",
169
+ dtype: "q4f16",
170
+ device: "webgpu",
171
+ size: 312_526_859,
172
+ files: {
173
+ "config.json": 1_384,
174
+ "onnx/model_q4f16.onnx": 180_763,
175
+ "onnx/model_q4f16.onnx_data": 312_344_576,
176
+ "generation_config.json": 136,
177
+ },
178
+ group: "Liquid AI LFM2",
179
+ },
180
+ lfm2700: {
181
+ modelId: "onnx-community/LFM2-700M-ONNX",
182
+ title: "LFM2-700M (q4f16)",
183
+ dtype: "q4f16",
184
+ device: "webgpu",
185
+ size: 579_061_875,
186
+ files: {
187
+ "config.json": 1386,
188
+ "generation_config.json": 136,
189
+ "onnx/model_q4f16.onnx": 180865,
190
+ "onnx/model_q4f16.onnx_data": 578879488,
191
+ },
192
+ group: "Liquid AI LFM2",
193
+ },
194
+ lfm21B: {
195
+ modelId: "onnx-community/LFM2-1.2B-ONNX",
196
+ title: "LFM2-1.2B (q4f16)",
197
+ dtype: "q4f16",
198
+ device: "webgpu",
199
+ size: 868_010_193,
200
+ files: {
201
+ "config.json": 1_387,
202
+ "onnx/model_q4f16.onnx": 180_958,
203
+ "onnx/model_q4f16.onnx_data": 867_827_712,
204
+ "generation_config.json": 136,
205
+ },
206
+ group: "Liquid AI LFM2",
207
  },
208
  } as const;
209
 
src/utils/tools.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { WebMCPTool } from "@utils/webMcp.ts";
2
 
3
  const getCurrentTimeTool: WebMCPTool = {
4
  name: "get_current_time",
 
1
+ import type { WebMCPTool } from "@utils/webMcp/types.ts";
2
 
3
  const getCurrentTimeTool: WebMCPTool = {
4
  name: "get_current_time",
src/utils/{webMcp.ts β†’ webMcp/execute.ts} RENAMED
@@ -1,48 +1,45 @@
1
- // some helper functions for tool-calling based on the WebMCP API Proposal
2
 
3
- type WebMCPProperty =
4
- | {
5
- type: "string";
6
- description: string;
7
- default?: string;
8
- }
9
- | {
10
- type: "number";
11
- description: string;
12
- default?: number;
13
- }
14
- | {
15
- type: "boolean";
16
- description: string;
17
- default?: boolean;
18
- };
19
 
20
- export interface WebMCPTool {
21
- name: string;
22
- description: string;
23
- inputSchema: {
24
- type: "object";
25
- properties: Record<string, WebMCPProperty>;
26
- required: Array<string>;
27
  };
28
- execute: (args: Record<string, any>) => Promise<string>;
29
- }
30
 
31
- export interface ChatTemplateTool {
32
- name: string;
33
- description: string;
34
- parameters: Record<string, any>;
35
- }
 
36
 
37
- export const webMCPToolToChatTemplateTool = (
38
- webMCPTool: WebMCPTool
39
- ): ChatTemplateTool => ({
40
- name: webMCPTool.name,
41
- description: webMCPTool.description,
42
- parameters: webMCPTool.inputSchema,
43
- });
 
 
44
 
45
- export const validateWebMCPToolArguments = (
 
 
 
 
46
  tool: WebMCPTool,
47
  args: Record<string, any>
48
  ): Record<string, any> => {
@@ -75,141 +72,3 @@ export const validateWebMCPToolArguments = (
75
 
76
  return returnArgs;
77
  };
78
-
79
- export const executeWebMCPTool = async (
80
- tool: WebMCPTool,
81
- args: Record<string, any> | string | undefined
82
- ) => {
83
- // Handle case where args is a JSON string instead of an object
84
- let parsedArgs: Record<string, any> = {};
85
-
86
- if (typeof args === "string") {
87
- try {
88
- parsedArgs = JSON.parse(args);
89
- } catch (error) {
90
- parsedArgs = {};
91
- }
92
- } else if (args) {
93
- parsedArgs = args;
94
- }
95
-
96
- const validatedArgs = validateWebMCPToolArguments(tool, parsedArgs);
97
- return await tool.execute(validatedArgs);
98
- };
99
-
100
- export interface ToolCallPayload {
101
- name: string;
102
- arguments?: Record<string, any> | string;
103
- id: string;
104
- }
105
-
106
- export const extractToolCalls = (
107
- text: string
108
- ): { toolCalls: ToolCallPayload[]; message: string } => {
109
- const matches = Array.from(
110
- text.matchAll(/<tool_call>([\s\S]*?)<\/tool_call>/g)
111
- );
112
- const toolCalls: ToolCallPayload[] = [];
113
-
114
- for (const match of matches) {
115
- try {
116
- const parsed = JSON.parse(match[1].trim());
117
- if (parsed && typeof parsed.name === "string") {
118
- toolCalls.push({
119
- name: parsed.name,
120
- arguments: parsed.arguments ?? {},
121
- id: JSON.stringify({
122
- name: parsed.name,
123
- arguments: parsed.arguments ?? {},
124
- }),
125
- });
126
- }
127
- } catch {
128
- // ignore malformed tool call payloads
129
- }
130
- }
131
-
132
- // Remove both complete and incomplete tool calls
133
- // Complete: <tool_call>...</tool_call>
134
- // Incomplete: <tool_call>... (no closing tag yet)
135
- const message = text
136
- .replace(/<tool_call>[\s\S]*?(?:<\/tool_call>|$)/g, "")
137
- .trim();
138
-
139
- return { toolCalls, message };
140
- };
141
-
142
- export const splitResponse = (
143
- text: string
144
- ): Array<string | ToolCallPayload> => {
145
- const result: Array<string | ToolCallPayload> = [];
146
- let lastIndex = 0;
147
-
148
- // Match only complete tool calls (with closing tag)
149
- const regex = /<tool_call>([\s\S]*?)<\/tool_call>/g;
150
- let match: RegExpExecArray | null;
151
-
152
- while ((match = regex.exec(text)) !== null) {
153
- // Add text before the tool call
154
- const textBefore = text.slice(lastIndex, match.index);
155
- if (textBefore) {
156
- result.push(textBefore);
157
- }
158
-
159
- // Parse and add the tool call
160
- try {
161
- const parsed = JSON.parse(match[1].trim());
162
- if (parsed && typeof parsed.name === "string") {
163
- result.push({
164
- name: parsed.name,
165
- arguments: parsed.arguments ?? {},
166
- id: JSON.stringify({
167
- name: parsed.name,
168
- arguments: parsed.arguments ?? {},
169
- }),
170
- });
171
- }
172
- } catch {
173
- // ignore malformed tool call payloads
174
- }
175
-
176
- lastIndex = regex.lastIndex;
177
- }
178
-
179
- // Check if there's an incomplete tool call
180
- const incompleteToolCallIndex = text.indexOf("<tool_call>", lastIndex);
181
-
182
- if (incompleteToolCallIndex !== -1) {
183
- // There's an incomplete tool call, only add text up to it
184
- const textBefore = text.slice(lastIndex, incompleteToolCallIndex);
185
- if (textBefore) {
186
- result.push(textBefore);
187
- }
188
- } else {
189
- // No incomplete tool call, add remaining text
190
- const remainingText = text.slice(lastIndex);
191
- if (remainingText) {
192
- result.push(remainingText);
193
- }
194
- }
195
-
196
- return result;
197
- };
198
-
199
- export const executeToolCall = async (
200
- toolCall: ToolCallPayload,
201
- tools: Array<WebMCPTool>
202
- ): Promise<{ id: string; result: string; time: number }> => {
203
- const started = performance.now();
204
- const toolToUse = tools.find((t) => t.name === toolCall.name);
205
- if (!toolToUse)
206
- throw new Error(`Tool '${toolCall.name}' not found or is disabled.`);
207
-
208
- const result = await executeWebMCPTool(toolToUse, toolCall.arguments);
209
- const ended = performance.now();
210
- return {
211
- id: toolCall.id,
212
- result,
213
- time: ended - started,
214
- };
215
- };
 
1
+ import type { ToolCallPayload, WebMCPTool } from "./types.ts";
2
 
3
+ export const executeToolCall = async (
4
+ toolCall: ToolCallPayload,
5
+ tools: Array<WebMCPTool>
6
+ ): Promise<{ id: string; result: string; time: number }> => {
7
+ const started = performance.now();
8
+ const toolToUse = tools.find((t) => t.name === toolCall.name);
9
+ if (!toolToUse)
10
+ throw new Error(`Tool '${toolCall.name}' not found or is disabled.`);
 
 
 
 
 
 
 
 
11
 
12
+ const result = await executeWebMCPTool(toolToUse, toolCall.arguments);
13
+ const ended = performance.now();
14
+ return {
15
+ id: toolCall.id,
16
+ result,
17
+ time: ended - started,
 
18
  };
19
+ };
 
20
 
21
+ const executeWebMCPTool = async (
22
+ tool: WebMCPTool,
23
+ args: Record<string, any> | string | undefined
24
+ ) => {
25
+ // Handle case where args is a JSON string instead of an object
26
+ let parsedArgs: Record<string, any> = {};
27
 
28
+ if (typeof args === "string") {
29
+ try {
30
+ parsedArgs = JSON.parse(args);
31
+ } catch (error) {
32
+ parsedArgs = {};
33
+ }
34
+ } else if (args) {
35
+ parsedArgs = args;
36
+ }
37
 
38
+ const validatedArgs = validateWebMCPToolArguments(tool, parsedArgs);
39
+ return await tool.execute(validatedArgs);
40
+ };
41
+
42
+ const validateWebMCPToolArguments = (
43
  tool: WebMCPTool,
44
  args: Record<string, any>
45
  ): Record<string, any> => {
 
72
 
73
  return returnArgs;
74
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/utils/webMcp/extract.ts ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { ToolCallMatch, ToolCallPayload } from "./types.ts";
2
+
3
+ export const splitResponse = (
4
+ text: string
5
+ ): Array<string | ToolCallPayload> => {
6
+ const result: Array<string | ToolCallPayload> = [];
7
+
8
+ const toolCallMatches: ToolCallMatch[] = [];
9
+
10
+ // Match XML syntax: <tool_call>...</tool_call>
11
+ const xmlRegex = /<tool_call>([\s\S]*?)<\/tool_call>/g;
12
+ let xmlMatch: RegExpExecArray | null;
13
+ while ((xmlMatch = xmlRegex.exec(text)) !== null) {
14
+ toolCallMatches.push({
15
+ start: xmlMatch.index,
16
+ end: xmlRegex.lastIndex,
17
+ content: xmlMatch[1],
18
+ syntax: "xml",
19
+ });
20
+ }
21
+
22
+ // Match pipe syntax: <|tool_call_start|>...<|tool_call_end|>
23
+ const pipeRegex = /<\|tool_call_start\|>([\s\S]*?)<\|tool_call_end\|>/g;
24
+ let pipeMatch: RegExpExecArray | null;
25
+ while ((pipeMatch = pipeRegex.exec(text)) !== null) {
26
+ toolCallMatches.push({
27
+ start: pipeMatch.index,
28
+ end: pipeRegex.lastIndex,
29
+ content: pipeMatch[1],
30
+ syntax: "pipes",
31
+ });
32
+ }
33
+
34
+ toolCallMatches.sort((a, b) => a.start - b.start);
35
+
36
+ let lastIndex = 0;
37
+
38
+ for (const match of toolCallMatches) {
39
+ const textBefore = text.slice(lastIndex, match.start);
40
+ if (textBefore.trim()) {
41
+ result.push(textBefore.trim());
42
+ }
43
+
44
+ let toolCall: ToolCallPayload | null = null;
45
+
46
+ if (match.syntax === "xml") {
47
+ // JSON syntax in XML tags
48
+ toolCall = extractXmlJsonToolCall(match.content);
49
+ } else {
50
+ // Pythonic function call syntax in pipe tags
51
+ toolCall = extractPythonicToolCall(match.content);
52
+ }
53
+
54
+ if (toolCall) {
55
+ result.push(toolCall);
56
+ }
57
+
58
+ lastIndex = match.end;
59
+ }
60
+
61
+ const incompleteXml = text.indexOf("<tool_call>", lastIndex);
62
+ const incompletePipe = text.indexOf("<|tool_call_start|>", lastIndex);
63
+
64
+ let incompleteIndex = -1;
65
+ if (incompleteXml !== -1 && incompletePipe !== -1) {
66
+ incompleteIndex = Math.min(incompleteXml, incompletePipe);
67
+ } else if (incompleteXml !== -1) {
68
+ incompleteIndex = incompleteXml;
69
+ } else if (incompletePipe !== -1) {
70
+ incompleteIndex = incompletePipe;
71
+ }
72
+
73
+ if (incompleteIndex !== -1) {
74
+ const textBefore = text.slice(lastIndex, incompleteIndex);
75
+ if (textBefore.trim()) {
76
+ result.push(textBefore.trim());
77
+ }
78
+ } else {
79
+ const remainingText = text.slice(lastIndex);
80
+ if (remainingText.trim()) {
81
+ result.push(remainingText.trim());
82
+ }
83
+ }
84
+
85
+ return result;
86
+ };
87
+
88
+ /**
89
+ * XML/JSON tool calling
90
+ * used by Granite 4.0
91
+ */
92
+
93
+ // used for Granite 4.0 models
94
+ export const extractXmlJsonToolCall = (
95
+ text: string
96
+ ): ToolCallPayload | null => {
97
+ const parsed = JSON.parse(text.trim());
98
+ try {
99
+ if (parsed && typeof parsed.name === "string") {
100
+ return {
101
+ name: parsed.name,
102
+ arguments: parsed.arguments ?? {},
103
+ id: JSON.stringify({
104
+ name: parsed.name,
105
+ arguments: parsed.arguments ?? {},
106
+ }),
107
+ };
108
+ }
109
+ } catch (e) {
110
+ return null;
111
+ }
112
+ };
113
+
114
+ /**
115
+ * Pipe/Pythonic tool calling
116
+ * used by Liquid AI LFM2
117
+ */
118
+
119
+ export const extractPythonicToolCall = (
120
+ text: string
121
+ ): ToolCallPayload | null => {
122
+ try {
123
+ // Remove surrounding brackets if present: [get_joke(...)] -> get_joke(...)
124
+ const trimmed = text.trim().replace(/^\[|]$/g, "");
125
+
126
+ // Extract function name and arguments: get_joke(category="Programming")
127
+ const functionMatch = trimmed.match(/^(\w+)\((.*)\)$/s);
128
+ if (!functionMatch) return null;
129
+
130
+ const name = functionMatch[1];
131
+ const argsString = functionMatch[2];
132
+
133
+ // Parse arguments using the helper function
134
+ const args: Record<string, any> = {};
135
+
136
+ if (argsString.trim()) {
137
+ const argPairs = parsePythonicArguments(argsString);
138
+
139
+ for (const pair of argPairs) {
140
+ // Match key=value patterns where value can be quoted or unquoted
141
+ const argMatch = pair.match(/^(\w+)=(.+)$/);
142
+ if (argMatch) {
143
+ const key = argMatch[1];
144
+ let value = argMatch[2].trim();
145
+
146
+ // Remove quotes if present
147
+ if (
148
+ (value.startsWith('"') && value.endsWith('"')) ||
149
+ (value.startsWith("'") && value.endsWith("'"))
150
+ ) {
151
+ value = value.slice(1, -1);
152
+ }
153
+
154
+ args[key] = value;
155
+ }
156
+ }
157
+ }
158
+
159
+ return {
160
+ name,
161
+ arguments: args,
162
+ id: JSON.stringify({ name, arguments: args }),
163
+ };
164
+ } catch {
165
+ return null;
166
+ }
167
+ };
168
+
169
+ const parsePythonicArguments = (argsString: string): string[] => {
170
+ const args: string[] = [];
171
+ let current = "";
172
+ let inQuotes = false;
173
+ let quoteChar = "";
174
+ let depth = 0;
175
+
176
+ for (let i = 0; i < argsString.length; i++) {
177
+ const char = argsString[i];
178
+
179
+ if (!inQuotes && (char === '"' || char === "'")) {
180
+ inQuotes = true;
181
+ quoteChar = char;
182
+ current += char;
183
+ } else if (inQuotes && char === quoteChar) {
184
+ inQuotes = false;
185
+ quoteChar = "";
186
+ current += char;
187
+ } else if (!inQuotes && char === "(") {
188
+ depth++;
189
+ current += char;
190
+ } else if (!inQuotes && char === ")") {
191
+ depth--;
192
+ current += char;
193
+ } else if (!inQuotes && char === "," && depth === 0) {
194
+ args.push(current.trim());
195
+ current = "";
196
+ } else {
197
+ current += char;
198
+ }
199
+ }
200
+
201
+ if (current.trim()) {
202
+ args.push(current.trim());
203
+ }
204
+
205
+ return args;
206
+ };
src/utils/webMcp/format.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import type { ChatTemplateTool, WebMCPTool } from "./types.ts";
2
+
3
+ export const webMCPToolToChatTemplateTool = (
4
+ webMCPTool: WebMCPTool
5
+ ): ChatTemplateTool => ({
6
+ name: webMCPTool.name,
7
+ description: webMCPTool.description,
8
+ parameters: webMCPTool.inputSchema,
9
+ });
src/utils/webMcp/index.ts ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ export { splitResponse } from "./extract.ts";
2
+ export { executeToolCall } from "./execute.ts";
3
+ export { webMCPToolToChatTemplateTool } from "./format.ts";
src/utils/webMcp/types.ts ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ type WebMCPProperty =
2
+ | {
3
+ type: "string";
4
+ description: string;
5
+ default?: string;
6
+ }
7
+ | {
8
+ type: "number";
9
+ description: string;
10
+ default?: number;
11
+ }
12
+ | {
13
+ type: "boolean";
14
+ description: string;
15
+ default?: boolean;
16
+ };
17
+
18
+ export interface WebMCPTool {
19
+ name: string;
20
+ description: string;
21
+ inputSchema: {
22
+ type: "object";
23
+ properties: Record<string, WebMCPProperty>;
24
+ required: Array<string>;
25
+ };
26
+ execute: (args: Record<string, any>) => Promise<string>;
27
+ }
28
+
29
+ export interface ChatTemplateTool {
30
+ name: string;
31
+ description: string;
32
+ parameters: Record<string, any>;
33
+ }
34
+
35
+ export interface ToolCallPayload {
36
+ name: string;
37
+ arguments?: Record<string, any> | string;
38
+ id: string;
39
+ }
40
+
41
+ export interface ToolCallMatch {
42
+ start: number;
43
+ end: number;
44
+ content: string;
45
+ syntax: "xml" | "pipes";
46
+ }
src/utils/webMcp/webMcp.test.ts ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, expect, test } from "vitest";
2
+
3
+ import { splitResponse } from "./index";
4
+
5
+ describe("splitResponse", () => {
6
+ test("simple text response", () => {
7
+ expect(splitResponse("hello")).toStrictEqual(["hello"]);
8
+ expect(
9
+ splitResponse(
10
+ "You are a helpful assistant with access to the following tools"
11
+ )
12
+ ).toStrictEqual([
13
+ "You are a helpful assistant with access to the following tools",
14
+ ]);
15
+ });
16
+
17
+ describe("XML/Json tool calling", () => {
18
+ test("full tool call", () => {
19
+ expect(
20
+ splitResponse(
21
+ `<tool_call>{"name": "get_current_time", "arguments": {}}</tool_call>`
22
+ )
23
+ ).toStrictEqual([
24
+ {
25
+ name: "get_current_time",
26
+ arguments: {},
27
+ id: '{"name":"get_current_time","arguments":{}}',
28
+ },
29
+ ]);
30
+ });
31
+
32
+ test("full tool call with arguments", () => {
33
+ expect(
34
+ splitResponse(
35
+ `<tool_call>{"name": "get_joke", "arguments": {"category": "Programming"}}</tool_call>`
36
+ )
37
+ ).toStrictEqual([
38
+ {
39
+ name: "get_joke",
40
+ arguments: {
41
+ category: "Programming",
42
+ },
43
+ id: '{"name":"get_joke","arguments":{"category":"Programming"}}',
44
+ },
45
+ ]);
46
+ });
47
+
48
+ test("partial tool call", () => {
49
+ expect(splitResponse(`<tool_call>{"name":`)).toStrictEqual([]);
50
+ });
51
+
52
+ test("partial tool call with leading text", () => {
53
+ expect(splitResponse(`Hello world <tool_call>{"name":`)).toStrictEqual([
54
+ "Hello world",
55
+ ]);
56
+ });
57
+
58
+ test("tool call with leading text", () => {
59
+ expect(
60
+ splitResponse(
61
+ `Hello world <tool_call>{"name": "get_current_time", "arguments": {}}</tool_call>`
62
+ )
63
+ ).toStrictEqual([
64
+ "Hello world",
65
+ {
66
+ name: "get_current_time",
67
+ arguments: {},
68
+ id: '{"name":"get_current_time","arguments":{}}',
69
+ },
70
+ ]);
71
+ });
72
+
73
+ test("tool call with surrounding text", () => {
74
+ expect(
75
+ splitResponse(
76
+ `Hello world <tool_call>{"name": "get_current_time", "arguments": {}}</tool_call> This is a test`
77
+ )
78
+ ).toStrictEqual([
79
+ "Hello world",
80
+ {
81
+ name: "get_current_time",
82
+ arguments: {},
83
+ id: '{"name":"get_current_time","arguments":{}}',
84
+ },
85
+ "This is a test",
86
+ ]);
87
+ });
88
+ });
89
+
90
+ describe("Pipe/Pythonic tool calling", () => {
91
+ test("full tool call without arguments", () => {
92
+ expect(
93
+ splitResponse(
94
+ `<|tool_call_start|>[get_current_time()]<|tool_call_end|>`
95
+ )
96
+ ).toStrictEqual([
97
+ {
98
+ name: "get_current_time",
99
+ arguments: {},
100
+ id: '{"name":"get_current_time","arguments":{}}',
101
+ },
102
+ ]);
103
+ });
104
+
105
+ test("full tool call with single argument", () => {
106
+ expect(
107
+ splitResponse(
108
+ `<|tool_call_start|>[get_joke(category="Programming")]<|tool_call_end|>`
109
+ )
110
+ ).toStrictEqual([
111
+ {
112
+ name: "get_joke",
113
+ arguments: {
114
+ category: "Programming",
115
+ },
116
+ id: '{"name":"get_joke","arguments":{"category":"Programming"}}',
117
+ },
118
+ ]);
119
+ });
120
+
121
+ test("full tool call with multiple arguments", () => {
122
+ expect(
123
+ splitResponse(
124
+ `<|tool_call_start|>[get_joke(category="Programming", type="setup", contains="error")]<|tool_call_end|>`
125
+ )
126
+ ).toStrictEqual([
127
+ {
128
+ name: "get_joke",
129
+ arguments: {
130
+ category: "Programming",
131
+ type: "setup",
132
+ contains: "error",
133
+ },
134
+ id: '{"name":"get_joke","arguments":{"category":"Programming","type":"setup","contains":"error"}}',
135
+ },
136
+ ]);
137
+ });
138
+
139
+ test("partial tool call", () => {
140
+ expect(splitResponse(`<|tool_call_start|>[get_current`)).toStrictEqual(
141
+ []
142
+ );
143
+ });
144
+
145
+ test("partial tool call with leading text", () => {
146
+ expect(
147
+ splitResponse(`Hello world <|tool_call_start|>[get_joke(`)
148
+ ).toStrictEqual(["Hello world"]);
149
+ });
150
+
151
+ test("tool call with leading text", () => {
152
+ expect(
153
+ splitResponse(
154
+ `Hello world <|tool_call_start|>[get_current_time()]<|tool_call_end|>`
155
+ )
156
+ ).toStrictEqual([
157
+ "Hello world",
158
+ {
159
+ name: "get_current_time",
160
+ arguments: {},
161
+ id: '{"name":"get_current_time","arguments":{}}',
162
+ },
163
+ ]);
164
+ });
165
+
166
+ test("tool call with surrounding text", () => {
167
+ expect(
168
+ splitResponse(
169
+ `Hello world <|tool_call_start|>[get_current_time()]<|tool_call_end|> This is a test`
170
+ )
171
+ ).toStrictEqual([
172
+ "Hello world",
173
+ {
174
+ name: "get_current_time",
175
+ arguments: {},
176
+ id: '{"name":"get_current_time","arguments":{}}',
177
+ },
178
+ "This is a test",
179
+ ]);
180
+ });
181
+ });
182
+ });