@linxiraos/pi-stats 1.1.26 → 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 +12 -5
- 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 +7 -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 +13 -1
- package/dist/types/user-metrics.d.ts +23 -1
- package/package.json +5 -5
- package/src/aggregator.ts +31 -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 +80 -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 +90 -73
- package/src/client/routes/OverviewRoute.tsx +109 -63
- package/src/client/routes/ProjectsRoute.tsx +73 -46
- package/src/client/routes/ProvidersRoute.tsx +151 -114
- package/src/client/routes/RequestsRoute.tsx +69 -46
- package/src/client/routes/ToolsRoute.tsx +108 -94
- package/src/client/routes/TracesRoute.tsx +25 -23
- package/src/client/styles.css +5 -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/index.ts +2 -1
- 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 +13 -1
- package/src/user-metrics.ts +220 -6
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { useMemo, useState } from "react";
|
|
2
2
|
import { getRecentRequests } from "../api";
|
|
3
|
+
import { t } from "../i18n";
|
|
3
4
|
import {
|
|
4
5
|
formatCompact,
|
|
5
6
|
formatDurationMs,
|
|
7
|
+
formatErrorRate,
|
|
6
8
|
formatEstimatedCost,
|
|
7
9
|
formatFolder,
|
|
8
10
|
formatInteger,
|
|
9
11
|
formatMessageCost,
|
|
10
|
-
formatPercent,
|
|
11
12
|
formatRelativeTime,
|
|
12
13
|
formatTimestamp,
|
|
13
14
|
} from "../data/formatters";
|
|
@@ -47,7 +48,7 @@ type StatusFilter = "all" | RequestStatus;
|
|
|
47
48
|
export function RequestsRoute({ active, range, onRequestClick }: RequestsRouteProps) {
|
|
48
49
|
const [step, setStep] = useState(0);
|
|
49
50
|
const limit = LOAD_STEPS[step];
|
|
50
|
-
const log = useQuery(["requests-log", limit], () => getRecentRequests(limit), { enabled: active });
|
|
51
|
+
const log = useQuery(["requests-log", limit], ({ signal }) => getRecentRequests(limit, signal), { enabled: active });
|
|
51
52
|
const [search, setSearch] = useState("");
|
|
52
53
|
const [status, setStatus] = useState<StatusFilter>("all");
|
|
53
54
|
const meta = rangeMeta(range);
|
|
@@ -88,7 +89,9 @@ export function RequestsRoute({ active, range, onRequestClick }: RequestsRoutePr
|
|
|
88
89
|
disabled={log.refreshing}
|
|
89
90
|
onClick={() => setStep(step + 1)}
|
|
90
91
|
>
|
|
91
|
-
{log.refreshing && log.stale
|
|
92
|
+
{log.refreshing && log.stale
|
|
93
|
+
? t("requests.loading")
|
|
94
|
+
: t("requests.load-latest", { count: formatInteger(nextStep) })}
|
|
92
95
|
</button>
|
|
93
96
|
) : undefined;
|
|
94
97
|
|
|
@@ -96,7 +99,7 @@ export function RequestsRoute({ active, range, onRequestClick }: RequestsRoutePr
|
|
|
96
99
|
value,
|
|
97
100
|
label: (
|
|
98
101
|
<>
|
|
99
|
-
{value === "all" ? "
|
|
102
|
+
{value === "all" ? t("requests.status-all") : REQUEST_STATUS[value].label}{" "}
|
|
100
103
|
<span className="dim num">{formatCompact(counts[value])}</span>
|
|
101
104
|
</>
|
|
102
105
|
),
|
|
@@ -105,8 +108,8 @@ export function RequestsRoute({ active, range, onRequestClick }: RequestsRoutePr
|
|
|
105
108
|
return (
|
|
106
109
|
<div className="page">
|
|
107
110
|
<PageHeader
|
|
108
|
-
title="
|
|
109
|
-
description={
|
|
111
|
+
title={t("common.requests")}
|
|
112
|
+
description={t("requests.description", { window: t(meta.windowKey) })}
|
|
110
113
|
/>
|
|
111
114
|
|
|
112
115
|
<QueryView query={log} skeleton={<Skeleton height={96} style={{ borderRadius: 12 }} />}>
|
|
@@ -114,38 +117,45 @@ export function RequestsRoute({ active, range, onRequestClick }: RequestsRoutePr
|
|
|
114
117
|
<div data-stale={log.stale}>
|
|
115
118
|
<StatGrid min={160}>
|
|
116
119
|
<Stat
|
|
117
|
-
label="
|
|
120
|
+
label={t("common.requests")}
|
|
118
121
|
value={formatInteger(summary.requests)}
|
|
119
122
|
hint={
|
|
120
123
|
summary.oldest === null
|
|
121
|
-
?
|
|
122
|
-
:
|
|
124
|
+
? t("requests.none-in-window", { window: t(meta.windowKey) })
|
|
125
|
+
: t("requests.since", { time: formatRelativeTime(summary.oldest) })
|
|
123
126
|
}
|
|
124
127
|
/>
|
|
125
128
|
<Stat
|
|
126
|
-
label="
|
|
129
|
+
label={t("common.failed")}
|
|
127
130
|
value={formatInteger(summary.failed)}
|
|
128
|
-
hint={
|
|
131
|
+
hint={t("requests.failed-hint", {
|
|
132
|
+
rate: summary.requests > 0 ? formatErrorRate(summary.failed / summary.requests) : "–",
|
|
133
|
+
count: formatInteger(summary.aborted),
|
|
134
|
+
})}
|
|
129
135
|
/>
|
|
130
136
|
<Stat
|
|
131
|
-
label="
|
|
132
|
-
title="
|
|
137
|
+
label={t("common.tokens")}
|
|
138
|
+
title={t("requests.tokens-hint")}
|
|
133
139
|
value={formatCompact(summary.tokens)}
|
|
134
140
|
/>
|
|
135
141
|
<Stat
|
|
136
|
-
label="
|
|
137
|
-
title="
|
|
142
|
+
label={t("requests.api-equivalent-cost")}
|
|
143
|
+
title={t("requests.cost-hint")}
|
|
138
144
|
value={formatEstimatedCost(summary.cost, summary.unpriced)}
|
|
139
|
-
hint={
|
|
145
|
+
hint={
|
|
146
|
+
summary.unpriced > 0
|
|
147
|
+
? t("requests.unpriced-n", { count: formatInteger(summary.unpriced) })
|
|
148
|
+
: undefined
|
|
149
|
+
}
|
|
140
150
|
/>
|
|
141
151
|
<Stat
|
|
142
|
-
label="
|
|
152
|
+
label={t("requests.median-duration")}
|
|
143
153
|
value={formatDurationMs(summary.medianDuration)}
|
|
144
|
-
hint={
|
|
154
|
+
hint={t("requests.p95", { value: formatDurationMs(summary.p95Duration) })}
|
|
145
155
|
/>
|
|
146
156
|
<Stat
|
|
147
|
-
label="
|
|
148
|
-
title="
|
|
157
|
+
label={t("requests.median-ttft")}
|
|
158
|
+
title={t("requests.ttft-hint")}
|
|
149
159
|
value={formatDurationMs(summary.medianTtft)}
|
|
150
160
|
/>
|
|
151
161
|
</StatGrid>
|
|
@@ -155,20 +165,27 @@ export function RequestsRoute({ active, range, onRequestClick }: RequestsRoutePr
|
|
|
155
165
|
|
|
156
166
|
<Card
|
|
157
167
|
index={1}
|
|
158
|
-
title="
|
|
168
|
+
title={t("requests.log-title")}
|
|
159
169
|
description={
|
|
160
170
|
log.data === null
|
|
161
|
-
? "
|
|
171
|
+
? t("requests.log-loading")
|
|
162
172
|
: complete
|
|
163
|
-
?
|
|
164
|
-
|
|
173
|
+
? t("requests.log-count-in-window", {
|
|
174
|
+
shown: formatInteger(filtered.length),
|
|
175
|
+
total: formatInteger(summary.requests),
|
|
176
|
+
window: t(meta.windowKey),
|
|
177
|
+
})
|
|
178
|
+
: t("requests.log-count-latest", {
|
|
179
|
+
shown: formatInteger(filtered.length),
|
|
180
|
+
total: formatInteger(summary.requests),
|
|
181
|
+
})
|
|
165
182
|
}
|
|
166
183
|
actions={
|
|
167
184
|
<>
|
|
168
|
-
<SearchInput value={search} onChange={setSearch} placeholder="
|
|
185
|
+
<SearchInput value={search} onChange={setSearch} placeholder={t("requests.search-placeholder")} />
|
|
169
186
|
<Segmented
|
|
170
187
|
size="sm"
|
|
171
|
-
aria-label="
|
|
188
|
+
aria-label={t("requests.status")}
|
|
172
189
|
options={statusOptions}
|
|
173
190
|
value={status}
|
|
174
191
|
onChange={setStatus}
|
|
@@ -181,9 +198,11 @@ export function RequestsRoute({ active, range, onRequestClick }: RequestsRoutePr
|
|
|
181
198
|
log.data !== null && !complete ? (
|
|
182
199
|
<>
|
|
183
200
|
<span>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
201
|
+
{t("requests.footer", {
|
|
202
|
+
count: formatInteger(view.loaded),
|
|
203
|
+
time: summary.oldest === null ? "–" : formatTimestamp(summary.oldest),
|
|
204
|
+
window: t(meta.windowKey),
|
|
205
|
+
})}
|
|
187
206
|
</span>
|
|
188
207
|
{loadMore}
|
|
189
208
|
</>
|
|
@@ -202,8 +221,12 @@ export function RequestsRoute({ active, range, onRequestClick }: RequestsRoutePr
|
|
|
202
221
|
dense
|
|
203
222
|
empty={
|
|
204
223
|
<EmptyState
|
|
205
|
-
title={
|
|
206
|
-
|
|
224
|
+
title={
|
|
225
|
+
summary.requests === 0
|
|
226
|
+
? t("requests.empty-in-window", { window: t(meta.windowKey) })
|
|
227
|
+
: t("requests.empty-no-match")
|
|
228
|
+
}
|
|
229
|
+
hint={summary.requests === 0 ? undefined : t("requests.empty-no-match-hint")}
|
|
207
230
|
/>
|
|
208
231
|
}
|
|
209
232
|
/>
|
|
@@ -217,13 +240,13 @@ export function RequestsRoute({ active, range, onRequestClick }: RequestsRoutePr
|
|
|
217
240
|
const REQUEST_COLUMNS: readonly Column<MessageStats>[] = [
|
|
218
241
|
{
|
|
219
242
|
key: "model",
|
|
220
|
-
header: "
|
|
243
|
+
header: t("common.model"),
|
|
221
244
|
sort: row => row.model,
|
|
222
245
|
render: row => <LabelCell primary={<span className="mono">{row.model}</span>} secondary={row.provider} />,
|
|
223
246
|
},
|
|
224
247
|
{
|
|
225
248
|
key: "time",
|
|
226
|
-
header: "
|
|
249
|
+
header: t("requests.when"),
|
|
227
250
|
sort: row => row.timestamp,
|
|
228
251
|
render: row => (
|
|
229
252
|
<span className="muted" title={formatTimestamp(row.timestamp)}>
|
|
@@ -233,7 +256,7 @@ const REQUEST_COLUMNS: readonly Column<MessageStats>[] = [
|
|
|
233
256
|
},
|
|
234
257
|
{
|
|
235
258
|
key: "project",
|
|
236
|
-
header: "
|
|
259
|
+
header: t("common.project"),
|
|
237
260
|
sort: row => row.folder,
|
|
238
261
|
render: row => (
|
|
239
262
|
<span className="mono muted truncate" title={row.folder} style={{ display: "block", maxWidth: 180 }}>
|
|
@@ -243,57 +266,57 @@ const REQUEST_COLUMNS: readonly Column<MessageStats>[] = [
|
|
|
243
266
|
},
|
|
244
267
|
{
|
|
245
268
|
key: "input",
|
|
246
|
-
header: "
|
|
247
|
-
title: "
|
|
269
|
+
header: t("common.input"),
|
|
270
|
+
title: t("requests.uncached-input"),
|
|
248
271
|
align: "right",
|
|
249
272
|
sort: row => row.usage.input,
|
|
250
273
|
render: row => <span className="num">{formatCompact(row.usage.input)}</span>,
|
|
251
274
|
},
|
|
252
275
|
{
|
|
253
276
|
key: "cache",
|
|
254
|
-
header: "
|
|
255
|
-
title: "
|
|
277
|
+
header: t("common.cache-read"),
|
|
278
|
+
title: t("requests.cache-read-title"),
|
|
256
279
|
align: "right",
|
|
257
280
|
sort: row => row.usage.cacheRead,
|
|
258
281
|
render: row => (
|
|
259
|
-
<span className="num" title={
|
|
282
|
+
<span className="num" title={t("requests.cache-write", { count: formatInteger(row.usage.cacheWrite) })}>
|
|
260
283
|
{formatCompact(row.usage.cacheRead)}
|
|
261
284
|
</span>
|
|
262
285
|
),
|
|
263
286
|
},
|
|
264
287
|
{
|
|
265
288
|
key: "output",
|
|
266
|
-
header: "
|
|
289
|
+
header: t("common.output"),
|
|
267
290
|
align: "right",
|
|
268
291
|
sort: row => row.usage.output,
|
|
269
292
|
render: row => <span className="num">{formatCompact(row.usage.output)}</span>,
|
|
270
293
|
},
|
|
271
294
|
{
|
|
272
295
|
key: "cost",
|
|
273
|
-
header: "
|
|
274
|
-
title: "
|
|
296
|
+
header: t("common.cost"),
|
|
297
|
+
title: t("requests.cost-estimate"),
|
|
275
298
|
align: "right",
|
|
276
299
|
sort: row => row.usage.cost.total,
|
|
277
300
|
render: row => <span className="num">{formatMessageCost(row, 4)}</span>,
|
|
278
301
|
},
|
|
279
302
|
{
|
|
280
303
|
key: "duration",
|
|
281
|
-
header: "
|
|
304
|
+
header: t("common.duration"),
|
|
282
305
|
align: "right",
|
|
283
306
|
sort: row => row.duration ?? -1,
|
|
284
307
|
render: row => <span className="num">{formatDurationMs(row.duration)}</span>,
|
|
285
308
|
},
|
|
286
309
|
{
|
|
287
310
|
key: "ttft",
|
|
288
|
-
header: "
|
|
289
|
-
title: "
|
|
311
|
+
header: t("requests.ttft"),
|
|
312
|
+
title: t("requests.ttft-hint"),
|
|
290
313
|
align: "right",
|
|
291
314
|
sort: row => row.ttft ?? -1,
|
|
292
315
|
render: row => <span className="num muted">{formatDurationMs(row.ttft)}</span>,
|
|
293
316
|
},
|
|
294
317
|
{
|
|
295
318
|
key: "status",
|
|
296
|
-
header: "
|
|
319
|
+
header: t("requests.status"),
|
|
297
320
|
sort: row => requestStatus(row),
|
|
298
321
|
render: row => {
|
|
299
322
|
const status = REQUEST_STATUS[requestStatus(row)];
|