@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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ArrowUpRight, X } from "lucide-react";
|
|
2
2
|
import { useMemo, useState } from "react";
|
|
3
3
|
import { getRecentErrors } from "../api";
|
|
4
|
+
import { t } from "../i18n";
|
|
4
5
|
import { BarList } from "../charts";
|
|
5
6
|
import { modelKey } from "../data/colors";
|
|
6
7
|
import {
|
|
@@ -44,7 +45,9 @@ const LOAD_STEPS = [50, 200, 1_000] as const;
|
|
|
44
45
|
export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps) {
|
|
45
46
|
const [step, setStep] = useState(0);
|
|
46
47
|
const limit = LOAD_STEPS[step];
|
|
47
|
-
const errors = useQuery(["errors", range, limit], () => getRecentErrors(range, limit), {
|
|
48
|
+
const errors = useQuery(["errors", range, limit], ({ signal }) => getRecentErrors(range, limit, signal), {
|
|
49
|
+
enabled: active,
|
|
50
|
+
});
|
|
48
51
|
const [selectedSignature, setSelectedSignature] = useState<string | null>(null);
|
|
49
52
|
const [selectedModel, setSelectedModel] = useState<string | null>(null);
|
|
50
53
|
const [search, setSearch] = useState("");
|
|
@@ -97,9 +100,7 @@ export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps)
|
|
|
97
100
|
const footer =
|
|
98
101
|
errors.data !== null && !complete ? (
|
|
99
102
|
<>
|
|
100
|
-
<span>
|
|
101
|
-
Showing the latest {formatInteger(loaded)} failures in {meta.windowLabel}; older ones are not loaded.
|
|
102
|
-
</span>
|
|
103
|
+
<span>{t("errors.footer", { count: formatInteger(loaded), window: t(meta.windowKey) })}</span>
|
|
103
104
|
{nextStep !== null && (
|
|
104
105
|
<button
|
|
105
106
|
type="button"
|
|
@@ -108,7 +109,9 @@ export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps)
|
|
|
108
109
|
disabled={errors.refreshing}
|
|
109
110
|
onClick={() => setStep(step + 1)}
|
|
110
111
|
>
|
|
111
|
-
{errors.refreshing && errors.stale
|
|
112
|
+
{errors.refreshing && errors.stale
|
|
113
|
+
? t("errors.loading")
|
|
114
|
+
: t("errors.load-latest", { count: formatInteger(nextStep) })}
|
|
112
115
|
</button>
|
|
113
116
|
)}
|
|
114
117
|
</>
|
|
@@ -120,35 +123,44 @@ export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps)
|
|
|
120
123
|
|
|
121
124
|
return (
|
|
122
125
|
<div className="page">
|
|
123
|
-
<PageHeader
|
|
124
|
-
title="Errors"
|
|
125
|
-
description={`Failed model requests in ${meta.windowLabel}, grouped by error signature. Open a failure for its full payload.`}
|
|
126
|
-
/>
|
|
126
|
+
<PageHeader title={t("errors.title")} description={t("errors.description", { window: t(meta.windowKey) })} />
|
|
127
127
|
|
|
128
128
|
<QueryView query={errors} skeleton={<Skeleton height={96} style={{ borderRadius: 12 }} />}>
|
|
129
129
|
{() => (
|
|
130
130
|
<div data-stale={errors.stale}>
|
|
131
131
|
<StatGrid min={170}>
|
|
132
132
|
<Stat
|
|
133
|
-
label="
|
|
133
|
+
label={t("errors.failures")}
|
|
134
134
|
value={formatInteger(loaded)}
|
|
135
|
-
hint={
|
|
135
|
+
hint={
|
|
136
|
+
complete
|
|
137
|
+
? t("errors.hint-in-window", { window: t(meta.windowKey) })
|
|
138
|
+
: t("errors.hint-latest-loaded", { count: formatInteger(loaded) })
|
|
139
|
+
}
|
|
136
140
|
/>
|
|
137
141
|
<Stat
|
|
138
|
-
label="
|
|
139
|
-
title="
|
|
142
|
+
label={t("errors.signatures")}
|
|
143
|
+
title={t("errors.signatures-hint")}
|
|
140
144
|
value={formatInteger(view.groups.length)}
|
|
141
|
-
hint={
|
|
145
|
+
hint={
|
|
146
|
+
view.groups[0]
|
|
147
|
+
? t("errors.signatures-top", { count: formatInteger(view.groups[0].count) })
|
|
148
|
+
: undefined
|
|
149
|
+
}
|
|
142
150
|
/>
|
|
143
151
|
<Stat
|
|
144
|
-
label="
|
|
152
|
+
label={t("errors.affected-models")}
|
|
145
153
|
value={formatInteger(view.models.length)}
|
|
146
154
|
hint={view.models[0]?.model}
|
|
147
155
|
/>
|
|
148
156
|
<Stat
|
|
149
|
-
label="
|
|
157
|
+
label={t("errors.last-failure")}
|
|
150
158
|
value={view.newest ? formatRelativeTime(view.newest.timestamp) : "–"}
|
|
151
|
-
hint={
|
|
159
|
+
hint={
|
|
160
|
+
view.newest
|
|
161
|
+
? formatTimestamp(view.newest.timestamp)
|
|
162
|
+
: t("errors.none-in-window", { window: t(meta.windowKey) })
|
|
163
|
+
}
|
|
152
164
|
/>
|
|
153
165
|
</StatGrid>
|
|
154
166
|
</div>
|
|
@@ -158,8 +170,8 @@ export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps)
|
|
|
158
170
|
<div className="grid grid-main-side">
|
|
159
171
|
<Card
|
|
160
172
|
index={1}
|
|
161
|
-
title="
|
|
162
|
-
description="
|
|
173
|
+
title={t("errors.error-signatures")}
|
|
174
|
+
description={t("errors.signatures-card-description")}
|
|
163
175
|
flush
|
|
164
176
|
stale={errors.stale}
|
|
165
177
|
>
|
|
@@ -169,8 +181,8 @@ export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps)
|
|
|
169
181
|
isEmpty={rows => rows.length === 0}
|
|
170
182
|
empty={
|
|
171
183
|
<EmptyState
|
|
172
|
-
title={
|
|
173
|
-
hint="
|
|
184
|
+
title={t("errors.empty-no-failures", { window: t(meta.windowKey) })}
|
|
185
|
+
hint={t("errors.empty-all-succeeded")}
|
|
174
186
|
/>
|
|
175
187
|
}
|
|
176
188
|
>
|
|
@@ -196,15 +208,15 @@ export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps)
|
|
|
196
208
|
|
|
197
209
|
<Card
|
|
198
210
|
index={2}
|
|
199
|
-
title="
|
|
200
|
-
description="
|
|
211
|
+
title={t("errors.by-model")}
|
|
212
|
+
description={t("errors.by-model-description")}
|
|
201
213
|
stale={errors.stale}
|
|
202
214
|
>
|
|
203
215
|
<QueryView
|
|
204
216
|
query={errors}
|
|
205
217
|
skeleton={<Skeleton height={180} />}
|
|
206
218
|
isEmpty={rows => rows.length === 0}
|
|
207
|
-
empty={<EmptyState title="
|
|
219
|
+
empty={<EmptyState title={t("errors.empty-no-affected-models")} />}
|
|
208
220
|
>
|
|
209
221
|
{() => (
|
|
210
222
|
<BarList
|
|
@@ -228,11 +240,14 @@ export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps)
|
|
|
228
240
|
|
|
229
241
|
<Card
|
|
230
242
|
index={3}
|
|
231
|
-
title="
|
|
243
|
+
title={t("errors.failures")}
|
|
232
244
|
description={
|
|
233
245
|
errors.data === null
|
|
234
|
-
? "
|
|
235
|
-
:
|
|
246
|
+
? t("errors.loading-failures")
|
|
247
|
+
: t("errors.filtered-of-total", {
|
|
248
|
+
shown: formatInteger(filtered.length),
|
|
249
|
+
total: formatInteger(loaded),
|
|
250
|
+
})
|
|
236
251
|
}
|
|
237
252
|
actions={
|
|
238
253
|
<>
|
|
@@ -259,7 +274,7 @@ export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps)
|
|
|
259
274
|
<X size={12} />
|
|
260
275
|
</button>
|
|
261
276
|
)}
|
|
262
|
-
<SearchInput value={search} onChange={setSearch} placeholder="
|
|
277
|
+
<SearchInput value={search} onChange={setSearch} placeholder={t("errors.search-placeholder")} />
|
|
263
278
|
</>
|
|
264
279
|
}
|
|
265
280
|
flush
|
|
@@ -270,7 +285,7 @@ export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps)
|
|
|
270
285
|
query={errors}
|
|
271
286
|
skeleton={<TableSkeleton rows={10} />}
|
|
272
287
|
isEmpty={rows => rows.length === 0}
|
|
273
|
-
empty={<EmptyState title={
|
|
288
|
+
empty={<EmptyState title={t("errors.empty-no-failures", { window: t(meta.windowKey) })} />}
|
|
274
289
|
>
|
|
275
290
|
{() => (
|
|
276
291
|
<Table
|
|
@@ -281,7 +296,7 @@ export function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps)
|
|
|
281
296
|
initialSort={{ key: "time", dir: "desc" }}
|
|
282
297
|
limit={50}
|
|
283
298
|
dense
|
|
284
|
-
empty={<EmptyState title="
|
|
299
|
+
empty={<EmptyState title={t("errors.no-match")} hint={t("errors.no-match-hint")} />}
|
|
285
300
|
/>
|
|
286
301
|
)}
|
|
287
302
|
</QueryView>
|
|
@@ -294,7 +309,7 @@ function buildGroupColumns(maxCount: number): Column<ErrorGroupView>[] {
|
|
|
294
309
|
return [
|
|
295
310
|
{
|
|
296
311
|
key: "signature",
|
|
297
|
-
header: "
|
|
312
|
+
header: t("errors.signature"),
|
|
298
313
|
wrap: true,
|
|
299
314
|
sort: group => group.signature,
|
|
300
315
|
render: group => (
|
|
@@ -305,18 +320,22 @@ function buildGroupColumns(maxCount: number): Column<ErrorGroupView>[] {
|
|
|
305
320
|
},
|
|
306
321
|
{
|
|
307
322
|
key: "models",
|
|
308
|
-
header: "
|
|
323
|
+
header: t("common.models"),
|
|
309
324
|
sort: group => group.models.length,
|
|
310
325
|
render: group => (
|
|
311
326
|
<LabelCell
|
|
312
327
|
primary={<span className="mono">{group.models[0]?.model}</span>}
|
|
313
|
-
secondary={
|
|
328
|
+
secondary={
|
|
329
|
+
group.models.length > 1
|
|
330
|
+
? t("errors.more-models", { count: formatInteger(group.models.length - 1) })
|
|
331
|
+
: group.models[0]?.provider
|
|
332
|
+
}
|
|
314
333
|
/>
|
|
315
334
|
),
|
|
316
335
|
},
|
|
317
336
|
{
|
|
318
337
|
key: "last",
|
|
319
|
-
header: "
|
|
338
|
+
header: t("errors.last-seen"),
|
|
320
339
|
sort: group => group.lastSeen,
|
|
321
340
|
render: group => (
|
|
322
341
|
<span className="muted" title={formatTimestamp(group.lastSeen)}>
|
|
@@ -326,7 +345,7 @@ function buildGroupColumns(maxCount: number): Column<ErrorGroupView>[] {
|
|
|
326
345
|
},
|
|
327
346
|
{
|
|
328
347
|
key: "count",
|
|
329
|
-
header: "
|
|
348
|
+
header: t("errors.failures"),
|
|
330
349
|
align: "right",
|
|
331
350
|
width: 120,
|
|
332
351
|
sort: group => group.count,
|
|
@@ -341,15 +360,20 @@ function SignatureDetail({ group, onRequestClick }: { group: ErrorGroupView; onR
|
|
|
341
360
|
const latestId = group.latest.id;
|
|
342
361
|
return (
|
|
343
362
|
<div className="errors-detail">
|
|
344
|
-
<pre className="code-block errors-detail-message">
|
|
363
|
+
<pre className="code-block errors-detail-message">
|
|
364
|
+
{group.latest.errorMessage ?? t("errors.unknown-error")}
|
|
365
|
+
</pre>
|
|
345
366
|
<div className="errors-detail-meta">
|
|
346
367
|
<span className="muted">
|
|
347
|
-
{
|
|
348
|
-
|
|
368
|
+
{t("errors.group-meta", {
|
|
369
|
+
count: formatInteger(group.count),
|
|
370
|
+
first: formatTimestamp(group.firstSeen),
|
|
371
|
+
last: formatTimestamp(group.lastSeen),
|
|
372
|
+
})}
|
|
349
373
|
</span>
|
|
350
374
|
{latestId !== undefined && (
|
|
351
375
|
<button type="button" className="btn" data-size="sm" onClick={() => onRequestClick(latestId)}>
|
|
352
|
-
|
|
376
|
+
{t("errors.open-latest")} <ArrowUpRight size={13} />
|
|
353
377
|
</button>
|
|
354
378
|
)}
|
|
355
379
|
</div>
|
|
@@ -368,7 +392,7 @@ function SignatureDetail({ group, onRequestClick }: { group: ErrorGroupView; onR
|
|
|
368
392
|
const FAILURE_COLUMNS: readonly Column<MessageStats>[] = [
|
|
369
393
|
{
|
|
370
394
|
key: "time",
|
|
371
|
-
header: "
|
|
395
|
+
header: t("errors.when"),
|
|
372
396
|
width: 120,
|
|
373
397
|
sort: row => row.timestamp,
|
|
374
398
|
render: row => (
|
|
@@ -379,23 +403,23 @@ const FAILURE_COLUMNS: readonly Column<MessageStats>[] = [
|
|
|
379
403
|
},
|
|
380
404
|
{
|
|
381
405
|
key: "model",
|
|
382
|
-
header: "
|
|
406
|
+
header: t("common.model"),
|
|
383
407
|
sort: row => row.model,
|
|
384
408
|
render: row => <LabelCell primary={<span className="mono">{row.model}</span>} secondary={row.provider} />,
|
|
385
409
|
},
|
|
386
410
|
{
|
|
387
411
|
key: "error",
|
|
388
|
-
header: "
|
|
412
|
+
header: t("common.error"),
|
|
389
413
|
sort: row => row.errorMessage ?? "",
|
|
390
414
|
render: row => (
|
|
391
415
|
<span className="errors-message" title={row.errorMessage ?? undefined}>
|
|
392
|
-
{row.errorMessage ?? "
|
|
416
|
+
{row.errorMessage ?? t("errors.unknown-error")}
|
|
393
417
|
</span>
|
|
394
418
|
),
|
|
395
419
|
},
|
|
396
420
|
{
|
|
397
421
|
key: "project",
|
|
398
|
-
header: "
|
|
422
|
+
header: t("common.project"),
|
|
399
423
|
sort: row => row.folder,
|
|
400
424
|
render: row => (
|
|
401
425
|
<span className="mono muted" title={row.folder}>
|
|
@@ -405,15 +429,15 @@ const FAILURE_COLUMNS: readonly Column<MessageStats>[] = [
|
|
|
405
429
|
},
|
|
406
430
|
{
|
|
407
431
|
key: "tokens",
|
|
408
|
-
header: "
|
|
432
|
+
header: t("common.tokens"),
|
|
409
433
|
align: "right",
|
|
410
434
|
sort: row => row.usage.totalTokens,
|
|
411
435
|
render: row => <span className="num">{formatInteger(row.usage.totalTokens)}</span>,
|
|
412
436
|
},
|
|
413
437
|
{
|
|
414
438
|
key: "cost",
|
|
415
|
-
header: "
|
|
416
|
-
title: "
|
|
439
|
+
header: t("common.cost"),
|
|
440
|
+
title: t("errors.cost-hint"),
|
|
417
441
|
align: "right",
|
|
418
442
|
sort: row => row.usage.cost.total,
|
|
419
443
|
render: row => <span className="num">{formatMessageCost(row, 4)}</span>,
|