dsh-livebench-panel 0.1.7 → 0.1.8
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 +98 -66
- package/lib/index.js +102 -16
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -98,10 +98,12 @@ window.__ModuleLoader__.load({
|
|
|
98
98
|
const [log, setLog] = useState("");
|
|
99
99
|
const [exitCode, setExitCode] = useState(null);
|
|
100
100
|
const [startError, setStartError] = useState(null);
|
|
101
|
+
const [homeInput, setHomeInput] = useState("");
|
|
102
|
+
const [homeBusy, setHomeBusy] = useState(false);
|
|
101
103
|
const [results, setResults] = useState(null);
|
|
102
|
-
const [
|
|
103
|
-
const [
|
|
104
|
-
try { return JSON.parse(localStorage.getItem("
|
|
104
|
+
const [pickedModels, setPickedModels] = useState(() => new Set());
|
|
105
|
+
const [modelOrder, setModelOrder] = useState(() => {
|
|
106
|
+
try { return JSON.parse(localStorage.getItem("dlb_model_order_v1")) ?? []; } catch { return []; }
|
|
105
107
|
});
|
|
106
108
|
const dragKey = useRef(null);
|
|
107
109
|
const logRef = useRef(null);
|
|
@@ -253,6 +255,24 @@ window.__ModuleLoader__.load({
|
|
|
253
255
|
await refreshStatus();
|
|
254
256
|
};
|
|
255
257
|
|
|
258
|
+
const saveHome = async () => {
|
|
259
|
+
setHomeBusy(true);
|
|
260
|
+
setStartError(null);
|
|
261
|
+
try {
|
|
262
|
+
const payload = await api("/home", {
|
|
263
|
+
method: "POST",
|
|
264
|
+
headers: { "content-type": "application/json" },
|
|
265
|
+
body: JSON.stringify({ path: homeInput }),
|
|
266
|
+
});
|
|
267
|
+
if (!payload.ok) setStartError(payload.error ?? "设置失败");
|
|
268
|
+
else { setHomeInput(""); await loadConfig(); }
|
|
269
|
+
} catch (error) {
|
|
270
|
+
setStartError(String(error.message ?? error));
|
|
271
|
+
} finally {
|
|
272
|
+
setHomeBusy(false);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
256
276
|
const setField = (key) => (event) => {
|
|
257
277
|
const value = event.target.value;
|
|
258
278
|
setSel((prev) => {
|
|
@@ -265,78 +285,75 @@ window.__ModuleLoader__.load({
|
|
|
265
285
|
});
|
|
266
286
|
};
|
|
267
287
|
|
|
268
|
-
// ---- 成绩矩阵:
|
|
269
|
-
// 附带:time
|
|
288
|
+
// ---- 成绩矩阵:model 为行标识、category/task 为列标识 ----
|
|
289
|
+
// 附带:time 列(该模型评测开始时间 = 行内最早判分时间)、
|
|
270
290
|
// 行拖拽排序(localStorage 持久化)、行选择与单行/批量删除。
|
|
271
291
|
const taskKey = (category, task) => `${category}/${task}`;
|
|
272
292
|
const matrix = useMemo(() => {
|
|
273
293
|
if (!results) return null;
|
|
274
294
|
const models = [...new Set(results.rows.map((r) => r.model))].sort();
|
|
275
|
-
const
|
|
295
|
+
const tasks = [...new Set(results.rows.map((r) => taskKey(r.category, r.task)))].sort();
|
|
296
|
+
const cells = new Map(); // `${model}\u0000${taskKey}` -> row
|
|
297
|
+
const startTimes = new Map(); // model -> min tstamp
|
|
276
298
|
for (const row of results.rows) {
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
if (row.time && row.time < entry.startTime) entry.startTime = row.time;
|
|
281
|
-
byTask.set(key, entry);
|
|
299
|
+
cells.set(`${row.model}\u0000${taskKey(row.category, row.task)}`, row);
|
|
300
|
+
const prev = startTimes.get(row.model);
|
|
301
|
+
if (row.time && (prev === undefined || row.time < prev)) startTimes.set(row.model, row.time);
|
|
282
302
|
}
|
|
283
|
-
return { models, tasks
|
|
303
|
+
return { models, tasks, cells, startTimes };
|
|
284
304
|
}, [results]);
|
|
285
|
-
const
|
|
305
|
+
const sortedModels = useMemo(() => {
|
|
286
306
|
if (!matrix) return [];
|
|
287
|
-
const indexOf = new Map(
|
|
288
|
-
return [...matrix.
|
|
289
|
-
const ia = indexOf.has(a
|
|
290
|
-
const ib = indexOf.has(b
|
|
307
|
+
const indexOf = new Map(modelOrder.map((key, index) => [key, index]));
|
|
308
|
+
return [...matrix.models].sort((a, b) => {
|
|
309
|
+
const ia = indexOf.has(a) ? indexOf.get(a) : Number.MAX_SAFE_INTEGER;
|
|
310
|
+
const ib = indexOf.has(b) ? indexOf.get(b) : Number.MAX_SAFE_INTEGER;
|
|
291
311
|
if (ia !== ib) return ia - ib;
|
|
292
|
-
return a
|
|
312
|
+
return a.localeCompare(b);
|
|
293
313
|
});
|
|
294
|
-
}, [matrix,
|
|
314
|
+
}, [matrix, modelOrder]);
|
|
295
315
|
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
try { localStorage.setItem("
|
|
316
|
+
const persistModelOrder = (keys) => {
|
|
317
|
+
setModelOrder(keys);
|
|
318
|
+
try { localStorage.setItem("dlb_model_order_v1", JSON.stringify(keys)); } catch { /* private mode */ }
|
|
299
319
|
};
|
|
300
320
|
|
|
301
|
-
const onRowDrop = (
|
|
302
|
-
const
|
|
321
|
+
const onRowDrop = (targetModel) => {
|
|
322
|
+
const sourceModel = dragKey.current;
|
|
303
323
|
dragKey.current = null;
|
|
304
|
-
if (!
|
|
305
|
-
const keys =
|
|
306
|
-
const from = keys.indexOf(
|
|
307
|
-
const to = keys.indexOf(
|
|
324
|
+
if (!sourceModel || sourceModel === targetModel) return;
|
|
325
|
+
const keys = sortedModels.slice();
|
|
326
|
+
const from = keys.indexOf(sourceModel);
|
|
327
|
+
const to = keys.indexOf(targetModel);
|
|
308
328
|
if (from < 0 || to < 0) return;
|
|
309
329
|
keys.splice(to, 0, keys.splice(from, 1)[0]);
|
|
310
|
-
|
|
330
|
+
persistModelOrder(keys);
|
|
311
331
|
};
|
|
312
332
|
|
|
313
|
-
const togglePicked = (
|
|
333
|
+
const togglePicked = (model) => setPickedModels((prev) => {
|
|
314
334
|
const next = new Set(prev);
|
|
315
|
-
if (next.has(
|
|
335
|
+
if (next.has(model)) next.delete(model); else next.add(model);
|
|
316
336
|
return next;
|
|
317
337
|
});
|
|
318
338
|
|
|
319
|
-
//
|
|
320
|
-
const
|
|
321
|
-
if (!results ||
|
|
339
|
+
// 删除一个模型行 = 删除该模型在所有任务下的判分与答案记录
|
|
340
|
+
const deleteModels = async (modelsToDelete) => {
|
|
341
|
+
if (!results || modelsToDelete.length === 0) return;
|
|
322
342
|
const serverRows = [];
|
|
323
|
-
for (const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (row.category === category && row.task === task) {
|
|
327
|
-
serverRows.push({ model: row.model, category, task });
|
|
328
|
-
}
|
|
343
|
+
for (const row of results.rows) {
|
|
344
|
+
if (modelsToDelete.includes(row.model)) {
|
|
345
|
+
serverRows.push({ model: row.model, category: row.category, task: row.task });
|
|
329
346
|
}
|
|
330
347
|
}
|
|
331
348
|
if (serverRows.length === 0) return;
|
|
332
|
-
if (!window.confirm(`确认删除所选 ${
|
|
349
|
+
if (!window.confirm(`确认删除所选 ${modelsToDelete.length} 个模型的全部评测成绩(共 ${serverRows.length} 条记录,含答案)?不可恢复。`)) return;
|
|
333
350
|
await api("/delete", {
|
|
334
351
|
method: "POST",
|
|
335
352
|
headers: { "content-type": "application/json" },
|
|
336
353
|
body: JSON.stringify({ rows: serverRows }),
|
|
337
354
|
});
|
|
338
|
-
|
|
339
|
-
|
|
355
|
+
setPickedModels(new Set());
|
|
356
|
+
persistModelOrder(modelOrder.filter((model) => !modelsToDelete.includes(model)));
|
|
340
357
|
await loadResults();
|
|
341
358
|
};
|
|
342
359
|
|
|
@@ -350,10 +367,21 @@ window.__ModuleLoader__.load({
|
|
|
350
367
|
return h("div", { className: c("root") },
|
|
351
368
|
h("div", { className: c("head") },
|
|
352
369
|
h("h2", null, "LiveBench"),
|
|
353
|
-
h("span", { className: c("sub") },
|
|
370
|
+
h("span", { className: c("sub") }, config?.root ? `LiveBench 评测面板 · ${config.root}` : "LiveBench 评测面板"),
|
|
354
371
|
h("span", { className: c("badge"), "data-ok": config?.available ? "1" : "0" },
|
|
355
372
|
config === null ? "检测中…" : config.available ? "LiveBench 就绪" : "未找到 LiveBench"),
|
|
356
373
|
),
|
|
374
|
+
h("div", { className: c("row") },
|
|
375
|
+
h("span", { className: c("label") }, "LiveBench 路径"),
|
|
376
|
+
h("input", {
|
|
377
|
+
className: c("input"), style: { flex: "1", minWidth: "240px" },
|
|
378
|
+
placeholder: config?.root ?? "V:\\…\\LiveBench 或 ~/LiveBench",
|
|
379
|
+
value: homeInput, onChange: (event) => setHomeInput(event.target.value),
|
|
380
|
+
}),
|
|
381
|
+
h("button", { className: c("btnGhost") + " " + c("btn"), disabled: homeBusy || homeInput.trim().length === 0, onClick: saveHome },
|
|
382
|
+
homeBusy ? "保存中…" : "设置路径"),
|
|
383
|
+
h("span", { className: c("hint") }, "需含 livebench\\run_livebench.py 与 .venv;也可用环境变量 DSH_LIVEBENCH_HOME"),
|
|
384
|
+
),
|
|
357
385
|
configError !== null && h("p", { className: c("error") }, `加载配置失败:${configError}`),
|
|
358
386
|
h("div", { className: c("card") },
|
|
359
387
|
h("div", { className: c("grid") },
|
|
@@ -431,49 +459,53 @@ window.__ModuleLoader__.load({
|
|
|
431
459
|
),
|
|
432
460
|
h("pre", { className: c("log"), ref: logRef }, log || "(暂无输出)"),
|
|
433
461
|
),
|
|
434
|
-
|
|
462
|
+
sortedModels.length > 0 && h("div", { className: c("card") },
|
|
435
463
|
h("div", { className: c("row") },
|
|
436
|
-
h("span", { className: c("label") }, "
|
|
464
|
+
h("span", { className: c("label") }, "评测成绩(model 为行;分数 = 平均分 ×100;行首可拖动排序,顺序保存在本机)"),
|
|
437
465
|
h("button", {
|
|
438
466
|
className: c("btn") + " " + c("btnDanger"),
|
|
439
|
-
disabled:
|
|
440
|
-
onClick: () =>
|
|
441
|
-
}, `删除所选(${
|
|
467
|
+
disabled: pickedModels.size === 0,
|
|
468
|
+
onClick: () => deleteModels([...pickedModels]),
|
|
469
|
+
}, `删除所选(${pickedModels.size})`),
|
|
442
470
|
),
|
|
443
471
|
h("table", { className: c("table") },
|
|
444
472
|
h("thead", null, h("tr", null,
|
|
445
473
|
h("th", { style: { width: "26px" } }, "⠿"),
|
|
446
474
|
h("th", { style: { width: "30px" } },
|
|
447
|
-
h("input", { type: "checkbox", checked:
|
|
448
|
-
onChange: (event) =>
|
|
449
|
-
h("th", null, "
|
|
450
|
-
matrix.
|
|
475
|
+
h("input", { type: "checkbox", checked: pickedModels.size > 0 && pickedModels.size === sortedModels.length,
|
|
476
|
+
onChange: (event) => setPickedModels(event.target.checked ? new Set(sortedModels) : new Set()) })),
|
|
477
|
+
h("th", null, "model"),
|
|
478
|
+
matrix.tasks.map((taskKeyCol) => {
|
|
479
|
+
const [cat, task] = taskKeyCol.split("/");
|
|
480
|
+
return h("th", { key: taskKeyCol, title: `${cat} / ${task}` }, task);
|
|
481
|
+
}),
|
|
451
482
|
h("th", null, "删除"),
|
|
452
483
|
h("th", null, "time"),
|
|
453
484
|
)),
|
|
454
485
|
h("tbody", null,
|
|
455
|
-
|
|
456
|
-
const checked =
|
|
486
|
+
sortedModels.map((model) => {
|
|
487
|
+
const checked = pickedModels.has(model);
|
|
488
|
+
const startTime = matrix.startTimes.get(model);
|
|
457
489
|
return h("tr", {
|
|
458
|
-
key,
|
|
490
|
+
key: model,
|
|
459
491
|
draggable: true,
|
|
460
|
-
onDragStart: () => { dragKey.current =
|
|
492
|
+
onDragStart: () => { dragKey.current = model; },
|
|
461
493
|
onDragOver: (event) => event.preventDefault(),
|
|
462
|
-
onDrop: () => onRowDrop(
|
|
494
|
+
onDrop: () => onRowDrop(model),
|
|
463
495
|
},
|
|
464
496
|
h("td", { className: c("drag"), title: "拖动排序" }, "⠿"),
|
|
465
|
-
h("td", null, h("input", { type: "checkbox", checked, onChange: () => togglePicked(
|
|
466
|
-
h("td", null,
|
|
467
|
-
matrix.
|
|
468
|
-
const row =
|
|
469
|
-
return h("td", { key:
|
|
497
|
+
h("td", null, h("input", { type: "checkbox", checked, onChange: () => togglePicked(model) })),
|
|
498
|
+
h("td", null, model),
|
|
499
|
+
matrix.tasks.map((taskKeyCol) => {
|
|
500
|
+
const row = matrix.cells.get(`${model}\u0000${taskKeyCol}`);
|
|
501
|
+
return h("td", { key: taskKeyCol, className: c("score") },
|
|
470
502
|
row ? `${row.score.toFixed(1)} (${row.judged}/${row.total})` : "—");
|
|
471
503
|
}),
|
|
472
504
|
h("td", null, h("button", {
|
|
473
|
-
className: c("btnGhost") + " " + c("btn"), title: "
|
|
474
|
-
onClick: () =>
|
|
505
|
+
className: c("btnGhost") + " " + c("btn"), title: "删除该模型的所有评测成绩",
|
|
506
|
+
onClick: () => deleteModels([model]),
|
|
475
507
|
}, "删除")),
|
|
476
|
-
h("td", null, fmtTime(
|
|
508
|
+
h("td", null, fmtTime(startTime)),
|
|
477
509
|
);
|
|
478
510
|
})),
|
|
479
511
|
),
|
package/lib/index.js
CHANGED
|
@@ -39,8 +39,61 @@ const inject = ["webServer"];
|
|
|
39
39
|
const API = "/dsh-livebench-panel/api";
|
|
40
40
|
/** Profile whose settings.yaml holds the harness provider config. */
|
|
41
41
|
const PROFILE = "web";
|
|
42
|
-
/** LiveBench
|
|
43
|
-
const
|
|
42
|
+
/** LiveBench location resolution: saved user path > env var > user home > legacy. */
|
|
43
|
+
const CONFIG_FILE = join(process.env.DSH_HOME ?? join(homedir(), ".dsh"), "livebench-panel.json");
|
|
44
|
+
const LEGACY_LIVEBENCH_HOME = "V:\\PythonProject\\C_UtilizeSpace\\LiveBench";
|
|
45
|
+
|
|
46
|
+
/** Read the user-saved LiveBench path (set from the panel UI). */
|
|
47
|
+
function savedLivebenchHome() {
|
|
48
|
+
try {
|
|
49
|
+
const path = JSON.parse(readFileSync(CONFIG_FILE, "utf8")).livebenchHome;
|
|
50
|
+
return typeof path === "string" && path.trim().length > 0 ? path.trim() : null;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Candidate roots, most explicit first. Windows and Linux both covered. */
|
|
57
|
+
function livebenchCandidates() {
|
|
58
|
+
return [
|
|
59
|
+
savedLivebenchHome(),
|
|
60
|
+
process.env.DSH_LIVEBENCH_HOME ?? null,
|
|
61
|
+
join(homedir(), "LiveBench"),
|
|
62
|
+
LEGACY_LIVEBENCH_HOME,
|
|
63
|
+
].filter((candidate) => typeof candidate === "string" && candidate.length > 0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A root counts as a LiveBench checkout when the venv python and entry script exist. */
|
|
67
|
+
function isLivebenchRoot(root) {
|
|
68
|
+
return existsSync(join(root, ".venv", "Scripts", "python.exe")) || existsSync(join(root, ".venv", "bin", "python"))
|
|
69
|
+
? existsSync(join(root, "livebench", "run_livebench.py"))
|
|
70
|
+
: false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Resolve the LiveBench layout from the first valid candidate. */
|
|
74
|
+
function livebenchLayout() {
|
|
75
|
+
const candidates = livebenchCandidates();
|
|
76
|
+
for (const root of candidates) {
|
|
77
|
+
const pythonExe = join(root, ".venv", "Scripts", process.platform === "win32" ? "python.exe" : "python");
|
|
78
|
+
if (existsSync(pythonExe) && existsSync(join(root, "livebench", "run_livebench.py"))) {
|
|
79
|
+
return {
|
|
80
|
+
root,
|
|
81
|
+
livebenchDir: join(root, "livebench"),
|
|
82
|
+
dataDir: join(root, "livebench", "data", "live_bench"),
|
|
83
|
+
pythonExe,
|
|
84
|
+
available: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const root = candidates[0] ?? join(homedir(), "LiveBench");
|
|
89
|
+
return {
|
|
90
|
+
root,
|
|
91
|
+
livebenchDir: join(root, "livebench"),
|
|
92
|
+
dataDir: join(root, "livebench", "data", "live_bench"),
|
|
93
|
+
pythonExe: join(root, ".venv", process.platform === "win32" ? join("Scripts", "python.exe") : join("bin", "python")),
|
|
94
|
+
available: false,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
44
97
|
/** Question-set releases LiveBench accepts (mirrors livebench/common.py). */
|
|
45
98
|
const RELEASES = [
|
|
46
99
|
"2024-06-24", "2024-07-26", "2024-08-31", "2024-11-25",
|
|
@@ -210,19 +263,6 @@ function effortsOfModel(model) {
|
|
|
210
263
|
return efforts;
|
|
211
264
|
}
|
|
212
265
|
|
|
213
|
-
/** LiveBench layout on disk. */
|
|
214
|
-
function livebenchLayout() {
|
|
215
|
-
const root = process.env.DSH_LIVEBENCH_HOME ?? DEFAULT_LIVEBENCH_HOME;
|
|
216
|
-
const venvPython = join(root, ".venv", "Scripts", "python.exe");
|
|
217
|
-
return {
|
|
218
|
-
root,
|
|
219
|
-
livebenchDir: join(root, "livebench"),
|
|
220
|
-
dataDir: join(root, "livebench", "data", "live_bench"),
|
|
221
|
-
pythonExe: venvPython,
|
|
222
|
-
available: existsSync(venvPython) && existsSync(join(root, "livebench", "run_livebench.py")),
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
266
|
/** Scan data/live_bench/<category>/<task>/question.jsonl into a category→tasks map. */
|
|
227
267
|
function scanCategoryTasks(dataDir) {
|
|
228
268
|
const tasks = {};
|
|
@@ -695,7 +735,14 @@ function apply(ctx) {
|
|
|
695
735
|
return;
|
|
696
736
|
}
|
|
697
737
|
try {
|
|
698
|
-
|
|
738
|
+
if (process.platform === "win32") {
|
|
739
|
+
// proc.kill() only terminates run_livebench.py itself; its child
|
|
740
|
+
// (cmd → gen_api_answer.py → …) would survive. taskkill /T /F
|
|
741
|
+
// takes down the whole tree.
|
|
742
|
+
spawn("taskkill", ["/PID", String(run.proc.pid), "/T", "/F"], { windowsHide: true });
|
|
743
|
+
} else {
|
|
744
|
+
run.proc.kill();
|
|
745
|
+
}
|
|
699
746
|
} catch (error) {
|
|
700
747
|
sendJson(res, 500, { ok: false, error: String(error.message ?? error) });
|
|
701
748
|
return;
|
|
@@ -718,6 +765,45 @@ function apply(ctx) {
|
|
|
718
765
|
},
|
|
719
766
|
}), `${name}: results route`);
|
|
720
767
|
|
|
768
|
+
ctx.effect(() => ctx.webServer.register({
|
|
769
|
+
kind: "exact",
|
|
770
|
+
path: `${API}/home`,
|
|
771
|
+
handler: async (req, res) => {
|
|
772
|
+
if (req.method !== "POST") {
|
|
773
|
+
sendJson(res, 405, { ok: false, error: "method not allowed" });
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (!originAllowed(req)) {
|
|
777
|
+
sendJson(res, 403, { ok: false, error: "forbidden origin" });
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
let body;
|
|
781
|
+
try {
|
|
782
|
+
body = JSON.parse(await readBody(req));
|
|
783
|
+
} catch {
|
|
784
|
+
sendJson(res, 400, { ok: false, error: "invalid request body" });
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const path = typeof body.path === "string" ? body.path.trim() : "";
|
|
788
|
+
if (path.length === 0 || path.length > 260 || /[<>:"|?*\0]/.test(path.replace(/^[A-Za-z]:/, ""))) {
|
|
789
|
+
sendJson(res, 400, { ok: false, error: "invalid path" });
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
if (!existsSync(join(path, "livebench", "run_livebench.py"))) {
|
|
793
|
+
sendJson(res, 400, { ok: false, error: `该目录下没有 livebench\\run_livebench.py:${path}` });
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
try {
|
|
797
|
+
writeFileSync(CONFIG_FILE, JSON.stringify({ livebenchHome: path }, null, 2), "utf8");
|
|
798
|
+
} catch (error) {
|
|
799
|
+
sendJson(res, 500, { ok: false, error: `cannot save config: ${error.message}` });
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
const layout = livebenchLayout();
|
|
803
|
+
sendJson(res, 200, { ok: true, root: layout.root, available: layout.available });
|
|
804
|
+
},
|
|
805
|
+
}), `${name}: home route`);
|
|
806
|
+
|
|
721
807
|
ctx.effect(() => ctx.webServer.register({
|
|
722
808
|
kind: "exact",
|
|
723
809
|
path: `${API}/delete`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-livebench-panel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
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",
|