whitphx HF Staff commited on
Commit
94b17d0
Β·
1 Parent(s): 13af1f3

Batch command

Browse files
client/.env.example ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # HuggingFace API Token (optional)
2
+ # Get your token from https://huggingface.co/settings/tokens
3
+ # Required for accessing private models or higher API rate limits
4
+ HF_TOKEN=your_token_here
5
+
6
+ # Benchmark Server URL (optional)
7
+ # Default: http://localhost:7860
8
+ BENCH_SERVER_URL=http://localhost:7860
client/.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ node_modules/
2
+ .env
client/package-lock.json CHANGED
@@ -8,6 +8,10 @@
8
  "name": "client",
9
  "version": "0.0.1",
10
  "dependencies": {
 
 
 
 
11
  "table": "^6.9.0",
12
  "yargs": "^17.7.2"
13
  },
@@ -459,6 +463,49 @@
459
  "node": ">=18"
460
  }
461
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462
  "node_modules/@types/yargs": {
463
  "version": "17.0.33",
464
  "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
@@ -525,6 +572,19 @@
525
  "node": ">=8"
526
  }
527
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
528
  "node_modules/cliui": {
529
  "version": "8.0.1",
530
  "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -557,6 +617,18 @@
557
  "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
558
  "license": "MIT"
559
  },
 
 
 
 
 
 
 
 
 
 
 
 
560
  "node_modules/emoji-regex": {
561
  "version": "8.0.0",
562
  "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -688,12 +760,34 @@
688
  "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
689
  "license": "MIT"
690
  },
 
 
 
 
 
 
 
 
 
691
  "node_modules/lodash.truncate": {
692
  "version": "4.4.2",
693
  "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
694
  "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
695
  "license": "MIT"
696
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
697
  "node_modules/require-directory": {
698
  "version": "2.1.1",
699
  "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -722,6 +816,12 @@
722
  "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
723
  }
724
  },
 
 
 
 
 
 
725
  "node_modules/slice-ansi": {
726
  "version": "4.0.0",
727
  "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
@@ -815,6 +915,12 @@
815
  "node": ">=14.17"
816
  }
817
  },
 
 
 
 
 
 
818
  "node_modules/wrap-ansi": {
819
  "version": "7.0.0",
820
  "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
 
8
  "name": "client",
9
  "version": "0.0.1",
10
  "dependencies": {
11
+ "@huggingface/hub": "^2.6.12",
12
+ "@types/prompts": "^2.4.9",
13
+ "dotenv": "^17.2.3",
14
+ "prompts": "^2.4.2",
15
  "table": "^6.9.0",
16
  "yargs": "^17.7.2"
17
  },
 
463
  "node": ">=18"
464
  }
465
  },
466
+ "node_modules/@huggingface/hub": {
467
+ "version": "2.6.12",
468
+ "resolved": "https://registry.npmjs.org/@huggingface/hub/-/hub-2.6.12.tgz",
469
+ "integrity": "sha512-/AZN2LAtrt4B8S83/Ru4wMorHY4NPwIYXo60SkuD6c/Mr135t1UqffD1vqcqtDYVf0hhLiyVmm1LMU1CXi8iKQ==",
470
+ "license": "MIT",
471
+ "dependencies": {
472
+ "@huggingface/tasks": "^0.19.50"
473
+ },
474
+ "bin": {
475
+ "hfjs": "dist/cli.js"
476
+ },
477
+ "engines": {
478
+ "node": ">=18"
479
+ },
480
+ "optionalDependencies": {
481
+ "cli-progress": "^3.12.0"
482
+ }
483
+ },
484
+ "node_modules/@huggingface/tasks": {
485
+ "version": "0.19.50",
486
+ "resolved": "https://registry.npmjs.org/@huggingface/tasks/-/tasks-0.19.50.tgz",
487
+ "integrity": "sha512-kHrfiDsJttkuwpdp7PgFiFHaK9rj+COJTIZ+221gk9vdF4B5QySW7hQT4aOGkwwZP+4qbXGhjMNGg/bxOq+LwA==",
488
+ "license": "MIT"
489
+ },
490
+ "node_modules/@types/node": {
491
+ "version": "24.7.2",
492
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz",
493
+ "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==",
494
+ "license": "MIT",
495
+ "dependencies": {
496
+ "undici-types": "~7.14.0"
497
+ }
498
+ },
499
+ "node_modules/@types/prompts": {
500
+ "version": "2.4.9",
501
+ "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz",
502
+ "integrity": "sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==",
503
+ "license": "MIT",
504
+ "dependencies": {
505
+ "@types/node": "*",
506
+ "kleur": "^3.0.3"
507
+ }
508
+ },
509
  "node_modules/@types/yargs": {
510
  "version": "17.0.33",
511
  "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
 
572
  "node": ">=8"
573
  }
574
  },
575
+ "node_modules/cli-progress": {
576
+ "version": "3.12.0",
577
+ "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
578
+ "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
579
+ "license": "MIT",
580
+ "optional": true,
581
+ "dependencies": {
582
+ "string-width": "^4.2.3"
583
+ },
584
+ "engines": {
585
+ "node": ">=4"
586
+ }
587
+ },
588
  "node_modules/cliui": {
589
  "version": "8.0.1",
590
  "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
 
617
  "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
618
  "license": "MIT"
619
  },
620
+ "node_modules/dotenv": {
621
+ "version": "17.2.3",
622
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
623
+ "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
624
+ "license": "BSD-2-Clause",
625
+ "engines": {
626
+ "node": ">=12"
627
+ },
628
+ "funding": {
629
+ "url": "https://dotenvx.com"
630
+ }
631
+ },
632
  "node_modules/emoji-regex": {
633
  "version": "8.0.0",
634
  "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
 
760
  "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
761
  "license": "MIT"
762
  },
763
+ "node_modules/kleur": {
764
+ "version": "3.0.3",
765
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
766
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
767
+ "license": "MIT",
768
+ "engines": {
769
+ "node": ">=6"
770
+ }
771
+ },
772
  "node_modules/lodash.truncate": {
773
  "version": "4.4.2",
774
  "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
775
  "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
776
  "license": "MIT"
777
  },
778
+ "node_modules/prompts": {
779
+ "version": "2.4.2",
780
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
781
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
782
+ "license": "MIT",
783
+ "dependencies": {
784
+ "kleur": "^3.0.3",
785
+ "sisteransi": "^1.0.5"
786
+ },
787
+ "engines": {
788
+ "node": ">= 6"
789
+ }
790
+ },
791
  "node_modules/require-directory": {
792
  "version": "2.1.1",
793
  "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
 
816
  "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
817
  }
818
  },
819
+ "node_modules/sisteransi": {
820
+ "version": "1.0.5",
821
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
822
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
823
+ "license": "MIT"
824
+ },
825
  "node_modules/slice-ansi": {
826
  "version": "4.0.0",
827
  "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
 
915
  "node": ">=14.17"
916
  }
917
  },
918
+ "node_modules/undici-types": {
919
+ "version": "7.14.0",
920
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz",
921
+ "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==",
922
+ "license": "MIT"
923
+ },
924
  "node_modules/wrap-ansi": {
925
  "version": "7.0.0",
926
  "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
client/package.json CHANGED
@@ -7,6 +7,10 @@
7
  "cli": "tsx src/index.ts"
8
  },
9
  "dependencies": {
 
 
 
 
10
  "table": "^6.9.0",
11
  "yargs": "^17.7.2"
12
  },
 
7
  "cli": "tsx src/index.ts"
8
  },
9
  "dependencies": {
10
+ "@huggingface/hub": "^2.6.12",
11
+ "@types/prompts": "^2.4.9",
12
+ "dotenv": "^17.2.3",
13
+ "prompts": "^2.4.2",
14
  "table": "^6.9.0",
15
  "yargs": "^17.7.2"
16
  },
client/src/hf-api.ts ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Hugging Face API Client
3
+ *
4
+ * Uses the official @huggingface/hub library to search and retrieve models
5
+ */
6
+
7
+ import { listModels } from "@huggingface/hub";
8
+ import type { ModelEntry, PipelineType } from "@huggingface/hub";
9
+ import { config as dotenvConfig } from "dotenv";
10
+
11
+ // Load environment variables from .env file
12
+ dotenvConfig();
13
+
14
+ export interface SearchModelsOptions {
15
+ task?: PipelineType;
16
+ search?: string;
17
+ limit?: number;
18
+ }
19
+
20
+ /**
21
+ * Search models on Hugging Face Hub
22
+ */
23
+ export async function searchModels(options: SearchModelsOptions = {}): Promise<ModelEntry[]> {
24
+ const { task, search, limit } = options;
25
+
26
+ const resultGenerator = listModels({
27
+ search: {
28
+ task: task,
29
+ query: search,
30
+ tags: ["transformers.js"]
31
+ },
32
+ limit: limit,
33
+ credentials: {
34
+ accessToken: process.env.HF_TOKEN,
35
+ },
36
+ });
37
+
38
+ const models: ModelEntry[] = [];
39
+ // Fetch models using the official HF Hub library
40
+ for await (const model of resultGenerator) {
41
+ // Filter out private and gated models
42
+ if (!model.private && !model.gated) {
43
+ models.push(model);
44
+ }
45
+
46
+ // Stop when we reach the limit (if specified)
47
+ if (limit !== undefined && models.length >= limit) {
48
+ break;
49
+ }
50
+ }
51
+
52
+ return models;
53
+ }
54
+
55
+ /**
56
+ * Format model for display
57
+ */
58
+ export function formatModel(model: ModelEntry): string {
59
+ const downloads = model.downloads ? `${(model.downloads / 1000).toFixed(1)}k` : "N/A";
60
+ const likes = model.likes || 0;
61
+ const task = model.task || "unknown";
62
+ const name = model.name || model.id;
63
+
64
+ return `${name} | Task: ${task} | Downloads: ${downloads} | Likes: ${likes}`;
65
+ }
client/src/index.ts CHANGED
@@ -3,6 +3,9 @@
3
  import yargs from "yargs";
4
  import { hideBin } from "yargs/helpers";
5
  import { table } from "table";
 
 
 
6
 
7
  const SERVER_URL = process.env.BENCH_SERVER_URL || "http://localhost:7860";
8
 
@@ -264,6 +267,185 @@ yargs(hideBin(process.argv))
264
  }
265
  }
266
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  .demandCommand(1, "You need to specify a command")
268
  .help()
269
  .alias("h", "help")
 
3
  import yargs from "yargs";
4
  import { hideBin } from "yargs/helpers";
5
  import { table } from "table";
6
+ import prompts from "prompts";
7
+ import { searchModels, formatModel } from "./hf-api.js";
8
+ import type { ModelEntry } from "@huggingface/hub";
9
 
10
  const SERVER_URL = process.env.BENCH_SERVER_URL || "http://localhost:7860";
11
 
 
267
  }
268
  }
269
  )
270
+ .command(
271
+ "batch <task> [query]",
272
+ "Search HuggingFace models and submit benchmarks for them",
273
+ (yargs) => {
274
+ return yargs
275
+ .positional("task", {
276
+ describe: "Task type (e.g., feature-extraction, text-classification, fill-mask)",
277
+ type: "string",
278
+ demandOption: true,
279
+ })
280
+ .positional("query", {
281
+ describe: "Optional search query to filter model names",
282
+ type: "string",
283
+ })
284
+ .option("limit", {
285
+ describe: "Maximum number of models to benchmark",
286
+ type: "number",
287
+ })
288
+ .option("sort", {
289
+ describe: "Sort models by",
290
+ choices: ["downloads", "likes", "lastModified", "trending"] as const,
291
+ default: "downloads" as const,
292
+ })
293
+ .option("platform", {
294
+ describe: "Platform(s) to run on (can specify multiple)",
295
+ type: "array",
296
+ default: ["node"],
297
+ })
298
+ .option("mode", {
299
+ describe: "Cache mode(s) (can specify multiple)",
300
+ type: "array",
301
+ default: ["warm"],
302
+ })
303
+ .option("repeats", {
304
+ describe: "Number of times to repeat the benchmark",
305
+ type: "number",
306
+ default: 3,
307
+ })
308
+ .option("batch-size", {
309
+ describe: "Batch size(s) for inference (can specify multiple)",
310
+ type: "array",
311
+ default: [1],
312
+ })
313
+ .option("device", {
314
+ describe: "Device(s) for platform (can specify multiple)",
315
+ type: "array",
316
+ default: ["webgpu"],
317
+ })
318
+ .option("browser", {
319
+ describe: "Browser(s) for web platform (can specify multiple)",
320
+ type: "array",
321
+ default: ["chromium"],
322
+ })
323
+ .option("yes", {
324
+ alias: "y",
325
+ describe: "Skip confirmation prompt",
326
+ type: "boolean",
327
+ default: false,
328
+ });
329
+ },
330
+ async (argv) => {
331
+ console.log(`Searching for ${argv.task} models${argv.query ? ` matching "${argv.query}"` : ""}...\n`);
332
+
333
+ const models = await searchModels({
334
+ task: argv.task,
335
+ search: argv.query,
336
+ limit: argv.limit,
337
+ sort: argv.sort,
338
+ });
339
+
340
+ if (models.length === 0) {
341
+ console.log("No models found.");
342
+ return;
343
+ }
344
+
345
+ console.log(`Found ${models.length} models:\n`);
346
+ models.forEach((model, index) => {
347
+ console.log(`${index + 1}. ${formatModel(model)}`);
348
+ });
349
+
350
+ // Generate all combinations
351
+ const platforms = argv.platform as string[];
352
+ const modes = argv.mode as string[];
353
+ const batchSizes = argv.batchSize as number[];
354
+ const devices = argv.device as string[];
355
+ const browsers = argv.browser as string[];
356
+
357
+ const combinations: Array<{
358
+ modelId: string;
359
+ platform: string;
360
+ mode: string;
361
+ batchSize: number;
362
+ device: string;
363
+ browser: string;
364
+ }> = [];
365
+
366
+ for (const model of models) {
367
+ for (const platform of platforms) {
368
+ for (const mode of modes) {
369
+ for (const batchSize of batchSizes) {
370
+ for (const device of devices) {
371
+ for (const browser of browsers) {
372
+ combinations.push({
373
+ modelId: (model as any).name || model.id,
374
+ platform,
375
+ mode,
376
+ batchSize,
377
+ device,
378
+ browser,
379
+ });
380
+ }
381
+ }
382
+ }
383
+ }
384
+ }
385
+ }
386
+
387
+ console.log(`\nπŸ“Š Benchmark Plan:`);
388
+ console.log(` Models: ${models.length}`);
389
+ console.log(` Platforms: ${platforms.join(", ")}`);
390
+ console.log(` Modes: ${modes.join(", ")}`);
391
+ console.log(` Batch Sizes: ${batchSizes.join(", ")}`);
392
+ console.log(` Devices: ${devices.join(", ")}`);
393
+ console.log(` Browsers: ${browsers.join(", ")}`);
394
+ console.log(` Total benchmarks: ${combinations.length}`);
395
+
396
+ // Ask for confirmation unless -y flag is used
397
+ if (!argv.yes) {
398
+ const response = await prompts({
399
+ type: "confirm",
400
+ name: "proceed",
401
+ message: `Proceed with submitting ${combinations.length} benchmark(s)?`,
402
+ initial: true,
403
+ });
404
+
405
+ if (!response.proceed) {
406
+ console.log("\nCancelled.");
407
+ return;
408
+ }
409
+ }
410
+
411
+ console.log(`\nSubmitting ${combinations.length} benchmarks...`);
412
+
413
+ const submitted: string[] = [];
414
+ const failed: Array<{ combo: string; error: string }> = [];
415
+
416
+ for (const combo of combinations) {
417
+ try {
418
+ const options: SubmitOptions = {
419
+ modelId: combo.modelId,
420
+ task: argv.task,
421
+ platform: combo.platform as "node" | "web",
422
+ mode: combo.mode as "warm" | "cold",
423
+ repeats: argv.repeats,
424
+ batchSize: combo.batchSize,
425
+ device: combo.device,
426
+ browser: combo.browser as "chromium" | "firefox" | "webkit",
427
+ };
428
+
429
+ const result = await submitBenchmark(options);
430
+ const desc = `${combo.modelId} [${combo.platform}/${combo.device}/${combo.mode}/b${combo.batchSize}]`;
431
+ submitted.push(desc);
432
+ console.log(`βœ“ Queued: ${desc} (${result.id})`);
433
+ } catch (error: any) {
434
+ const desc = `${combo.modelId} [${combo.platform}/${combo.device}]`;
435
+ failed.push({ combo: desc, error: error.message });
436
+ console.log(`βœ— Failed: ${desc} - ${error.message}`);
437
+ }
438
+ }
439
+
440
+ console.log(`\nπŸ“Š Summary:`);
441
+ console.log(` βœ“ Submitted: ${submitted.length}`);
442
+ console.log(` βœ— Failed: ${failed.length}`);
443
+
444
+ if (submitted.length > 0) {
445
+ console.log(`\nCheck status with: bench-client queue`);
446
+ }
447
+ }
448
+ )
449
  .demandCommand(1, "You need to specify a command")
450
  .help()
451
  .alias("h", "help")