dsh-model-health 0.2.1 → 0.2.2
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/dist/client.js +76 -45
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -17,7 +17,7 @@ window.__ModuleLoader__.load({
|
|
|
17
17
|
var exports = module.exports;
|
|
18
18
|
|
|
19
19
|
const React = require("react");
|
|
20
|
-
const { useState, useEffect, useCallback } = React;
|
|
20
|
+
const { useState, useEffect, useCallback, useRef } = React;
|
|
21
21
|
|
|
22
22
|
// ── 多语言词典(locale 服务,语言跟随 harness)─────────────────────────
|
|
23
23
|
const NS = "dsh-model-health";
|
|
@@ -48,6 +48,8 @@ window.__ModuleLoader__.load({
|
|
|
48
48
|
statusOk: "可用",
|
|
49
49
|
statusFail: "不可用",
|
|
50
50
|
statusSkip: "跳过",
|
|
51
|
+
testOne: "测试",
|
|
52
|
+
testOneTip: "点击测试此模型",
|
|
51
53
|
};
|
|
52
54
|
const en = {
|
|
53
55
|
tab: "Model Health",
|
|
@@ -76,6 +78,8 @@ window.__ModuleLoader__.load({
|
|
|
76
78
|
statusOk: "Available",
|
|
77
79
|
statusFail: "Unavailable",
|
|
78
80
|
statusSkip: "Skipped",
|
|
81
|
+
testOne: "Test",
|
|
82
|
+
testOneTip: "Click to test this model",
|
|
79
83
|
};
|
|
80
84
|
|
|
81
85
|
// 内联样式(适配 DSH 明暗主题的 CSS 变量)
|
|
@@ -139,6 +143,20 @@ window.__ModuleLoader__.load({
|
|
|
139
143
|
opacity: 0.5,
|
|
140
144
|
cursor: "not-allowed",
|
|
141
145
|
},
|
|
146
|
+
// 状态列里的"测试"小按钮(未测试的模型显示它代替"未测试"文字)
|
|
147
|
+
btnTestSmall: {
|
|
148
|
+
boxSizing: "border-box",
|
|
149
|
+
height: 22,
|
|
150
|
+
padding: "0 12px",
|
|
151
|
+
fontSize: 12,
|
|
152
|
+
lineHeight: "16px",
|
|
153
|
+
cursor: "pointer",
|
|
154
|
+
border: "1px solid var(--dsw-alias-button-primary-fill, #4f46e5)",
|
|
155
|
+
borderRadius: 11,
|
|
156
|
+
background: "transparent",
|
|
157
|
+
color: "var(--dsw-alias-button-primary-fill, #4f46e5)",
|
|
158
|
+
whiteSpace: "nowrap",
|
|
159
|
+
},
|
|
142
160
|
progress: {
|
|
143
161
|
fontSize: 12,
|
|
144
162
|
color: "var(--dsw-alias-label-tertiary, #888)",
|
|
@@ -355,6 +373,9 @@ window.__ModuleLoader__.load({
|
|
|
355
373
|
// 初始值从 localStorage 读取,实现跨刷新持久化
|
|
356
374
|
const [testResults, setTestResults] = useState(loadStoredResults);
|
|
357
375
|
const [testing, setTesting] = useState(false);
|
|
376
|
+
// 请求序号:同一模型重复触发测试(单测 + 测试全部并发)时,
|
|
377
|
+
// 只有最新一次请求的结果允许写入,避免旧响应覆盖新状态
|
|
378
|
+
const seqRef = useRef({});
|
|
358
379
|
const [progress, setProgress] = useState({ done: 0, total: 0 });
|
|
359
380
|
// 可选列显示状态:{ [colKey]: boolean },默认 false
|
|
360
381
|
const [optionalCols, setOptionalCols] = useState({});
|
|
@@ -386,6 +407,42 @@ window.__ModuleLoader__.load({
|
|
|
386
407
|
loadData();
|
|
387
408
|
}, [loadData]);
|
|
388
409
|
|
|
410
|
+
// 测试单个模型:该行置为 testing → POST /api/model-health/test → 写回结果
|
|
411
|
+
// ("测试全部"与状态列的"测试"按钮共用此逻辑)
|
|
412
|
+
const testModel = useCallback(async (key) => {
|
|
413
|
+
const seq = (seqRef.current[key] || 0) + 1;
|
|
414
|
+
seqRef.current[key] = seq;
|
|
415
|
+
setTestResults((s) => ({ ...s, [key]: { status: "testing" } }));
|
|
416
|
+
|
|
417
|
+
let result;
|
|
418
|
+
try {
|
|
419
|
+
const resp = await fetch("/api/model-health/test", {
|
|
420
|
+
method: "POST",
|
|
421
|
+
headers: { "Content-Type": "application/json" },
|
|
422
|
+
body: JSON.stringify({ key }),
|
|
423
|
+
cache: "no-store",
|
|
424
|
+
});
|
|
425
|
+
const json = await resp.json();
|
|
426
|
+
result = !json.ok
|
|
427
|
+
? { status: "fail", error: json.error }
|
|
428
|
+
: {
|
|
429
|
+
status: json.status || "fail",
|
|
430
|
+
latency: json.latency,
|
|
431
|
+
error: json.error,
|
|
432
|
+
};
|
|
433
|
+
} catch (e) {
|
|
434
|
+
result = { status: "fail", error: e.message };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// 期间若同一模型又发起了更新的测试,丢弃这份过期结果
|
|
438
|
+
if (seqRef.current[key] !== seq) return;
|
|
439
|
+
setTestResults((s) => {
|
|
440
|
+
const next = { ...s, [key]: result };
|
|
441
|
+
saveStoredResults(next);
|
|
442
|
+
return next;
|
|
443
|
+
});
|
|
444
|
+
}, []);
|
|
445
|
+
|
|
389
446
|
// 测试全部:并发发起所有测试请求,每个完成即更新对应行状态
|
|
390
447
|
const testAll = useCallback(async () => {
|
|
391
448
|
const data = state.data;
|
|
@@ -393,59 +450,20 @@ window.__ModuleLoader__.load({
|
|
|
393
450
|
const total = data.models.length;
|
|
394
451
|
setTesting(true);
|
|
395
452
|
setProgress({ done: 0, total });
|
|
396
|
-
// 初始化所有为 testing(按 provider/modelId key)
|
|
397
|
-
const init = {};
|
|
398
|
-
for (const m of data.models) init[m.key] = { status: "testing" };
|
|
399
|
-
setTestResults(init);
|
|
400
|
-
saveStoredResults(init);
|
|
401
453
|
|
|
402
454
|
let done = 0;
|
|
403
455
|
// 并发测试,但限制并发数避免压垮 host/网络
|
|
404
456
|
const CONCURRENCY = 6;
|
|
405
457
|
const queue = data.models.map((m) => m.key);
|
|
406
458
|
|
|
407
|
-
// 更新单个结果并同步到 localStorage
|
|
408
|
-
function updateResult(key, result) {
|
|
409
|
-
setTestResults((s) => {
|
|
410
|
-
const next = { ...s, [key]: result };
|
|
411
|
-
saveStoredResults(next);
|
|
412
|
-
return next;
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
async function runOne(key) {
|
|
417
|
-
try {
|
|
418
|
-
const resp = await fetch("/api/model-health/test", {
|
|
419
|
-
method: "POST",
|
|
420
|
-
headers: { "Content-Type": "application/json" },
|
|
421
|
-
body: JSON.stringify({ key }),
|
|
422
|
-
cache: "no-store",
|
|
423
|
-
});
|
|
424
|
-
const json = await resp.json();
|
|
425
|
-
if (!json.ok) {
|
|
426
|
-
updateResult(key, { status: "fail", error: json.error });
|
|
427
|
-
} else {
|
|
428
|
-
updateResult(key, {
|
|
429
|
-
status: json.status || "fail",
|
|
430
|
-
latency: json.latency,
|
|
431
|
-
error: json.error,
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
} catch (e) {
|
|
435
|
-
updateResult(key, { status: "fail", error: e.message });
|
|
436
|
-
} finally {
|
|
437
|
-
done++;
|
|
438
|
-
setProgress({ done, total });
|
|
439
|
-
if (done >= total) setTesting(false);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// 限制并发的 worker pool
|
|
444
459
|
async function worker() {
|
|
445
460
|
while (queue.length > 0) {
|
|
446
461
|
const key = queue.shift();
|
|
447
462
|
if (key === undefined) break;
|
|
448
|
-
await
|
|
463
|
+
await testModel(key);
|
|
464
|
+
done++;
|
|
465
|
+
setProgress({ done, total });
|
|
466
|
+
if (done >= total) setTesting(false);
|
|
449
467
|
}
|
|
450
468
|
}
|
|
451
469
|
const workers = Array.from(
|
|
@@ -453,7 +471,7 @@ window.__ModuleLoader__.load({
|
|
|
453
471
|
() => worker()
|
|
454
472
|
);
|
|
455
473
|
await Promise.all(workers);
|
|
456
|
-
}, [state.data]);
|
|
474
|
+
}, [state.data, testModel]);
|
|
457
475
|
|
|
458
476
|
const h = React.createElement;
|
|
459
477
|
|
|
@@ -611,6 +629,19 @@ window.__ModuleLoader__.load({
|
|
|
611
629
|
}, row.provider);
|
|
612
630
|
}
|
|
613
631
|
if (col.key === "_status") {
|
|
632
|
+
// 未测试:显示可点击的"测试"按钮,点击即测单个模型
|
|
633
|
+
if (tr.status === "idle") {
|
|
634
|
+
return h("td", {
|
|
635
|
+
key: col.key,
|
|
636
|
+
style: STYLES.td,
|
|
637
|
+
},
|
|
638
|
+
h("button", {
|
|
639
|
+
style: STYLES.btnTestSmall,
|
|
640
|
+
title: t("testOneTip"),
|
|
641
|
+
onClick: () => testModel(row.key),
|
|
642
|
+
}, t("testOne"))
|
|
643
|
+
);
|
|
644
|
+
}
|
|
614
645
|
const hasError = tr.error && tr.status === "fail";
|
|
615
646
|
return h("td", {
|
|
616
647
|
key: col.key,
|
package/package.json
CHANGED