dsh-livebench-panel 0.2.20 → 0.2.21
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 +13 -5
- package/lib/index.js +35 -7
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -578,7 +578,7 @@ window.__ModuleLoader__.load({
|
|
|
578
578
|
),
|
|
579
579
|
h("div", { className: c("field") },
|
|
580
580
|
h("label", { className: c("label") }, "max-tokens(默认 32000)"),
|
|
581
|
-
h("input", { className: c("input"), type: "number", min: 256, max:
|
|
581
|
+
h("input", { className: c("input"), type: "number", min: 256, max: 200000, value: sel.maxTokens, onChange: setField("maxTokens"), title: "单题输出上限。推理模型在难题上容易把预算全花在思考里、正文为空(成绩表括号里记为「没做出来」),这类题可以把上限调大后重跑。" }),
|
|
582
582
|
),
|
|
583
583
|
h("div", { className: c("field") },
|
|
584
584
|
h("label", { className: c("label") }, "题目序号范围(可选,0 起,含首尾)"),
|
|
@@ -795,12 +795,20 @@ window.__ModuleLoader__.load({
|
|
|
795
795
|
h("div", { className: c("dim") }, "×"));
|
|
796
796
|
}
|
|
797
797
|
const sub = `(-${row.notDone ?? 0}/${row.configured ?? 0})`;
|
|
798
|
-
const
|
|
798
|
+
const emptyCount = row.empty ?? 0;
|
|
799
|
+
// 「没做出来」= API 失败 + 空答案(思考吃满 max-tokens 没产出正文),
|
|
800
|
+
// 两者都不进正确率分母
|
|
801
|
+
const notProduced = row.notProduced ?? row.errors ?? 0;
|
|
802
|
+
const allFailed = row.answered > 0 && notProduced >= row.answered;
|
|
799
803
|
const basePrefix = row.baselineLabel ? `【${row.baselineLabel}】` : "";
|
|
804
|
+
const reasons = [
|
|
805
|
+
row.errors > 0 ? `${row.errors} 题 API 失败` : null,
|
|
806
|
+
emptyCount > 0 ? `${emptyCount} 题思考占满 max-tokens、未产出答案` : null,
|
|
807
|
+
].filter(Boolean).join(",");
|
|
800
808
|
const title = basePrefix + (allFailed
|
|
801
|
-
? `本次选择 ${row.configured ?? 0}
|
|
802
|
-
: `本次选择 ${row.configured ?? 0}
|
|
803
|
-
(
|
|
809
|
+
? `本次选择 ${row.configured ?? 0} 题,全部没做出来(${reasons}),不计入正确率 ${sub}`
|
|
810
|
+
: `本次选择 ${row.configured ?? 0} 题,做出来 ${row.done ?? 0} 题,没做出来 ${row.notDone ?? 0} 题 ${sub}`
|
|
811
|
+
+ (reasons ? `(其中 ${reasons})` : "")); if (allFailed || row.judged === 0) {
|
|
804
812
|
return h("td", { key: taskKeyCol, className: cls, title },
|
|
805
813
|
h("div", { className: c("dim") }, "—"),
|
|
806
814
|
h("div", { className: c("cellSub") }, sub));
|
package/lib/index.js
CHANGED
|
@@ -610,6 +610,23 @@ function parseAnswerLineLight(line) {
|
|
|
610
610
|
*/
|
|
611
611
|
let resultsCache = { at: 0, data: null };
|
|
612
612
|
|
|
613
|
+
/**
|
|
614
|
+
* 答案行是否"没产出内容"。
|
|
615
|
+
*
|
|
616
|
+
* 两类:
|
|
617
|
+
* 1. `eval_status: token_exhaustion` —— 推理模型把 max_tokens 全烧在思考上,正文为空;
|
|
618
|
+
* 2. `turns` / `choices` 是空串或空数组 —— 上游 200 但没给内容。
|
|
619
|
+
*
|
|
620
|
+
* 这两种都不是"做错了",而是**没做出来**,不该进正确率的分母(用户口径:
|
|
621
|
+
* 做对的题 / 做出来的题)。它们也不是 $ERROR$(那是 API 失败),所以单列一类。
|
|
622
|
+
*
|
|
623
|
+
* 用正则直接扫原始行,避免为了判定空答案去 JSON.parse 上百万字节的答案文件。
|
|
624
|
+
*/
|
|
625
|
+
const EMPTY_ANSWER_RE = /"eval_status"\s*:\s*"token_exhaustion"|"turns"\s*:\s*\[\s*(?:""\s*)?\]|"choices"\s*:\s*\[\s*""\s*\]/;
|
|
626
|
+
function isEmptyAnswerLine(line) {
|
|
627
|
+
return EMPTY_ANSWER_RE.test(line);
|
|
628
|
+
}
|
|
629
|
+
|
|
613
630
|
async function readTextFileAsync(path) {
|
|
614
631
|
const fsPromises = await import("node:fs/promises");
|
|
615
632
|
try {
|
|
@@ -651,11 +668,12 @@ async function computeResults(dataDir) {
|
|
|
651
668
|
const text = await readTextFileAsync(join(answerDir, file));
|
|
652
669
|
if (text === null) continue;
|
|
653
670
|
const key = `${model}\u0000${task}`;
|
|
654
|
-
const stat = answers.get(key) ?? { answered: 0, errors: 0 };
|
|
671
|
+
const stat = answers.get(key) ?? { answered: 0, errors: 0, empty: 0 };
|
|
655
672
|
for (const line of text.split("\n")) {
|
|
656
673
|
if (line.trim().length === 0) continue;
|
|
657
674
|
stat.answered += 1;
|
|
658
675
|
if (line.includes('"$ERROR$"')) stat.errors += 1;
|
|
676
|
+
else if (isEmptyAnswerLine(line)) stat.empty += 1;
|
|
659
677
|
}
|
|
660
678
|
// 空文件(尚未产出答案,或成绩已被删除只留下 0 字节壳)不计入结果:
|
|
661
679
|
// 否则删掉的记录会在下一次扫描时凭“文件名仍在”重新冒出来。
|
|
@@ -691,8 +709,13 @@ async function computeResults(dataDir) {
|
|
|
691
709
|
const category = entry?.category ?? catByTask.get(task) ?? "";
|
|
692
710
|
const total = totals.get(task) ?? 0;
|
|
693
711
|
const judgedCount = entry?.n ?? 0;
|
|
694
|
-
//
|
|
695
|
-
|
|
712
|
+
// 「做出来」的题 = 尝试数 - API 失败 - 空答案。
|
|
713
|
+
// 关键:思考吃满 max_tokens、正文为空的题属于"没做出来",不是"做错了",
|
|
714
|
+
// 必须从正确率分母里剔除,否则一个模型 8 题里只答出 1 题(0.9167 分)
|
|
715
|
+
// 会被算成 11.5% 而不是 91.7%。
|
|
716
|
+
const empty = stat.empty ?? 0;
|
|
717
|
+
const notProduced = stat.errors + empty;
|
|
718
|
+
const done = Math.max(0, stat.answered - notProduced);
|
|
696
719
|
// 选择题数 = 用户在「题目序号范围」里选择的题数(end 含端点)。
|
|
697
720
|
// 有元数据就直接用范围大小;没有元数据(历史数据)时用本次实际写入的答案行数兜底:
|
|
698
721
|
// LiveBench 会为范围内每一题写一行(访问失败也写 $ERROR$),所以跑完后
|
|
@@ -727,8 +750,8 @@ async function computeResults(dataDir) {
|
|
|
727
750
|
// 实际尝试数比设定还多(断点重跑等)时,以实际为准
|
|
728
751
|
if (configured < stat.answered) configured = stat.answered;
|
|
729
752
|
const notDone = Math.max(0, configured - done);
|
|
730
|
-
// 正确率 = 做对的题 /
|
|
731
|
-
const judgedDone = Math.max(0, judgedCount -
|
|
753
|
+
// 正确率 = 做对的题 / **做出来的题**(API 失败、思考吃满 token 没产出答案的都不进分母)
|
|
754
|
+
const judgedDone = Math.max(0, judgedCount - notProduced);
|
|
732
755
|
const score = entry && judgedDone > 0 ? (entry.sum / judgedDone) * 100 : null;
|
|
733
756
|
return {
|
|
734
757
|
model,
|
|
@@ -740,6 +763,8 @@ async function computeResults(dataDir) {
|
|
|
740
763
|
time: entry && entry.time > 0 ? entry.time : null,
|
|
741
764
|
answered: stat.answered,
|
|
742
765
|
errors: stat.errors,
|
|
766
|
+
empty,
|
|
767
|
+
notProduced,
|
|
743
768
|
done,
|
|
744
769
|
configured,
|
|
745
770
|
notDone,
|
|
@@ -974,7 +999,10 @@ function apply(ctx) {
|
|
|
974
999
|
"--bench-name",
|
|
975
1000
|
...(benchNames !== null ? benchNames : [benchParts.join("/")]),
|
|
976
1001
|
"--livebench-release-option", release,
|
|
977
|
-
|
|
1002
|
+
// 上限放到 200k:推理模型在难题上会把 max_tokens 全烧在思考里,正文为空
|
|
1003
|
+
// (eval_status=token_exhaustion),此时"做不出来"其实是预算不够而不是能力不够。
|
|
1004
|
+
// 旧上限 32768 在 olympiad 这类题上会直接把参考模型卡死。
|
|
1005
|
+
"--max-tokens", String(asInt(body.maxTokens, 256, 200000, 32000)),
|
|
978
1006
|
"--parallel-requests", String(asInt(body.parallel, 1, 8, 1)),
|
|
979
1007
|
// 流式:中转网关(Cloudflare 等)对非流式请求有 ~100s 超时(524),
|
|
980
1008
|
// 高推理强度模型思考数分钟必然超时。流式保持字节流动可规避。
|
|
@@ -1406,4 +1434,4 @@ function apply(ctx) {
|
|
|
1406
1434
|
}), `${name}: delete route`);
|
|
1407
1435
|
}
|
|
1408
1436
|
|
|
1409
|
-
export { name, inject, apply, writeGeneratedModelConfig, readProviders, validateBenchName, clampedRangeLength, releaseQuestionCount };
|
|
1437
|
+
export { name, inject, apply, writeGeneratedModelConfig, readProviders, validateBenchName, clampedRangeLength, releaseQuestionCount, isEmptyAnswerLine };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-livebench-panel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.21",
|
|
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)",
|