dsh-livebench-panel 0.1.2 → 0.1.4
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 +23 -2
- package/lib/index.js +32 -13
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -151,8 +151,21 @@ window.__ModuleLoader__.load({
|
|
|
151
151
|
const categories = useMemo(() => (config ? Object.keys(config.tasks) : []), [config]);
|
|
152
152
|
const taskList = useMemo(() => {
|
|
153
153
|
if (!config || sel.category.length === 0) return [];
|
|
154
|
-
return config.tasks[sel.category] ??
|
|
154
|
+
return Object.keys(config.tasks[sel.category] ?? {});
|
|
155
155
|
}, [config, sel.category]);
|
|
156
|
+
// 有效题数:LiveBench 会丢弃「发布晚于所选 release」和「在所选 release
|
|
157
|
+
// 前已退役」的题目,这里按题目桶 (发布日, 移除日) 精确复算。
|
|
158
|
+
const countFor = useCallback((category, task) => {
|
|
159
|
+
if (!config || category.length === 0) return null;
|
|
160
|
+
const meta = (config.tasks[category] ?? {})[task];
|
|
161
|
+
if (!meta || !Array.isArray(meta.buckets)) return null;
|
|
162
|
+
const option = sel.release;
|
|
163
|
+
return meta.buckets.reduce((sum, b) => {
|
|
164
|
+
const releasedOk = b.r !== "" && b.r <= option;
|
|
165
|
+
const notRemoved = b.rm === "" || b.rm > option;
|
|
166
|
+
return sum + (releasedOk && notRemoved ? b.n : 0);
|
|
167
|
+
}, 0);
|
|
168
|
+
}, [config, sel.release]);
|
|
156
169
|
const selectedProvider = useMemo(
|
|
157
170
|
() => (config ? config.providers.find((p) => p.id === sel.provider) ?? null : null),
|
|
158
171
|
[config, sel.provider],
|
|
@@ -168,6 +181,10 @@ window.__ModuleLoader__.load({
|
|
|
168
181
|
|
|
169
182
|
const onStart = async () => {
|
|
170
183
|
setStartError(null);
|
|
184
|
+
if (sel.category !== "" && sel.task !== "" && countFor(sel.category, sel.task) === 0) {
|
|
185
|
+
setStartError(`任务 ${sel.task} 在 release ${sel.release} 下没有可用题目(该批题目已退役)。请换一个 release 或任务。`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
171
188
|
setBusy(true);
|
|
172
189
|
try {
|
|
173
190
|
const payload = await api("/start", {
|
|
@@ -263,7 +280,11 @@ window.__ModuleLoader__.load({
|
|
|
263
280
|
h("label", { className: c("label") }, "任务"),
|
|
264
281
|
h("select", { className: c("select"), value: sel.task, onChange: setField("task") },
|
|
265
282
|
h("option", { value: "" }, sel.category === "" ? "(先选分类)" : "全部任务"),
|
|
266
|
-
taskList.map((task) =>
|
|
283
|
+
taskList.map((task) => {
|
|
284
|
+
const n = countFor(sel.category, task);
|
|
285
|
+
const label = n === null ? task : `${task}(${n} 题)`;
|
|
286
|
+
return h("option", { key: task, value: task, disabled: n === 0 }, n === 0 ? `${task}(此release无题)` : label);
|
|
287
|
+
})),
|
|
267
288
|
),
|
|
268
289
|
h("div", { className: c("field") },
|
|
269
290
|
h("label", { className: c("label") }, "题目序号范围(可选)"),
|
package/lib/index.js
CHANGED
|
@@ -227,18 +227,25 @@ function livebenchLayout() {
|
|
|
227
227
|
function scanCategoryTasks(dataDir) {
|
|
228
228
|
const tasks = {};
|
|
229
229
|
if (!existsSync(dataDir)) return tasks;
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
categoryDirs = readDirSafe(dataDir);
|
|
233
|
-
} catch {
|
|
234
|
-
return tasks;
|
|
235
|
-
}
|
|
230
|
+
const categoryDirs = readDirSafe(dataDir);
|
|
236
231
|
for (const category of categoryDirs) {
|
|
237
232
|
const catDir = join(dataDir, category);
|
|
238
233
|
for (const task of readDirSafe(catDir)) {
|
|
239
|
-
|
|
240
|
-
|
|
234
|
+
const questionFile = join(catDir, task, "question.jsonl");
|
|
235
|
+
if (!existsSync(questionFile)) continue;
|
|
236
|
+
// Bucket questions by (release date, removal date) so the UI can
|
|
237
|
+
// compute how many questions survive a chosen release option:
|
|
238
|
+
// LiveBench drops questions released after the option and questions
|
|
239
|
+
// removed at or before the option.
|
|
240
|
+
const buckets = [];
|
|
241
|
+
for (const q of readJsonl(questionFile)) {
|
|
242
|
+
const r = typeof q.livebench_release_date === "string" ? q.livebench_release_date : "";
|
|
243
|
+
const rm = typeof q.livebench_removal_date === "string" ? q.livebench_removal_date : "";
|
|
244
|
+
const bucket = buckets.find((b) => b.r === r && b.rm === rm);
|
|
245
|
+
if (bucket) bucket.n += 1;
|
|
246
|
+
else buckets.push({ r, rm, n: 1 });
|
|
241
247
|
}
|
|
248
|
+
((tasks[category] ??= {})[task] = { buckets, total: buckets.reduce((sum, b) => sum + b.n, 0) });
|
|
242
249
|
}
|
|
243
250
|
}
|
|
244
251
|
return tasks;
|
|
@@ -403,7 +410,7 @@ function apply(ctx) {
|
|
|
403
410
|
/** The single run slot. */
|
|
404
411
|
let run = null;
|
|
405
412
|
|
|
406
|
-
const startRun = (body) => {
|
|
413
|
+
const startRun = async (body) => {
|
|
407
414
|
const layout = livebenchLayout();
|
|
408
415
|
if (!layout.available) {
|
|
409
416
|
return { status: 409, payload: { ok: false, error: `LiveBench venv not found under ${layout.root}` } };
|
|
@@ -473,11 +480,23 @@ function apply(ctx) {
|
|
|
473
480
|
PATH: `${join(layout.root, ".venv", "Scripts")}${delimiter}${process.env.PATH ?? ""}`,
|
|
474
481
|
};
|
|
475
482
|
// Only OpenAI-compatible providers can be routed with --api-base; for
|
|
476
|
-
// those, hand the key over via env (never the command line).
|
|
483
|
+
// those, hand the key over via env (never the command line). The key is
|
|
484
|
+
// resolved through the harness credential seam when available (values may
|
|
485
|
+
// live encrypted in .credentials.yaml rather than in the process env),
|
|
486
|
+
// falling back to the plain environment variable.
|
|
477
487
|
if (provider && provider.api === "openai-completions" && provider.baseURL) {
|
|
478
488
|
args.push("--api-base", provider.baseURL);
|
|
479
|
-
if (provider.keyEnv
|
|
480
|
-
|
|
489
|
+
if (provider.keyEnv) {
|
|
490
|
+
let key;
|
|
491
|
+
try {
|
|
492
|
+
const credentials = ctx.get ? ctx.get("credentials") : undefined;
|
|
493
|
+
if (credentials && typeof credentials.resolve === "function") {
|
|
494
|
+
const resolved = await credentials.resolve(provider.keyEnv);
|
|
495
|
+
if (resolved && typeof resolved.value === "string" && resolved.value.length > 0) key = resolved.value;
|
|
496
|
+
}
|
|
497
|
+
} catch { /* credential seam unavailable — fall through to env */ }
|
|
498
|
+
if (!key && process.env[provider.keyEnv]) key = process.env[provider.keyEnv];
|
|
499
|
+
if (key) env.LIVEBENCH_API_KEY = key;
|
|
481
500
|
}
|
|
482
501
|
}
|
|
483
502
|
|
|
@@ -581,7 +600,7 @@ function apply(ctx) {
|
|
|
581
600
|
sendJson(res, 409, { ok: false, error: "another evaluation is already running" });
|
|
582
601
|
return;
|
|
583
602
|
}
|
|
584
|
-
const result = startRun(body);
|
|
603
|
+
const result = await startRun(body);
|
|
585
604
|
sendJson(res, result.status, result.payload);
|
|
586
605
|
},
|
|
587
606
|
}), `${name}: start route`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-livebench-panel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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",
|