dsh-livebench-panel 0.1.8 → 0.1.10
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 +1 -1
- package/lib/index.js +81 -29
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -448,7 +448,7 @@ window.__ModuleLoader__.load({
|
|
|
448
448
|
running && h("button", { className: c("btn") + " " + c("btnDanger"), onClick: onStop }, "停止"),
|
|
449
449
|
h("button", { className: c("btnGhost") + " " + c("btn"), onClick: () => { loadConfig(); loadResults(); } }, "刷新"),
|
|
450
450
|
selectedProvider && !selectedProvider.routable && h("span", { className: c("hint") },
|
|
451
|
-
"该 provider
|
|
451
|
+
"该 provider 的协议或端点未知,无法自动路由:LiveBench 将按模型名原生尝试,未注册的模型名会失败。"),
|
|
452
452
|
),
|
|
453
453
|
startError !== null && h("p", { className: c("error") }, startError),
|
|
454
454
|
),
|
package/lib/index.js
CHANGED
|
@@ -176,10 +176,10 @@ function readProviders(profileDir) {
|
|
|
176
176
|
if (piAiData !== null) {
|
|
177
177
|
for (const provider of providers) {
|
|
178
178
|
if (provider.baseURL === null) {
|
|
179
|
-
const
|
|
180
|
-
if (
|
|
181
|
-
provider.baseURL =
|
|
182
|
-
provider.api =
|
|
179
|
+
const info = builtinProtocolInfo(piAiData, provider.id);
|
|
180
|
+
if (info) {
|
|
181
|
+
provider.baseURL = info.baseURL;
|
|
182
|
+
provider.api = info.api;
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
}
|
|
@@ -215,17 +215,25 @@ function resolvePiAiDataDir(profileDir) {
|
|
|
215
215
|
return null;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
/**
|
|
219
|
-
|
|
218
|
+
/**
|
|
219
|
+
* Read one provider's built-in protocol + baseUrl from pi-ai data.
|
|
220
|
+
* Prefers OpenAI Chat Completions; Anthropic Messages is also routable
|
|
221
|
+
* (LiveBench talks it natively and honors ANTHROPIC_BASE_URL/ANTHROPIC_API_KEY).
|
|
222
|
+
*/
|
|
223
|
+
function builtinProtocolInfo(piAiDataDir, providerId) {
|
|
220
224
|
if (!/^[a-z0-9-]+$/.test(providerId)) return null;
|
|
221
225
|
const file = join(piAiDataDir, `${providerId}.json`);
|
|
222
226
|
if (!existsSync(file)) return null;
|
|
223
227
|
try {
|
|
224
228
|
const data = JSON.parse(readFileSync(file, "utf8"));
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
+
for (const api of ["openai-completions", "anthropic-messages"]) {
|
|
230
|
+
const group = data?.[api];
|
|
231
|
+
if (group && typeof group === "object") {
|
|
232
|
+
for (const entry of Object.values(group)) {
|
|
233
|
+
if (entry && typeof entry.baseUrl === "string" && entry.baseUrl.length > 0) {
|
|
234
|
+
return { api, baseURL: entry.baseUrl };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
229
237
|
}
|
|
230
238
|
}
|
|
231
239
|
} catch { /* unreadable data file — treat as unknown */ }
|
|
@@ -355,21 +363,40 @@ function displayModelName(providerId, modelId) {
|
|
|
355
363
|
* The file is regenerated on every start; secrets never go in here.
|
|
356
364
|
* @returns {string|null} error message, or null on success.
|
|
357
365
|
*/
|
|
358
|
-
function writeGeneratedModelConfig(layout, YAML, { displayName, modelId, reasoningEffort }) {
|
|
366
|
+
function writeGeneratedModelConfig(layout, YAML, { displayName, modelId, reasoningEffort, protocol }) {
|
|
359
367
|
const configDir = join(layout.livebenchDir, "model", "model_configs");
|
|
360
368
|
const target = join(configDir, "dsh_panel_generated.yaml");
|
|
361
369
|
void YAML;
|
|
362
|
-
const
|
|
370
|
+
const lines = [
|
|
363
371
|
"# Generated by dsh-livebench-panel — regenerated on every evaluation start.",
|
|
364
372
|
"---",
|
|
365
373
|
`display_name: ${displayName}`,
|
|
366
374
|
"api_name:",
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
""
|
|
372
|
-
|
|
375
|
+
];
|
|
376
|
+
if (protocol === "anthropic") {
|
|
377
|
+
// Route through LiveBench's native anthropic client; the endpoint is
|
|
378
|
+
// pointed at the provider proxy via ANTHROPIC_BASE_URL (spawn env).
|
|
379
|
+
lines.push(` anthropic: ${modelId}`, "default_provider: anthropic");
|
|
380
|
+
if (reasoningEffort) {
|
|
381
|
+
// note: reasoning_effort is an OpenAI-style knob and is intentionally
|
|
382
|
+
// not forwarded on the anthropic protocol path.
|
|
383
|
+
}
|
|
384
|
+
} else if (protocol === "openai_responses") {
|
|
385
|
+
// OpenAI Responses API proxy: provider name matches
|
|
386
|
+
// get_api_function('openai_responses') → chat_completion_openai_responses,
|
|
387
|
+
// which converts reasoning_effort to reasoning:{effort} itself.
|
|
388
|
+
lines.push(` openai_responses: ${modelId}`);
|
|
389
|
+
if (reasoningEffort) {
|
|
390
|
+
lines.push("api_kwargs:", " default:", ` reasoning_effort: ${reasoningEffort}`);
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
lines.push(` local: ${modelId}`);
|
|
394
|
+
if (reasoningEffort) {
|
|
395
|
+
lines.push("api_kwargs:", " default:", ` reasoning_effort: ${reasoningEffort}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
lines.push("");
|
|
399
|
+
const doc = lines.join("\n");
|
|
373
400
|
try {
|
|
374
401
|
if (!existsSync(configDir)) return `model_configs directory not found: ${configDir}`;
|
|
375
402
|
writeFileSync(target, doc, "utf8");
|
|
@@ -532,8 +559,26 @@ function apply(ctx) {
|
|
|
532
559
|
const hasEffortSuffix = reasoningEffort !== null && reasoningEffort !== "off";
|
|
533
560
|
const displayName = displayModelName(providerId || "direct", modelId) + (hasEffortSuffix ? "@" + reasoningEffort : "");
|
|
534
561
|
let cliModel = modelId;
|
|
535
|
-
|
|
536
|
-
|
|
562
|
+
let writeError = null;
|
|
563
|
+
// Anthropic-protocol proxies cannot go through --api-base (that path
|
|
564
|
+
// speaks OpenAI Chat Completions). Instead the generated model config
|
|
565
|
+
// selects LiveBench's native anthropic client and the spawn env points
|
|
566
|
+
// the SDK at the proxy (ANTHROPIC_BASE_URL / ANTHROPIC_API_KEY).
|
|
567
|
+
const isAnthropicRoute = provider && provider.api === "anthropic-messages" && provider.baseURL;
|
|
568
|
+
// OpenAI Responses-API proxies (api: openai-responses) select LiveBench's
|
|
569
|
+
// openai_responses client; --api-base + LIVEBENCH_API_KEY route them the
|
|
570
|
+
// usual way, and reasoning_effort converts to reasoning:{effort} inside.
|
|
571
|
+
const isOpenAIResponsesRoute = provider
|
|
572
|
+
&& (provider.api === "openai-responses" || provider.api === "openai_responses")
|
|
573
|
+
&& provider.baseURL;
|
|
574
|
+
const needConfig = isAnthropicRoute || isOpenAIResponsesRoute || hasEffortSuffix;
|
|
575
|
+
if (needConfig) {
|
|
576
|
+
writeError = writeGeneratedModelConfig(layout, loadYaml(profileDir), {
|
|
577
|
+
displayName,
|
|
578
|
+
modelId,
|
|
579
|
+
reasoningEffort: isAnthropicRoute ? null : reasoningEffort,
|
|
580
|
+
protocol: isAnthropicRoute ? "anthropic" : isOpenAIResponsesRoute ? "openai_responses" : "openai",
|
|
581
|
+
});
|
|
537
582
|
if (writeError) return { status: 500, payload: { ok: false, error: writeError } };
|
|
538
583
|
cliModel = displayName;
|
|
539
584
|
}
|
|
@@ -567,15 +612,13 @@ function apply(ctx) {
|
|
|
567
612
|
// livebench import (shortuuid etc.).
|
|
568
613
|
PATH: `${join(layout.root, ".venv", "Scripts")}${delimiter}${process.env.PATH ?? ""}`,
|
|
569
614
|
};
|
|
570
|
-
//
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
args.push("--api-base", provider.baseURL);
|
|
615
|
+
// Provider routing: keys are resolved through the harness credential seam
|
|
616
|
+
// when available (values may live encrypted in .credentials.yaml rather
|
|
617
|
+
// than in the process env), falling back to the plain environment
|
|
618
|
+
// variable. Secrets travel via env only — never the command line.
|
|
619
|
+
if (provider && provider.baseURL) {
|
|
620
|
+
let key;
|
|
577
621
|
if (provider.keyEnv) {
|
|
578
|
-
let key;
|
|
579
622
|
try {
|
|
580
623
|
const credentials = ctx.get ? ctx.get("credentials") : undefined;
|
|
581
624
|
if (credentials && typeof credentials.resolve === "function") {
|
|
@@ -584,7 +627,15 @@ function apply(ctx) {
|
|
|
584
627
|
}
|
|
585
628
|
} catch { /* credential seam unavailable — fall through to env */ }
|
|
586
629
|
if (!key && process.env[provider.keyEnv]) key = process.env[provider.keyEnv];
|
|
630
|
+
}
|
|
631
|
+
if (provider.api === "openai-completions") {
|
|
632
|
+
args.push("--api-base", provider.baseURL);
|
|
587
633
|
if (key) env.LIVEBENCH_API_KEY = key;
|
|
634
|
+
} else if (provider.api === "anthropic-messages") {
|
|
635
|
+
// The generated model config selects the native anthropic client;
|
|
636
|
+
// the Anthropic SDK picks endpoint+key up from these env vars.
|
|
637
|
+
if (key) env.ANTHROPIC_API_KEY = key;
|
|
638
|
+
env.ANTHROPIC_BASE_URL = provider.baseURL;
|
|
588
639
|
}
|
|
589
640
|
}
|
|
590
641
|
|
|
@@ -657,7 +708,8 @@ function apply(ctx) {
|
|
|
657
708
|
providers: providers.map(({ id, name: pname, models, api, baseURL }) => ({
|
|
658
709
|
id,
|
|
659
710
|
name: pname,
|
|
660
|
-
routable:
|
|
711
|
+
routable: ["openai-completions", "openai-responses", "openai_responses", "anthropic-messages"].includes(api)
|
|
712
|
+
&& typeof baseURL === "string" && baseURL.length > 0,
|
|
661
713
|
baseURL: baseURL ?? null,
|
|
662
714
|
models,
|
|
663
715
|
})),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-livebench-panel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
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",
|