dsh-livebench-panel 0.2.11 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +22 -15
- package/lib/index.js +76 -10
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -587,21 +587,28 @@ window.__ModuleLoader__.load({
|
|
|
587
587
|
h("td", null, model),
|
|
588
588
|
matrix.tasks.map((taskKeyCol) => {
|
|
589
589
|
const row = matrix.cells.get(`${model}__@__${taskKeyCol}`);
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
title = "有答案但尚未判分";
|
|
599
|
-
} else {
|
|
600
|
-
cellText = `${row.score.toFixed(1)} (${row.judged}/${row.total})`;
|
|
601
|
-
if (row.errors > 0) title += ` · 其中 ${row.errors} 题 API 失败计 0 分`;
|
|
602
|
-
}
|
|
590
|
+
// 单元格:上方正确率,下方 (-没做/设定/总)。
|
|
591
|
+
// 没做/没做完(访问失败、网络中断)不计入正确率分母。
|
|
592
|
+
const subStyle = { fontSize: "10.5px", color: "var(--dsw-alias-label-tertiary)", fontWeight: "400" };
|
|
593
|
+
const sub = `(-${row ? (row.notDone ?? 0) : 0}/${row ? (row.configured ?? 0) : 0}/${row ? row.total : 0})`;
|
|
594
|
+
if (!row) {
|
|
595
|
+
return h("td", { key: taskKeyCol, className: c("score"), title: "该模型未执行此任务(无任何记录)" },
|
|
596
|
+
h("div", null, "×"),
|
|
597
|
+
h("div", { style: subStyle }, sub));
|
|
603
598
|
}
|
|
604
|
-
|
|
599
|
+
const allFailed = row.answered > 0 && row.errors >= row.answered;
|
|
600
|
+
const title = allFailed
|
|
601
|
+
? `该任务 ${row.errors} 题全部访问失败(未做完),不计入正确率`
|
|
602
|
+
: `做完 ${row.done ?? 0} 题,没做/失败 ${row.notDone ?? 0} 题` +
|
|
603
|
+
(row.errors > 0 ? `(其中 ${row.errors} 题 API 失败)` : "");
|
|
604
|
+
if (allFailed || row.judged === 0) {
|
|
605
|
+
return h("td", { key: taskKeyCol, className: c("score"), title },
|
|
606
|
+
h("div", null, "—"),
|
|
607
|
+
h("div", { style: subStyle }, sub));
|
|
608
|
+
}
|
|
609
|
+
return h("td", { key: taskKeyCol, className: c("score"), title },
|
|
610
|
+
h("div", null, row.score.toFixed(1)),
|
|
611
|
+
h("div", { style: subStyle }, sub));
|
|
605
612
|
}),
|
|
606
613
|
h("td", null, h("button", {
|
|
607
614
|
className: c("btnGhost") + " " + c("btn"), title: "删除该模型的所有评测成绩",
|
|
@@ -658,7 +665,7 @@ window.__ModuleLoader__.load({
|
|
|
658
665
|
"提示:回答为 $ERROR$ 表示该题 API 调用失败(网络/鉴权/参数问题)计 0 分,可在上方日志区查看具体错误;zebra_puzzle(逻辑谜题)是公认最难的任务,低分属正常现象。"),
|
|
659
666
|
),
|
|
660
667
|
h("p", { className: c("hint") },
|
|
661
|
-
"
|
|
668
|
+
"格式说明:单元格上方为正确率(= 做对 / 做完),下方括号 (-没做/设定/总):没做或没做完的题(访问失败、网络中断、未跑到)不计入正确率。× 表示该模型完全没跑过该任务。正式榜单可用 release 2024-11-25。"),
|
|
662
669
|
);
|
|
663
670
|
}
|
|
664
671
|
/**
|
package/lib/index.js
CHANGED
|
@@ -307,6 +307,22 @@ function readDirSafe(dir) {
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
/** 列出目录下的【文件】(可按扩展名过滤)——注意 readDirSafe 只列目录。 */
|
|
311
|
+
function readFilesSafe(dir, ext) {
|
|
312
|
+
try {
|
|
313
|
+
return readdirSync(dir).filter((f) => {
|
|
314
|
+
try {
|
|
315
|
+
if (!statSync(join(dir, f)).isFile()) return false;
|
|
316
|
+
return ext === undefined || f.endsWith(ext);
|
|
317
|
+
} catch {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
} catch {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
310
326
|
function readdirNames(dir) {
|
|
311
327
|
try {
|
|
312
328
|
return readdirSync(dir).filter((entry) => {
|
|
@@ -506,11 +522,22 @@ async function readTextFileAsync(path) {
|
|
|
506
522
|
async function computeResults(dataDir) {
|
|
507
523
|
const now = Date.now();
|
|
508
524
|
if (resultsCache.data !== null && now - resultsCache.at < 5000) return resultsCache.data;
|
|
509
|
-
const judged = new Map(); // `${model}
|
|
525
|
+
const judged = new Map(); // `${model}\u0000${task}` -> {model, category, task, sum, n, time}
|
|
510
526
|
const totals = new Map(); // task -> question count
|
|
511
|
-
const answers = new Map(); // `${model}
|
|
527
|
+
const answers = new Map(); // `${model}\u0000${task}` -> {answered, errors}
|
|
512
528
|
const catByTask = new Map();
|
|
513
529
|
if (!existsSync(dataDir)) return { rows: [], taskTotals: {} };
|
|
530
|
+
// 读取每次评测的配置范围(displayName -> {benchNames, begin, end})
|
|
531
|
+
const runMeta = new Map();
|
|
532
|
+
try {
|
|
533
|
+
const metaDir = join(dataDir, ".dsh_runs");
|
|
534
|
+
for (const f of readdirSync(metaDir)) {
|
|
535
|
+
if (!f.endsWith(".json")) continue;
|
|
536
|
+
try {
|
|
537
|
+
runMeta.set(f.slice(0, -5), JSON.parse(readFileSync(join(metaDir, f), "utf8")));
|
|
538
|
+
} catch { /* 单个元数据损坏忽略 */ }
|
|
539
|
+
}
|
|
540
|
+
} catch { /* 无元数据目录(历史数据) */ }
|
|
514
541
|
for (const category of readDirSafe(dataDir)) {
|
|
515
542
|
const catDir = join(dataDir, category);
|
|
516
543
|
for (const task of readDirSafe(catDir)) {
|
|
@@ -519,12 +546,11 @@ async function computeResults(dataDir) {
|
|
|
519
546
|
const qText = await readTextFileAsync(join(taskDir, "question.jsonl"));
|
|
520
547
|
totals.set(task, qText === null ? 0 : qText.split("\n").reduce((n, l) => n + (l.trim().length > 0 ? 1 : 0), 0));
|
|
521
548
|
const answerDir = join(taskDir, "model_answer");
|
|
522
|
-
for (const file of
|
|
523
|
-
if (!file.endsWith(".jsonl")) continue;
|
|
549
|
+
for (const file of readFilesSafe(answerDir, ".jsonl")) {
|
|
524
550
|
const model = file.slice(0, -".jsonl".length);
|
|
525
551
|
const text = await readTextFileAsync(join(answerDir, file));
|
|
526
552
|
if (text === null) continue;
|
|
527
|
-
const key = `${model}
|
|
553
|
+
const key = `${model}\u0000${task}`;
|
|
528
554
|
const stat = answers.get(key) ?? { answered: 0, errors: 0 };
|
|
529
555
|
for (const line of text.split("\n")) {
|
|
530
556
|
if (line.trim().length === 0) continue;
|
|
@@ -542,7 +568,7 @@ async function computeResults(dataDir) {
|
|
|
542
568
|
const model = typeof row.model === "string" ? row.model : null;
|
|
543
569
|
const score = typeof row.score === "number" ? row.score : Number(row.score);
|
|
544
570
|
if (model === null || !Number.isFinite(score) || score < 0) continue;
|
|
545
|
-
const key = `${model}
|
|
571
|
+
const key = `${model}\u0000${task}`;
|
|
546
572
|
const entry = judged.get(key) ?? { model, category, task, sum: 0, n: 0, time: 0 };
|
|
547
573
|
entry.sum += score;
|
|
548
574
|
entry.n += 1;
|
|
@@ -559,18 +585,45 @@ async function computeResults(dataDir) {
|
|
|
559
585
|
const rows = [...keys].map((key) => {
|
|
560
586
|
const entry = judged.get(key);
|
|
561
587
|
const stat = answers.get(key) ?? { answered: 0, errors: 0 };
|
|
562
|
-
const [model, task] = key.split("
|
|
588
|
+
const [model, task] = key.split("\u0000");
|
|
563
589
|
const category = entry?.category ?? catByTask.get(task) ?? "";
|
|
590
|
+
const total = totals.get(task) ?? 0;
|
|
591
|
+
const judgedCount = entry?.n ?? 0;
|
|
592
|
+
// 做完的题 = 实际产出的有效答案(尝试数 - 访问失败数)
|
|
593
|
+
const done = Math.max(0, stat.answered - stat.errors);
|
|
594
|
+
// 设定题数:优先用评测元数据的范围(end 含端点),否则用尝试/判分中的较大值
|
|
595
|
+
let configured = Math.max(stat.answered, judgedCount);
|
|
596
|
+
const meta = runMeta.get(model);
|
|
597
|
+
if (meta !== undefined) {
|
|
598
|
+
const inScope = (meta.benchNames || []).some((bn) => {
|
|
599
|
+
const parts = String(bn).split("/").filter((s) => s.length > 0);
|
|
600
|
+
if (parts.length >= 3) return parts[1] === category && parts[2] === task;
|
|
601
|
+
if (parts.length === 2) return parts[1] === category;
|
|
602
|
+
return true;
|
|
603
|
+
});
|
|
604
|
+
if (inScope) {
|
|
605
|
+
const hasRange = meta.begin !== null && meta.begin !== undefined && meta.end !== null && meta.end !== undefined;
|
|
606
|
+
configured = hasRange ? Math.min(total, Number(meta.end) - Number(meta.begin) + 1) : total;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (configured < stat.answered) configured = stat.answered;
|
|
610
|
+
const notDone = Math.max(0, configured - done);
|
|
611
|
+
// 正确率 = 做对的题 / 已判分的做完题(访问失败/未做的题不计入分母)
|
|
612
|
+
const judgedDone = Math.max(0, judgedCount - stat.errors);
|
|
613
|
+
const score = entry && judgedDone > 0 ? (entry.sum / judgedDone) * 100 : null;
|
|
564
614
|
return {
|
|
565
615
|
model,
|
|
566
616
|
category,
|
|
567
617
|
task,
|
|
568
|
-
score
|
|
618
|
+
score,
|
|
569
619
|
judged: entry?.n ?? 0,
|
|
570
|
-
total
|
|
620
|
+
total,
|
|
571
621
|
time: entry && entry.time > 0 ? entry.time : null,
|
|
572
622
|
answered: stat.answered,
|
|
573
623
|
errors: stat.errors,
|
|
624
|
+
done,
|
|
625
|
+
configured,
|
|
626
|
+
notDone,
|
|
574
627
|
};
|
|
575
628
|
});
|
|
576
629
|
const result = { rows, taskTotals: Object.fromEntries(totals) };
|
|
@@ -808,6 +861,19 @@ function apply(ctx) {
|
|
|
808
861
|
}
|
|
809
862
|
}
|
|
810
863
|
|
|
864
|
+
// 记录本次评测的配置范围:/results 用它计算每个任务的"设定题数"
|
|
865
|
+
// (未做/失败的题数 = 设定题数 - 实际产出的有效答案数)。
|
|
866
|
+
try {
|
|
867
|
+
const metaDir = join(layout.dataDir, ".dsh_runs");
|
|
868
|
+
if (!existsSync(metaDir)) mkdirSync(metaDir, { recursive: true });
|
|
869
|
+
writeFileSync(join(metaDir, displayName + ".json"), JSON.stringify({
|
|
870
|
+
benchNames: benchNames !== null ? benchNames : [benchParts.join("/")],
|
|
871
|
+
begin: body.begin ?? null,
|
|
872
|
+
end: body.end ?? null,
|
|
873
|
+
startedAt: new Date().toISOString(),
|
|
874
|
+
}), "utf8");
|
|
875
|
+
} catch { /* 元数据写入失败不影响评测 */ }
|
|
876
|
+
|
|
811
877
|
const child = spawn(layout.pythonExe, args, {
|
|
812
878
|
cwd: layout.livebenchDir,
|
|
813
879
|
env,
|
|
@@ -865,7 +931,7 @@ function apply(ctx) {
|
|
|
865
931
|
startRun(retryBody, record.autoRetries + 1).catch((e) => {
|
|
866
932
|
appendLog(record, `[自动补跑启动失败] ${e.message}`);
|
|
867
933
|
});
|
|
868
|
-
}, 15000);
|
|
934
|
+
}, [15000, 60000, 180000][Math.min(record.autoRetries, 2)]);
|
|
869
935
|
}
|
|
870
936
|
} catch (e) {
|
|
871
937
|
record.log.push(`[自动补跑检查失败] ${e.message}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-livebench-panel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
4
|
"description": "DSH web plugin: a LiveBench tab in the Trajectory view (right of 对话/轨迹). Run LiveBench evaluations against every model configured in the DeepSeek Harness — pick provider/model, category, task, release and question range from dropdowns, watch progress, and read scores in place.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|