dsh-llm-verifier 0.1.2 → 0.1.3

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 CHANGED
@@ -10,46 +10,87 @@ window.__ModuleLoader__.load({
10
10
  //#region src/client.tsx
11
11
  const NS = "llm-verifier";
12
12
  const shell = {
13
+ width: "100%",
14
+ maxWidth: 720,
13
15
  display: "flex",
14
16
  flexDirection: "column",
15
- gap: 18,
16
- padding: "8px 4px 32px",
17
- color: "var(--dsw-text-primary)"
17
+ gap: 12,
18
+ padding: "0 0 32px",
19
+ color: "var(--dsw-alias-label-primary)"
20
+ };
21
+ const settingsHeading = {
22
+ margin: 0,
23
+ fontSize: 16,
24
+ fontWeight: 500,
25
+ lineHeight: "24px",
26
+ color: "var(--dsw-alias-label-primary)"
18
27
  };
19
- const card = {
28
+ const settingsIntro = {
29
+ margin: 0,
30
+ fontSize: 14,
31
+ lineHeight: "22px",
32
+ color: "var(--dsw-alias-label-tertiary)"
33
+ };
34
+ const group = {
35
+ width: "100%",
20
36
  display: "flex",
21
37
  flexDirection: "column",
22
- gap: 0,
23
- padding: "16px 16px 0",
24
- border: "1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))",
25
- borderRadius: 12,
26
- background: "var(--dsw-alias-bg-module, rgba(20, 31, 57, 0.42))",
27
- overflow: "hidden"
38
+ borderTop: "1px solid var(--dsw-alias-border-l2)"
28
39
  };
29
- const sectionTitle = {
30
- display: "flex",
31
- gap: 10,
32
- alignItems: "center",
33
- padding: "0 0 12px",
34
- borderBottom: "1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))"
40
+ const groupTitle = {
41
+ margin: 0,
42
+ padding: "18px 0 8px",
43
+ fontSize: 14,
44
+ fontWeight: 500,
45
+ lineHeight: "22px",
46
+ color: "var(--dsw-alias-label-primary)"
35
47
  };
36
48
  const row = {
49
+ minHeight: 64,
37
50
  display: "grid",
38
- gridTemplateColumns: "minmax(150px, 1fr) minmax(220px, 1.4fr)",
39
- gap: 18,
51
+ gridTemplateColumns: "minmax(180px, 1fr) minmax(230px, 288px)",
52
+ gap: 24,
40
53
  alignItems: "center",
41
- padding: "14px 0",
42
- borderBottom: "1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))"
54
+ padding: "12px 0",
55
+ borderBottom: "1px solid var(--dsw-alias-border-l2)"
43
56
  };
44
57
  const selectStyle = {
58
+ boxSizing: "border-box",
45
59
  width: "100%",
46
- minHeight: 38,
47
- padding: "0 12px",
48
- borderRadius: 10,
49
- color: "var(--dsw-text-primary)",
50
- background: "var(--dsw-surface-sunken)",
51
- border: "1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))"
60
+ height: 36,
61
+ padding: "0 34px 0 12px",
62
+ borderRadius: 8,
63
+ color: "var(--dsw-alias-label-primary)",
64
+ background: "var(--dsw-alias-bg-input)",
65
+ border: "1px solid var(--dsw-alias-border-l2)",
66
+ font: "inherit",
67
+ fontSize: 14,
68
+ lineHeight: "22px",
69
+ outline: "none"
52
70
  };
71
+ const toggleStyle = (enabled) => ({
72
+ position: "relative",
73
+ justifySelf: "end",
74
+ width: 40,
75
+ height: 22,
76
+ padding: 0,
77
+ border: 0,
78
+ borderRadius: 999,
79
+ cursor: "pointer",
80
+ transition: "background .15s ease",
81
+ background: enabled ? "var(--dsw-alias-state-success-primary)" : "var(--dsw-alias-bg-input)"
82
+ });
83
+ const toggleThumbStyle = (enabled) => ({
84
+ position: "absolute",
85
+ top: 3,
86
+ left: enabled ? 21 : 3,
87
+ width: 16,
88
+ height: 16,
89
+ borderRadius: "50%",
90
+ background: "var(--dsw-static-neutral-00, #fff)",
91
+ boxShadow: "0 1px 3px rgba(0,0,0,.28)",
92
+ transition: "left .15s ease"
93
+ });
53
94
  const dashboardCard = {
54
95
  border: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.13))",
55
96
  background: "color-mix(in srgb, var(--dsw-alias-bg-module, #171925) 88%, transparent)",
@@ -95,17 +136,32 @@ window.__ModuleLoader__.load({
95
136
  return error instanceof Error ? error.message : String(error);
96
137
  }
97
138
  function Label({ title, help }) {
98
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
99
- style: { fontWeight: 600 },
100
- children: title
101
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
102
- style: {
103
- fontSize: 12,
104
- color: "var(--dsw-text-secondary)",
105
- marginTop: 3
106
- },
107
- children: help
108
- })] });
139
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
140
+ style: { minWidth: 0 },
141
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
142
+ style: {
143
+ fontSize: 14,
144
+ fontWeight: 400,
145
+ lineHeight: "22px",
146
+ color: "var(--dsw-alias-label-primary)"
147
+ },
148
+ children: title
149
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
150
+ style: {
151
+ fontSize: 12,
152
+ lineHeight: "18px",
153
+ color: "var(--dsw-alias-label-tertiary)",
154
+ marginTop: 2
155
+ },
156
+ children: help
157
+ })]
158
+ });
159
+ }
160
+ function GroupTitle({ children }) {
161
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
162
+ style: groupTitle,
163
+ children
164
+ });
109
165
  }
110
166
  function compact(value) {
111
167
  return new Intl.NumberFormat("zh-CN", {
@@ -212,15 +268,27 @@ window.__ModuleLoader__.load({
212
268
  if (!loaded || !draft) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
213
269
  style: shell,
214
270
  children: [
215
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: "LLM Verifier" }),
216
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: error ?? "正在读取 DSH 模型和设置…" }),
217
- error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
271
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
272
+ style: settingsHeading,
273
+ children: "LLM Verifier"
274
+ }),
275
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
276
+ style: settingsIntro,
277
+ children: error ?? "正在读取 DSH 模型和设置…"
278
+ }),
279
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
280
+ variant: "outline",
218
281
  onClick: () => void load(),
219
282
  children: "重试"
220
- })
283
+ }) })
221
284
  ]
222
285
  });
223
286
  const numeric = (key, min = 0) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
287
+ style: {
288
+ width: "100%",
289
+ height: 36,
290
+ borderRadius: 8
291
+ },
224
292
  type: "number",
225
293
  min,
226
294
  value: String(draft[key]),
@@ -229,66 +297,53 @@ window.__ModuleLoader__.load({
229
297
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
230
298
  style: shell,
231
299
  children: [
232
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
233
- style: { margin: "0 0 6px" },
300
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
301
+ style: settingsHeading,
234
302
  children: "LLM Verifier"
235
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
236
- style: {
237
- margin: 0,
238
- color: "var(--dsw-text-secondary)"
239
- },
240
- children: "选择任意已在 DSH「模型」页配置并启用的模型作为独立裁判。设置实时生效。"
241
- })] }),
242
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
243
- style: card,
303
+ }),
304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
305
+ style: settingsIntro,
306
+ children: "选择已在 DSH「模型」中配置的模型作为独立裁判。修改后点击页面底部的保存按钮生效。"
307
+ }),
308
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
309
+ style: group,
244
310
  children: [
245
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
246
- style: sectionTitle,
247
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: draft.enabled ? "done" : "error" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "工具开关" })]
248
- }),
311
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupTitle, { children: "工具" }),
249
312
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
250
313
  style: row,
251
314
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
252
315
  title: "启用 Verifier 工具",
253
- help: draft.enabled ? "四个 verifier 工具可被 Agent 调用,每次调用会向裁判模型发起请求。" : "已停用:Agent 调用 verifier 工具会立即返回错误,不产生任何模型请求。"
316
+ help: draft.enabled ? "允许 Agent 调用四个 verifier 工具并向裁判模型发起请求。" : "停用后,所有 verifier 工具都会立即返回错误。"
254
317
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
318
+ type: "button",
319
+ role: "switch",
320
+ "aria-checked": draft.enabled,
321
+ "aria-label": "启用 Verifier 工具",
255
322
  onClick: () => patch("enabled", !draft.enabled),
256
- style: {
257
- width: 80,
258
- height: 32,
259
- borderRadius: 16,
260
- border: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,0.16))",
261
- background: draft.enabled ? "var(--dsw-success, #2f9e5b)" : "var(--dsw-surface-sunken)",
262
- color: "#fff",
263
- cursor: "pointer",
264
- fontSize: 13,
265
- fontWeight: 600
266
- },
267
- children: draft.enabled ? "已启用" : "已停用"
323
+ style: toggleStyle(draft.enabled),
324
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: toggleThumbStyle(draft.enabled) })
268
325
  })]
269
326
  }),
270
- !draft.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
327
+ !draft.enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
271
328
  style: {
272
- padding: "0 0 14px",
329
+ margin: "8px 0 0",
273
330
  fontSize: 12,
274
- color: "var(--dsw-state-warn-primary, #d9a441)"
331
+ lineHeight: "18px",
332
+ color: "var(--dsw-alias-state-warn-label)"
275
333
  },
276
- children: "⚠ 当前停用 verifier_compare / verifier_select / verifier_track / verifier_current_session 四个工具"
334
+ children: "verifier_compareverifier_selectverifier_track verifier_current_session 当前不可用。"
277
335
  })
278
336
  ]
279
337
  }),
280
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
281
- style: card,
338
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
339
+ style: group,
282
340
  children: [
283
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
284
- style: sectionTitle,
285
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "done" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "裁判模型" })]
286
- }),
341
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupTitle, { children: "裁判模型" }),
287
342
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
288
343
  style: row,
289
344
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
290
345
  title: "供应商",
291
- help: "只显示当前 DSH 中可路由的供应商"
346
+ help: "当前 DSH 中可路由的模型供应商。"
292
347
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
293
348
  style: selectStyle,
294
349
  value: draft.provider,
@@ -318,7 +373,7 @@ window.__ModuleLoader__.load({
318
373
  style: row,
319
374
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
320
375
  title: "模型",
321
- help: "模型目录来自 DSH adapter,选择结果会持久化"
376
+ help: "用作裁判的具体模型。"
322
377
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
323
378
  style: selectStyle,
324
379
  value: draft.model,
@@ -345,7 +400,7 @@ window.__ModuleLoader__.load({
345
400
  style: row,
346
401
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
347
402
  title: "推理强度",
348
- help: "由所选模型 adapter 声明;留空使用模型默认值"
403
+ help: "留空时使用所选模型的默认值。"
349
404
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
350
405
  style: selectStyle,
351
406
  value: draft.reasoningEffort ?? "",
@@ -363,101 +418,117 @@ window.__ModuleLoader__.load({
363
418
  style: row,
364
419
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
365
420
  title: "最大输出 Token",
366
- help: "每个裁判请求的输出上限"
421
+ help: "单次裁判请求允许生成的最大 Token 数。"
367
422
  }), numeric("maxTokens", 1)]
368
423
  })
369
424
  ]
370
425
  }),
371
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
372
- style: card,
426
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
427
+ style: group,
373
428
  children: [
374
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
375
- style: sectionTitle,
376
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "执行控制" })
377
- }),
429
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupTitle, { children: "执行" }),
378
430
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
379
431
  style: row,
380
432
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
381
433
  title: "最大并发",
382
- help: "所有 verifier 工具共享的请求并发上限"
434
+ help: "所有 verifier 工具共享的请求并发上限。"
383
435
  }), numeric("maxConcurrency", 1)]
384
436
  }),
385
437
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
386
438
  style: row,
387
439
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
388
440
  title: "最多重试",
389
- help: "短暂网络、限流和服务端错误的重试次数"
441
+ help: "限流、超时和短暂网络错误的重试次数。"
390
442
  }), numeric("maxRetries", 0)]
391
443
  }),
392
444
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
393
445
  style: row,
394
446
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
395
- title: "请求超时(毫秒)",
396
- help: "单个模型请求的超时时间"
447
+ title: "请求超时",
448
+ help: "单次模型请求的超时时间,单位为毫秒。"
397
449
  }), numeric("timeoutMs", 1)]
398
450
  }),
399
451
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
400
452
  style: row,
401
453
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
402
454
  title: "缓存条目上限",
403
- help: "持久评分缓存保留的最大条目数"
455
+ help: "本地持久评分缓存保留的最大条目数。"
404
456
  }), numeric("cacheMaxEntries", 1)]
405
457
  })
406
458
  ]
407
459
  }),
408
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
409
- style: card,
460
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
461
+ style: group,
410
462
  children: [
411
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
412
- style: sectionTitle,
413
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "费用估算(每百万 Token,USD)" })
414
- }),
463
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupTitle, { children: "费用估算" }),
415
464
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
416
465
  style: row,
417
466
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
418
467
  title: "输入价格",
419
- help: "仅用于结果中的 estimatedCostUsd"
468
+ help: "每百万输入 Token 的美元价格,仅用于统计估算。"
420
469
  }), numeric("estimatedInputUsdPerMillion", 0)]
421
470
  }),
422
471
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
423
472
  style: row,
424
473
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
425
474
  title: "输出价格",
426
- help: "仅用于结果中的 estimatedCostUsd"
475
+ help: "每百万输出 Token 的美元价格,仅用于统计估算。"
427
476
  }), numeric("estimatedOutputUsdPerMillion", 0)]
428
477
  })
429
478
  ]
430
479
  }),
431
480
  loaded.failures.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
432
481
  style: {
433
- ...card,
434
- borderColor: "var(--dsw-alias-state-warn-primary, #d9a441)",
435
- paddingBottom: 16
482
+ padding: "10px 12px",
483
+ borderRadius: 8,
484
+ background: "var(--dsw-alias-state-warn-bg)",
485
+ color: "var(--dsw-alias-state-warn-label)",
486
+ fontSize: 12,
487
+ lineHeight: "18px"
436
488
  },
437
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "部分模型目录读取失败" }), loaded.failures.map((x) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: x }, x))]
489
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
490
+ style: {
491
+ fontWeight: 500,
492
+ marginBottom: 3
493
+ },
494
+ children: "部分模型目录读取失败"
495
+ }), loaded.failures.map((x) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: x }, x))]
438
496
  }),
439
- error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
440
- style: { color: "var(--dsw-danger)" },
497
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
498
+ style: {
499
+ margin: 0,
500
+ fontSize: 12,
501
+ lineHeight: "18px",
502
+ color: "var(--dsw-alias-state-error-primary)"
503
+ },
441
504
  children: error
442
505
  }),
443
- saved && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
444
- style: { color: "var(--dsw-success)" },
445
- children: "已保存,后续 verifier 调用将使用新设置。"
506
+ saved && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
507
+ style: {
508
+ margin: 0,
509
+ fontSize: 12,
510
+ lineHeight: "18px",
511
+ color: "var(--dsw-alias-state-success-primary)"
512
+ },
513
+ children: "设置已保存。"
446
514
  }),
447
515
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
448
516
  style: {
449
517
  display: "flex",
450
- gap: 10
518
+ justifyContent: "flex-end",
519
+ gap: 8,
520
+ paddingTop: 4
451
521
  },
452
522
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
453
- disabled: busy || !loaded.writable,
454
- onClick: () => void save(),
455
- children: busy ? "保存中…" : "保存设置"
456
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
457
523
  variant: "outline",
458
524
  disabled: busy,
459
525
  onClick: () => void load(),
460
526
  children: "重新载入"
527
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
528
+ variant: "primary",
529
+ disabled: busy || !loaded.writable,
530
+ onClick: () => void save(),
531
+ children: busy ? "保存中…" : "保存设置"
461
532
  })]
462
533
  })
463
534
  ]
@@ -1,12 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Button, IconDataOutline16, IconRefreshOutline16, Input, StateDot } from '@deepseek-ai/dsh-client-ui-primitives';
2
+ import { Button, IconDataOutline16, IconRefreshOutline16, Input } from '@deepseek-ai/dsh-client-ui-primitives';
3
3
  import { useEffect, useMemo, useState } from 'react';
4
4
  const NS = 'llm-verifier';
5
- const shell = { display: 'flex', flexDirection: 'column', gap: 18, padding: '8px 4px 32px', color: 'var(--dsw-text-primary)' };
6
- const card = { display: 'flex', flexDirection: 'column', gap: 0, padding: '16px 16px 0', border: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))', borderRadius: 12, background: 'var(--dsw-alias-bg-module, rgba(20, 31, 57, 0.42))', overflow: 'hidden' };
7
- const sectionTitle = { display: 'flex', gap: 10, alignItems: 'center', padding: '0 0 12px', borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))' };
8
- const row = { display: 'grid', gridTemplateColumns: 'minmax(150px, 1fr) minmax(220px, 1.4fr)', gap: 18, alignItems: 'center', padding: '14px 0', borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))' };
9
- const selectStyle = { width: '100%', minHeight: 38, padding: '0 12px', borderRadius: 10, color: 'var(--dsw-text-primary)', background: 'var(--dsw-surface-sunken)', border: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))' };
5
+ const shell = { width: '100%', maxWidth: 720, display: 'flex', flexDirection: 'column', gap: 12, padding: '0 0 32px', color: 'var(--dsw-alias-label-primary)' };
6
+ const settingsHeading = { margin: 0, fontSize: 16, fontWeight: 500, lineHeight: '24px', color: 'var(--dsw-alias-label-primary)' };
7
+ const settingsIntro = { margin: 0, fontSize: 14, lineHeight: '22px', color: 'var(--dsw-alias-label-tertiary)' };
8
+ const group = { width: '100%', display: 'flex', flexDirection: 'column', borderTop: '1px solid var(--dsw-alias-border-l2)' };
9
+ const groupTitle = { margin: 0, padding: '18px 0 8px', fontSize: 14, fontWeight: 500, lineHeight: '22px', color: 'var(--dsw-alias-label-primary)' };
10
+ const row = { minHeight: 64, display: 'grid', gridTemplateColumns: 'minmax(180px, 1fr) minmax(230px, 288px)', gap: 24, alignItems: 'center', padding: '12px 0', borderBottom: '1px solid var(--dsw-alias-border-l2)' };
11
+ const selectStyle = { boxSizing: 'border-box', width: '100%', height: 36, padding: '0 34px 0 12px', borderRadius: 8, color: 'var(--dsw-alias-label-primary)', background: 'var(--dsw-alias-bg-input)', border: '1px solid var(--dsw-alias-border-l2)', font: 'inherit', fontSize: 14, lineHeight: '22px', outline: 'none' };
12
+ const toggleStyle = (enabled) => ({ position: 'relative', justifySelf: 'end', width: 40, height: 22, padding: 0, border: 0, borderRadius: 999, cursor: 'pointer', transition: 'background .15s ease', background: enabled ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-bg-input)' });
13
+ const toggleThumbStyle = (enabled) => ({ position: 'absolute', top: 3, left: enabled ? 21 : 3, width: 16, height: 16, borderRadius: '50%', background: 'var(--dsw-static-neutral-00, #fff)', boxShadow: '0 1px 3px rgba(0,0,0,.28)', transition: 'left .15s ease' });
10
14
  const dashboardCard = { border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.13))', background: 'color-mix(in srgb, var(--dsw-alias-bg-module, #171925) 88%, transparent)', borderRadius: 16, boxShadow: '0 12px 36px rgba(0,0,0,.12)' };
11
15
  const muted = { color: 'var(--dsw-text-secondary)', fontSize: 12 };
12
16
  const toolLabels = { verifier_compare: '两项对比', verifier_select: '多项优选', verifier_track: '进度跟踪', verifier_current_session: '会话验收' };
@@ -14,7 +18,8 @@ const toolColors = { verifier_compare: '#4f8cff', verifier_select: '#8b6df6', ve
14
18
  function record(value) { return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : {}; }
15
19
  function values(view) { const v = record(view.value); return { enabled: v.enabled !== false, provider: String(v.provider ?? ''), model: String(v.model ?? ''), ...(typeof v.reasoningEffort === 'string' ? { reasoningEffort: v.reasoningEffort } : {}), maxTokens: Number(v.maxTokens ?? 32768), maxConcurrency: Number(v.maxConcurrency ?? 8), maxRetries: Number(v.maxRetries ?? 3), timeoutMs: Number(v.timeoutMs ?? 300000), cacheMaxEntries: Number(v.cacheMaxEntries ?? 10000), estimatedInputUsdPerMillion: Number(v.estimatedInputUsdPerMillion ?? 0), estimatedOutputUsdPerMillion: Number(v.estimatedOutputUsdPerMillion ?? 0) }; }
16
20
  function message(error) { return error instanceof Error ? error.message : String(error); }
17
- function Label({ title, help }) { return _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 600 }, children: title }), _jsx("div", { style: { fontSize: 12, color: 'var(--dsw-text-secondary)', marginTop: 3 }, children: help })] }); }
21
+ function Label({ title, help }) { return _jsxs("div", { style: { minWidth: 0 }, children: [_jsx("div", { style: { fontSize: 14, fontWeight: 400, lineHeight: '22px', color: 'var(--dsw-alias-label-primary)' }, children: title }), _jsx("div", { style: { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)', marginTop: 2 }, children: help })] }); }
22
+ function GroupTitle({ children }) { return _jsx("h3", { style: groupTitle, children: children }); }
18
23
  function compact(value) { return new Intl.NumberFormat('zh-CN', { notation: value >= 10_000 ? 'compact' : 'standard', maximumFractionDigits: value >= 1_000 ? 1 : 0 }).format(value); }
19
24
  function money(value) { return '$' + value.toLocaleString('en-US', { minimumFractionDigits: value < 0.01 ? 4 : 2, maximumFractionDigits: value < 0.01 ? 4 : 2 }); }
20
25
  function duration(value) { if (value < 1000)
@@ -69,9 +74,9 @@ function VerifierSettings({ api }) {
69
74
  setBusy(false);
70
75
  } };
71
76
  if (!loaded || !draft)
72
- return _jsxs("div", { style: shell, children: [_jsx("h2", { children: "LLM Verifier" }), _jsx("p", { children: error ?? '正在读取 DSH 模型和设置…' }), error && _jsx(Button, { onClick: () => void load(), children: "\u91CD\u8BD5" })] });
73
- const numeric = (key, min = 0) => _jsx(Input, { type: "number", min: min, value: String(draft[key]), onChange: e => patch(key, Number(e.target.value)) });
74
- return _jsxs("div", { style: shell, children: [_jsxs("div", { children: [_jsx("h2", { style: { margin: '0 0 6px' }, children: "LLM Verifier" }), _jsx("p", { style: { margin: 0, color: 'var(--dsw-text-secondary)' }, children: "\u9009\u62E9\u4EFB\u610F\u5DF2\u5728 DSH\u300C\u6A21\u578B\u300D\u9875\u914D\u7F6E\u5E76\u542F\u7528\u7684\u6A21\u578B\u4F5C\u4E3A\u72EC\u7ACB\u88C1\u5224\u3002\u8BBE\u7F6E\u5B9E\u65F6\u751F\u6548\u3002" })] }), _jsxs("div", { style: card, children: [_jsxs("div", { style: sectionTitle, children: [_jsx(StateDot, { state: draft.enabled ? 'done' : 'error' }), _jsx("strong", { children: "\u5DE5\u5177\u5F00\u5173" })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u542F\u7528 Verifier \u5DE5\u5177", help: draft.enabled ? '四个 verifier 工具可被 Agent 调用,每次调用会向裁判模型发起请求。' : '已停用:Agent 调用 verifier 工具会立即返回错误,不产生任何模型请求。' }), _jsx("button", { onClick: () => patch('enabled', !draft.enabled), style: { width: 80, height: 32, borderRadius: 16, border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,0.16))', background: draft.enabled ? 'var(--dsw-success, #2f9e5b)' : 'var(--dsw-surface-sunken)', color: '#fff', cursor: 'pointer', fontSize: 13, fontWeight: 600 }, children: draft.enabled ? '已启用' : '已停用' })] }), !draft.enabled && _jsx("div", { style: { padding: '0 0 14px', fontSize: 12, color: 'var(--dsw-state-warn-primary, #d9a441)' }, children: "\u26A0 \u5F53\u524D\u505C\u7528 verifier_compare / verifier_select / verifier_track / verifier_current_session \u56DB\u4E2A\u5DE5\u5177" })] }), _jsxs("div", { style: card, children: [_jsxs("div", { style: sectionTitle, children: [_jsx(StateDot, { state: "done" }), _jsx("strong", { children: "\u88C1\u5224\u6A21\u578B" })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u4F9B\u5E94\u5546", help: "\u53EA\u663E\u793A\u5F53\u524D DSH \u4E2D\u53EF\u8DEF\u7531\u7684\u4F9B\u5E94\u5546" }), _jsx("select", { style: selectStyle, value: draft.provider, onChange: e => { const provider = e.target.value; const first = loaded.groups.find(g => g.id === provider)?.models[0]; setDraft({ ...draft, provider, ...(first ? { model: first.id, reasoningEffort: first.reasoning?.defaultEffort } : {}) }); }, children: loaded.groups.map(g => _jsxs("option", { value: g.id, children: [g.name, " \u00B7 ", g.id] }, g.id)) })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6A21\u578B", help: "\u6A21\u578B\u76EE\u5F55\u6765\u81EA DSH adapter\uFF0C\u9009\u62E9\u7ED3\u679C\u4F1A\u6301\u4E45\u5316" }), _jsx("select", { style: selectStyle, value: draft.model, onChange: e => { const model = e.target.value; const found = models.find(m => m.id === model); setDraft({ ...draft, model, ...(found?.reasoning?.defaultEffort ? { reasoningEffort: found.reasoning.defaultEffort } : { reasoningEffort: undefined }) }); }, children: models.map(m => _jsxs("option", { value: m.id, children: [m.name, " \u00B7 ", m.id] }, m.id)) })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u63A8\u7406\u5F3A\u5EA6", help: "\u7531\u6240\u9009\u6A21\u578B adapter \u58F0\u660E\uFF1B\u7559\u7A7A\u4F7F\u7528\u6A21\u578B\u9ED8\u8BA4\u503C" }), _jsxs("select", { style: selectStyle, value: draft.reasoningEffort ?? '', onChange: e => patch('reasoningEffort', e.target.value || undefined), children: [_jsx("option", { value: "", children: "\u6A21\u578B\u9ED8\u8BA4" }), efforts.map(e => _jsx("option", { value: e.id, children: e.name }, e.id))] })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6700\u5927\u8F93\u51FA Token", help: "\u6BCF\u4E2A\u88C1\u5224\u8BF7\u6C42\u7684\u8F93\u51FA\u4E0A\u9650" }), numeric('maxTokens', 1)] })] }), _jsxs("div", { style: card, children: [_jsx("div", { style: sectionTitle, children: _jsx("strong", { children: "\u6267\u884C\u63A7\u5236" }) }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6700\u5927\u5E76\u53D1", help: "\u6240\u6709 verifier \u5DE5\u5177\u5171\u4EAB\u7684\u8BF7\u6C42\u5E76\u53D1\u4E0A\u9650" }), numeric('maxConcurrency', 1)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6700\u591A\u91CD\u8BD5", help: "\u77ED\u6682\u7F51\u7EDC\u3001\u9650\u6D41\u548C\u670D\u52A1\u7AEF\u9519\u8BEF\u7684\u91CD\u8BD5\u6B21\u6570" }), numeric('maxRetries', 0)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u8BF7\u6C42\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09", help: "\u5355\u4E2A\u6A21\u578B\u8BF7\u6C42\u7684\u8D85\u65F6\u65F6\u95F4" }), numeric('timeoutMs', 1)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u7F13\u5B58\u6761\u76EE\u4E0A\u9650", help: "\u6301\u4E45\u8BC4\u5206\u7F13\u5B58\u4FDD\u7559\u7684\u6700\u5927\u6761\u76EE\u6570" }), numeric('cacheMaxEntries', 1)] })] }), _jsxs("div", { style: card, children: [_jsx("div", { style: sectionTitle, children: _jsx("strong", { children: "\u8D39\u7528\u4F30\u7B97\uFF08\u6BCF\u767E\u4E07 Token\uFF0CUSD\uFF09" }) }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u8F93\u5165\u4EF7\u683C", help: "\u4EC5\u7528\u4E8E\u7ED3\u679C\u4E2D\u7684 estimatedCostUsd" }), numeric('estimatedInputUsdPerMillion', 0)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u8F93\u51FA\u4EF7\u683C", help: "\u4EC5\u7528\u4E8E\u7ED3\u679C\u4E2D\u7684 estimatedCostUsd" }), numeric('estimatedOutputUsdPerMillion', 0)] })] }), loaded.failures.length > 0 && _jsxs("div", { style: { ...card, borderColor: 'var(--dsw-alias-state-warn-primary, #d9a441)', paddingBottom: 16 }, children: [_jsx("strong", { children: "\u90E8\u5206\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" }), loaded.failures.map(x => _jsx("div", { children: x }, x))] }), error && _jsx("div", { style: { color: 'var(--dsw-danger)' }, children: error }), saved && _jsx("div", { style: { color: 'var(--dsw-success)' }, children: "\u5DF2\u4FDD\u5B58\uFF0C\u540E\u7EED verifier \u8C03\u7528\u5C06\u4F7F\u7528\u65B0\u8BBE\u7F6E\u3002" }), _jsxs("div", { style: { display: 'flex', gap: 10 }, children: [_jsx(Button, { disabled: busy || !loaded.writable, onClick: () => void save(), children: busy ? '保存中…' : '保存设置' }), _jsx(Button, { variant: "outline", disabled: busy, onClick: () => void load(), children: "\u91CD\u65B0\u8F7D\u5165" })] })] });
77
+ return _jsxs("div", { style: shell, children: [_jsx("h2", { style: settingsHeading, children: "LLM Verifier" }), _jsx("p", { style: settingsIntro, children: error ?? '正在读取 DSH 模型和设置…' }), error && _jsx("div", { children: _jsx(Button, { variant: "outline", onClick: () => void load(), children: "\u91CD\u8BD5" }) })] });
78
+ const numeric = (key, min = 0) => _jsx(Input, { style: { width: '100%', height: 36, borderRadius: 8 }, type: "number", min: min, value: String(draft[key]), onChange: e => patch(key, Number(e.target.value)) });
79
+ return _jsxs("div", { style: shell, children: [_jsx("h2", { style: settingsHeading, children: "LLM Verifier" }), _jsx("p", { style: settingsIntro, children: "\u9009\u62E9\u5DF2\u5728 DSH\u300C\u6A21\u578B\u300D\u4E2D\u914D\u7F6E\u7684\u6A21\u578B\u4F5C\u4E3A\u72EC\u7ACB\u88C1\u5224\u3002\u4FEE\u6539\u540E\u70B9\u51FB\u9875\u9762\u5E95\u90E8\u7684\u4FDD\u5B58\u6309\u94AE\u751F\u6548\u3002" }), _jsxs("section", { style: group, children: [_jsx(GroupTitle, { children: "\u5DE5\u5177" }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u542F\u7528 Verifier \u5DE5\u5177", help: draft.enabled ? '允许 Agent 调用四个 verifier 工具并向裁判模型发起请求。' : '停用后,所有 verifier 工具都会立即返回错误。' }), _jsx("button", { type: "button", role: "switch", "aria-checked": draft.enabled, "aria-label": "\u542F\u7528 Verifier \u5DE5\u5177", onClick: () => patch('enabled', !draft.enabled), style: toggleStyle(draft.enabled), children: _jsx("span", { style: toggleThumbStyle(draft.enabled) }) })] }), !draft.enabled && _jsx("p", { style: { margin: '8px 0 0', fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-warn-label)' }, children: "verifier_compare\u3001verifier_select\u3001verifier_track \u548C verifier_current_session \u5F53\u524D\u4E0D\u53EF\u7528\u3002" })] }), _jsxs("section", { style: group, children: [_jsx(GroupTitle, { children: "\u88C1\u5224\u6A21\u578B" }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u4F9B\u5E94\u5546", help: "\u5F53\u524D DSH \u4E2D\u53EF\u8DEF\u7531\u7684\u6A21\u578B\u4F9B\u5E94\u5546\u3002" }), _jsx("select", { style: selectStyle, value: draft.provider, onChange: e => { const provider = e.target.value; const first = loaded.groups.find(g => g.id === provider)?.models[0]; setDraft({ ...draft, provider, ...(first ? { model: first.id, reasoningEffort: first.reasoning?.defaultEffort } : {}) }); }, children: loaded.groups.map(g => _jsxs("option", { value: g.id, children: [g.name, " \u00B7 ", g.id] }, g.id)) })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6A21\u578B", help: "\u7528\u4F5C\u88C1\u5224\u7684\u5177\u4F53\u6A21\u578B\u3002" }), _jsx("select", { style: selectStyle, value: draft.model, onChange: e => { const model = e.target.value; const found = models.find(m => m.id === model); setDraft({ ...draft, model, ...(found?.reasoning?.defaultEffort ? { reasoningEffort: found.reasoning.defaultEffort } : { reasoningEffort: undefined }) }); }, children: models.map(m => _jsxs("option", { value: m.id, children: [m.name, " \u00B7 ", m.id] }, m.id)) })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u63A8\u7406\u5F3A\u5EA6", help: "\u7559\u7A7A\u65F6\u4F7F\u7528\u6240\u9009\u6A21\u578B\u7684\u9ED8\u8BA4\u503C\u3002" }), _jsxs("select", { style: selectStyle, value: draft.reasoningEffort ?? '', onChange: e => patch('reasoningEffort', e.target.value || undefined), children: [_jsx("option", { value: "", children: "\u6A21\u578B\u9ED8\u8BA4" }), efforts.map(e => _jsx("option", { value: e.id, children: e.name }, e.id))] })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6700\u5927\u8F93\u51FA Token", help: "\u5355\u6B21\u88C1\u5224\u8BF7\u6C42\u5141\u8BB8\u751F\u6210\u7684\u6700\u5927 Token \u6570\u3002" }), numeric('maxTokens', 1)] })] }), _jsxs("section", { style: group, children: [_jsx(GroupTitle, { children: "\u6267\u884C" }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6700\u5927\u5E76\u53D1", help: "\u6240\u6709 verifier \u5DE5\u5177\u5171\u4EAB\u7684\u8BF7\u6C42\u5E76\u53D1\u4E0A\u9650\u3002" }), numeric('maxConcurrency', 1)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6700\u591A\u91CD\u8BD5", help: "\u9650\u6D41\u3001\u8D85\u65F6\u548C\u77ED\u6682\u7F51\u7EDC\u9519\u8BEF\u7684\u91CD\u8BD5\u6B21\u6570\u3002" }), numeric('maxRetries', 0)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u8BF7\u6C42\u8D85\u65F6", help: "\u5355\u6B21\u6A21\u578B\u8BF7\u6C42\u7684\u8D85\u65F6\u65F6\u95F4\uFF0C\u5355\u4F4D\u4E3A\u6BEB\u79D2\u3002" }), numeric('timeoutMs', 1)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u7F13\u5B58\u6761\u76EE\u4E0A\u9650", help: "\u672C\u5730\u6301\u4E45\u8BC4\u5206\u7F13\u5B58\u4FDD\u7559\u7684\u6700\u5927\u6761\u76EE\u6570\u3002" }), numeric('cacheMaxEntries', 1)] })] }), _jsxs("section", { style: group, children: [_jsx(GroupTitle, { children: "\u8D39\u7528\u4F30\u7B97" }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u8F93\u5165\u4EF7\u683C", help: "\u6BCF\u767E\u4E07\u8F93\u5165 Token \u7684\u7F8E\u5143\u4EF7\u683C\uFF0C\u4EC5\u7528\u4E8E\u7EDF\u8BA1\u4F30\u7B97\u3002" }), numeric('estimatedInputUsdPerMillion', 0)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u8F93\u51FA\u4EF7\u683C", help: "\u6BCF\u767E\u4E07\u8F93\u51FA Token \u7684\u7F8E\u5143\u4EF7\u683C\uFF0C\u4EC5\u7528\u4E8E\u7EDF\u8BA1\u4F30\u7B97\u3002" }), numeric('estimatedOutputUsdPerMillion', 0)] })] }), loaded.failures.length > 0 && _jsxs("div", { style: { padding: '10px 12px', borderRadius: 8, background: 'var(--dsw-alias-state-warn-bg)', color: 'var(--dsw-alias-state-warn-label)', fontSize: 12, lineHeight: '18px' }, children: [_jsx("div", { style: { fontWeight: 500, marginBottom: 3 }, children: "\u90E8\u5206\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" }), loaded.failures.map(x => _jsx("div", { children: x }, x))] }), error && _jsx("p", { style: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-error-primary)' }, children: error }), saved && _jsx("p", { style: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-success-primary)' }, children: "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u3002" }), _jsxs("div", { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, paddingTop: 4 }, children: [_jsx(Button, { variant: "outline", disabled: busy, onClick: () => void load(), children: "\u91CD\u65B0\u8F7D\u5165" }), _jsx(Button, { variant: "primary", disabled: busy || !loaded.writable, onClick: () => void save(), children: busy ? '保存中…' : '保存设置' })] })] });
75
80
  }
76
81
  function Metric({ label, value, note, accent }) {
77
82
  return _jsxs("div", { style: { ...dashboardCard, padding: '16px 18px', minWidth: 0 }, children: [_jsx("div", { style: muted, children: label }), _jsx("div", { style: { fontSize: 25, fontWeight: 750, lineHeight: 1.2, margin: '7px 0 5px', color: accent }, children: value }), _jsx("div", { style: { ...muted, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: note })] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-llm-verifier",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Configurable DSH-native LLM verifier with a Web settings page",
5
5
  "repository": {
6
6
  "type": "git",
package/src/client.tsx CHANGED
@@ -3,7 +3,7 @@ import type { ModelProviderGroup, SettingsNamespaceView } from '@deepseek-ai/dsh
3
3
  import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
4
4
  import type {} from '@deepseek-ai/dsh-client-ui-conversation/client'
5
5
  import type {} from '@deepseek-ai/dsh-client-ui-slots'
6
- import { Button, IconDataOutline16, IconRefreshOutline16, Input, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'
6
+ import { Button, IconDataOutline16, IconRefreshOutline16, Input } from '@deepseek-ai/dsh-client-ui-primitives'
7
7
  import { useEffect, useMemo, useState } from 'react'
8
8
 
9
9
  const NS = 'llm-verifier'
@@ -18,11 +18,15 @@ interface Totals extends RunStats { invocations: number; successes: number; fail
18
18
  interface StatisticsOverview { generatedAt: number; fromMs: number; toMs: number; sessionId?: string; totals: Totals; daily: DailyStatistics[]; tools: ToolStatistics[]; models: ModelStatistics[]; recent: InvocationRecord[] }
19
19
  interface StatisticsPageProps { sessionId: string; rpc: { call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<{ ok: boolean; value?: unknown; error?: { message: string } }> } }
20
20
 
21
- const shell: React.CSSProperties = { display: 'flex', flexDirection: 'column', gap: 18, padding: '8px 4px 32px', color: 'var(--dsw-text-primary)' }
22
- const card: React.CSSProperties = { display: 'flex', flexDirection: 'column', gap: 0, padding: '16px 16px 0', border: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))', borderRadius: 12, background: 'var(--dsw-alias-bg-module, rgba(20, 31, 57, 0.42))', overflow: 'hidden' }
23
- const sectionTitle: React.CSSProperties = { display: 'flex', gap: 10, alignItems: 'center', padding: '0 0 12px', borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))' }
24
- const row: React.CSSProperties = { display: 'grid', gridTemplateColumns: 'minmax(150px, 1fr) minmax(220px, 1.4fr)', gap: 18, alignItems: 'center', padding: '14px 0', borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))' }
25
- const selectStyle: React.CSSProperties = { width: '100%', minHeight: 38, padding: '0 12px', borderRadius: 10, color: 'var(--dsw-text-primary)', background: 'var(--dsw-surface-sunken)', border: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))' }
21
+ const shell: React.CSSProperties = { width: '100%', maxWidth: 720, display: 'flex', flexDirection: 'column', gap: 12, padding: '0 0 32px', color: 'var(--dsw-alias-label-primary)' }
22
+ const settingsHeading: React.CSSProperties = { margin: 0, fontSize: 16, fontWeight: 500, lineHeight: '24px', color: 'var(--dsw-alias-label-primary)' }
23
+ const settingsIntro: React.CSSProperties = { margin: 0, fontSize: 14, lineHeight: '22px', color: 'var(--dsw-alias-label-tertiary)' }
24
+ const group: React.CSSProperties = { width: '100%', display: 'flex', flexDirection: 'column', borderTop: '1px solid var(--dsw-alias-border-l2)' }
25
+ const groupTitle: React.CSSProperties = { margin: 0, padding: '18px 0 8px', fontSize: 14, fontWeight: 500, lineHeight: '22px', color: 'var(--dsw-alias-label-primary)' }
26
+ const row: React.CSSProperties = { minHeight: 64, display: 'grid', gridTemplateColumns: 'minmax(180px, 1fr) minmax(230px, 288px)', gap: 24, alignItems: 'center', padding: '12px 0', borderBottom: '1px solid var(--dsw-alias-border-l2)' }
27
+ const selectStyle: React.CSSProperties = { boxSizing: 'border-box', width: '100%', height: 36, padding: '0 34px 0 12px', borderRadius: 8, color: 'var(--dsw-alias-label-primary)', background: 'var(--dsw-alias-bg-input)', border: '1px solid var(--dsw-alias-border-l2)', font: 'inherit', fontSize: 14, lineHeight: '22px', outline: 'none' }
28
+ const toggleStyle = (enabled: boolean): React.CSSProperties => ({ position: 'relative', justifySelf: 'end', width: 40, height: 22, padding: 0, border: 0, borderRadius: 999, cursor: 'pointer', transition: 'background .15s ease', background: enabled ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-bg-input)' })
29
+ const toggleThumbStyle = (enabled: boolean): React.CSSProperties => ({ position: 'absolute', top: 3, left: enabled ? 21 : 3, width: 16, height: 16, borderRadius: '50%', background: 'var(--dsw-static-neutral-00, #fff)', boxShadow: '0 1px 3px rgba(0,0,0,.28)', transition: 'left .15s ease' })
26
30
  const dashboardCard: React.CSSProperties = { border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.13))', background: 'color-mix(in srgb, var(--dsw-alias-bg-module, #171925) 88%, transparent)', borderRadius: 16, boxShadow: '0 12px 36px rgba(0,0,0,.12)' }
27
31
  const muted: React.CSSProperties = { color: 'var(--dsw-text-secondary)', fontSize: 12 }
28
32
  const toolLabels: Record<string, string> = { verifier_compare: '两项对比', verifier_select: '多项优选', verifier_track: '进度跟踪', verifier_current_session: '会话验收' }
@@ -31,7 +35,8 @@ const toolColors: Record<string, string> = { verifier_compare: '#4f8cff', verifi
31
35
  function record(value: unknown): Record<string, unknown> { return typeof value === 'object' && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : {} }
32
36
  function values(view: SettingsNamespaceView): Values { const v=record(view.value); return { enabled:v.enabled!==false,provider:String(v.provider??''),model:String(v.model??''),...(typeof v.reasoningEffort==='string'?{reasoningEffort:v.reasoningEffort}:{}),maxTokens:Number(v.maxTokens??32768),maxConcurrency:Number(v.maxConcurrency??8),maxRetries:Number(v.maxRetries??3),timeoutMs:Number(v.timeoutMs??300000),cacheMaxEntries:Number(v.cacheMaxEntries??10000),estimatedInputUsdPerMillion:Number(v.estimatedInputUsdPerMillion??0),estimatedOutputUsdPerMillion:Number(v.estimatedOutputUsdPerMillion??0) } }
33
37
  function message(error: unknown): string { return error instanceof Error ? error.message : String(error) }
34
- function Label({title,help}:{title:string;help:string}) { return <div><div style={{fontWeight:600}}>{title}</div><div style={{fontSize:12,color:'var(--dsw-text-secondary)',marginTop:3}}>{help}</div></div> }
38
+ function Label({title,help}:{title:string;help:string}) { return <div style={{minWidth:0}}><div style={{fontSize:14,fontWeight:400,lineHeight:'22px',color:'var(--dsw-alias-label-primary)'}}>{title}</div><div style={{fontSize:12,lineHeight:'18px',color:'var(--dsw-alias-label-tertiary)',marginTop:2}}>{help}</div></div> }
39
+ function GroupTitle({children}:{children:React.ReactNode}) { return <h3 style={groupTitle}>{children}</h3> }
35
40
  function compact(value: number): string { return new Intl.NumberFormat('zh-CN', { notation: value >= 10_000 ? 'compact' : 'standard', maximumFractionDigits: value >= 1_000 ? 1 : 0 }).format(value) }
36
41
  function money(value: number): string { return '$' + value.toLocaleString('en-US', { minimumFractionDigits: value < 0.01 ? 4 : 2, maximumFractionDigits: value < 0.01 ? 4 : 2 }) }
37
42
  function duration(value: number): string { if (value < 1000) return Math.round(value) + ' ms'; if (value < 60_000) return (value / 1000).toFixed(1) + ' s'; return (value / 60_000).toFixed(1) + ' min' }
@@ -47,25 +52,39 @@ function VerifierSettings({ api }:{api:any}) {
47
52
  const selected=models.find(m=>m.id===draft?.model); const efforts=selected?.reasoning?.efforts??[]
48
53
  const patch=<K extends keyof Values>(key:K,value:Values[K])=>setDraft(v=>v?{...v,[key]:value}:v)
49
54
  const save=async()=>{if(!loaded||!draft)return;setBusy(true);setSaved(false);setError(null);try{const section={...record(loaded.settings.user),...draft};if(!draft.reasoningEffort)delete section.reasoningEffort;const res=await api.settings.update({ns:NS,patch:section,expectedRevision:loaded.settings.revision});if(!res.result.ok)throw new Error(res.result.error.message);setLoaded(v=>v?{...v,settings:res.result.value}:v);setDraft(values(res.result.value));setSaved(true)}catch(e){setError(message(e))}finally{setBusy(false)}}
50
- if(!loaded||!draft)return <div style={shell}><h2>LLM Verifier</h2><p>{error??'正在读取 DSH 模型和设置…'}</p>{error&&<Button onClick={()=>void load()}>重试</Button>}</div>
51
- const numeric=(key:keyof Values,min=0)=><Input type="number" min={min} value={String(draft[key])} onChange={e=>patch(key,Number(e.target.value) as never)} />
55
+ if(!loaded||!draft)return <div style={shell}><h2 style={settingsHeading}>LLM Verifier</h2><p style={settingsIntro}>{error??'正在读取 DSH 模型和设置…'}</p>{error&&<div><Button variant="outline" onClick={()=>void load()}>重试</Button></div>}</div>
56
+ const numeric=(key:keyof Values,min=0)=><Input style={{width:'100%',height:36,borderRadius:8}} type="number" min={min} value={String(draft[key])} onChange={e=>patch(key,Number(e.target.value) as never)} />
52
57
  return <div style={shell}>
53
- <div><h2 style={{margin:'0 0 6px'}}>LLM Verifier</h2><p style={{margin:0,color:'var(--dsw-text-secondary)'}}>选择任意已在 DSH「模型」页配置并启用的模型作为独立裁判。设置实时生效。</p></div>
54
- <div style={card}><div style={sectionTitle}><StateDot state={draft.enabled?'done':'error'}/><strong>工具开关</strong></div>
55
- <div style={row}><Label title="启用 Verifier 工具" help={draft.enabled?'四个 verifier 工具可被 Agent 调用,每次调用会向裁判模型发起请求。':'已停用:Agent 调用 verifier 工具会立即返回错误,不产生任何模型请求。'}/><button onClick={()=>patch('enabled',!draft.enabled)} style={{width:80,height:32,borderRadius:16,border:'1px solid var(--dsw-alias-border-l2, rgba(255,255,255,0.16))',background:draft.enabled?'var(--dsw-success, #2f9e5b)':'var(--dsw-surface-sunken)',color:'#fff',cursor:'pointer',fontSize:13,fontWeight:600}}>{draft.enabled?'已启用':'已停用'}</button></div>
56
- {!draft.enabled&&<div style={{padding:'0 0 14px',fontSize:12,color:'var(--dsw-state-warn-primary, #d9a441)'}}>⚠ 当前停用 verifier_compare / verifier_select / verifier_track / verifier_current_session 四个工具</div>}
57
- </div>
58
- <div style={card}><div style={sectionTitle}><StateDot state="done"/><strong>裁判模型</strong></div>
59
- <div style={row}><Label title="供应商" help="只显示当前 DSH 中可路由的供应商"/><select style={selectStyle} value={draft.provider} onChange={e=>{const provider=e.target.value;const first=loaded.groups.find(g=>g.id===provider)?.models[0];setDraft({...draft,provider,...(first?{model:first.id,reasoningEffort:first.reasoning?.defaultEffort}:{})})}}>{loaded.groups.map(g=><option key={g.id} value={g.id}>{g.name} · {g.id}</option>)}</select></div>
60
- <div style={row}><Label title="模型" help="模型目录来自 DSH adapter,选择结果会持久化"/><select style={selectStyle} value={draft.model} onChange={e=>{const model=e.target.value;const found=models.find(m=>m.id===model);setDraft({...draft,model,...(found?.reasoning?.defaultEffort?{reasoningEffort:found.reasoning.defaultEffort}:{reasoningEffort:undefined})})}}>{models.map(m=><option key={m.id} value={m.id}>{m.name} · {m.id}</option>)}</select></div>
61
- <div style={row}><Label title="推理强度" help="由所选模型 adapter 声明;留空使用模型默认值"/><select style={selectStyle} value={draft.reasoningEffort??''} onChange={e=>patch('reasoningEffort',e.target.value||undefined)}><option value="">模型默认</option>{efforts.map(e=><option key={e.id} value={e.id}>{e.name}</option>)}</select></div>
62
- <div style={row}><Label title="最大输出 Token" help="每个裁判请求的输出上限"/>{numeric('maxTokens',1)}</div>
63
- </div>
64
- <div style={card}><div style={sectionTitle}><strong>执行控制</strong></div><div style={row}><Label title="最大并发" help="所有 verifier 工具共享的请求并发上限"/>{numeric('maxConcurrency',1)}</div><div style={row}><Label title="最多重试" help="短暂网络、限流和服务端错误的重试次数"/>{numeric('maxRetries',0)}</div><div style={row}><Label title="请求超时(毫秒)" help="单个模型请求的超时时间"/>{numeric('timeoutMs',1)}</div><div style={row}><Label title="缓存条目上限" help="持久评分缓存保留的最大条目数"/>{numeric('cacheMaxEntries',1)}</div></div>
65
- <div style={card}><div style={sectionTitle}><strong>费用估算(每百万 Token,USD)</strong></div><div style={row}><Label title="输入价格" help="仅用于结果中的 estimatedCostUsd"/>{numeric('estimatedInputUsdPerMillion',0)}</div><div style={row}><Label title="输出价格" help="仅用于结果中的 estimatedCostUsd"/>{numeric('estimatedOutputUsdPerMillion',0)}</div></div>
66
- {loaded.failures.length>0&&<div style={{...card,borderColor:'var(--dsw-alias-state-warn-primary, #d9a441)',paddingBottom:16}}><strong>部分模型目录读取失败</strong>{loaded.failures.map(x=><div key={x}>{x}</div>)}</div>}
67
- {error&&<div style={{color:'var(--dsw-danger)'}}>{error}</div>}{saved&&<div style={{color:'var(--dsw-success)'}}>已保存,后续 verifier 调用将使用新设置。</div>}
68
- <div style={{display:'flex',gap:10}}><Button disabled={busy||!loaded.writable} onClick={()=>void save()}>{busy?'保存中…':'保存设置'}</Button><Button variant="outline" disabled={busy} onClick={()=>void load()}>重新载入</Button></div>
58
+ <h2 style={settingsHeading}>LLM Verifier</h2>
59
+ <p style={settingsIntro}>选择已在 DSH「模型」中配置的模型作为独立裁判。修改后点击页面底部的保存按钮生效。</p>
60
+
61
+ <section style={group}><GroupTitle>工具</GroupTitle>
62
+ <div style={row}><Label title="启用 Verifier 工具" help={draft.enabled?'允许 Agent 调用四个 verifier 工具并向裁判模型发起请求。':'停用后,所有 verifier 工具都会立即返回错误。'}/><button type="button" role="switch" aria-checked={draft.enabled} aria-label="启用 Verifier 工具" onClick={()=>patch('enabled',!draft.enabled)} style={toggleStyle(draft.enabled)}><span style={toggleThumbStyle(draft.enabled)}/></button></div>
63
+ {!draft.enabled&&<p style={{margin:'8px 0 0',fontSize:12,lineHeight:'18px',color:'var(--dsw-alias-state-warn-label)'}}>verifier_compare、verifier_select、verifier_track 和 verifier_current_session 当前不可用。</p>}
64
+ </section>
65
+
66
+ <section style={group}><GroupTitle>裁判模型</GroupTitle>
67
+ <div style={row}><Label title="供应商" help="当前 DSH 中可路由的模型供应商。"/><select style={selectStyle} value={draft.provider} onChange={e=>{const provider=e.target.value;const first=loaded.groups.find(g=>g.id===provider)?.models[0];setDraft({...draft,provider,...(first?{model:first.id,reasoningEffort:first.reasoning?.defaultEffort}:{})})}}>{loaded.groups.map(g=><option key={g.id} value={g.id}>{g.name} · {g.id}</option>)}</select></div>
68
+ <div style={row}><Label title="模型" help="用作裁判的具体模型。"/><select style={selectStyle} value={draft.model} onChange={e=>{const model=e.target.value;const found=models.find(m=>m.id===model);setDraft({...draft,model,...(found?.reasoning?.defaultEffort?{reasoningEffort:found.reasoning.defaultEffort}:{reasoningEffort:undefined})})}}>{models.map(m=><option key={m.id} value={m.id}>{m.name} · {m.id}</option>)}</select></div>
69
+ <div style={row}><Label title="推理强度" help="留空时使用所选模型的默认值。"/><select style={selectStyle} value={draft.reasoningEffort??''} onChange={e=>patch('reasoningEffort',e.target.value||undefined)}><option value="">模型默认</option>{efforts.map(e=><option key={e.id} value={e.id}>{e.name}</option>)}</select></div>
70
+ <div style={row}><Label title="最大输出 Token" help="单次裁判请求允许生成的最大 Token 数。"/>{numeric('maxTokens',1)}</div>
71
+ </section>
72
+
73
+ <section style={group}><GroupTitle>执行</GroupTitle>
74
+ <div style={row}><Label title="最大并发" help="所有 verifier 工具共享的请求并发上限。"/>{numeric('maxConcurrency',1)}</div>
75
+ <div style={row}><Label title="最多重试" help="限流、超时和短暂网络错误的重试次数。"/>{numeric('maxRetries',0)}</div>
76
+ <div style={row}><Label title="请求超时" help="单次模型请求的超时时间,单位为毫秒。"/>{numeric('timeoutMs',1)}</div>
77
+ <div style={row}><Label title="缓存条目上限" help="本地持久评分缓存保留的最大条目数。"/>{numeric('cacheMaxEntries',1)}</div>
78
+ </section>
79
+
80
+ <section style={group}><GroupTitle>费用估算</GroupTitle>
81
+ <div style={row}><Label title="输入价格" help="每百万输入 Token 的美元价格,仅用于统计估算。"/>{numeric('estimatedInputUsdPerMillion',0)}</div>
82
+ <div style={row}><Label title="输出价格" help="每百万输出 Token 的美元价格,仅用于统计估算。"/>{numeric('estimatedOutputUsdPerMillion',0)}</div>
83
+ </section>
84
+
85
+ {loaded.failures.length>0&&<div style={{padding:'10px 12px',borderRadius:8,background:'var(--dsw-alias-state-warn-bg)',color:'var(--dsw-alias-state-warn-label)',fontSize:12,lineHeight:'18px'}}><div style={{fontWeight:500,marginBottom:3}}>部分模型目录读取失败</div>{loaded.failures.map(x=><div key={x}>{x}</div>)}</div>}
86
+ {error&&<p style={{margin:0,fontSize:12,lineHeight:'18px',color:'var(--dsw-alias-state-error-primary)'}}>{error}</p>}{saved&&<p style={{margin:0,fontSize:12,lineHeight:'18px',color:'var(--dsw-alias-state-success-primary)'}}>设置已保存。</p>}
87
+ <div style={{display:'flex',justifyContent:'flex-end',gap:8,paddingTop:4}}><Button variant="outline" disabled={busy} onClick={()=>void load()}>重新载入</Button><Button variant="primary" disabled={busy||!loaded.writable} onClick={()=>void save()}>{busy?'保存中…':'保存设置'}</Button></div>
69
88
  </div>
70
89
  }
71
90