@oh-my-pi/omp-stats 18.4.12 → 18.5.1
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 +8 -0
- package/dist/client/index.css +1 -1
- package/dist/client/index.js +73 -73
- package/dist/types/client/data/formatters.d.ts +2 -0
- package/dist/types/types.d.ts +1 -1
- package/package.json +4 -4
- package/src/aggregator.ts +3 -0
- package/src/client/data/formatters.ts +7 -0
- package/src/client/routes/ModelsRoute.tsx +3 -2
- package/src/client/routes/OverviewRoute.tsx +2 -1
- package/src/client/routes/ProjectsRoute.tsx +2 -1
- package/src/client/routes/ProvidersRoute.tsx +3 -2
- package/src/client/routes/RequestsRoute.tsx +2 -2
- package/src/client/routes/ToolsRoute.tsx +4 -3
- package/src/client/styles.css +1 -0
- package/src/client/ui/RequestDrawer.tsx +1 -1
- package/src/index.ts +2 -1
- package/src/types.ts +1 -1
|
@@ -14,6 +14,8 @@ export declare function isUnpricedMessage(message: Pick<MessageStats, "provider"
|
|
|
14
14
|
/** Format one request's cost, distinguishing unpriced usage from free usage. */
|
|
15
15
|
export declare function formatMessageCost(message: Pick<MessageStats, "provider" | "usage" | "costUnpriced">, digits?: number): string;
|
|
16
16
|
export declare function formatPercent(value: number, digits?: number): string;
|
|
17
|
+
/** Show small nonzero failure rates without rounding them to zero. */
|
|
18
|
+
export declare function formatErrorRate(value: number): string;
|
|
17
19
|
export declare function formatDurationMs(value: number | null, digits?: number): string;
|
|
18
20
|
/** Wall-clock length: seconds below a minute, then `17m 41s` / `2h 05m`. */
|
|
19
21
|
export declare function formatElapsed(ms: number): string;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -61,7 +61,7 @@ export interface MessageStatsInput extends Omit<MessageStats, "usage" | "costUnp
|
|
|
61
61
|
export interface RequestDetails extends MessageStats {
|
|
62
62
|
/** The full conversation history or just the last turn. */
|
|
63
63
|
messages: unknown[];
|
|
64
|
-
/** The model's response. */
|
|
64
|
+
/** The model's response; `null` for `model_usage` entries, which journal no payload. */
|
|
65
65
|
output: unknown;
|
|
66
66
|
}
|
|
67
67
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/omp-stats",
|
|
4
|
-
"version": "18.
|
|
4
|
+
"version": "18.5.1",
|
|
5
5
|
"description": "Local observability dashboard for omp AI usage statistics",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": {
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@oh-my-pi/pi-ai": "18.
|
|
46
|
-
"@oh-my-pi/pi-catalog": "18.
|
|
47
|
-
"@oh-my-pi/pi-utils": "18.
|
|
45
|
+
"@oh-my-pi/pi-ai": "18.5.1",
|
|
46
|
+
"@oh-my-pi/pi-catalog": "18.5.1",
|
|
47
|
+
"@oh-my-pi/pi-utils": "18.5.1",
|
|
48
48
|
"lucide-react": "^1.24.0",
|
|
49
49
|
"react": "19.2.7",
|
|
50
50
|
"react-dom": "19.2.7"
|
package/src/aggregator.ts
CHANGED
|
@@ -565,6 +565,9 @@ export async function getRequestDetails(id: number): Promise<RequestDetails | nu
|
|
|
565
565
|
if (!msg) return null;
|
|
566
566
|
|
|
567
567
|
const entry = await getSessionEntry(msg.sessionFile, msg.entryId);
|
|
568
|
+
// Role-model attempts (judge, auto-thinking, …) journal only a `model_usage`
|
|
569
|
+
// record: usage and outcome, no request or response payload.
|
|
570
|
+
if (entry?.type === "model_usage") return { ...msg, messages: [entry], output: null };
|
|
568
571
|
if (entry?.type !== "message" || !("message" in entry)) return null;
|
|
569
572
|
|
|
570
573
|
return {
|
|
@@ -56,6 +56,13 @@ export function formatPercent(value: number, digits = 1): string {
|
|
|
56
56
|
return `${(value * 100).toFixed(digits)}%`;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/** Show small nonzero failure rates without rounding them to zero. */
|
|
60
|
+
export function formatErrorRate(value: number): string {
|
|
61
|
+
const percent = value * 100;
|
|
62
|
+
if (percent > 0 && percent < 0.005) return "<0.01%";
|
|
63
|
+
return formatPercent(value, percent > 0 && percent < 0.1 ? 2 : 1);
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
export function formatDurationMs(value: number | null, digits?: number): string {
|
|
60
67
|
if (value === null) return "-";
|
|
61
68
|
const sec = value / 1000;
|
|
@@ -7,6 +7,7 @@ import { buildModelColorLookup, modelKey, OTHER_COLOR } from "../data/colors";
|
|
|
7
7
|
import {
|
|
8
8
|
formatCompact,
|
|
9
9
|
formatDurationMs,
|
|
10
|
+
formatErrorRate,
|
|
10
11
|
formatEstimatedCost,
|
|
11
12
|
formatInteger,
|
|
12
13
|
formatPercent,
|
|
@@ -388,7 +389,7 @@ function buildModelColumns(view: ModelsView, expandedKey: string | null, bucketW
|
|
|
388
389
|
) : (
|
|
389
390
|
<span title={`${formatInteger(row.failedRequests)} failed`}>
|
|
390
391
|
<Badge tone={errorRateTone(row.errorRate)} mono>
|
|
391
|
-
{
|
|
392
|
+
{formatErrorRate(row.errorRate)}
|
|
392
393
|
</Badge>
|
|
393
394
|
</span>
|
|
394
395
|
),
|
|
@@ -477,7 +478,7 @@ function ModelDetail({
|
|
|
477
478
|
label: "Error rate",
|
|
478
479
|
value: (
|
|
479
480
|
<span className={`tone-${errorRateTone(model.errorRate)}`}>
|
|
480
|
-
{
|
|
481
|
+
{formatErrorRate(model.errorRate)}{" "}
|
|
481
482
|
<span className="dim">({formatInteger(model.failedRequests)} failed)</span>
|
|
482
483
|
</span>
|
|
483
484
|
),
|
|
@@ -5,6 +5,7 @@ import { Legend, ShareBar, TimeChart } from "../charts";
|
|
|
5
5
|
import {
|
|
6
6
|
formatCompact,
|
|
7
7
|
formatDurationMs,
|
|
8
|
+
formatErrorRate,
|
|
8
9
|
formatEstimatedCost,
|
|
9
10
|
formatInteger,
|
|
10
11
|
formatMessageCost,
|
|
@@ -133,7 +134,7 @@ export function OverviewRoute({ active, range, onRequestClick }: OverviewRoutePr
|
|
|
133
134
|
/>
|
|
134
135
|
<Stat
|
|
135
136
|
label="Error rate"
|
|
136
|
-
value={
|
|
137
|
+
value={formatErrorRate(overall.errorRate)}
|
|
137
138
|
hint={`${formatInteger(overall.successfulRequests)} succeeded`}
|
|
138
139
|
spark={series.errors}
|
|
139
140
|
sparkColor="var(--bad)"
|
|
@@ -4,6 +4,7 @@ import { BarList } from "../charts";
|
|
|
4
4
|
import {
|
|
5
5
|
formatCompact,
|
|
6
6
|
formatDurationMs,
|
|
7
|
+
formatErrorRate,
|
|
7
8
|
formatEstimatedCost,
|
|
8
9
|
formatFolder,
|
|
9
10
|
formatInteger,
|
|
@@ -296,7 +297,7 @@ function folderColumns(maxRequests: number, maxCost: number): Column<FolderRowVi
|
|
|
296
297
|
render: row => (
|
|
297
298
|
<span title={`${formatInteger(row.failedRequests)} failed`}>
|
|
298
299
|
<Badge tone={row.failedRequests > 0 ? errorRateTone(row.errorRate) : "neutral"} mono>
|
|
299
|
-
{
|
|
300
|
+
{formatErrorRate(row.errorRate)}
|
|
300
301
|
</Badge>
|
|
301
302
|
</span>
|
|
302
303
|
),
|
|
@@ -6,6 +6,7 @@ import { buildColorLookup, OTHER_COLOR, SERIES_COLORS } from "../data/colors";
|
|
|
6
6
|
import {
|
|
7
7
|
formatCompact,
|
|
8
8
|
formatCost,
|
|
9
|
+
formatErrorRate,
|
|
9
10
|
formatEstimatedCost,
|
|
10
11
|
formatInteger,
|
|
11
12
|
formatPercent,
|
|
@@ -178,7 +179,7 @@ export function ProvidersRoute({ active, range }: ProvidersRouteProps) {
|
|
|
178
179
|
/>
|
|
179
180
|
<Stat
|
|
180
181
|
label="Error rate"
|
|
181
|
-
value={
|
|
182
|
+
value={formatErrorRate(t.requests > 0 ? t.failed / t.requests : 0)}
|
|
182
183
|
hint={`${formatInteger(t.requests - t.failed)} succeeded`}
|
|
183
184
|
/>
|
|
184
185
|
</StatGrid>
|
|
@@ -338,7 +339,7 @@ function ProviderTotalsTable({
|
|
|
338
339
|
<span className="row" style={{ gap: 8, justifyContent: "flex-end" }}>
|
|
339
340
|
<span className="num dim">{formatInteger(p.failedRequests)}</span>
|
|
340
341
|
<Badge tone={p.failedRequests > 0 ? errorRateTone(rate) : "neutral"} mono>
|
|
341
|
-
{
|
|
342
|
+
{formatErrorRate(rate)}
|
|
342
343
|
</Badge>
|
|
343
344
|
</span>
|
|
344
345
|
);
|
|
@@ -3,11 +3,11 @@ import { getRecentRequests } from "../api";
|
|
|
3
3
|
import {
|
|
4
4
|
formatCompact,
|
|
5
5
|
formatDurationMs,
|
|
6
|
+
formatErrorRate,
|
|
6
7
|
formatEstimatedCost,
|
|
7
8
|
formatFolder,
|
|
8
9
|
formatInteger,
|
|
9
10
|
formatMessageCost,
|
|
10
|
-
formatPercent,
|
|
11
11
|
formatRelativeTime,
|
|
12
12
|
formatTimestamp,
|
|
13
13
|
} from "../data/formatters";
|
|
@@ -125,7 +125,7 @@ export function RequestsRoute({ active, range, onRequestClick }: RequestsRoutePr
|
|
|
125
125
|
<Stat
|
|
126
126
|
label="Failed"
|
|
127
127
|
value={formatInteger(summary.failed)}
|
|
128
|
-
hint={`${summary.requests > 0 ?
|
|
128
|
+
hint={`${summary.requests > 0 ? formatErrorRate(summary.failed / summary.requests) : "–"} · ${formatInteger(summary.aborted)} aborted`}
|
|
129
129
|
/>
|
|
130
130
|
<Stat
|
|
131
131
|
label="Tokens"
|
|
@@ -5,6 +5,7 @@ import { Legend, Sparkline, TimeChart, useHiddenSeries } from "../charts";
|
|
|
5
5
|
import { buildColorLookup, OTHER_COLOR } from "../data/colors";
|
|
6
6
|
import {
|
|
7
7
|
formatCompact,
|
|
8
|
+
formatErrorRate,
|
|
8
9
|
formatEstimatedCost,
|
|
9
10
|
formatInteger,
|
|
10
11
|
formatPercent,
|
|
@@ -96,7 +97,7 @@ export function ToolsRoute({ active, range }: ToolsRouteProps) {
|
|
|
96
97
|
<Stat
|
|
97
98
|
label="Error rate"
|
|
98
99
|
title="Tool results that came back flagged as errors"
|
|
99
|
-
value={
|
|
100
|
+
value={formatErrorRate(t.calls > 0 ? t.errors / t.calls : 0)}
|
|
100
101
|
hint={`${formatInteger(t.calls - t.errors)} succeeded`}
|
|
101
102
|
spark={view.totalErrors}
|
|
102
103
|
sparkColor="var(--bad)"
|
|
@@ -389,7 +390,7 @@ function buildToolColumns(
|
|
|
389
390
|
<span className="row" style={{ gap: 8, justifyContent: "flex-end" }}>
|
|
390
391
|
<span className="num dim">{formatInteger(row.errors)}</span>
|
|
391
392
|
<Badge tone={row.errors > 0 ? errorRateTone(row.errorRate) : "neutral"} mono>
|
|
392
|
-
{
|
|
393
|
+
{formatErrorRate(row.errorRate)}
|
|
393
394
|
</Badge>
|
|
394
395
|
</span>
|
|
395
396
|
),
|
|
@@ -519,7 +520,7 @@ function ToolModelTable({
|
|
|
519
520
|
<span className="row" style={{ gap: 8, justifyContent: "flex-end" }}>
|
|
520
521
|
<span className="num dim">{formatInteger(row.errors)}</span>
|
|
521
522
|
<Badge tone={row.errors > 0 ? errorRateTone(row.errorRate) : "neutral"} mono>
|
|
522
|
-
{
|
|
523
|
+
{formatErrorRate(row.errorRate)}
|
|
523
524
|
</Badge>
|
|
524
525
|
</span>
|
|
525
526
|
),
|
package/src/client/styles.css
CHANGED
|
@@ -175,7 +175,7 @@ function RequestDetailsBody({ details, aborted }: { details: RequestDetails; abo
|
|
|
175
175
|
</div>
|
|
176
176
|
</section>
|
|
177
177
|
|
|
178
|
-
<JsonBlock data={output} title="Output message" />
|
|
178
|
+
{output != null && <JsonBlock data={output} title="Output message" />}
|
|
179
179
|
<JsonBlock data={messages} title="Session entry" initialCollapsed />
|
|
180
180
|
<JsonBlock data={row} title="Stats row" initialCollapsed />
|
|
181
181
|
</>
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { formatDuration, formatNumber, formatPercent, normalizePremiumRequests } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
6
|
+
import { formatErrorRate } from "./client/data/formatters";
|
|
6
7
|
import { getDashboardStats, getTotalMessageCount, syncAllSessions } from "./aggregator";
|
|
7
8
|
import { closeDb } from "./db";
|
|
8
9
|
import { refreshRollups } from "./rollup";
|
|
@@ -58,7 +59,7 @@ export async function printStatsSummary(): Promise<void> {
|
|
|
58
59
|
|
|
59
60
|
console.log(chalk.bold("Overall:"));
|
|
60
61
|
console.log(` Requests: ${formatNumber(overall.totalRequests)} (${formatNumber(overall.failedRequests)} errors)`);
|
|
61
|
-
console.log(` Error Rate: ${
|
|
62
|
+
console.log(` Error Rate: ${formatErrorRate(overall.errorRate)}`);
|
|
62
63
|
console.log(` Total Tokens: ${formatNumber(overall.totalInputTokens + overall.totalOutputTokens)}`);
|
|
63
64
|
console.log(` Input Tokens: ${formatNumber(overall.totalInputTokens)}`);
|
|
64
65
|
console.log(` Output Tokens: ${formatNumber(overall.totalOutputTokens)}`);
|
package/src/types.ts
CHANGED
|
@@ -63,7 +63,7 @@ export interface MessageStatsInput extends Omit<MessageStats, "usage" | "costUnp
|
|
|
63
63
|
export interface RequestDetails extends MessageStats {
|
|
64
64
|
/** The full conversation history or just the last turn. */
|
|
65
65
|
messages: unknown[];
|
|
66
|
-
/** The model's response. */
|
|
66
|
+
/** The model's response; `null` for `model_usage` entries, which journal no payload. */
|
|
67
67
|
output: unknown;
|
|
68
68
|
}
|
|
69
69
|
|