Spaces:
Runtime error
Runtime error
Update batch command to accept an option to exclude existing benchmarks
Browse files- 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: ${
|
| 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 ${
|
| 455 |
initial: true,
|
| 456 |
});
|
| 457 |
|
|
@@ -461,12 +596,12 @@ yargs(hideBin(process.argv))
|
|
| 461 |
}
|
| 462 |
}
|
| 463 |
|
| 464 |
-
console.log(`\nSubmitting ${
|
| 465 |
|
| 466 |
const submitted: string[] = [];
|
| 467 |
const failed: Array<{ combo: string; error: string }> = [];
|
| 468 |
|
| 469 |
-
for (const combo of
|
| 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";
|