dsh-livebench-panel 0.2.21 → 0.2.22
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 +5 -1
- package/lib/index.js +45 -2
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -805,10 +805,14 @@ window.__ModuleLoader__.load({
|
|
|
805
805
|
row.errors > 0 ? `${row.errors} 题 API 失败` : null,
|
|
806
806
|
emptyCount > 0 ? `${emptyCount} 题思考占满 max-tokens、未产出答案` : null,
|
|
807
807
|
].filter(Boolean).join(",");
|
|
808
|
+
const missing = Array.isArray(row.missingIndexes) ? row.missingIndexes : null;
|
|
809
|
+
const missingText = missing !== null && missing.length > 0
|
|
810
|
+
? `\n没做出来的题号(0 起,可复制去重跑):${missing.join("、")}`
|
|
811
|
+
: "";
|
|
808
812
|
const title = basePrefix + (allFailed
|
|
809
813
|
? `本次选择 ${row.configured ?? 0} 题,全部没做出来(${reasons}),不计入正确率 ${sub}`
|
|
810
814
|
: `本次选择 ${row.configured ?? 0} 题,做出来 ${row.done ?? 0} 题,没做出来 ${row.notDone ?? 0} 题 ${sub}`
|
|
811
|
-
+ (reasons ? `(其中 ${reasons})` : "")); if (allFailed || row.judged === 0) {
|
|
815
|
+
+ (reasons ? `(其中 ${reasons})` : "")) + missingText; if (allFailed || row.judged === 0) {
|
|
812
816
|
return h("td", { key: taskKeyCol, className: cls, title },
|
|
813
817
|
h("div", { className: c("dim") }, "—"),
|
|
814
818
|
h("div", { className: c("cellSub") }, sub));
|
package/lib/index.js
CHANGED
|
@@ -627,6 +627,33 @@ function isEmptyAnswerLine(line) {
|
|
|
627
627
|
return EMPTY_ANSWER_RE.test(line);
|
|
628
628
|
}
|
|
629
629
|
|
|
630
|
+
/** 从答案行里抽 question_id(只做正则,不 JSON.parse)。 */
|
|
631
|
+
const QUESTION_ID_RE = /"question_id"\s*:\s*"([A-Za-z0-9_.:-]{8,120})"/;
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Baseline 评测里"没做出来的题号"。
|
|
635
|
+
*
|
|
636
|
+
* 编号是 **该题库内的 0 起下标**,与「题目序号范围」的编号一致,所以可以直接复制去重跑。
|
|
637
|
+
* 范围先按题库实际题数裁剪 —— 「0-1000」这种越界输入不会把编号算歪(基准永远是实际题数)。
|
|
638
|
+
*
|
|
639
|
+
* @param {object|undefined} meta 运行元数据(需要 baselineIds / begin / end)
|
|
640
|
+
* @param {Set<string>} seen 答案文件里实际出现的 question_id
|
|
641
|
+
* @returns {number[]|null} 不是 baseline 运行时返回 null
|
|
642
|
+
*/
|
|
643
|
+
function missingBaselineIndexes(meta, seen) {
|
|
644
|
+
if (!meta || !Array.isArray(meta.baselineIds) || meta.baselineIds.length === 0) return null;
|
|
645
|
+
const ids = meta.baselineIds;
|
|
646
|
+
const hasBegin = meta.begin !== null && meta.begin !== undefined;
|
|
647
|
+
const hasEnd = meta.end !== null && meta.end !== undefined;
|
|
648
|
+
const from = hasBegin ? Math.max(0, Math.min(ids.length, Number(meta.begin))) : 0;
|
|
649
|
+
const to = hasEnd ? Math.max(0, Math.min(ids.length, Number(meta.end) + 1)) : ids.length;
|
|
650
|
+
const out = [];
|
|
651
|
+
for (let i = from; i < Math.max(from, to); i += 1) {
|
|
652
|
+
if (!seen.has(ids[i])) out.push(i);
|
|
653
|
+
}
|
|
654
|
+
return out;
|
|
655
|
+
}
|
|
656
|
+
|
|
630
657
|
async function readTextFileAsync(path) {
|
|
631
658
|
const fsPromises = await import("node:fs/promises");
|
|
632
659
|
try {
|
|
@@ -668,12 +695,21 @@ async function computeResults(dataDir) {
|
|
|
668
695
|
const text = await readTextFileAsync(join(answerDir, file));
|
|
669
696
|
if (text === null) continue;
|
|
670
697
|
const key = `${model}\u0000${task}`;
|
|
671
|
-
const stat = answers.get(key) ?? { answered: 0, errors: 0, empty: 0 };
|
|
698
|
+
const stat = answers.get(key) ?? { answered: 0, errors: 0, empty: 0, seen: null };
|
|
699
|
+
// baseline 评测要回答"少做了哪几个题",所以需要收集实际出现的 question_id。
|
|
700
|
+
// 只用正则抽 id(不 JSON.parse),对几百 KB~几 MB 的答案文件也够快。
|
|
701
|
+
const runMetaEntry = runMeta.get(model);
|
|
702
|
+
const trackIds = runMetaEntry && Array.isArray(runMetaEntry.baselineIds) && runMetaEntry.baselineIds.length > 0;
|
|
703
|
+
if (trackIds && stat.seen === null) stat.seen = new Set();
|
|
672
704
|
for (const line of text.split("\n")) {
|
|
673
705
|
if (line.trim().length === 0) continue;
|
|
674
706
|
stat.answered += 1;
|
|
675
707
|
if (line.includes('"$ERROR$"')) stat.errors += 1;
|
|
676
708
|
else if (isEmptyAnswerLine(line)) stat.empty += 1;
|
|
709
|
+
if (trackIds) {
|
|
710
|
+
const idMatch = line.match(QUESTION_ID_RE);
|
|
711
|
+
if (idMatch !== null) stat.seen.add(idMatch[1]);
|
|
712
|
+
}
|
|
677
713
|
}
|
|
678
714
|
// 空文件(尚未产出答案,或成绩已被删除只留下 0 字节壳)不计入结果:
|
|
679
715
|
// 否则删掉的记录会在下一次扫描时凭“文件名仍在”重新冒出来。
|
|
@@ -753,6 +789,9 @@ async function computeResults(dataDir) {
|
|
|
753
789
|
// 正确率 = 做对的题 / **做出来的题**(API 失败、思考吃满 token 没产出答案的都不进分母)
|
|
754
790
|
const judgedDone = Math.max(0, judgedCount - notProduced);
|
|
755
791
|
const score = entry && judgedDone > 0 ? (entry.sum / judgedDone) * 100 : null;
|
|
792
|
+
// Baseline 评测:算出**具体哪几个题号没做出来**(题号 = 该题库内的 0 起下标,
|
|
793
|
+
// 与「题目序号范围」的编号一致,方便直接复制去重跑)。
|
|
794
|
+
const missingIndexes = missingBaselineIndexes(meta, stat.seen ?? new Set());
|
|
756
795
|
return {
|
|
757
796
|
model,
|
|
758
797
|
category,
|
|
@@ -768,6 +807,8 @@ async function computeResults(dataDir) {
|
|
|
768
807
|
done,
|
|
769
808
|
configured,
|
|
770
809
|
notDone,
|
|
810
|
+
// baseline 专有:没做出来的题号(0 起,相对该题库)
|
|
811
|
+
missingIndexes,
|
|
771
812
|
// 本次运行的开始时间(ISO);旧数据没有元数据时为 null,前端退回解析 displayName 里的运行戳
|
|
772
813
|
runStartedAt: meta !== undefined && typeof meta.startedAt === "string" ? meta.startedAt : null,
|
|
773
814
|
// 非 null 表示这一行跑的是 Baseline 题库子集,不是该任务全量
|
|
@@ -1093,6 +1134,8 @@ function apply(ctx) {
|
|
|
1093
1134
|
baselineTask: baselinePick !== null ? baselinePick.task : null,
|
|
1094
1135
|
baselinePicks: baselinePick !== null ? baselinePick.picks : null,
|
|
1095
1136
|
baselineSize: baselinePick !== null ? baselinePick.ids.length : null,
|
|
1137
|
+
// 实际使用的有序 id 列表:/results 靠它算出"少做了哪几题"
|
|
1138
|
+
baselineIds: baselinePick !== null ? baselinePick.ids : null,
|
|
1096
1139
|
startedAt: new Date().toISOString(),
|
|
1097
1140
|
}), "utf8");
|
|
1098
1141
|
} catch { /* 元数据写入失败不影响评测 */ }
|
|
@@ -1434,4 +1477,4 @@ function apply(ctx) {
|
|
|
1434
1477
|
}), `${name}: delete route`);
|
|
1435
1478
|
}
|
|
1436
1479
|
|
|
1437
|
-
export { name, inject, apply, writeGeneratedModelConfig, readProviders, validateBenchName, clampedRangeLength, releaseQuestionCount, isEmptyAnswerLine };
|
|
1480
|
+
export { name, inject, apply, writeGeneratedModelConfig, readProviders, validateBenchName, clampedRangeLength, releaseQuestionCount, isEmptyAnswerLine, missingBaselineIndexes };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-livebench-panel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.22",
|
|
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. Ships a Baseline probe set built from the questions a reference model cannot solve.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "cszr (Vithrive)",
|