dsh-llm-verifier 0.6.0 → 0.6.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/README.md +3 -0
- package/lib/{auto-DAMeKRkK.js → auto-caXDU2pw.js} +19 -1
- package/lib/{auto-DAMeKRkK.js.map → auto-caXDU2pw.js.map} +1 -1
- package/lib/client.js +493 -331
- package/lib/client.js.map +1 -1
- package/lib/index.js +201 -52
- package/lib/index.js.map +1 -1
- package/lib/replay.js +1 -1
- package/lib/types/client-i18n.d.ts +36 -11
- package/lib/types/client-i18n.d.ts.map +1 -1
- package/lib/types/client-i18n.js +50 -9
- package/lib/types/client-i18n.js.map +1 -1
- package/lib/types/client.d.ts +17 -7
- package/lib/types/client.d.ts.map +1 -1
- package/lib/types/client.js +165 -44
- package/lib/types/client.js.map +1 -1
- package/lib/types/decisions.d.ts +8 -0
- package/lib/types/decisions.d.ts.map +1 -1
- package/lib/types/decisions.js +13 -1
- package/lib/types/decisions.js.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +47 -31
- package/lib/types/index.js.map +1 -1
- package/lib/types/process-selection.d.ts +27 -5
- package/lib/types/process-selection.d.ts.map +1 -1
- package/lib/types/process-selection.js +17 -8
- package/lib/types/process-selection.js.map +1 -1
- package/lib/types/router.d.ts +22 -0
- package/lib/types/router.d.ts.map +1 -1
- package/lib/types/router.js +20 -0
- package/lib/types/router.js.map +1 -1
- package/lib/types/statistics.d.ts +8 -0
- package/lib/types/statistics.d.ts.map +1 -1
- package/lib/types/statistics.js +1 -1
- package/lib/types/statistics.js.map +1 -1
- package/lib/types/verifier-activity.d.ts +189 -0
- package/lib/types/verifier-activity.d.ts.map +1 -0
- package/lib/types/verifier-activity.js +223 -0
- package/lib/types/verifier-activity.js.map +1 -0
- package/package.json +1 -1
- package/src/client-i18n.ts +55 -16
- package/src/client.test.ts +101 -16
- package/src/client.tsx +238 -103
- package/src/decisions.test.ts +16 -0
- package/src/decisions.ts +22 -1
- package/src/index.test.ts +32 -0
- package/src/index.ts +48 -33
- package/src/process-selection.test.ts +13 -0
- package/src/process-selection.ts +39 -14
- package/src/router.test.ts +46 -0
- package/src/router.ts +31 -0
- package/src/statistics.test.ts +16 -0
- package/src/statistics.ts +9 -1
- package/src/verifier-activity.test.ts +164 -0
- package/src/verifier-activity.ts +321 -0
- package/lib/types/process-activity.d.ts +0 -109
- package/lib/types/process-activity.d.ts.map +0 -1
- package/lib/types/process-activity.js +0 -135
- package/lib/types/process-activity.js.map +0 -1
- package/src/process-activity.test.ts +0 -91
- package/src/process-activity.ts +0 -177
package/lib/types/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Button, IconDataOutline16, IconRefreshOutline16, Input } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
2
|
+
import { Button, IconDataOutline16, IconRefreshOutline16, Input, StateDot } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
3
|
import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
-
import { zh, en, dictionaries, toolLabels, tFormat, useLanguage, detectLanguage, compact, money, duration, dateTime, resolveCacheDirOnSave, sameSettingValue, sectionForSave, WORST_CASE_ROUTE_CALLS_PER_JUDGE, WORST_CASE_FINAL_CALLS_PER_JUDGE, WORST_CASE_TASK_PER_JUDGE, WORST_CASE_SESSION_PER_JUDGE, WORST_CASE_CRITERIA_PER_COMPARISON, computeJudgeCount, computeWorstCaseBudget, evaluateBudgetWarning, isVerdictFailed, formatPercentage, formatVerdictDetails,
|
|
4
|
+
import { zh, en, dictionaries, toolLabels, tFormat, useLanguage, detectLanguage, compact, money, duration, dateTime, resolveCacheDirOnSave, sameSettingValue, sectionForSave, WORST_CASE_ROUTE_CALLS_PER_JUDGE, WORST_CASE_FINAL_CALLS_PER_JUDGE, WORST_CASE_TASK_PER_JUDGE, WORST_CASE_SESSION_PER_JUDGE, WORST_CASE_CRITERIA_PER_COMPARISON, computeJudgeCount, computeWorstCaseBudget, evaluateBudgetWarning, isVerdictFailed, formatPercentage, formatVerdictDetails, verifierActivityText, } from "./client-i18n.js";
|
|
5
5
|
import { CRITERIA_PRESETS, DEFAULT_GROUND_TRUTH_NOTE, buildPairwisePrompt } from "./core.js";
|
|
6
6
|
import { MAX_EXTRA_JUDGES, normalizeExtraJudges, judgeIdentity, judgeConflict, addExtraJudge, removeExtraJudge, serializeExtraJudges, } from "./client-judges.js";
|
|
7
7
|
import { FIELDS, PROFILES, SECTIONS, acceptsNumber, activeProfile, applyProfile, isFieldChanged, issueMap, issueMessageKey, recommendedBudgets, resetField, sectionSummary, renderSections, textIssue, validateValues, valuesFromView, } from "./client-fields.js";
|
|
@@ -15,7 +15,7 @@ const sectionTitle = { display: 'flex', alignItems: 'center', gap: 8, width: '10
|
|
|
15
15
|
const sectionHeadingStyle = { fontSize: 14, fontWeight: 500, lineHeight: '22px' };
|
|
16
16
|
const sectionSummaryStyle = { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', minWidth: 0, flex: '1 1 auto' };
|
|
17
17
|
const badgeStyle = { flex: '0 0 auto', padding: '0 6px', borderRadius: 999, fontSize: 11, lineHeight: '16px', color: 'var(--dsw-alias-label-tertiary)', border: '1px solid var(--dsw-alias-border-l2)' };
|
|
18
|
-
const linkButton = { border: 0, background: 'transparent', padding: 0, font: 'inherit', fontSize: 12, lineHeight: '18px', cursor: 'pointer', color: 'var(--dsw-alias-
|
|
18
|
+
const linkButton = { border: 0, background: 'transparent', padding: 0, font: 'inherit', fontSize: 12, lineHeight: '18px', cursor: 'pointer', color: 'var(--dsw-alias-link)' };
|
|
19
19
|
const row = { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '6px 24px', minHeight: 56, padding: '10px 0', borderBottom: '1px solid var(--dsw-alias-border-l2)' };
|
|
20
20
|
const labelCell = { flex: '1 1 220px', minWidth: 0 };
|
|
21
21
|
const controlCell = { flex: '0 1 268px', minWidth: 170, display: 'flex', justifyContent: 'flex-end' };
|
|
@@ -23,16 +23,31 @@ const fieldTitle = { fontSize: 14, fontWeight: 400, lineHeight: '22px', color: '
|
|
|
23
23
|
const fieldHelp = { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)', marginTop: 2 };
|
|
24
24
|
const fullLine = { flex: '1 1 100%', margin: '0 0 2px', fontSize: 12, lineHeight: '18px' };
|
|
25
25
|
const unitStyle = { flex: '0 0 auto', fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' };
|
|
26
|
-
const sliderStyle = { width: '100%', margin: 0, accentColor: 'var(--dsw-alias-
|
|
26
|
+
const sliderStyle = { width: '100%', margin: 0, accentColor: 'var(--dsw-alias-state-business-primary)' };
|
|
27
27
|
const toolbarStyle = { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8, padding: '10px 0', borderTop: '1px solid var(--dsw-alias-border-l2)' };
|
|
28
28
|
const summaryLineStyle = { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' };
|
|
29
|
-
const stickyBar = { position: 'sticky', bottom: 0, zIndex: 5, display: 'flex', flexDirection: 'column', gap: 6, padding: '10px 0 12px', borderTop: '1px solid var(--dsw-alias-border-l2)', background: 'var(--dsw-alias-bg-module
|
|
29
|
+
const stickyBar = { position: 'sticky', bottom: 0, zIndex: 5, display: 'flex', flexDirection: 'column', gap: 6, padding: '10px 0 12px', borderTop: '1px solid var(--dsw-alias-border-l2)', background: 'var(--dsw-alias-bg-module-platform)', boxShadow: '0 -10px 24px var(--dsw-alias-bg-mask-2)' };
|
|
30
30
|
const statusStyle = { fontSize: 12, lineHeight: '18px', display: 'inline-flex', alignItems: 'center', gap: 8, minWidth: 0, flexWrap: 'wrap' };
|
|
31
|
-
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-
|
|
32
|
-
const toggleStyle = (enabled) => ({ position: 'relative', flex: '0 0 auto', 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-
|
|
33
|
-
const toggleThumbStyle = (enabled) => ({ position: 'absolute', top: 3, left: enabled ? 21 : 3, width: 16, height: 16, borderRadius: '50%', background: 'var(--dsw-static-neutral-00
|
|
34
|
-
const dashboardCard = { border: '1px solid var(--dsw-alias-border-l2
|
|
35
|
-
const muted = { color: 'var(--dsw-
|
|
31
|
+
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-specific-input-major)', border: '1px solid var(--dsw-alias-border-l2)', font: 'inherit', fontSize: 14, lineHeight: '22px', outline: 'none' };
|
|
32
|
+
const toggleStyle = (enabled) => ({ position: 'relative', flex: '0 0 auto', 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-specific-input-major)' });
|
|
33
|
+
const toggleThumbStyle = (enabled) => ({ position: 'absolute', top: 3, left: enabled ? 21 : 3, width: 16, height: 16, borderRadius: '50%', background: 'var(--dsw-static-neutral-00)', boxShadow: '0 1px 3px rgba(0,0,0,.28)', transition: 'left .15s ease' });
|
|
34
|
+
const dashboardCard = { border: '1px solid var(--dsw-alias-border-l2)', background: 'var(--dsw-alias-bg-module-platform)', borderRadius: 16, boxShadow: '0 12px 36px var(--dsw-alias-bg-mask-2)' };
|
|
35
|
+
const muted = { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12 };
|
|
36
|
+
/** Chart palette rides the state aliases so bars and line stay legible in both themes. */
|
|
37
|
+
const chartBarColor = 'var(--dsw-alias-state-business-primary)';
|
|
38
|
+
const chartLineColor = 'var(--dsw-alias-state-warn-primary)';
|
|
39
|
+
function toneChip(tone) {
|
|
40
|
+
const token = tone === 'error' ? 'var(--dsw-alias-state-error-primary)'
|
|
41
|
+
: tone === 'warn' ? 'var(--dsw-alias-state-warn-primary)'
|
|
42
|
+
: tone === 'pass' ? 'var(--dsw-alias-state-success-primary)'
|
|
43
|
+
: 'var(--dsw-alias-state-business-primary)';
|
|
44
|
+
return {
|
|
45
|
+
background: `color-mix(in srgb, ${token} 14%, transparent)`,
|
|
46
|
+
border: `1px solid color-mix(in srgb, ${token} 32%, transparent)`,
|
|
47
|
+
// Amber's 500 step is a fill; its 600 step is the readable text rung.
|
|
48
|
+
color: tone === 'warn' ? 'var(--dsw-alias-state-warn-label)' : token,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
36
51
|
const toolColors = { verifier_route_classify: '#d97706', verifier_compare: '#4f8cff', verifier_select: '#8b6df6', verifier_track: '#2fc5c9', verifier_best_of_n: '#e2569b', verifier_current_session: '#f5a524' };
|
|
37
52
|
function record(value) { return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : {}; }
|
|
38
53
|
function values(view) { return valuesFromView(record(view.value)); }
|
|
@@ -324,7 +339,7 @@ export function VerifierSettings({ remote }) {
|
|
|
324
339
|
const first = preset[0];
|
|
325
340
|
if (!first)
|
|
326
341
|
return null;
|
|
327
|
-
return _jsxs("details", { style: { margin: '2px 0 6px 4px', fontSize: 12 }, children: [_jsx("summary", { style: { cursor: 'pointer', color: 'var(--dsw-
|
|
342
|
+
return _jsxs("details", { style: { margin: '2px 0 6px 4px', fontSize: 12 }, children: [_jsx("summary", { style: { cursor: 'pointer', color: 'var(--dsw-alias-label-secondary)' }, children: tFormat(t['field.criteriaPreset.previewSummary'], { count: preset.length }) }), _jsx("pre", { style: { whiteSpace: 'pre-wrap', wordBreak: 'break-word', maxHeight: 240, overflow: 'auto', background: 'var(--dsw-alias-markdown-code-block)', border: '1px solid var(--dsw-alias-border-l1)', borderRadius: 8, padding: 10, marginTop: 8 }, children: buildPairwisePrompt(t['field.criteriaPreset.sampleTask'], t['field.criteriaPreset.sampleA'], t['field.criteriaPreset.sampleB'], first, DEFAULT_GROUND_TRUTH_NOTE) })] });
|
|
328
343
|
})() : null;
|
|
329
344
|
return _jsxs(Fragment, { children: [_jsxs("div", { style: row, "data-field": String(field.key), children: [_jsxs("div", { style: labelCell, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }, children: [_jsx("span", { style: fieldTitle, children: translate(field.titleKey) ?? String(field.key) }), changed && _jsx("span", { style: badgeStyle, children: t['settings.fieldChanged'] }), changed && _jsx("button", { type: "button", style: linkButton, disabled: busy, onClick: () => resetOne(field), children: t['settings.fieldReset'] })] }), _jsx("div", { style: fieldHelp, children: helpFor(field) })] }), _jsx("div", { style: controlCell, children: renderControl(field) }), message && _jsx("p", { style: { ...fullLine, color: 'var(--dsw-alias-state-error-primary)' }, children: issueText(message) }), warning && _jsx("p", { style: { ...fullLine, color: 'var(--dsw-alias-state-warn-label)' }, children: warning })] }), preview] }, String(field.key));
|
|
330
345
|
};
|
|
@@ -350,7 +365,7 @@ export function VerifierSettings({ remote }) {
|
|
|
350
365
|
const open = openSections[entry.section.id] === true;
|
|
351
366
|
const summary = sectionSummary(entry.section.id, draft, translate, format);
|
|
352
367
|
return _jsxs("section", { style: group, children: [_jsxs("button", { type: "button", style: sectionTitle, "aria-expanded": open, "aria-label": tFormat(translate(open ? 'settings.sectionCollapse' : 'settings.sectionExpand') ?? '', { title }), onClick: () => setSectionOpen(entry.section.id, !open), children: [_jsx("span", { style: sectionHeadingStyle, children: title }), entry.section.tier === 'advanced' && _jsx("span", { style: badgeStyle, children: t['settings.advancedBadge'] }), summary && _jsx("span", { style: sectionSummaryStyle, children: summary }), _jsx("span", { style: { marginLeft: summary ? 0 : 'auto', color: 'var(--dsw-alias-label-tertiary)', fontSize: 12 }, children: open ? '▾' : '▸' })] }), open && entry.fields.map(field => (field.kind === 'custom' ? renderJudges() : renderRow(field))), open && entry.section.id === 'tools' && !draft.enabled && _jsx("p", { style: { margin: '8px 0 0', fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-warn-label)' }, children: t['field.enabled.warnDisabled'] }), open && entry.section.id === 'autoVerify' && draft.autoVerifyMode !== 'manual' && _jsx("p", { style: { margin: '8px 0 0', fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-warn-label)' }, children: t['field.autoVerify.warnNotice'] }), open && entry.section.id === 'budgets' && _jsx("div", { style: { padding: '10px 0' }, children: _jsx(Button, { variant: "outline", disabled: busy, onClick: fillRecommended, children: t['settings.recommend'] }) })] }, entry.section.id);
|
|
353
|
-
}), loaded.failures.length > 0 && _jsxs("div", { style: { padding: '10px 12px', borderRadius: 8, background: 'var(--dsw-alias-state-warn-
|
|
368
|
+
}), loaded.failures.length > 0 && _jsxs("div", { style: { padding: '10px 12px', borderRadius: 8, background: 'var(--dsw-alias-state-warn-tertiary)', color: 'var(--dsw-alias-state-warn-label)', fontSize: 12, lineHeight: '18px' }, children: [_jsx("div", { style: { fontWeight: 500, marginBottom: 3 }, children: t['settings.catalogFailures'] }), loaded.failures.map(failure => _jsx("div", { children: failure }, failure))] }), _jsx("div", { style: stickyBar, children: _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }, children: [issues.length > 0
|
|
354
369
|
? _jsxs("span", { style: { ...statusStyle, color: 'var(--dsw-alias-state-error-primary)' }, children: [tFormat(t['settings.invalid.summary'], { count: issues.length }), _jsx("button", { type: "button", style: linkButton, onClick: () => jumpTo(issues[0].key), children: t['settings.jumpToIssue'] })] })
|
|
355
370
|
: error
|
|
356
371
|
? _jsx("span", { style: { ...statusStyle, color: 'var(--dsw-alias-state-error-primary)' }, children: error })
|
|
@@ -379,9 +394,9 @@ function TrendChart({ daily, days, lang }) {
|
|
|
379
394
|
const points = rows.map((row, index) => `${x(index)},${y(row.calls)}`).join(' ');
|
|
380
395
|
const step = rows.length > 16 ? Math.ceil(rows.length / 7) : Math.max(1, Math.ceil(rows.length / 7));
|
|
381
396
|
const barWidth = Math.max(3, Math.min(18, innerW / Math.max(rows.length, 1) * .55));
|
|
382
|
-
return _jsxs("div", { style: { width: '100%', overflowX: 'auto' }, children: [_jsxs("svg", { viewBox: `0 0 ${width} ${height}`, style: { display: 'block', width: '100%', minWidth: 620, height: 'auto' }, "aria-label": t['chart.ariaLabel'], children: [[0, .25, .5, .75, 1].map(ratio => _jsxs("g", { children: [_jsx("line", { x1: pad.l, x2: width - pad.r, y1: pad.t + innerH * ratio, y2: pad.t + innerH * ratio, stroke: "
|
|
397
|
+
return _jsxs("div", { style: { width: '100%', overflowX: 'auto' }, children: [_jsxs("svg", { viewBox: `0 0 ${width} ${height}`, style: { display: 'block', width: '100%', minWidth: 620, height: 'auto' }, "aria-label": t['chart.ariaLabel'], children: [[0, .25, .5, .75, 1].map(ratio => _jsxs("g", { children: [_jsx("line", { x1: pad.l, x2: width - pad.r, y1: pad.t + innerH * ratio, y2: pad.t + innerH * ratio, stroke: "var(--dsw-alias-border-l2)" }), _jsx("text", { x: pad.l - 8, y: pad.t + innerH * ratio + 4, textAnchor: "end", fontSize: "10", fill: "var(--dsw-alias-label-secondary)", children: Math.round(max * (1 - ratio)) })] }, ratio)), rows.map((row, index) => _jsx("rect", { x: x(index) - barWidth / 2, y: y(row.invocations), width: barWidth, height: pad.t + innerH - y(row.invocations), rx: "2", fill: chartBarColor, opacity: ".82" }, row.date)), _jsx("polyline", { points: points, fill: "none", stroke: chartLineColor, strokeWidth: "2.4", strokeLinejoin: "round", strokeLinecap: "round" }), rows.map((row, index) => index % step === 0 || index === rows.length - 1 ? _jsxs("text", { x: x(index), y: height - 14, textAnchor: "middle", fontSize: "10", fill: "var(--dsw-alias-label-secondary)", children: [Number(row.date.slice(5, 7)), "/", Number(row.date.slice(8, 10))] }, row.date) : null)] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'center', gap: 18, ...muted }, children: [_jsxs("span", { children: [_jsx("i", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: chartBarColor, marginRight: 6 } }), t['chart.legendToolCalls']] }), _jsxs("span", { children: [_jsx("i", { style: { display: 'inline-block', width: 14, height: 2, background: chartLineColor, marginRight: 6, verticalAlign: 'middle' } }), t['chart.legendModelCalls']] })] })] });
|
|
383
398
|
}
|
|
384
|
-
export function StatisticsPage({ sessionId, rpc, isGlobal }) {
|
|
399
|
+
export function StatisticsPage({ sessionId, rpc, isGlobal, blankComposerSeat }) {
|
|
385
400
|
const lang = useLanguage();
|
|
386
401
|
const t = dictionaries[lang];
|
|
387
402
|
const labels = toolLabels[lang];
|
|
@@ -395,6 +410,24 @@ export function StatisticsPage({ sessionId, rpc, isGlobal }) {
|
|
|
395
410
|
// that loaded all of them would ship megabytes of text for rows nobody opened.
|
|
396
411
|
const [snapshot, setSnapshot] = useState(null);
|
|
397
412
|
const snapshotRequest = useRef(0);
|
|
413
|
+
// Details are a controlled disclosure rather than a per-row <details>: both entry points then sit
|
|
414
|
+
// on the SAME line and each panel opens directly beneath it, instead of the snapshot landing
|
|
415
|
+
// under a details block that happens to be expanded.
|
|
416
|
+
const [openDetails, setOpenDetails] = useState(null);
|
|
417
|
+
// A read-only dashboard has nothing to type into, yet the Conversation shell renders its
|
|
418
|
+
// composer for every View. The handle is only present on the Conversation View instance, and
|
|
419
|
+
// disposing it on unmount brings the real card back with its draft intact. Mount-only: the
|
|
420
|
+
// takeover belongs to this View's lifetime, and a host that rebuilds the injected props object
|
|
421
|
+
// must not make the seat flap between registrations.
|
|
422
|
+
const seatHandle = useRef(blankComposerSeat);
|
|
423
|
+
seatHandle.current = blankComposerSeat;
|
|
424
|
+
useEffect(() => {
|
|
425
|
+
const take = seatHandle.current;
|
|
426
|
+
if (typeof take !== 'function')
|
|
427
|
+
return undefined;
|
|
428
|
+
const dispose = take();
|
|
429
|
+
return () => { dispose(); };
|
|
430
|
+
}, []);
|
|
398
431
|
const toggleSnapshot = async (id) => {
|
|
399
432
|
if (snapshot?.id === id) {
|
|
400
433
|
setSnapshot(null);
|
|
@@ -410,6 +443,13 @@ export function StatisticsPage({ sessionId, rpc, isGlobal }) {
|
|
|
410
443
|
const result = await rpc.call('/api', 'llm-verifier/statistics', payload);
|
|
411
444
|
if (result && result.ok === true)
|
|
412
445
|
record = result.value.decision;
|
|
446
|
+
else if (result && result.ok === false) {
|
|
447
|
+
// A rejection is the host's real answer — "no snapshot for this row" is one of them —
|
|
448
|
+
// so it is shown as-is instead of being retried over the raw fetch endpoint.
|
|
449
|
+
if (snapshotRequest.current === request)
|
|
450
|
+
setSnapshot({ id, error: result.error?.message ?? t['recent.decisionMissing'] });
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
413
453
|
}
|
|
414
454
|
catch (rpcError) {
|
|
415
455
|
console.warn('[llm-verifier] decision rpc.call failed, trying fetch fallback:', rpcError);
|
|
@@ -555,18 +595,23 @@ export function StatisticsPage({ sessionId, rpc, isGlobal }) {
|
|
|
555
595
|
return () => controller.abort();
|
|
556
596
|
}, [days, sessionOnly, sessionId, refresh, rpc, t, queryKey]);
|
|
557
597
|
const totals = data?.totals;
|
|
558
|
-
return _jsx("main", { style: { height: '100%', overflow: 'auto', boxSizing: 'border-box', padding: '22px clamp(16px, 3vw, 38px) 48px', color: 'var(--dsw-text-primary)', background: 'radial-gradient(circle at 10% 0%, rgba(115,77,255,.09), transparent 32%), radial-gradient(circle at 100% 8%, rgba(47,197,201,.07), transparent 28%)' }, children: _jsxs("div", { style: { maxWidth: 1180, margin: '0 auto', display: 'flex', flexDirection: 'column', gap: 16 }, children: [_jsxs("header", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16, flexWrap: 'wrap' }, children: [_jsxs("div", { children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10 }, children: [_jsx("span", { style: { display: 'grid', placeItems: 'center', width: 34, height: 34, borderRadius: 10, background: 'rgba(79,140,255,.14)', color: '#6da0ff' }, children: _jsx(IconDataOutline16, { size: 18 }) }), _jsx("h2", { style: { margin: 0, fontSize: 23 }, children: isGlobal ? t['global.panelTitle'] : t['stats.pageTitle'] })] }), _jsx("p", { style: { margin: '7px 0 0 44px', ...muted }, children: isGlobal ? t['global.panelIntro'] : tFormat(t['stats.updatedAt'], { time: data ? dateTime(data.generatedAt, lang) : '--' }) })] }), _jsxs("div", { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }, children: [_jsx("div", { style: { display: 'flex', padding: 3, borderRadius: 10, background: 'var(--dsw-surface-sunken)', border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))' }, children: [7, 30, 90].map(value => _jsx("button", { "aria-pressed": days === value, onClick: () => setDays(value), style: { border: 0, borderRadius: 7, padding: '6px 10px', cursor: 'pointer', color: days === value ? '#fff' : 'var(--dsw-text-secondary)', background: days === value ? '#3f68d8' : 'transparent' }, children: tFormat(t['stats.daysUnit'], { days: value }) }, value)) }), Boolean(sessionId) && _jsx("button", { "aria-pressed": sessionOnly, onClick: () => setSessionOnly(value => !value), style: { border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.15))', borderRadius: 9, padding: '7px 11px', cursor: 'pointer', color: 'var(--dsw-text-primary)', background: sessionOnly ? 'rgba(79,140,255,.18)' : 'var(--dsw-surface-sunken)' }, children: sessionOnly ? t['stats.currentSession'] : t['stats.allSessions'] }), _jsx("button", { type: "button", disabled: probe?.busy === true, onClick: () => void runProbe(), style: { border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.15))', borderRadius: 9, padding: '7px 11px', cursor: 'pointer', color: 'var(--dsw-text-primary)', background: 'var(--dsw-surface-sunken)' }, children: probe?.busy === true ? t['probe.running'] : t['probe.button'] }), _jsx("button", { title: t['stats.refresh'], onClick: () => setRefresh(value => value + 1), style: { display: 'grid', placeItems: 'center', width: 34, height: 34, borderRadius: 9, border: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.15))', color: 'var(--dsw-text-primary)', background: 'var(--dsw-surface-sunken)', cursor: 'pointer' }, children: _jsx(IconRefreshOutline16, { size: 16 }) })] })] }), error && _jsxs("div", { style: { ...dashboardCard, padding: 18, borderColor: 'var(--dsw-danger, #e85858)', color: 'var(--dsw-danger, #e85858)' }, children: [error, _jsx("div", { style: { ...muted, marginTop: 6 }, children: t['stats.hostRestartHint'] })] }), probe !== null && _jsxs("section", { style: { ...dashboardCard, padding: '16px 18px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [_jsx("strong", { children: t['probe.title'] }), _jsx("span", { style: muted, children: t['probe.note'] })] }), probe.error !== undefined && _jsx("div", { style: { marginTop: 10, fontSize: 12, color: '#e76565' }, children: probe.error }), probe.value !== undefined && _jsxs(_Fragment, { children: [_jsx("div", { style: { ...muted, marginTop: 8 }, children: tFormat(t['probe.rubric'], { source: probe.value.rubric.source, count: probe.value.rubric.count, file: probe.value.rubric.file ?? '' }) }), probe.value.rubric.error !== undefined && _jsx("div", { style: { marginTop: 6, fontSize: 12, color: '#e3bd63' }, children: tFormat(t['probe.rubricFallback'], { error: probe.value.rubric.error }) }), _jsx("div", { style: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 8 }, children: probe.value.judges.map(judge => _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center', fontSize: 12, padding: '9px 11px', borderRadius: 9, background: 'var(--dsw-surface-sunken)' }, children: [_jsx("span", { style: { color: judge.ok ? '#77d49b' : '#e76565', fontWeight: 600 }, children: judge.ok ? 'OK' : t['probe.failed'] }), _jsx("strong", { children: judge.label }), _jsxs("span", { style: muted, children: [judge.provider, "/", judge.model] }), judge.ok ? _jsxs(_Fragment, { children: [judge.channel !== undefined && _jsxs("span", { style: muted, children: [tFormat(t['probe.channel'], { channel: judge.channel }), judge.channelProbed === true ? ' · ' + t['probe.reprobed'] : ''] }), judge.scoreA !== undefined && judge.scoreB !== undefined && _jsx("span", { style: muted, children: tFormat(t['probe.scores'], { a: formatPercentage(judge.scoreA), b: formatPercentage(judge.scoreB) }) }), _jsx("span", { style: muted, children: tFormat(t['probe.latency'], { ms: String(judge.latencyMs) }) }), judge.calls !== undefined && _jsx("span", { style: muted, children: tFormat(t['probe.calls'], { calls: String(judge.calls) }) })] }) : _jsx("span", { style: { color: '#e76565' }, children: judge.error })] }, judge.label + judge.model)) })] })] }), loading && !data ? _jsx("div", { style: { ...dashboardCard, padding: 32, textAlign: 'center', ...muted }, children: t['stats.loading'] }) : _jsxs(_Fragment, { children: [_jsxs("section", { style: { ...dashboardCard, padding: '22px 24px', display: 'grid', gridTemplateColumns: 'minmax(220px,1.4fr) minmax(240px,1fr)', gap: 24, alignItems: 'center' }, children: [_jsxs("div", { children: [_jsx("div", { style: muted, children: tFormat(t['stats.costSummary'], { days }) }), _jsx("div", { style: { fontSize: 38, fontWeight: 780, letterSpacing: '-.03em', margin: '5px 0' }, children: money(totals?.estimatedCostUsd ?? 0) }), _jsx("div", { style: muted, children: tFormat(t['stats.callsSummary'], { invocations: compact(totals?.invocations ?? 0, lang), calls: compact(totals?.calls ?? 0, lang) }) })] }), _jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr auto', gap: '9px 16px', fontSize: 13 }, children: [_jsx("span", { style: muted, children: t['stats.successCalls'] }), _jsxs("strong", { children: [compact(totals?.successes ?? 0, lang), " ", _jsxs("small", { style: { color: '#77d49b' }, children: ["\u25B2 ", ((totals?.successRate ?? 0) * 100).toFixed(1), "%"] })] }), _jsx("span", { style: muted, children: t['stats.failedCalls'] }), _jsx("strong", { children: compact(totals?.failures ?? 0, lang) }), _jsx("span", { style: muted, children: t['stats.avgDuration'] }), _jsx("strong", { children: duration(totals?.averageDurationMs ?? 0) })] })] }), _jsxs("section", { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(180px,1fr))', gap: 12 }, children: [_jsx(Metric, { label: t['metric.cacheHitRate'], value: ((totals?.cacheHitRate ?? 0) * 100).toFixed(1) + '%', note: tFormat(t['metric.cacheHitNote'], { hits: compact(totals?.cacheHits ?? 0, lang), total: compact((totals?.cacheHits ?? 0) + (totals?.cacheMisses ?? 0), lang) }), accent: "#b7dd64" }), _jsx(Metric, { label: t['metric.prefixCacheHitRate'], value: ((totals?.prefixCacheHitRate ?? 0) * 100).toFixed(1) + '%', note: tFormat(t['metric.prefixCacheHitNote'], { cached: compact(totals?.cachedInputTokens ?? 0, lang), input: compact((totals?.inputTokens ?? 0) + (totals?.cachedInputTokens ?? 0), lang) }) }), _jsx(Metric, { label: t['metric.tokens'], value: compact(totals?.tokens ?? 0, lang), note: tFormat(t['metric.tokensNote'], { input: compact((totals?.inputTokens ?? 0) + (totals?.cachedInputTokens ?? 0), lang), output: compact(totals?.outputTokens ?? 0, lang) }) }), _jsx(Metric, { label: t['metric.avgModelCalls'], value: (totals?.invocations ?? 0) > 0 ? ((totals?.calls ?? 0) / (totals?.invocations ?? 1)).toFixed(1) : '0', note: tFormat(t['metric.avgModelCallsNote'], { attempts: compact(totals?.attempts ?? 0, lang), retries: compact(totals?.retries ?? 0, lang) }) }), _jsx(Metric, { label: t['metric.scoringMode'], value: compact(totals?.topLogprobScores ?? 0, lang), note: tFormat(t['metric.scoringModeNote'], { explicit: compact(totals?.explicitTagScores ?? 0, lang) }) })] }), _jsxs("section", { style: { ...dashboardCard, padding: '18px 20px 16px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }, children: [_jsx("strong", { children: t['chart.title'] }), _jsx("span", { style: muted, children: sessionOnly ? t['stats.currentSession'] : t['stats.allSessions'] })] }), _jsx(TrendChart, { daily: data?.daily ?? [], days: days, lang: lang })] }), _jsxs("section", { style: { ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }, children: [_jsx("strong", { children: t['table.title'] }), _jsx("span", { style: muted, children: tFormat(t['table.toolCount'], { count: (data?.tools ?? []).length }) })] }), _jsx("div", { style: { overflowX: 'auto' }, children: _jsxs("table", { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13, minWidth: 760 }, children: [_jsx("thead", { children: _jsx("tr", { style: { textAlign: 'left', color: 'var(--dsw-text-secondary)', background: 'var(--dsw-surface-sunken)' }, children: [t['table.colTool'], t['table.colInvocations'], t['table.colSuccessRate'], t['table.colAvgDuration'], t['table.colModelCalls'], t['table.colTokens'], t['table.colCacheHits'], t['table.colEstimatedCost']].map(value => _jsx("th", { style: { padding: '10px 12px', fontWeight: 500 }, children: value }, value)) }) }), _jsxs("tbody", { children: [(data?.tools ?? []).map(tool => _jsxs("tr", { style: { borderTop: '1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.1))' }, children: [_jsxs("td", { style: { padding: '13px 12px' }, children: [_jsx("span", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: '50%', background: toolColors[tool.toolName] ?? '#8691a8', marginRight: 8 } }), _jsx("strong", { children: labels[tool.toolName] ?? tool.toolName }), _jsx("div", { style: { ...muted, margin: '3px 0 0 16px' }, children: tool.toolName })] }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.invocations, lang) }), _jsxs("td", { style: { padding: '13px 12px', color: tool.successRate >= .9 ? '#77d49b' : tool.successRate >= .7 ? '#e3bd63' : '#ed7777' }, children: [(tool.successRate * 100).toFixed(1), "%"] }), _jsx("td", { style: { padding: '13px 12px' }, children: duration(tool.averageDurationMs) }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.calls, lang) }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.tokens, lang) }), _jsxs("td", { style: { padding: '13px 12px' }, children: [tool.cacheHits, "/", tool.cacheHits + tool.cacheMisses] }), _jsx("td", { style: { padding: '13px 12px' }, children: money(tool.estimatedCostUsd) })] }, tool.toolName)), (data?.tools.length ?? 0) === 0 && _jsx("tr", { children: _jsx("td", { colSpan: 8, style: { padding: 28, textAlign: 'center', ...muted }, children: t['table.empty'] }) })] })] }) })] }), _jsxs("section", { style: { display: 'grid', gridTemplateColumns: 'minmax(0,1fr) minmax(290px,.45fr)', gap: 16, alignItems: 'start' }, children: [_jsxs("div", { style: { ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }, children: [_jsx("strong", { children: t['recent.title'] }), _jsx("span", { style: muted, children: t['recent.maxCount'] })] }), _jsxs("div", { style: { maxHeight: 360, overflow: 'auto' }, children: [(data?.recent ?? []).map(item => {
|
|
598
|
+
return _jsx("main", { style: { height: '100%', overflow: 'auto', boxSizing: 'border-box', padding: '22px clamp(16px, 3vw, 38px) 48px', color: 'var(--dsw-alias-label-primary)', background: 'radial-gradient(circle at 10% 0%, rgba(115,77,255,.09), transparent 32%), radial-gradient(circle at 100% 8%, rgba(47,197,201,.07), transparent 28%)' }, children: _jsxs("div", { style: { maxWidth: 1180, margin: '0 auto', display: 'flex', flexDirection: 'column', gap: 16 }, children: [_jsxs("header", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16, flexWrap: 'wrap' }, children: [_jsxs("div", { children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10 }, children: [_jsx("span", { style: { display: 'grid', placeItems: 'center', width: 34, height: 34, borderRadius: 10, background: 'color-mix(in srgb, var(--dsw-alias-state-business-primary) 14%, transparent)', color: 'var(--dsw-alias-state-business-primary)' }, children: _jsx(IconDataOutline16, { size: 18 }) }), _jsx("h2", { style: { margin: 0, fontSize: 23 }, children: isGlobal ? t['global.panelTitle'] : t['stats.pageTitle'] })] }), _jsx("p", { style: { margin: '7px 0 0 44px', ...muted }, children: isGlobal ? t['global.panelIntro'] : tFormat(t['stats.updatedAt'], { time: data ? dateTime(data.generatedAt, lang) : '--' }) })] }), _jsxs("div", { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }, children: [_jsx("div", { style: { display: 'flex', padding: 3, borderRadius: 10, background: 'var(--dsw-alias-bg-multi-select)', border: '1px solid var(--dsw-alias-border-l2)' }, children: [7, 30, 90].map(value => _jsx("button", { "aria-pressed": days === value, onClick: () => setDays(value), style: { border: 0, borderRadius: 7, padding: '6px 10px', cursor: 'pointer', color: days === value ? 'var(--dsw-alias-state-business-primary)' : 'var(--dsw-alias-label-secondary)', background: days === value ? 'color-mix(in srgb, var(--dsw-alias-state-business-primary) 16%, transparent)' : 'transparent' }, children: tFormat(t['stats.daysUnit'], { days: value }) }, value)) }), Boolean(sessionId) && _jsx("button", { "aria-pressed": sessionOnly, onClick: () => setSessionOnly(value => !value), style: { border: '1px solid var(--dsw-alias-border-l3)', borderRadius: 9, padding: '7px 11px', cursor: 'pointer', color: sessionOnly ? 'var(--dsw-alias-state-business-primary)' : 'var(--dsw-alias-label-primary)', background: sessionOnly ? 'color-mix(in srgb, var(--dsw-alias-state-business-primary) 14%, transparent)' : 'var(--dsw-alias-interactive-bg-hover)' }, children: sessionOnly ? t['stats.currentSession'] : t['stats.allSessions'] }), _jsx("button", { type: "button", disabled: probe?.busy === true, onClick: () => void runProbe(), style: { border: '1px solid var(--dsw-alias-border-l3)', borderRadius: 9, padding: '7px 11px', cursor: 'pointer', color: 'var(--dsw-alias-label-primary)', background: 'var(--dsw-alias-interactive-bg-hover)' }, children: probe?.busy === true ? t['probe.running'] : t['probe.button'] }), _jsx("button", { title: t['stats.refresh'], onClick: () => setRefresh(value => value + 1), style: { display: 'grid', placeItems: 'center', width: 34, height: 34, borderRadius: 9, border: '1px solid var(--dsw-alias-border-l3)', color: 'var(--dsw-alias-label-primary)', background: 'var(--dsw-alias-interactive-bg-hover)', cursor: 'pointer' }, children: _jsx(IconRefreshOutline16, { size: 16 }) })] })] }), error && _jsxs("div", { style: { ...dashboardCard, padding: 18, borderColor: 'var(--dsw-alias-state-error-primary)', color: 'var(--dsw-alias-state-error-primary)' }, children: [error, _jsx("div", { style: { ...muted, marginTop: 6 }, children: t['stats.hostRestartHint'] })] }), probe !== null && _jsxs("section", { style: { ...dashboardCard, padding: '16px 18px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [_jsx("strong", { children: t['probe.title'] }), _jsx("span", { style: muted, children: t['probe.note'] })] }), probe.error !== undefined && _jsx("div", { style: { marginTop: 10, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' }, children: probe.error }), probe.value !== undefined && _jsxs(_Fragment, { children: [_jsx("div", { style: { ...muted, marginTop: 8 }, children: tFormat(t['probe.rubric'], { source: probe.value.rubric.source, count: probe.value.rubric.count, file: probe.value.rubric.file ?? '' }) }), probe.value.rubric.error !== undefined && _jsx("div", { style: { marginTop: 6, fontSize: 12, color: 'var(--dsw-alias-state-warn-label)' }, children: tFormat(t['probe.rubricFallback'], { error: probe.value.rubric.error }) }), _jsx("div", { style: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 8 }, children: probe.value.judges.map(judge => _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center', fontSize: 12, padding: '9px 11px', borderRadius: 9, background: 'var(--dsw-alias-interactive-bg-hover)' }, children: [_jsx("span", { style: { color: judge.ok ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)', fontWeight: 600 }, children: judge.ok ? 'OK' : t['probe.failed'] }), _jsx("strong", { children: judge.label }), _jsxs("span", { style: muted, children: [judge.provider, "/", judge.model] }), judge.ok ? _jsxs(_Fragment, { children: [judge.channel !== undefined && _jsxs("span", { style: muted, children: [tFormat(t['probe.channel'], { channel: judge.channel }), judge.channelProbed === true ? ' · ' + t['probe.reprobed'] : ''] }), judge.scoreA !== undefined && judge.scoreB !== undefined && _jsx("span", { style: muted, children: tFormat(t['probe.scores'], { a: formatPercentage(judge.scoreA), b: formatPercentage(judge.scoreB) }) }), _jsx("span", { style: muted, children: tFormat(t['probe.latency'], { ms: String(judge.latencyMs) }) }), judge.calls !== undefined && _jsx("span", { style: muted, children: tFormat(t['probe.calls'], { calls: String(judge.calls) }) })] }) : _jsx("span", { style: { color: 'var(--dsw-alias-state-error-primary)' }, children: judge.error })] }, judge.label + judge.model)) })] })] }), loading && !data ? _jsx("div", { style: { ...dashboardCard, padding: 32, textAlign: 'center', ...muted }, children: t['stats.loading'] }) : _jsxs(_Fragment, { children: [_jsxs("section", { style: { ...dashboardCard, padding: '22px 24px', display: 'grid', gridTemplateColumns: 'minmax(220px,1.4fr) minmax(240px,1fr)', gap: 24, alignItems: 'center' }, children: [_jsxs("div", { children: [_jsx("div", { style: muted, children: tFormat(t['stats.costSummary'], { days }) }), _jsx("div", { style: { fontSize: 38, fontWeight: 780, letterSpacing: '-.03em', margin: '5px 0' }, children: money(totals?.estimatedCostUsd ?? 0) }), _jsx("div", { style: muted, children: tFormat(t['stats.callsSummary'], { invocations: compact(totals?.invocations ?? 0, lang), calls: compact(totals?.calls ?? 0, lang) }) })] }), _jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr auto', gap: '9px 16px', fontSize: 13 }, children: [_jsx("span", { style: muted, children: t['stats.successCalls'] }), _jsxs("strong", { children: [compact(totals?.successes ?? 0, lang), " ", _jsxs("small", { style: { color: 'var(--dsw-alias-state-success-primary)' }, children: ["\u25B2 ", ((totals?.successRate ?? 0) * 100).toFixed(1), "%"] })] }), _jsx("span", { style: muted, children: t['stats.failedCalls'] }), _jsx("strong", { children: compact(totals?.failures ?? 0, lang) }), _jsx("span", { style: muted, children: t['stats.avgDuration'] }), _jsx("strong", { children: duration(totals?.averageDurationMs ?? 0) })] })] }), _jsxs("section", { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(180px,1fr))', gap: 12 }, children: [_jsx(Metric, { label: t['metric.cacheHitRate'], value: ((totals?.cacheHitRate ?? 0) * 100).toFixed(1) + '%', note: tFormat(t['metric.cacheHitNote'], { hits: compact(totals?.cacheHits ?? 0, lang), total: compact((totals?.cacheHits ?? 0) + (totals?.cacheMisses ?? 0), lang) }), accent: "var(--dsw-alias-state-success-primary)" }), _jsx(Metric, { label: t['metric.prefixCacheHitRate'], value: ((totals?.prefixCacheHitRate ?? 0) * 100).toFixed(1) + '%', note: tFormat(t['metric.prefixCacheHitNote'], { cached: compact(totals?.cachedInputTokens ?? 0, lang), input: compact((totals?.inputTokens ?? 0) + (totals?.cachedInputTokens ?? 0), lang) }) }), _jsx(Metric, { label: t['metric.tokens'], value: compact(totals?.tokens ?? 0, lang), note: tFormat(t['metric.tokensNote'], { input: compact((totals?.inputTokens ?? 0) + (totals?.cachedInputTokens ?? 0), lang), output: compact(totals?.outputTokens ?? 0, lang) }) }), _jsx(Metric, { label: t['metric.avgModelCalls'], value: (totals?.invocations ?? 0) > 0 ? ((totals?.calls ?? 0) / (totals?.invocations ?? 1)).toFixed(1) : '0', note: tFormat(t['metric.avgModelCallsNote'], { attempts: compact(totals?.attempts ?? 0, lang), retries: compact(totals?.retries ?? 0, lang) }) }), _jsx(Metric, { label: t['metric.scoringMode'], value: compact(totals?.topLogprobScores ?? 0, lang), note: tFormat(t['metric.scoringModeNote'], { explicit: compact(totals?.explicitTagScores ?? 0, lang) }) })] }), _jsxs("section", { style: { ...dashboardCard, padding: '18px 20px 16px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }, children: [_jsx("strong", { children: t['chart.title'] }), _jsx("span", { style: muted, children: sessionOnly ? t['stats.currentSession'] : t['stats.allSessions'] })] }), _jsx(TrendChart, { daily: data?.daily ?? [], days: days, lang: lang })] }), _jsxs("section", { style: { ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }, children: [_jsx("strong", { children: t['table.title'] }), _jsx("span", { style: muted, children: tFormat(t['table.toolCount'], { count: (data?.tools ?? []).length }) })] }), _jsx("div", { style: { overflowX: 'auto' }, children: _jsxs("table", { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13, minWidth: 760 }, children: [_jsx("thead", { children: _jsx("tr", { style: { textAlign: 'left', color: 'var(--dsw-alias-label-secondary)', background: 'var(--dsw-alias-interactive-bg-hover)' }, children: [t['table.colTool'], t['table.colInvocations'], t['table.colSuccessRate'], t['table.colAvgDuration'], t['table.colModelCalls'], t['table.colTokens'], t['table.colCacheHits'], t['table.colEstimatedCost']].map(value => _jsx("th", { style: { padding: '10px 12px', fontWeight: 500 }, children: value }, value)) }) }), _jsxs("tbody", { children: [(data?.tools ?? []).map(tool => _jsxs("tr", { style: { borderTop: '1px solid var(--dsw-alias-border-l2)' }, children: [_jsxs("td", { style: { padding: '13px 12px' }, children: [_jsx("span", { style: { display: 'inline-block', width: 8, height: 8, borderRadius: '50%', background: toolColors[tool.toolName] ?? '#8691a8', marginRight: 8 } }), _jsx("strong", { children: labels[tool.toolName] ?? tool.toolName }), _jsx("div", { style: { ...muted, margin: '3px 0 0 16px' }, children: tool.toolName })] }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.invocations, lang) }), _jsxs("td", { style: { padding: '13px 12px', color: tool.successRate >= .9 ? 'var(--dsw-alias-state-success-primary)' : tool.successRate >= .7 ? 'var(--dsw-alias-state-warn-label)' : 'var(--dsw-alias-state-error-primary)' }, children: [(tool.successRate * 100).toFixed(1), "%"] }), _jsx("td", { style: { padding: '13px 12px' }, children: duration(tool.averageDurationMs) }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.calls, lang) }), _jsx("td", { style: { padding: '13px 12px' }, children: compact(tool.tokens, lang) }), _jsxs("td", { style: { padding: '13px 12px' }, children: [tool.cacheHits, "/", tool.cacheHits + tool.cacheMisses] }), _jsx("td", { style: { padding: '13px 12px' }, children: money(tool.estimatedCostUsd) })] }, tool.toolName)), (data?.tools.length ?? 0) === 0 && _jsx("tr", { children: _jsx("td", { colSpan: 8, style: { padding: 28, textAlign: 'center', ...muted }, children: t['table.empty'] }) })] })] }) })] }), _jsxs("section", { style: { display: 'grid', gridTemplateColumns: 'minmax(0,1fr) minmax(290px,.45fr)', gap: 16, alignItems: 'start' }, children: [_jsxs("div", { style: { ...dashboardCard, padding: '18px 18px 8px', overflow: 'hidden' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', margin: '0 2px 12px' }, children: [_jsx("strong", { children: t['recent.title'] }), _jsx("span", { style: muted, children: t['recent.maxCount'] })] }), _jsxs("div", { style: { maxHeight: 360, overflow: 'auto' }, children: [(data?.recent ?? []).map(item => {
|
|
559
599
|
const verdictInfo = item.verdict ? formatVerdictDetails(item.verdict, t) : undefined;
|
|
560
600
|
const failed = !item.success || (verdictInfo ? verdictInfo.isFailed : false);
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
601
|
+
const detailsOpen = openDetails === item.id;
|
|
602
|
+
const panelStyle = { margin: '8px 0 0 15px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, padding: '8px 10px', background: 'var(--dsw-alias-interactive-bg-hover)', fontSize: 11 };
|
|
603
|
+
// The row is a column: identity plus timing on the first line, then the two controls on
|
|
604
|
+
// one line, then whichever panel they opened. Panels therefore span the whole row
|
|
605
|
+
// instead of being squeezed into the identity column.
|
|
606
|
+
return _jsxs("div", { style: { padding: '11px 8px', borderTop: '1px solid var(--dsw-alias-border-l2)' }, children: [_jsxs("div", { style: { display: 'grid', gridTemplateColumns: 'minmax(170px,1fr) auto', gap: 12 }, children: [_jsxs("div", { children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: { width: 7, height: 7, borderRadius: '50%', background: failed ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-state-success-primary)' } }), _jsx("strong", { style: { fontSize: 13 }, children: labels[item.toolName] ?? item.toolName }), _jsxs("span", { style: muted, children: [item.provider, "/", item.model] })] }), !item.success && _jsx("div", { title: item.errorMessage, style: { margin: '5px 0 0 15px', fontSize: 11, color: 'var(--dsw-alias-state-error-primary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: item.errorMessage ?? item.errorName }), item.verdict && verdictInfo && _jsxs("div", { style: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6, margin: '6px 0 0 15px', fontSize: 11 }, children: [verdictInfo.outcomeText && _jsx("span", { style: { padding: '1px 5px', borderRadius: 4, fontWeight: 500, fontSize: 11, ...toneChip(verdictInfo.isFailed ? 'error' : item.verdict.outcome === 'tie' ? 'warn' : 'pass') }, children: verdictInfo.outcomeText }), verdictInfo.phaseText && _jsx("span", { style: { padding: '1px 5px', borderRadius: 4, background: 'var(--dsw-alias-interactive-bg-hover)', color: 'var(--dsw-alias-label-secondary)', border: '1px solid var(--dsw-alias-border-l2)' }, children: verdictInfo.phaseText }), verdictInfo.scoreText && _jsx("span", { style: { color: (typeof item.verdict.threshold === 'number' && typeof item.verdict.score === 'number' && item.verdict.score < item.verdict.threshold) ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-label-primary)' }, children: verdictInfo.scoreText }), verdictInfo.checkpointsText && _jsx("span", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: verdictInfo.checkpointsText }), verdictInfo.criteriaText && _jsx("span", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: verdictInfo.criteriaText }), verdictInfo.winnerText && _jsx("span", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: verdictInfo.winnerText }), item.route && _jsx("span", { style: { padding: '1px 5px', borderRadius: 4, fontSize: 11, color: 'var(--dsw-alias-label-secondary)', ...toneChip('neutral') }, children: tFormat(t['recent.route.badge'], { stage: item.route.stage, destination: item.route.destination }) }), item.stats.usageIncomplete === true && _jsx("span", { style: { padding: '1px 5px', borderRadius: 4, fontSize: 11, ...toneChip('warn') }, children: t['recent.detail.usageIncomplete'] })] })] }), _jsxs("div", { style: { textAlign: 'right' }, children: [_jsx("div", { style: { fontSize: 12 }, children: duration(item.durationMs) }), _jsx("div", { style: { ...muted, marginTop: 3 }, children: dateTime(item.startedAt, lang) })] })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, margin: '6px 0 0 15px' }, children: [_jsx("button", { type: "button", "aria-expanded": snapshot?.id === item.id, onClick: () => void toggleSnapshot(item.id), style: { fontSize: 11, padding: '2px 7px', borderRadius: 5, cursor: 'pointer', color: 'var(--dsw-alias-label-secondary)', background: 'var(--dsw-alias-interactive-bg-hover)', border: '1px solid var(--dsw-alias-border-l2)' }, children: snapshot?.id === item.id ? t['recent.decisionHide'] : t['recent.decision'] }), _jsx("button", { type: "button", "aria-expanded": detailsOpen, onClick: () => setOpenDetails(current => current === item.id ? null : item.id), style: { fontSize: 11, padding: '2px 7px', borderRadius: 5, cursor: 'pointer', color: 'var(--dsw-alias-label-secondary)', background: 'var(--dsw-alias-interactive-bg-hover)', border: '1px solid var(--dsw-alias-border-l2)' }, children: detailsOpen ? t['recent.detailsHide'] : t['recent.details'] })] }), snapshot?.id === item.id && _jsxs("div", { style: panelStyle, children: [snapshot.error !== undefined && _jsx("div", { style: { fontSize: 11, color: 'var(--dsw-alias-state-error-primary)' }, children: snapshot.error }), snapshot.record === undefined && snapshot.error === undefined && _jsx("div", { style: { ...muted, fontSize: 11 }, children: t['recent.decisionLoading'] }), snapshot.record !== undefined && snapshot.record.calls.length === 0 && _jsx("div", { style: { ...muted, fontSize: 11 }, children: t['recent.decisionEmpty'] }), (snapshot.record?.calls ?? []).map((call, index) => _jsxs("div", { style: { marginBottom: index === snapshot.record.calls.length - 1 ? 0 : 10 }, children: [_jsxs("div", { style: { fontSize: 11, color: 'var(--dsw-alias-label-secondary)' }, children: [call.label, " \u00B7 ", call.channel, call.score === undefined ? '' : ' · ' + formatPercentage(call.score)] }), _jsx("div", { style: { fontSize: 11, marginTop: 4 }, children: t['recent.decisionPrompt'] }), _jsx("pre", { style: { margin: 0, maxHeight: 180, overflow: 'auto', fontSize: 11, whiteSpace: 'pre-wrap', wordBreak: 'break-word', background: 'var(--dsw-alias-markdown-code-block)', padding: '6px 8px', borderRadius: 6 }, children: call.prompt }), _jsx("div", { style: { fontSize: 11, marginTop: 4 }, children: t['recent.decisionOutput'] }), _jsx("pre", { style: { margin: 0, maxHeight: 140, overflow: 'auto', fontSize: 11, whiteSpace: 'pre-wrap', wordBreak: 'break-word', background: 'var(--dsw-alias-markdown-code-block)', padding: '6px 8px', borderRadius: 6 }, children: call.output })] }, index))] }), detailsOpen && _jsxs("div", { style: panelStyle, children: [(item.verdict?.criteria?.length ?? 0) > 0 && _jsxs("div", { style: { display: 'grid', gridTemplateColumns: 'minmax(120px,1fr) auto auto', gap: '4px 14px', marginBottom: 8 }, children: [_jsx("strong", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: t['recent.detail.criterion'] }), _jsx("strong", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: t['recent.detail.score'] }), _jsx("strong", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: t['recent.detail.threshold'] }), item.verdict.criteria.map(criterion => {
|
|
607
|
+
const threshold = typeof item.verdict.threshold === 'number' ? item.verdict.threshold : undefined;
|
|
608
|
+
const missed = threshold !== undefined && criterion.score < threshold;
|
|
609
|
+
return _jsxs(Fragment, { children: [_jsx("span", { children: criterion.id }), _jsx("span", { style: { color: missed ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-state-success-primary)' }, children: formatPercentage(criterion.score) }), _jsx("span", { style: { color: 'var(--dsw-alias-label-secondary)' }, children: threshold === undefined ? '—' : formatPercentage(threshold) })] }, criterion.id);
|
|
610
|
+
})] }), _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 10, color: 'var(--dsw-alias-label-secondary)' }, children: [_jsx("span", { children: tFormat(t['recent.detail.calls'], { calls: compact(item.stats.calls, lang) }) }), _jsx("span", { children: tFormat(t['recent.detail.tokens'], { input: compact(item.stats.inputTokens, lang), cached: compact(item.stats.cachedInputTokens, lang), output: compact(item.stats.outputTokens, lang) }) }), _jsx("span", { children: tFormat(t['recent.detail.scoreCache'], { hits: compact(item.stats.cacheHits, lang), misses: compact(item.stats.cacheMisses, lang) }) }), _jsx("span", { children: tFormat(t['recent.detail.prefixCache'], { rate: ((item.stats.inputTokens + item.stats.cachedInputTokens) > 0 ? (100 * item.stats.cachedInputTokens / (item.stats.inputTokens + item.stats.cachedInputTokens)).toFixed(0) : '0') + '%' }) }), _jsx("span", { children: tFormat(t['recent.detail.cost'], { cost: money(item.stats.estimatedCostUsd) }) }), item.route && _jsx("span", { children: tFormat(t['recent.detail.route'], { cycle: item.route.cycleId, trigger: item.route.trigger, stage: item.route.stage, destination: item.route.destination, attempt: item.route.attempt ?? 1 }) }), item.route && item.route.reservedCalls !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeReserved'], { reserved: compact(item.route.reservedCalls, lang), actual: compact(item.stats.calls, lang) }) }), item.route?.skipReason !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeSkip'], { reason: item.route.skipReason }) }), item.route?.replayed !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeProcess'], { replayed: item.route.replayed === 'candidate' ? t['recent.detail.replayCandidate'] : item.route.replayed === 'original' ? t['recent.detail.replayOriginal'] : t['recent.detail.replayNone'], generated: compact(item.route.generatedCalls ?? 0, lang), judges: compact(item.route.judgeCalls ?? 0, lang) }) }), item.route?.sameCandidate === true && _jsx("span", { children: t['recent.detail.routeSameCandidate'] }), item.route?.alternativeAugmented === true && _jsx("span", { children: t['recent.detail.routeAugmented'] }), item.route?.alternativeModel !== undefined && _jsx("span", { children: tFormat(t['recent.detail.routeAlternativeModel'], { model: item.route.alternativeModel }) }), (item.stats.channelFallbacks ?? 0) > 0 && _jsx("span", { children: tFormat(t['recent.detail.channelFallback'], { count: compact(item.stats.channelFallbacks ?? 0, lang) }) })] })] })] }, item.id);
|
|
611
|
+
}), (data?.recent.length ?? 0) === 0 && _jsx("div", { style: { padding: 24, textAlign: 'center', ...muted }, children: t['recent.empty'] })] })] }), _jsxs("div", { style: { ...dashboardCard, padding: '18px' }, children: [_jsx("strong", { children: t['models.title'] }), _jsxs("div", { style: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 10 }, children: [(data?.models ?? []).map(model => _jsxs("div", { style: { padding: '11px 12px', borderRadius: 10, background: 'var(--dsw-alias-interactive-bg-hover)' }, children: [_jsx("div", { style: { fontWeight: 650, fontSize: 13, overflow: 'hidden', textOverflow: 'ellipsis' }, children: model.model }), _jsx("div", { style: { ...muted, marginTop: 3 }, children: model.provider }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', marginTop: 9, fontSize: 12 }, children: [_jsx("span", { children: tFormat(t['models.calls'], { calls: compact(model.calls, lang) }) }), _jsx("span", { children: tFormat(t['models.tokens'], { tokens: compact(model.tokens, lang) }) }), _jsx("strong", { children: money(model.estimatedCostUsd) })] })] }, model.provider + '\0' + model.model)), (data?.models.length ?? 0) === 0 && _jsx("div", { style: muted, children: t['models.empty'] })] })] })] })] })] }) });
|
|
567
612
|
}
|
|
568
613
|
export function VerifierSidebarIcon({ size, active }) {
|
|
569
|
-
return _jsx("span", { style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: size, height: size, color: active ? 'var(--dsw-alias-
|
|
614
|
+
return _jsx("span", { style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: size, height: size, color: active ? 'var(--dsw-alias-state-business-primary)' : 'currentColor' }, children: _jsx(IconDataOutline16, { size: Math.min(18, size) }) });
|
|
570
615
|
}
|
|
571
616
|
export function GlobalVerifierDashboard({ rpc }) {
|
|
572
617
|
return _jsx(StatisticsPage, { rpc: rpc, isGlobal: true });
|
|
@@ -580,20 +625,22 @@ export function RightSidebarVerifierTitle() {
|
|
|
580
625
|
return _jsx("span", { style: { fontSize: 13, fontWeight: 500 }, children: t['slot.statistics'] });
|
|
581
626
|
}
|
|
582
627
|
export const inject = ['slots', 'connection', 'remote', 'remote.session', 'remote.settings'];
|
|
583
|
-
/** Poll interval of the
|
|
584
|
-
export const
|
|
628
|
+
/** Poll interval of the chat chip while a turn runs (the answer is in-memory and cheap). */
|
|
629
|
+
export const VERIFIER_ACTIVITY_POLL_MS = 1_000;
|
|
585
630
|
/**
|
|
586
|
-
* Read the
|
|
631
|
+
* Read what the plugin is doing for one session (P06 cycle, routed review, final acceptance).
|
|
587
632
|
*
|
|
588
|
-
* The chip explains a pause the chat cannot explain by itself
|
|
589
|
-
*
|
|
590
|
-
*
|
|
633
|
+
* The chip explains a pause the chat cannot explain by itself, so a failed or refused read is
|
|
634
|
+
* SILENCE, never an error banner: the composer must not grow noise because a dashboard endpoint was
|
|
635
|
+
* unavailable.
|
|
591
636
|
* @param rpc - the host RPC handle, when the client exposes one.
|
|
592
637
|
* @param sessionId - the session to ask about.
|
|
593
638
|
* @param signal - abort signal of the owning effect.
|
|
594
639
|
* @returns The activity view, or null when there is nothing (or nothing readable).
|
|
595
640
|
*/
|
|
596
|
-
async function
|
|
641
|
+
async function readVerifierActivity(rpc, sessionId, signal) {
|
|
642
|
+
// The wire kind is historical (it once meant P06 only) and is kept so a page holding the older
|
|
643
|
+
// client bundle keeps talking to the same endpoint.
|
|
597
644
|
const payload = { kind: 'process', sessionId };
|
|
598
645
|
try {
|
|
599
646
|
if (rpc && typeof rpc.call === 'function') {
|
|
@@ -607,7 +654,7 @@ async function readProcessActivity(rpc, sessionId, signal) {
|
|
|
607
654
|
catch (rpcError) {
|
|
608
655
|
if (signal.aborted)
|
|
609
656
|
return null;
|
|
610
|
-
console.warn('[llm-verifier]
|
|
657
|
+
console.warn('[llm-verifier] activity rpc.call failed, trying fetch fallback:', rpcError);
|
|
611
658
|
}
|
|
612
659
|
}
|
|
613
660
|
const response = await fetch('/api/llm-verifier/statistics', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload), signal });
|
|
@@ -620,20 +667,58 @@ async function readProcessActivity(rpc, sessionId, signal) {
|
|
|
620
667
|
}
|
|
621
668
|
}
|
|
622
669
|
/**
|
|
623
|
-
*
|
|
670
|
+
* Dock-card geometry, copied from the host's own composer-stack cards (Todo/Goal/Queue).
|
|
671
|
+
*
|
|
672
|
+
* Those cards share one width axis — the chat content column minus the composer clearances and the
|
|
673
|
+
* four dock insets — and the fallbacks below only matter on a host that does not publish the three
|
|
674
|
+
* custom properties. Without this clamp the status line stretches to the window edge and its text
|
|
675
|
+
* sits far left of the composer it belongs to, which is exactly the misplacement it is here to fix.
|
|
676
|
+
*/
|
|
677
|
+
const activityDock = {
|
|
678
|
+
boxSizing: 'border-box',
|
|
679
|
+
width: 'calc(100% - var(--dsh-composer-side-clearance, 16px) - var(--dsh-composer-side-clearance, 16px) - var(--dsh-composer-dock-inset, 8px) - var(--dsh-composer-dock-inset, 8px) - var(--dsh-composer-dock-inset, 8px) - var(--dsh-composer-dock-inset, 8px))',
|
|
680
|
+
margin: '0 auto',
|
|
681
|
+
};
|
|
682
|
+
const activityBar = {
|
|
683
|
+
boxSizing: 'border-box',
|
|
684
|
+
display: 'flex',
|
|
685
|
+
alignItems: 'center',
|
|
686
|
+
gap: 10,
|
|
687
|
+
width: '100%',
|
|
688
|
+
maxWidth: 'calc(var(--dsh-composer-card-max-width, 952px) - 4 * var(--dsh-composer-dock-inset, 8px))',
|
|
689
|
+
height: 36,
|
|
690
|
+
margin: '0 auto',
|
|
691
|
+
padding: '0 12px',
|
|
692
|
+
border: '0.5px solid var(--dsw-alias-border-l1)',
|
|
693
|
+
borderRadius: 12,
|
|
694
|
+
background: 'var(--dsw-specific-tip)',
|
|
695
|
+
};
|
|
696
|
+
const activityTextStyle = {
|
|
697
|
+
minWidth: 0,
|
|
698
|
+
overflow: 'hidden',
|
|
699
|
+
textOverflow: 'ellipsis',
|
|
700
|
+
whiteSpace: 'nowrap',
|
|
701
|
+
fontSize: 13,
|
|
702
|
+
lineHeight: '20px',
|
|
703
|
+
color: 'var(--dsw-alias-label-secondary)',
|
|
704
|
+
};
|
|
705
|
+
/**
|
|
706
|
+
* The chat-visible half of the automatic stages: what the plugin is doing for this session right now.
|
|
624
707
|
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
708
|
+
* It answers the pauses the chat cannot explain: a P06 cycle buffers the reply, a routed review and
|
|
709
|
+
* the final acceptance hold the turn open while the judges run, and a PASSING acceptance otherwise
|
|
710
|
+
* says nothing at all. Polls only while a read can change the answer — while the turn runs, and
|
|
711
|
+
* while something is still being shown — so an idle conversation makes no requests.
|
|
627
712
|
* @param props - the input-dock owner values (the session snapshot) plus the injected RPC handle.
|
|
628
713
|
*/
|
|
629
|
-
export function
|
|
714
|
+
export function VerifierActivityChip({ session, rpc }) {
|
|
630
715
|
const lang = useLanguage();
|
|
631
716
|
const t = dictionaries[lang];
|
|
632
717
|
const raw = session?.sessionId;
|
|
633
718
|
const sessionId = raw === undefined || raw === null || String(raw) === '' ? undefined : String(raw);
|
|
634
719
|
const running = session?.running === true;
|
|
635
720
|
const [view, setView] = useState(null);
|
|
636
|
-
const rendered =
|
|
721
|
+
const rendered = verifierActivityText(view, t);
|
|
637
722
|
// Keyed on whether anything is RENDERED rather than on whether a read answered: an empty view has
|
|
638
723
|
// to stop the polling too, or a conversation that once ran a cycle would poll forever.
|
|
639
724
|
const showing = rendered !== null;
|
|
@@ -649,22 +734,57 @@ export function ProcessActivityChip({ session, rpc }) {
|
|
|
649
734
|
let cancelled = false;
|
|
650
735
|
const controller = new AbortController();
|
|
651
736
|
const load = async () => {
|
|
652
|
-
const value = await
|
|
737
|
+
const value = await readVerifierActivity(rpc, sessionId, controller.signal);
|
|
653
738
|
if (!cancelled)
|
|
654
739
|
setView(value);
|
|
655
740
|
};
|
|
656
741
|
void load();
|
|
657
|
-
const timer = setInterval(() => { void load(); },
|
|
742
|
+
const timer = setInterval(() => { void load(); }, VERIFIER_ACTIVITY_POLL_MS);
|
|
658
743
|
return () => { cancelled = true; clearInterval(timer); controller.abort(); };
|
|
659
744
|
}, [sessionId, running, showing, rpc]);
|
|
660
745
|
if (rendered === null)
|
|
661
746
|
return null;
|
|
662
|
-
const
|
|
663
|
-
return (
|
|
747
|
+
const dotState = rendered.tone === 'busy' ? 'ongoing' : rendered.tone === 'ok' ? 'done' : 'error';
|
|
748
|
+
return (_jsx("div", { style: activityDock, children: _jsxs("div", { style: activityBar, role: "status", "aria-live": "polite", children: [_jsx(StateDot, { state: dotState, size: 8 }), _jsx("span", { style: activityTextStyle, children: rendered.text })] }) }));
|
|
664
749
|
}
|
|
750
|
+
/**
|
|
751
|
+
* Marker the blank-composer takeover elects with; the value itself is never read.
|
|
752
|
+
*
|
|
753
|
+
* Any non-null selector result elects the entry, so a shared frozen object keeps the selector pure
|
|
754
|
+
* and allocation-free.
|
|
755
|
+
*/
|
|
756
|
+
const BLANK_COMPOSER = Object.freeze({ blank: true });
|
|
665
757
|
export function apply(ctx) {
|
|
666
758
|
const connection = ctx.get('connection');
|
|
667
759
|
const remote = ctx.remote;
|
|
760
|
+
/**
|
|
761
|
+
* Collapse the resident composer while the statistics View is the active one.
|
|
762
|
+
*
|
|
763
|
+
* The Conversation shell renders its composer for EVERY View — a View can only opt into the
|
|
764
|
+
* overlay geometry (`data-conversation-composer-overlay`), never into its absence — so the input
|
|
765
|
+
* card covers the very table the dashboard exists to show. The selector-routed composer chain is
|
|
766
|
+
* the host's own replacement point: while this View is mounted one entry elects an empty
|
|
767
|
+
* composer, and disposing that entry on unmount restores the real card with its draft intact.
|
|
768
|
+
* Tried LAST (`priority` is ascending) so a pending approval, question or subagent takeover still
|
|
769
|
+
* wins the seat. A host that does not know the key leaves the composer in place instead of
|
|
770
|
+
* failing the page.
|
|
771
|
+
* @returns Disposer that hands the seat back to the resident composer.
|
|
772
|
+
*/
|
|
773
|
+
const blankComposerSeat = () => {
|
|
774
|
+
try {
|
|
775
|
+
const dispose = ctx.slots.register({
|
|
776
|
+
name: 'conversation.composer',
|
|
777
|
+
id: 'llm-verifier-blank',
|
|
778
|
+
priority: 1000,
|
|
779
|
+
select: () => BLANK_COMPOSER,
|
|
780
|
+
}, (() => null));
|
|
781
|
+
return typeof dispose === 'function' ? dispose : () => { };
|
|
782
|
+
}
|
|
783
|
+
catch (error) {
|
|
784
|
+
console.warn('[llm-verifier] composer takeover registration failed:', error);
|
|
785
|
+
return () => { };
|
|
786
|
+
}
|
|
787
|
+
};
|
|
668
788
|
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
669
789
|
name: 'settings.section',
|
|
670
790
|
id: 'llm-verifier',
|
|
@@ -677,16 +797,17 @@ export function apply(ctx) {
|
|
|
677
797
|
id: 'llm-verifier-statistics',
|
|
678
798
|
order: 30,
|
|
679
799
|
label: () => (detectLanguage() === 'zh' ? zh['slot.statistics'] : en['slot.statistics']),
|
|
680
|
-
inject: () => ({ rpc: connection.rpc }),
|
|
800
|
+
inject: () => ({ rpc: connection.rpc, blankComposerSeat }),
|
|
681
801
|
}, StatisticsPage));
|
|
682
|
-
// P06 buffers the main reply
|
|
683
|
-
// above the composer —
|
|
802
|
+
// P06 buffers the main reply (the chat looks frozen) and the routed/final stages hold the turn
|
|
803
|
+
// open with nothing to show. The dock sits directly above the composer — where the user is already
|
|
804
|
+
// looking — and disappears on its own.
|
|
684
805
|
ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({
|
|
685
806
|
name: 'conversation.input.dock',
|
|
686
|
-
id: 'llm-verifier-
|
|
807
|
+
id: 'llm-verifier-activity',
|
|
687
808
|
order: 30,
|
|
688
809
|
inject: () => ({ rpc: connection.rpc }),
|
|
689
|
-
},
|
|
810
|
+
}, VerifierActivityChip));
|
|
690
811
|
const VERIFIER_TAB_KIND = 'llm-verifier';
|
|
691
812
|
const VERIFIER_TAB_ID = 'dsh-llm-verifier';
|
|
692
813
|
const registerRightSidebar = (tabs) => {
|