whitphx HF Staff commited on
Commit
62d9954
ยท
1 Parent(s): 13b1fcc

Update batch command to accept an option to exclude existing benchmarks

Browse files
Files changed (1) hide show
  1. client/src/index.ts +139 -4
client/src/index.ts CHANGED
@@ -7,6 +7,7 @@ import prompts from "prompts";
7
  import { searchModels, formatModel } from "./hf-api.js";
8
  import { PIPELINE_DATA } from "@huggingface/tasks";
9
  import type { ModelEntry } from "@huggingface/hub";
 
10
 
11
  const SERVER_URL = process.env.BENCH_SERVER_URL || "http://localhost:7860";
12
 
@@ -89,6 +90,89 @@ async function pollBenchmark(id: string, interval = 2000): Promise<any> {
89
  });
90
  }
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  yargs(hideBin(process.argv))
93
  .command(
94
  "submit <modelId> <task>",
@@ -327,6 +411,20 @@ yargs(hideBin(process.argv))
327
  describe: "Skip confirmation prompt",
328
  type: "boolean",
329
  default: false,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  });
331
  },
332
  async (argv) => {
@@ -436,6 +534,43 @@ yargs(hideBin(process.argv))
436
  }
437
  }
438
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  console.log(`\n๐Ÿ“Š Benchmark Plan:`);
440
  console.log(` Models: ${allModels.length}`);
441
  console.log(` Platforms: ${platforms.join(", ")}`);
@@ -444,14 +579,14 @@ yargs(hideBin(process.argv))
444
  console.log(` Devices: ${devices.join(", ")}`);
445
  console.log(` Browsers: ${browsers.join(", ")}`);
446
  console.log(` DTypes: ${dtypes.join(", ")}`);
447
- console.log(` Total benchmarks: ${combinations.length}`);
448
 
449
  // Ask for confirmation unless -y flag is used
450
  if (!argv.yes) {
451
  const response = await prompts({
452
  type: "confirm",
453
  name: "proceed",
454
- message: `Proceed with submitting ${combinations.length} benchmark(s)?`,
455
  initial: true,
456
  });
457
 
@@ -461,12 +596,12 @@ yargs(hideBin(process.argv))
461
  }
462
  }
463
 
464
- console.log(`\nSubmitting ${combinations.length} benchmarks...`);
465
 
466
  const submitted: string[] = [];
467
  const failed: Array<{ combo: string; error: string }> = [];
468
 
469
- for (const combo of combinations) {
470
  try {
471
  // Use task from model if not specified in command
472
  const modelTask = task || (combo as any).task || "feature-extraction";
 
7
  import { searchModels, formatModel } from "./hf-api.js";
8
  import { PIPELINE_DATA } from "@huggingface/tasks";
9
  import type { ModelEntry } from "@huggingface/hub";
10
+ import { listFiles } from "@huggingface/hub";
11
 
12
  const SERVER_URL = process.env.BENCH_SERVER_URL || "http://localhost:7860";
13
 
 
90
  });
91
  }
92
 
93
+ /**
94
+ * Fetch existing benchmark file paths from HuggingFace Dataset
95
+ * Returns a Set of file paths for quick lookup
96
+ */
97
+ async function fetchExistingBenchmarks(datasetRepo: string, token?: string): Promise<Set<string>> {
98
+ console.log(`\nFetching existing benchmarks from ${datasetRepo}...`);
99
+ const existingFiles = new Set<string>();
100
+
101
+ try {
102
+ for await (const file of listFiles({
103
+ repo: {
104
+ type: "dataset",
105
+ name: datasetRepo,
106
+ },
107
+ credentials: token ? { accessToken: token } : undefined,
108
+ })) {
109
+ if (file.path.endsWith(".json")) {
110
+ existingFiles.add(file.path);
111
+ }
112
+ }
113
+ console.log(` Found ${existingFiles.size} existing benchmarks\n`);
114
+ } catch (error: any) {
115
+ console.warn(` Warning: Failed to fetch existing benchmarks: ${error.message}`);
116
+ console.warn(` Proceeding without exclusion filter\n`);
117
+ }
118
+
119
+ return existingFiles;
120
+ }
121
+
122
+ /**
123
+ * Generate the expected file path for a benchmark combination
124
+ * Must match the path generation logic in the server
125
+ */
126
+ function generateBenchmarkPath(combo: {
127
+ task: string;
128
+ modelId: string;
129
+ platform: string;
130
+ mode: string;
131
+ device: string;
132
+ dtype: string;
133
+ batchSize: number;
134
+ browser?: string;
135
+ headed?: boolean;
136
+ }): string {
137
+ // Extract org and model name from modelId
138
+ const parts = combo.modelId.split("/");
139
+ const org = parts.length > 1 ? parts[0] : "default";
140
+ const modelName = parts.length > 1 ? parts[1] : parts[0];
141
+
142
+ // Build path components similar to server logic
143
+ const pathParts = [
144
+ combo.task,
145
+ org,
146
+ modelName,
147
+ ];
148
+
149
+ // Build filename parts
150
+ const filenameParts = [
151
+ combo.platform,
152
+ combo.mode,
153
+ combo.device,
154
+ `b${combo.batchSize}`,
155
+ ];
156
+
157
+ // Add dtype if specified and not fp32 (default)
158
+ if (combo.dtype && combo.dtype !== "fp32") {
159
+ filenameParts.push(combo.dtype);
160
+ }
161
+
162
+ // Add browser for web platform
163
+ if (combo.platform === "web" && combo.browser) {
164
+ filenameParts.push(combo.browser);
165
+ }
166
+
167
+ // Add headed indicator for web platform
168
+ if (combo.platform === "web" && combo.headed) {
169
+ filenameParts.push("headed");
170
+ }
171
+
172
+ const filename = filenameParts.join("_") + ".json";
173
+ return [...pathParts, filename].join("/");
174
+ }
175
+
176
  yargs(hideBin(process.argv))
177
  .command(
178
  "submit <modelId> <task>",
 
411
  describe: "Skip confirmation prompt",
412
  type: "boolean",
413
  default: false,
414
+ })
415
+ .option("exclude-existing", {
416
+ alias: "e",
417
+ describe: "Exclude benchmarks that already exist in the HF Dataset",
418
+ type: "boolean",
419
+ default: false,
420
+ })
421
+ .option("hf-dataset-repo", {
422
+ describe: "HuggingFace Dataset repository to check for existing benchmarks",
423
+ type: "string",
424
+ })
425
+ .option("hf-token", {
426
+ describe: "HuggingFace API token (for private datasets)",
427
+ type: "string",
428
  });
429
  },
430
  async (argv) => {
 
534
  }
535
  }
536
 
537
+ // Filter out existing benchmarks if requested
538
+ let filteredCombinations = combinations;
539
+ if (argv.excludeExisting) {
540
+ const datasetRepo = argv.hfDatasetRepo || process.env.HF_DATASET_REPO;
541
+ const hfToken = argv.hfToken || process.env.HF_TOKEN;
542
+
543
+ if (!datasetRepo) {
544
+ console.error("\nโŒ Error: --exclude-existing requires --hf-dataset-repo or HF_DATASET_REPO env var");
545
+ process.exit(1);
546
+ }
547
+
548
+ const existingFiles = await fetchExistingBenchmarks(datasetRepo, hfToken);
549
+
550
+ // Filter combinations
551
+ filteredCombinations = combinations.filter((combo) => {
552
+ const modelTask = task || combo.task || "feature-extraction";
553
+ const benchmarkPath = generateBenchmarkPath({
554
+ task: modelTask,
555
+ modelId: combo.modelId,
556
+ platform: combo.platform,
557
+ mode: combo.mode,
558
+ device: combo.device,
559
+ dtype: combo.dtype,
560
+ batchSize: combo.batchSize,
561
+ browser: combo.browser,
562
+ headed: false, // Default, as it's not in the combination
563
+ });
564
+ return !existingFiles.has(benchmarkPath);
565
+ });
566
+
567
+ const excludedCount = combinations.length - filteredCombinations.length;
568
+ console.log(`\n๐Ÿ“Š Exclusion Filter:`);
569
+ console.log(` Total combinations: ${combinations.length}`);
570
+ console.log(` Existing benchmarks: ${excludedCount}`);
571
+ console.log(` New benchmarks to run: ${filteredCombinations.length}\n`);
572
+ }
573
+
574
  console.log(`\n๐Ÿ“Š Benchmark Plan:`);
575
  console.log(` Models: ${allModels.length}`);
576
  console.log(` Platforms: ${platforms.join(", ")}`);
 
579
  console.log(` Devices: ${devices.join(", ")}`);
580
  console.log(` Browsers: ${browsers.join(", ")}`);
581
  console.log(` DTypes: ${dtypes.join(", ")}`);
582
+ console.log(` Total benchmarks: ${filteredCombinations.length}`);
583
 
584
  // Ask for confirmation unless -y flag is used
585
  if (!argv.yes) {
586
  const response = await prompts({
587
  type: "confirm",
588
  name: "proceed",
589
+ message: `Proceed with submitting ${filteredCombinations.length} benchmark(s)?`,
590
  initial: true,
591
  });
592
 
 
596
  }
597
  }
598
 
599
+ console.log(`\nSubmitting ${filteredCombinations.length} benchmarks...`);
600
 
601
  const submitted: string[] = [];
602
  const failed: Array<{ combo: string; error: string }> = [];
603
 
604
+ for (const combo of filteredCombinations) {
605
  try {
606
  // Use task from model if not specified in command
607
  const modelTask = task || (combo as any).task || "feature-extraction";