@linxiraos/pi-stats 1.1.27 → 1.2.0
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/CHANGELOG.md +10 -7
- package/build.ts +4 -0
- package/dist/client/favicon.ico +0 -0
- package/dist/client/index.css +1 -1
- package/dist/client/index.html +1 -0
- package/dist/client/index.js +96 -96
- package/dist/types/aggregator.d.ts +4 -1
- package/dist/types/client/api.d.ts +5 -1
- package/dist/types/client/app/nav.d.ts +5 -2
- package/dist/types/client/data/formatters.d.ts +5 -2
- package/dist/types/client/data/live.d.ts +16 -11
- package/dist/types/client/data/query-store.d.ts +39 -0
- package/dist/types/client/data/query.d.ts +10 -5
- package/dist/types/client/data/range.d.ts +4 -3
- package/dist/types/client/i18n/format.d.ts +9 -0
- package/dist/types/client/i18n/index.d.ts +18 -0
- package/dist/types/client/i18n/messages/charts.d.ts +8 -0
- package/dist/types/client/i18n/messages/common.d.ts +153 -0
- package/dist/types/client/i18n/messages/costs.d.ts +92 -0
- package/dist/types/client/i18n/messages/en.d.ts +1255 -0
- package/dist/types/client/i18n/messages/errors.d.ts +78 -0
- package/dist/types/client/i18n/messages/frustration.d.ts +191 -0
- package/dist/types/client/i18n/messages/gain.d.ts +54 -0
- package/dist/types/client/i18n/messages/models.d.ts +94 -0
- package/dist/types/client/i18n/messages/nav.d.ts +46 -0
- package/dist/types/client/i18n/messages/overview.d.ts +80 -0
- package/dist/types/client/i18n/messages/projects.d.ts +84 -0
- package/dist/types/client/i18n/messages/providers.d.ts +142 -0
- package/dist/types/client/i18n/messages/requests.d.ts +72 -0
- package/dist/types/client/i18n/messages/tools.d.ts +119 -0
- package/dist/types/client/i18n/messages/traces.d.ts +144 -0
- package/dist/types/client/i18n/messages/ui.d.ts +44 -0
- package/dist/types/client/i18n/messages/zh-CN.d.ts +13 -0
- package/dist/types/client/i18n/types.d.ts +15 -0
- package/dist/types/client/traces/Minimap.d.ts +2 -0
- package/dist/types/client/traces/TimelineCanvas.d.ts +29 -8
- package/dist/types/client/traces/TranscriptList.d.ts +3 -1
- package/dist/types/db.d.ts +14 -21
- package/dist/types/embedded-client.d.ts +5 -0
- package/dist/types/frustration.d.ts +18 -9
- package/dist/types/live.d.ts +5 -3
- package/dist/types/locale.d.ts +16 -0
- package/dist/types/parser.d.ts +0 -17
- package/dist/types/rollup.d.ts +9 -1
- package/dist/types/server.d.ts +6 -0
- package/dist/types/shared-types.d.ts +7 -0
- package/dist/types/trace.d.ts +18 -6
- package/dist/types/types.d.ts +12 -0
- package/dist/types/user-metrics.d.ts +23 -1
- package/package.json +4 -4
- package/src/aggregator.ts +28 -11
- package/src/client/api.ts +40 -5
- package/src/client/app/LiveChip.tsx +14 -13
- package/src/client/app/Shell.tsx +19 -13
- package/src/client/app/ThemeToggle.tsx +4 -3
- package/src/client/app/nav.ts +19 -16
- package/src/client/data/formatters.ts +73 -13
- package/src/client/data/live.tsx +85 -45
- package/src/client/data/query-store.ts +108 -0
- package/src/client/data/query.ts +47 -56
- package/src/client/data/range.ts +34 -14
- package/src/client/data/series.ts +7 -1
- package/src/client/favicon.ico +0 -0
- package/src/client/i18n/format.ts +28 -0
- package/src/client/i18n/index.ts +53 -0
- package/src/client/i18n/messages/charts.ts +8 -0
- package/src/client/i18n/messages/common.ts +157 -0
- package/src/client/i18n/messages/costs.ts +104 -0
- package/src/client/i18n/messages/en.ts +40 -0
- package/src/client/i18n/messages/errors.ts +80 -0
- package/src/client/i18n/messages/frustration.ts +198 -0
- package/src/client/i18n/messages/gain.ts +54 -0
- package/src/client/i18n/messages/models.ts +94 -0
- package/src/client/i18n/messages/nav.ts +46 -0
- package/src/client/i18n/messages/overview.ts +90 -0
- package/src/client/i18n/messages/projects.ts +84 -0
- package/src/client/i18n/messages/providers.ts +165 -0
- package/src/client/i18n/messages/requests.ts +73 -0
- package/src/client/i18n/messages/tools.ts +120 -0
- package/src/client/i18n/messages/traces.ts +160 -0
- package/src/client/i18n/messages/ui.ts +46 -0
- package/src/client/i18n/messages/zh-CN.ts +43 -0
- package/src/client/i18n/types.ts +19 -0
- package/src/client/index.tsx +4 -0
- package/src/client/routes/CostsRoute.tsx +61 -48
- package/src/client/routes/ErrorsRoute.tsx +71 -47
- package/src/client/routes/FrustrationRoute.tsx +131 -116
- package/src/client/routes/GainRoute.tsx +42 -30
- package/src/client/routes/ModelsRoute.tsx +87 -71
- package/src/client/routes/OverviewRoute.tsx +107 -62
- package/src/client/routes/ProjectsRoute.tsx +71 -45
- package/src/client/routes/ProvidersRoute.tsx +149 -113
- package/src/client/routes/RequestsRoute.tsx +68 -45
- package/src/client/routes/ToolsRoute.tsx +105 -92
- package/src/client/routes/TracesRoute.tsx +25 -23
- package/src/client/styles.css +4 -2
- package/src/client/traces/AggregatesPanel.tsx +19 -8
- package/src/client/traces/Minimap.tsx +65 -27
- package/src/client/traces/SpanDrawer.tsx +46 -21
- package/src/client/traces/SummaryStrip.tsx +27 -11
- package/src/client/traces/TimelineCanvas.tsx +245 -116
- package/src/client/traces/TraceView.tsx +51 -36
- package/src/client/traces/TranscriptList.tsx +20 -11
- package/src/client/traces/traces.css +26 -0
- package/src/client/ui/JsonBlock.tsx +3 -2
- package/src/client/ui/Modal.tsx +3 -2
- package/src/client/ui/RequestDrawer.tsx +33 -30
- package/src/client/ui/SearchInput.tsx +2 -1
- package/src/client/ui/States.tsx +3 -2
- package/src/client/ui/Table.tsx +7 -3
- package/src/db.ts +150 -135
- package/src/embedded-client.ts +9 -0
- package/src/frustration.ts +9 -30
- package/src/live.ts +36 -11
- package/src/locale.ts +29 -0
- package/src/parser.ts +37 -72
- package/src/port-conflict.ts +1 -1
- package/src/prompts/judge/frustration.json +19 -0
- package/src/rollup.ts +41 -20
- package/src/server.ts +67 -27
- package/src/shared-types.ts +7 -0
- package/src/trace.ts +54 -40
- package/src/types.ts +12 -0
- package/src/user-metrics.ts +220 -6
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
startFrustrationRun,
|
|
8
8
|
} from "../api";
|
|
9
9
|
import { Chart, type ChartSeries, Legend, useHiddenSeries } from "../charts";
|
|
10
|
+
import { t } from "../i18n";
|
|
10
11
|
import { SERIES_COLORS } from "../data/colors";
|
|
11
12
|
import { formatCost, formatInteger, formatPercent } from "../data/formatters";
|
|
12
13
|
import { useQuery } from "../data/query";
|
|
@@ -53,7 +54,7 @@ const ALL_CLASSES = "*";
|
|
|
53
54
|
|
|
54
55
|
export function FrustrationRoute({ active, range }: FrustrationRouteProps) {
|
|
55
56
|
const [running, setRunning] = useState(false);
|
|
56
|
-
const stats = useQuery(["frustration", range], () => getFrustrationDashboardStats(range), {
|
|
57
|
+
const stats = useQuery(["frustration", range], ({ signal }) => getFrustrationDashboardStats(range, signal), {
|
|
57
58
|
enabled: active,
|
|
58
59
|
pollMs: running ? RUNNING_POLL_MS : undefined,
|
|
59
60
|
});
|
|
@@ -73,8 +74,8 @@ export function FrustrationRoute({ active, range }: FrustrationRouteProps) {
|
|
|
73
74
|
return (
|
|
74
75
|
<div className="page">
|
|
75
76
|
<PageHeader
|
|
76
|
-
title="
|
|
77
|
-
description={
|
|
77
|
+
title={t("frustration.title")}
|
|
78
|
+
description={t("frustration.description", { window: t(meta.windowKey) })}
|
|
78
79
|
/>
|
|
79
80
|
|
|
80
81
|
<QueryView query={stats} skeleton={<ChartSkeleton height={96} />}>
|
|
@@ -101,7 +102,7 @@ export function FrustrationRoute({ active, range }: FrustrationRouteProps) {
|
|
|
101
102
|
<QueryView
|
|
102
103
|
query={stats}
|
|
103
104
|
skeleton={
|
|
104
|
-
<Card index={2} title="
|
|
105
|
+
<Card index={2} title={t("frustration.by-model")}>
|
|
105
106
|
<ChartSkeleton height={340} />
|
|
106
107
|
<TableSkeleton rows={6} />
|
|
107
108
|
</Card>
|
|
@@ -135,38 +136,45 @@ function formatElapsed(ms: number): string {
|
|
|
135
136
|
|
|
136
137
|
function FrustrationSummary({ overall }: { overall: FrustrationCounts }) {
|
|
137
138
|
const regex = overall.messages - overall.judged;
|
|
139
|
+
const unscored =
|
|
140
|
+
overall.unscored > 0 ? t("frustration.coverage-unscored", { count: formatInteger(overall.unscored) }) : "";
|
|
138
141
|
return (
|
|
139
|
-
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
142
|
+
<>
|
|
143
|
+
<StatGrid min={170}>
|
|
144
|
+
<Stat
|
|
145
|
+
label={t("frustration.user-messages")}
|
|
146
|
+
title={t("frustration.user-messages-title")}
|
|
147
|
+
value={formatInteger(overall.messages)}
|
|
148
|
+
/>
|
|
149
|
+
<Stat
|
|
150
|
+
label={t("frustration.judge-coverage")}
|
|
151
|
+
title={t("frustration.judge-coverage-title")}
|
|
152
|
+
value={rate(overall.judged, overall.messages)}
|
|
153
|
+
hint={`${t("frustration.coverage-hint", { judged: formatInteger(overall.judged), regex: formatInteger(regex) })}${unscored}`}
|
|
154
|
+
/>
|
|
155
|
+
<Stat
|
|
156
|
+
label={t("frustration.annoyed")}
|
|
157
|
+
title={t("frustration.annoyed-title")}
|
|
158
|
+
value={rate(overall.annoyed, overall.messages)}
|
|
159
|
+
hint={t("frustration.messages-n", { count: formatInteger(overall.annoyed) })}
|
|
160
|
+
/>
|
|
161
|
+
<Stat
|
|
162
|
+
label={t("frustration.at-assistant")}
|
|
163
|
+
title={t("frustration.at-assistant-title")}
|
|
164
|
+
value={rate(overall.atAssistant, overall.messages)}
|
|
165
|
+
hint={t("frustration.messages-n", { count: formatInteger(overall.atAssistant) })}
|
|
166
|
+
/>
|
|
167
|
+
<Stat
|
|
168
|
+
label={t("frustration.angry")}
|
|
169
|
+
title={t("frustration.angry-title")}
|
|
170
|
+
value={rate(overall.angry, overall.messages)}
|
|
171
|
+
hint={t("frustration.messages-n", { count: formatInteger(overall.angry) })}
|
|
172
|
+
/>
|
|
173
|
+
</StatGrid>
|
|
174
|
+
{overall.unscored > 0 && (
|
|
175
|
+
<p className="micro dim">{t("frustration.unscored-line", { count: formatInteger(overall.unscored) })}</p>
|
|
176
|
+
)}
|
|
177
|
+
</>
|
|
170
178
|
);
|
|
171
179
|
}
|
|
172
180
|
|
|
@@ -279,10 +287,10 @@ function JudgePanel({ active, range, judgeAvailable, job, onRunStarted, onRunCha
|
|
|
279
287
|
index={1}
|
|
280
288
|
title={
|
|
281
289
|
<>
|
|
282
|
-
|
|
290
|
+
{t("frustration.judge-panel")} <JobBadge job={job} judgeAvailable={judgeAvailable} />
|
|
283
291
|
</>
|
|
284
292
|
}
|
|
285
|
-
description="
|
|
293
|
+
description={t("frustration.judge-description")}
|
|
286
294
|
actions={
|
|
287
295
|
<button
|
|
288
296
|
type="button"
|
|
@@ -295,11 +303,11 @@ function JudgePanel({ active, range, judgeAvailable, job, onRunStarted, onRunCha
|
|
|
295
303
|
!judgeAvailable
|
|
296
304
|
? UNAVAILABLE_HINT
|
|
297
305
|
: running
|
|
298
|
-
? "
|
|
299
|
-
: "
|
|
306
|
+
? t("frustration.judge-running")
|
|
307
|
+
: t("frustration.quote-title")
|
|
300
308
|
}
|
|
301
309
|
>
|
|
302
|
-
<Sparkles size={13} />
|
|
310
|
+
<Sparkles size={13} /> {t("frustration.classify")}
|
|
303
311
|
</button>
|
|
304
312
|
}
|
|
305
313
|
>
|
|
@@ -317,7 +325,7 @@ function JudgePanel({ active, range, judgeAvailable, job, onRunStarted, onRunCha
|
|
|
317
325
|
)}
|
|
318
326
|
</div>
|
|
319
327
|
|
|
320
|
-
<Modal open={modalOpen} title="
|
|
328
|
+
<Modal open={modalOpen} title={t("frustration.classify")} onClose={closeModal} primaryAction={primaryAction}>
|
|
321
329
|
<EstimateBody estimate={estimate} />
|
|
322
330
|
{startError && (
|
|
323
331
|
<p className="micro tone-bad" role="alert">
|
|
@@ -334,46 +342,43 @@ function JobBadge({ job, judgeAvailable }: { job: FrustrationJobStatus; judgeAva
|
|
|
334
342
|
case "running":
|
|
335
343
|
return (
|
|
336
344
|
<Badge tone="ok">
|
|
337
|
-
<Dot tone="live" pulse />
|
|
345
|
+
<Dot tone="live" pulse /> {t("frustration.running")}
|
|
338
346
|
</Badge>
|
|
339
347
|
);
|
|
340
348
|
case "done":
|
|
341
|
-
return <Badge tone="ok">
|
|
349
|
+
return <Badge tone="ok">{t("frustration.last-run-done")}</Badge>;
|
|
342
350
|
case "cancelled":
|
|
343
|
-
return <Badge tone="warn">
|
|
351
|
+
return <Badge tone="warn">{t("frustration.last-run-cancelled")}</Badge>;
|
|
344
352
|
case "failed":
|
|
345
|
-
return <Badge tone="bad">
|
|
353
|
+
return <Badge tone="bad">{t("frustration.last-run-failed")}</Badge>;
|
|
346
354
|
case "idle":
|
|
347
|
-
return <Badge>{judgeAvailable ? "
|
|
355
|
+
return <Badge>{judgeAvailable ? t("frustration.not-run-yet") : t("frustration.regex-only")}</Badge>;
|
|
348
356
|
}
|
|
349
357
|
}
|
|
350
358
|
|
|
351
359
|
function EstimateBody({ estimate }: { estimate: EstimateState }) {
|
|
352
|
-
if (estimate.status === "loading") return <p className="muted">
|
|
360
|
+
if (estimate.status === "loading") return <p className="muted">{t("frustration.estimating")}</p>;
|
|
353
361
|
if (estimate.status === "error") return <p className="tone-bad">{estimate.message}</p>;
|
|
354
362
|
const quote = estimate.estimate;
|
|
355
363
|
if (!quote.available) return <p>{quote.reason}</p>;
|
|
356
364
|
if (quote.messages === 0) {
|
|
357
|
-
return <p>
|
|
365
|
+
return <p>{t("frustration.all-judged")}</p>;
|
|
358
366
|
}
|
|
359
367
|
return (
|
|
360
368
|
<>
|
|
361
369
|
<div className="frustration-quote">
|
|
362
|
-
<span className="dim micro">
|
|
370
|
+
<span className="dim micro">{t("frustration.estimated-cost")}</span>
|
|
363
371
|
<span className="num frustration-quote-value">≈ {formatCost(quote.cost)}</span>
|
|
364
372
|
</div>
|
|
365
373
|
<KeyValues
|
|
366
374
|
items={[
|
|
367
|
-
{ key: "messages", label: "
|
|
368
|
-
{ key: "chars", label: "
|
|
369
|
-
{ key: "tokens", label: "
|
|
370
|
-
{ key: "judge", label: "
|
|
375
|
+
{ key: "messages", label: t("frustration.unjudged-messages"), value: formatInteger(quote.messages) },
|
|
376
|
+
{ key: "chars", label: t("frustration.prose-chars"), value: formatInteger(quote.chars) },
|
|
377
|
+
{ key: "tokens", label: t("frustration.input-tokens"), value: formatInteger(quote.inputTokens) },
|
|
378
|
+
{ key: "judge", label: t("frustration.judge-model"), value: quote.judge },
|
|
371
379
|
]}
|
|
372
380
|
/>
|
|
373
|
-
<p className="micro dim">
|
|
374
|
-
Identical messages share one verdict, and verdicts are cached, so later runs only pay for new messages.
|
|
375
|
-
Nothing is spent until you proceed.
|
|
376
|
-
</p>
|
|
381
|
+
<p className="micro dim">{t("frustration.estimate-note")}</p>
|
|
377
382
|
</>
|
|
378
383
|
);
|
|
379
384
|
}
|
|
@@ -405,7 +410,7 @@ function JudgeProgress({
|
|
|
405
410
|
onClick={onCancel}
|
|
406
411
|
disabled={cancelling}
|
|
407
412
|
>
|
|
408
|
-
{cancelling ? "
|
|
413
|
+
{cancelling ? t("frustration.cancelling") : t("frustration.cancel-run")}
|
|
409
414
|
</button>
|
|
410
415
|
</div>
|
|
411
416
|
<div
|
|
@@ -418,58 +423,70 @@ function JudgeProgress({
|
|
|
418
423
|
<div className="meter-fill frustration-progress-fill" style={{ width: `${share * 100}%` }} />
|
|
419
424
|
</div>
|
|
420
425
|
<div className="row micro muted frustration-progress-meta">
|
|
421
|
-
<span className={job.failed > 0 ? "tone-bad" : undefined}>
|
|
426
|
+
<span className={job.failed > 0 ? "tone-bad" : undefined}>
|
|
427
|
+
{t("frustration.failed-n", { count: formatInteger(job.failed) })}
|
|
428
|
+
</span>
|
|
422
429
|
<span>
|
|
423
|
-
cost
|
|
430
|
+
{t("frustration.cost-so-far")} <span className="num">{formatCost(job.cost)}</span>
|
|
424
431
|
</span>
|
|
425
432
|
{job.judge && <span className="mono">{job.judge}</span>}
|
|
426
|
-
{job.startedAt !== null &&
|
|
433
|
+
{job.startedAt !== null && (
|
|
434
|
+
<span>{t("frustration.elapsed", { time: formatElapsed(Date.now() - job.startedAt) })}</span>
|
|
435
|
+
)}
|
|
427
436
|
{job.startedAt !== null && job.done > 0 && (
|
|
428
437
|
<span className="num">{(job.done / Math.max(1, (Date.now() - job.startedAt) / 1000)).toFixed(0)}/s</span>
|
|
429
438
|
)}
|
|
430
|
-
{job.concurrency > 0 &&
|
|
439
|
+
{job.concurrency > 0 && (
|
|
440
|
+
<span className="num">{t("frustration.in-flight", { count: formatInteger(job.concurrency) })}</span>
|
|
441
|
+
)}
|
|
431
442
|
</div>
|
|
432
443
|
</div>
|
|
433
444
|
);
|
|
434
445
|
}
|
|
435
446
|
|
|
436
447
|
function JudgeResultLine({ job }: { job: FrustrationJobStatus }) {
|
|
437
|
-
const progress =
|
|
438
|
-
|
|
439
|
-
|
|
448
|
+
const progress = t("frustration.messages-progress", {
|
|
449
|
+
done: formatInteger(job.done),
|
|
450
|
+
total: formatInteger(job.total),
|
|
451
|
+
});
|
|
452
|
+
const failed = job.failed > 0 ? t("frustration.failed-suffix", { count: formatInteger(job.failed) }) : "";
|
|
453
|
+
const judge = job.judge ? t("frustration.judge-with", { judge: job.judge }) : "";
|
|
440
454
|
const elapsed =
|
|
441
|
-
job.startedAt !== null && job.finishedAt !== null
|
|
455
|
+
job.startedAt !== null && job.finishedAt !== null
|
|
456
|
+
? t("frustration.in-time", { time: formatElapsed(job.finishedAt - job.startedAt) })
|
|
457
|
+
: "";
|
|
442
458
|
switch (job.state) {
|
|
443
459
|
case "idle":
|
|
444
|
-
return (
|
|
445
|
-
<p className="muted">
|
|
446
|
-
No judge run yet. Classify the unjudged messages to replace regex estimates with verdicts.
|
|
447
|
-
</p>
|
|
448
|
-
);
|
|
460
|
+
return <p className="muted">{t("frustration.no-run-yet")}</p>;
|
|
449
461
|
case "running":
|
|
450
462
|
return null;
|
|
451
463
|
case "done":
|
|
452
464
|
return (
|
|
453
465
|
<p className="muted">
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
466
|
+
{t("frustration.run-done-line", {
|
|
467
|
+
progress,
|
|
468
|
+
failed,
|
|
469
|
+
judge,
|
|
470
|
+
elapsed,
|
|
471
|
+
cost: formatCost(job.cost),
|
|
472
|
+
})}
|
|
458
473
|
</p>
|
|
459
474
|
);
|
|
460
475
|
case "cancelled":
|
|
461
476
|
return (
|
|
462
477
|
<p className="muted">
|
|
463
|
-
|
|
464
|
-
{failed}; spent <span className="num">{formatCost(job.cost)}</span>.
|
|
478
|
+
{t("frustration.run-cancelled-line", { progress, failed, cost: formatCost(job.cost) })}
|
|
465
479
|
</p>
|
|
466
480
|
);
|
|
467
481
|
case "failed":
|
|
468
482
|
return (
|
|
469
483
|
<p className="muted">
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
484
|
+
{t("frustration.run-failed-line", {
|
|
485
|
+
progress,
|
|
486
|
+
failed,
|
|
487
|
+
error: job.error ?? t("frustration.unknown-error"),
|
|
488
|
+
cost: formatCost(job.cost),
|
|
489
|
+
})}
|
|
473
490
|
</p>
|
|
474
491
|
);
|
|
475
492
|
}
|
|
@@ -638,8 +655,8 @@ function FrustrationModelsSection({ models, stale }: { models: FrustrationModelS
|
|
|
638
655
|
|
|
639
656
|
if (models.length === 0) {
|
|
640
657
|
return (
|
|
641
|
-
<Card index={2} title="
|
|
642
|
-
<EmptyState title="
|
|
658
|
+
<Card index={2} title={t("frustration.by-model")} stale={stale}>
|
|
659
|
+
<EmptyState title={t("frustration.no-messages")} hint={t("frustration.try-longer-range")} />
|
|
643
660
|
</Card>
|
|
644
661
|
);
|
|
645
662
|
}
|
|
@@ -650,15 +667,15 @@ function FrustrationModelsSection({ models, stale }: { models: FrustrationModelS
|
|
|
650
667
|
<>
|
|
651
668
|
<Card
|
|
652
669
|
index={2}
|
|
653
|
-
title="
|
|
654
|
-
description="
|
|
670
|
+
title={t("frustration.by-model")}
|
|
671
|
+
description={t("frustration.by-model-description")}
|
|
655
672
|
stale={stale}
|
|
656
673
|
>
|
|
657
674
|
<div className="stack frustration-chart-body">
|
|
658
675
|
<div className="frustration-classes">
|
|
659
676
|
<Segmented
|
|
660
677
|
size="sm"
|
|
661
|
-
aria-label="
|
|
678
|
+
aria-label={t("frustration.model-class")}
|
|
662
679
|
options={classOptions}
|
|
663
680
|
value={activeClass}
|
|
664
681
|
onChange={setSelectedClass}
|
|
@@ -676,14 +693,12 @@ function FrustrationModelsSection({ models, stale }: { models: FrustrationModelS
|
|
|
676
693
|
onToggle={toggleFamily}
|
|
677
694
|
/>
|
|
678
695
|
<span className="frustration-spacer" />
|
|
679
|
-
<label className="check" title={
|
|
696
|
+
<label className="check" title={t("frustration.small-models-title", { count: MIN_MESSAGES })}>
|
|
680
697
|
<input type="checkbox" checked={showSmall} onChange={e => setShowSmall(e.target.checked)} />
|
|
681
|
-
|
|
698
|
+
{t("frustration.include-small", { count: MIN_MESSAGES })}
|
|
699
|
+
{smallCount > 0 ? ` (${smallCount})` : ""}
|
|
682
700
|
</label>
|
|
683
|
-
<label
|
|
684
|
-
className="check"
|
|
685
|
-
title={`Hide models with under ${MIN_JUDGED_SHARE * 100}% judged messages (rates from regex signals)`}
|
|
686
|
-
>
|
|
701
|
+
<label className="check" title={t("frustration.hide-regex-title", { share: MIN_JUDGED_SHARE * 100 })}>
|
|
687
702
|
<input type="checkbox" checked={hideRegex} onChange={e => setHideRegex(e.target.checked)} />
|
|
688
703
|
Hide mostly regex{regexCount > 0 ? ` (${regexCount})` : ""}
|
|
689
704
|
</label>
|
|
@@ -715,16 +730,16 @@ function EncodingKey({ hasRegexRows }: { hasRegexRows: boolean }) {
|
|
|
715
730
|
</span>
|
|
716
731
|
<span className="legend-item">
|
|
717
732
|
<span className="frustration-key-line" />
|
|
718
|
-
|
|
733
|
+
{t("frustration.legend-trend")}
|
|
719
734
|
</span>
|
|
720
735
|
<span
|
|
721
736
|
className="legend-item frustration-key-wide"
|
|
722
|
-
title={
|
|
737
|
+
title={t("frustration.hatch-title", { share: MIN_JUDGED_SHARE * 100 })}
|
|
723
738
|
>
|
|
724
739
|
<span className="swatch frustration-key-swatch" data-variant="hatch" />
|
|
725
|
-
|
|
740
|
+
{hasRegexRows ? t("frustration.legend-hatched-run") : t("frustration.legend-hatched")}
|
|
726
741
|
</span>
|
|
727
|
-
<span className="legend-item dim">
|
|
742
|
+
<span className="legend-item dim">{t("frustration.legend-hue")}</span>
|
|
728
743
|
</div>
|
|
729
744
|
);
|
|
730
745
|
}
|
|
@@ -777,7 +792,7 @@ function FrustrationChart({
|
|
|
777
792
|
if (rows.length === 0) {
|
|
778
793
|
return (
|
|
779
794
|
<div className="frustration-chart-empty">
|
|
780
|
-
<EmptyState title="
|
|
795
|
+
<EmptyState title={t("frustration.no-model-match")} />
|
|
781
796
|
</div>
|
|
782
797
|
);
|
|
783
798
|
}
|
|
@@ -797,12 +812,12 @@ function FrustrationChart({
|
|
|
797
812
|
return (
|
|
798
813
|
<div className="frustration-tooltip-extra">
|
|
799
814
|
<div className="chart-tooltip-row chart-tooltip-total">
|
|
800
|
-
<span className="chart-tooltip-label">
|
|
815
|
+
<span className="chart-tooltip-label">{t("frustration.legend-annoyed-any")}</span>
|
|
801
816
|
<span className="chart-tooltip-value">{rate(row.annoyed, row.messages)}</span>
|
|
802
817
|
</div>
|
|
803
818
|
<div className="micro dim">
|
|
804
819
|
{formatInteger(row.messages)} messages · judged {formatInteger(row.judged)} (
|
|
805
|
-
{rate(row.judged, row.messages)}){isMostlyRegex(row) ? "
|
|
820
|
+
{rate(row.judged, row.messages)}){isMostlyRegex(row) ? t("frustration.row-mostly-regex") : ""}
|
|
806
821
|
</div>
|
|
807
822
|
</div>
|
|
808
823
|
);
|
|
@@ -824,7 +839,7 @@ function FrustrationTable({
|
|
|
824
839
|
(): Column<FrustrationModelStats>[] => [
|
|
825
840
|
{
|
|
826
841
|
key: "model",
|
|
827
|
-
header: "
|
|
842
|
+
header: t("common.model"),
|
|
828
843
|
sort: row => row.label,
|
|
829
844
|
render: row => (
|
|
830
845
|
<span className="row" title={row.key}>
|
|
@@ -838,29 +853,26 @@ function FrustrationTable({
|
|
|
838
853
|
},
|
|
839
854
|
{
|
|
840
855
|
key: "ids",
|
|
841
|
-
header: "
|
|
856
|
+
header: t("frustration.column-model-ids"),
|
|
842
857
|
wrap: true,
|
|
843
858
|
render: row => <span className="mono micro dim">{row.models.join(", ")}</span>,
|
|
844
859
|
},
|
|
845
860
|
{
|
|
846
861
|
key: "messages",
|
|
847
|
-
header: "
|
|
862
|
+
header: t("frustration.column-messages"),
|
|
848
863
|
align: "right",
|
|
849
864
|
sort: row => row.messages,
|
|
850
865
|
render: row => <span className="num">{formatInteger(row.messages)}</span>,
|
|
851
866
|
},
|
|
852
867
|
{
|
|
853
868
|
key: "judged",
|
|
854
|
-
header: "
|
|
869
|
+
header: t("frustration.column-judged"),
|
|
855
870
|
align: "right",
|
|
856
871
|
sort: row => (row.messages > 0 ? row.judged / row.messages : 0),
|
|
857
872
|
render: row =>
|
|
858
873
|
isMostlyRegex(row) ? (
|
|
859
|
-
<span
|
|
860
|
-
|
|
861
|
-
title="Mostly regex fallback: run the judge for accurate rates"
|
|
862
|
-
>
|
|
863
|
-
<Badge tone="warn">regex</Badge>
|
|
874
|
+
<span className="row frustration-judged" title={t("frustration.mostly-regex")}>
|
|
875
|
+
<Badge tone="warn">{t("frustration.regex-badge")}</Badge>
|
|
864
876
|
<span className="num dim">{rate(row.judged, row.messages)}</span>
|
|
865
877
|
</span>
|
|
866
878
|
) : (
|
|
@@ -869,33 +881,36 @@ function FrustrationTable({
|
|
|
869
881
|
},
|
|
870
882
|
{
|
|
871
883
|
key: "annoyed",
|
|
872
|
-
header: "
|
|
884
|
+
header: t("frustration.annoyed"),
|
|
873
885
|
align: "right",
|
|
874
886
|
sort: row => percent(row.annoyed, row.messages),
|
|
875
887
|
render: row => (
|
|
876
|
-
<span className="num" title={
|
|
888
|
+
<span className="num" title={t("frustration.messages-n", { count: formatInteger(row.annoyed) })}>
|
|
877
889
|
{rate(row.annoyed, row.messages)}
|
|
878
890
|
</span>
|
|
879
891
|
),
|
|
880
892
|
},
|
|
881
893
|
{
|
|
882
894
|
key: "atAssistant",
|
|
883
|
-
header: "
|
|
895
|
+
header: t("frustration.at-assistant"),
|
|
884
896
|
align: "right",
|
|
885
897
|
sort: row => percent(row.atAssistant, row.messages),
|
|
886
898
|
render: row => (
|
|
887
|
-
<span className="num" title={
|
|
899
|
+
<span className="num" title={t("frustration.messages-n", { count: formatInteger(row.atAssistant) })}>
|
|
888
900
|
{rate(row.atAssistant, row.messages)}
|
|
889
901
|
</span>
|
|
890
902
|
),
|
|
891
903
|
},
|
|
892
904
|
{
|
|
893
905
|
key: "angry",
|
|
894
|
-
header: "
|
|
906
|
+
header: t("frustration.angry"),
|
|
895
907
|
align: "right",
|
|
896
908
|
sort: row => percent(row.angry, row.messages),
|
|
897
909
|
render: row => (
|
|
898
|
-
<span
|
|
910
|
+
<span
|
|
911
|
+
className={row.angry > 0 ? "num tone-bad" : "num"}
|
|
912
|
+
title={t("frustration.messages-n", { count: formatInteger(row.angry) })}
|
|
913
|
+
>
|
|
899
914
|
{rate(row.angry, row.messages)}
|
|
900
915
|
</span>
|
|
901
916
|
),
|
|
@@ -907,8 +922,8 @@ function FrustrationTable({
|
|
|
907
922
|
return (
|
|
908
923
|
<Card
|
|
909
924
|
index={3}
|
|
910
|
-
title="
|
|
911
|
-
description="
|
|
925
|
+
title={t("frustration.model-versions")}
|
|
926
|
+
description={t("frustration.table-description")}
|
|
912
927
|
flush
|
|
913
928
|
stale={stale}
|
|
914
929
|
>
|
|
@@ -917,7 +932,7 @@ function FrustrationTable({
|
|
|
917
932
|
rowKey={row => row.key}
|
|
918
933
|
columns={columns}
|
|
919
934
|
dense
|
|
920
|
-
empty={<EmptyState title="
|
|
935
|
+
empty={<EmptyState title={t("frustration.no-model-match")} />}
|
|
921
936
|
/>
|
|
922
937
|
</Card>
|
|
923
938
|
);
|