@linxiraos/pi-stats 1.1.13 → 1.1.15
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 -35
- package/dist/client/index.css +1 -1
- package/dist/client/index.js +56 -56
- package/dist/client/styles.css +46 -46
- package/dist/types/client/components/chart-shared.d.ts +21 -21
- package/dist/types/client/components/models-table-shared.d.ts +14 -14
- package/dist/types/client/data/formatters.d.ts +7 -2
- package/dist/types/client/traces/trace-colors.d.ts +1 -1
- package/dist/types/client/types.d.ts +2 -0
- package/dist/types/db.d.ts +24 -2
- package/dist/types/parser.d.ts +45 -2
- package/dist/types/shared-types.d.ts +4 -0
- package/dist/types/trace.d.ts +1 -1
- package/dist/types/types.d.ts +21 -0
- package/package.json +4 -4
- package/src/aggregator.ts +4 -0
- package/src/client/app/AppLayout.tsx +1 -1
- package/src/client/app/NavRail.tsx +1 -1
- package/src/client/components/AgentTokenShare.tsx +6 -6
- package/src/client/components/chart-shared.tsx +22 -22
- package/src/client/data/formatters.ts +15 -3
- package/src/client/routes/BehaviorRoute.tsx +10 -10
- package/src/client/routes/ModelsRoute.tsx +1 -1
- package/src/client/routes/OverviewRoute.tsx +4 -4
- package/src/client/routes/ProvidersRoute.tsx +3 -3
- package/src/client/routes/TracesRoute.tsx +10 -3
- package/src/client/styles.css +54 -52
- package/src/client/traces/SummaryStrip.tsx +2 -2
- package/src/client/traces/trace-colors.ts +13 -13
- package/src/client/types.ts +2 -0
- package/src/db.ts +242 -46
- package/src/parser.ts +131 -18
- package/src/server.ts +1 -1
- package/src/shared-types.ts +4 -0
- package/src/trace.ts +52 -8
- package/src/types.ts +20 -0
package/dist/client/styles.css
CHANGED
|
@@ -481,21 +481,21 @@
|
|
|
481
481
|
}
|
|
482
482
|
:root {
|
|
483
483
|
color-scheme: dark;
|
|
484
|
-
--page:
|
|
485
|
-
--surface:
|
|
486
|
-
--surface-2:
|
|
487
|
-
--border:
|
|
488
|
-
--border-strong:
|
|
489
|
-
--text:
|
|
490
|
-
--muted:
|
|
491
|
-
--dim:
|
|
492
|
-
--accent:
|
|
493
|
-
--accent-fg:
|
|
494
|
-
--accent-muted:
|
|
495
|
-
--link:
|
|
496
|
-
--success:
|
|
497
|
-
--danger:
|
|
498
|
-
--warning:
|
|
484
|
+
--page: #161616;
|
|
485
|
+
--surface: #1e1e1e;
|
|
486
|
+
--surface-2: #232323;
|
|
487
|
+
--border: #2e2e2e;
|
|
488
|
+
--border-strong: #3c3c3c;
|
|
489
|
+
--text: #f5f5f5;
|
|
490
|
+
--muted: #9d9d9d;
|
|
491
|
+
--dim: #6e6e6e;
|
|
492
|
+
--accent: #eaeaea;
|
|
493
|
+
--accent-fg: #161616;
|
|
494
|
+
--accent-muted: rgb(234 234 234 / 0.18);
|
|
495
|
+
--link: #61afef;
|
|
496
|
+
--success: #98c379;
|
|
497
|
+
--danger: #f07178;
|
|
498
|
+
--warning: #e5c07b;
|
|
499
499
|
--text-primary: var(--text);
|
|
500
500
|
--text-secondary: var(--muted);
|
|
501
501
|
--text-muted: var(--dim);
|
|
@@ -513,42 +513,42 @@
|
|
|
513
513
|
@media (prefers-color-scheme: light) {
|
|
514
514
|
:root:not([data-theme]) {
|
|
515
515
|
color-scheme: light;
|
|
516
|
-
--page:
|
|
517
|
-
--surface:
|
|
518
|
-
--surface-2:
|
|
519
|
-
--border:
|
|
520
|
-
--border-strong:
|
|
521
|
-
--text:
|
|
522
|
-
--muted:
|
|
523
|
-
--dim:
|
|
524
|
-
--accent:
|
|
525
|
-
--accent-fg:
|
|
526
|
-
--accent-muted:
|
|
527
|
-
--link:
|
|
528
|
-
--success:
|
|
529
|
-
--danger:
|
|
516
|
+
--page: #fafafa;
|
|
517
|
+
--surface: #ffffff;
|
|
518
|
+
--surface-2: #f0f0f0;
|
|
519
|
+
--border: #e0e0e0;
|
|
520
|
+
--border-strong: #c8c8c8;
|
|
521
|
+
--text: #1f1f1f;
|
|
522
|
+
--muted: #616161;
|
|
523
|
+
--dim: #9e9e9e;
|
|
524
|
+
--accent: #1a1a1a;
|
|
525
|
+
--accent-fg: #fafafa;
|
|
526
|
+
--accent-muted: rgb(26 26 26 / 0.14);
|
|
527
|
+
--link: #4078c0;
|
|
528
|
+
--success: #50a14f;
|
|
529
|
+
--danger: #e45649;
|
|
530
530
|
--select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23555' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
|
531
|
-
--warning:
|
|
531
|
+
--warning: #986801;
|
|
532
532
|
}
|
|
533
533
|
}
|
|
534
534
|
[data-theme="light"] {
|
|
535
535
|
color-scheme: light;
|
|
536
|
-
--page:
|
|
537
|
-
--surface:
|
|
538
|
-
--surface-2:
|
|
539
|
-
--border:
|
|
540
|
-
--border-strong:
|
|
541
|
-
--text:
|
|
542
|
-
--muted:
|
|
543
|
-
--dim:
|
|
544
|
-
--accent:
|
|
545
|
-
--accent-fg:
|
|
546
|
-
--accent-muted:
|
|
547
|
-
--link:
|
|
548
|
-
--success:
|
|
549
|
-
--danger:
|
|
536
|
+
--page: #fafafa;
|
|
537
|
+
--surface: #ffffff;
|
|
538
|
+
--surface-2: #f0f0f0;
|
|
539
|
+
--border: #e0e0e0;
|
|
540
|
+
--border-strong: #c8c8c8;
|
|
541
|
+
--text: #1f1f1f;
|
|
542
|
+
--muted: #616161;
|
|
543
|
+
--dim: #9e9e9e;
|
|
544
|
+
--accent: #1a1a1a;
|
|
545
|
+
--accent-fg: #fafafa;
|
|
546
|
+
--accent-muted: rgb(26 26 26 / 0.14);
|
|
547
|
+
--link: #4078c0;
|
|
548
|
+
--success: #50a14f;
|
|
549
|
+
--danger: #e45649;
|
|
550
550
|
--select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23555' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
|
551
|
-
--warning:
|
|
551
|
+
--warning: #986801;
|
|
552
552
|
}
|
|
553
553
|
@layer base {
|
|
554
554
|
* {
|
|
@@ -1570,7 +1570,7 @@
|
|
|
1570
1570
|
width: 16px;
|
|
1571
1571
|
height: 16px;
|
|
1572
1572
|
border-radius: 4px;
|
|
1573
|
-
background: linear-gradient(135deg, #
|
|
1573
|
+
background: linear-gradient(135deg, #eaeaea 0%, #9d9d9d 50%, #3c3c3c 100%);
|
|
1574
1574
|
}
|
|
1575
1575
|
.stats-logo-text, .stats-logo-subtext {
|
|
1576
1576
|
grid-column: 2;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared chart primitives for the dashboard timeline charts: the
|
|
2
|
+
* Shared chart primitives for the dashboard timeline charts: the Zeta color
|
|
3
3
|
* palette, light/dark chart chrome, legend/tooltip + scale plumbing, dataset
|
|
4
4
|
* styling, and the top-N-by-model / aggregate bucketing used by the cost and
|
|
5
5
|
* behavior series.
|
|
@@ -12,22 +12,22 @@ export declare function buildModelColorLookup(records: readonly {
|
|
|
12
12
|
}[]): Map<string, string>;
|
|
13
13
|
export declare const CHART_THEMES: {
|
|
14
14
|
readonly dark: {
|
|
15
|
-
readonly legendLabel: "#
|
|
16
|
-
readonly tooltipBackground: "#
|
|
17
|
-
readonly tooltipTitle: "#
|
|
18
|
-
readonly tooltipBody: "#
|
|
15
|
+
readonly legendLabel: "#9d9d9d";
|
|
16
|
+
readonly tooltipBackground: "#232323";
|
|
17
|
+
readonly tooltipTitle: "#f5f5f5";
|
|
18
|
+
readonly tooltipBody: "#9d9d9d";
|
|
19
19
|
readonly tooltipBorder: "rgba(255, 255, 255, 0.12)";
|
|
20
20
|
readonly grid: "rgba(255, 255, 255, 0.06)";
|
|
21
|
-
readonly tick: "#
|
|
21
|
+
readonly tick: "#7a7a7a";
|
|
22
22
|
};
|
|
23
23
|
readonly light: {
|
|
24
|
-
readonly legendLabel: "#
|
|
24
|
+
readonly legendLabel: "#616161";
|
|
25
25
|
readonly tooltipBackground: "#ffffff";
|
|
26
|
-
readonly tooltipTitle: "#
|
|
27
|
-
readonly tooltipBody: "#
|
|
28
|
-
readonly tooltipBorder: "rgba(
|
|
29
|
-
readonly grid: "rgba(
|
|
30
|
-
readonly tick: "#
|
|
26
|
+
readonly tooltipTitle: "#1f1f1f";
|
|
27
|
+
readonly tooltipBody: "#616161";
|
|
28
|
+
readonly tooltipBorder: "rgba(0, 0, 0, 0.15)";
|
|
29
|
+
readonly grid: "rgba(0, 0, 0, 0.08)";
|
|
30
|
+
readonly tick: "#8c8c8c";
|
|
31
31
|
};
|
|
32
32
|
};
|
|
33
33
|
export type ChartTheme = (typeof CHART_THEMES)[keyof typeof CHART_THEMES];
|
|
@@ -56,7 +56,7 @@ export declare function buildSharedPlugins(opts: {
|
|
|
56
56
|
position: "top";
|
|
57
57
|
align: "start";
|
|
58
58
|
labels: {
|
|
59
|
-
color: "#
|
|
59
|
+
color: "#616161" | "#9d9d9d";
|
|
60
60
|
usePointStyle: boolean;
|
|
61
61
|
padding: number;
|
|
62
62
|
font: {
|
|
@@ -66,10 +66,10 @@ export declare function buildSharedPlugins(opts: {
|
|
|
66
66
|
};
|
|
67
67
|
};
|
|
68
68
|
tooltip: {
|
|
69
|
-
backgroundColor: "#
|
|
70
|
-
titleColor: "#
|
|
71
|
-
bodyColor: "#
|
|
72
|
-
borderColor: "rgba(
|
|
69
|
+
backgroundColor: "#232323" | "#ffffff";
|
|
70
|
+
titleColor: "#1f1f1f" | "#f5f5f5";
|
|
71
|
+
bodyColor: "#616161" | "#9d9d9d";
|
|
72
|
+
borderColor: "rgba(0, 0, 0, 0.15)" | "rgba(255, 255, 255, 0.12)";
|
|
73
73
|
borderWidth: number;
|
|
74
74
|
padding: number;
|
|
75
75
|
cornerRadius: number;
|
|
@@ -93,11 +93,11 @@ export declare function buildSharedScales(opts: {
|
|
|
93
93
|
}): {
|
|
94
94
|
sharedScaleBase: {
|
|
95
95
|
grid: {
|
|
96
|
-
color: "rgba(
|
|
96
|
+
color: "rgba(0, 0, 0, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
97
97
|
drawBorder: boolean;
|
|
98
98
|
};
|
|
99
99
|
ticks: {
|
|
100
|
-
color: "#
|
|
100
|
+
color: "#7a7a7a" | "#8c8c8c";
|
|
101
101
|
font: {
|
|
102
102
|
size: number;
|
|
103
103
|
};
|
|
@@ -105,11 +105,11 @@ export declare function buildSharedScales(opts: {
|
|
|
105
105
|
};
|
|
106
106
|
yScale: {
|
|
107
107
|
grid: {
|
|
108
|
-
color: "rgba(
|
|
108
|
+
color: "rgba(0, 0, 0, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
109
109
|
drawBorder: boolean;
|
|
110
110
|
};
|
|
111
111
|
ticks: {
|
|
112
|
-
color: "#
|
|
112
|
+
color: "#7a7a7a" | "#8c8c8c";
|
|
113
113
|
font: {
|
|
114
114
|
size: number;
|
|
115
115
|
};
|
|
@@ -34,7 +34,7 @@ export declare function detailChartPlugins(chartTheme: TableChartTheme): {
|
|
|
34
34
|
display: boolean;
|
|
35
35
|
position: "top";
|
|
36
36
|
labels: {
|
|
37
|
-
color: "#
|
|
37
|
+
color: "#616161" | "#9d9d9d";
|
|
38
38
|
usePointStyle: boolean;
|
|
39
39
|
padding: number;
|
|
40
40
|
font: {
|
|
@@ -43,10 +43,10 @@ export declare function detailChartPlugins(chartTheme: TableChartTheme): {
|
|
|
43
43
|
};
|
|
44
44
|
};
|
|
45
45
|
tooltip: {
|
|
46
|
-
backgroundColor: "#
|
|
47
|
-
titleColor: "#
|
|
48
|
-
bodyColor: "#
|
|
49
|
-
borderColor: "rgba(
|
|
46
|
+
backgroundColor: "#232323" | "#ffffff";
|
|
47
|
+
titleColor: "#1f1f1f" | "#f5f5f5";
|
|
48
|
+
bodyColor: "#616161" | "#9d9d9d";
|
|
49
|
+
borderColor: "rgba(0, 0, 0, 0.15)" | "rgba(255, 255, 255, 0.12)";
|
|
50
50
|
borderWidth: number;
|
|
51
51
|
cornerRadius: number;
|
|
52
52
|
};
|
|
@@ -58,10 +58,10 @@ export declare function detailChartPlugins(chartTheme: TableChartTheme): {
|
|
|
58
58
|
export declare function detailChartScalesSingleAxis(chartTheme: TableChartTheme): {
|
|
59
59
|
x: {
|
|
60
60
|
grid: {
|
|
61
|
-
color: "rgba(
|
|
61
|
+
color: "rgba(0, 0, 0, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
62
62
|
};
|
|
63
63
|
ticks: {
|
|
64
|
-
color: "#
|
|
64
|
+
color: "#7a7a7a" | "#8c8c8c";
|
|
65
65
|
font: {
|
|
66
66
|
size: number;
|
|
67
67
|
};
|
|
@@ -69,10 +69,10 @@ export declare function detailChartScalesSingleAxis(chartTheme: TableChartTheme)
|
|
|
69
69
|
};
|
|
70
70
|
y: {
|
|
71
71
|
grid: {
|
|
72
|
-
color: "rgba(
|
|
72
|
+
color: "rgba(0, 0, 0, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
73
73
|
};
|
|
74
74
|
ticks: {
|
|
75
|
-
color: "#
|
|
75
|
+
color: "#7a7a7a" | "#8c8c8c";
|
|
76
76
|
font: {
|
|
77
77
|
size: number;
|
|
78
78
|
};
|
|
@@ -88,10 +88,10 @@ export declare function detailChartScalesSingleAxis(chartTheme: TableChartTheme)
|
|
|
88
88
|
export declare function detailChartScalesDualAxis(chartTheme: TableChartTheme): {
|
|
89
89
|
x: {
|
|
90
90
|
grid: {
|
|
91
|
-
color: "rgba(
|
|
91
|
+
color: "rgba(0, 0, 0, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
92
92
|
};
|
|
93
93
|
ticks: {
|
|
94
|
-
color: "#
|
|
94
|
+
color: "#7a7a7a" | "#8c8c8c";
|
|
95
95
|
font: {
|
|
96
96
|
size: number;
|
|
97
97
|
};
|
|
@@ -102,10 +102,10 @@ export declare function detailChartScalesDualAxis(chartTheme: TableChartTheme):
|
|
|
102
102
|
display: boolean;
|
|
103
103
|
position: "left";
|
|
104
104
|
grid: {
|
|
105
|
-
color: "rgba(
|
|
105
|
+
color: "rgba(0, 0, 0, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
106
106
|
};
|
|
107
107
|
ticks: {
|
|
108
|
-
color: "#
|
|
108
|
+
color: "#7a7a7a" | "#8c8c8c";
|
|
109
109
|
font: {
|
|
110
110
|
size: number;
|
|
111
111
|
};
|
|
@@ -119,7 +119,7 @@ export declare function detailChartScalesDualAxis(chartTheme: TableChartTheme):
|
|
|
119
119
|
drawOnChartArea: boolean;
|
|
120
120
|
};
|
|
121
121
|
ticks: {
|
|
122
|
-
color: "#
|
|
122
|
+
color: "#7a7a7a" | "#8c8c8c";
|
|
123
123
|
font: {
|
|
124
124
|
size: number;
|
|
125
125
|
};
|
|
@@ -4,8 +4,13 @@ export declare function formatCompact(value: number): string;
|
|
|
4
4
|
export declare function formatCost(value: number, digits?: number): string;
|
|
5
5
|
/** Format an API-equivalent estimate, using N/A when all usage is unpriced. */
|
|
6
6
|
export declare function formatEstimatedCost(value: number, unpricedRequests: number, digits?: number): string;
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Format one request's cost, distinguishing unpriced usage from free usage.
|
|
9
|
+
* Mirrors the server's `unpricedRequestSql`: `xai-oauth` is billed through a
|
|
10
|
+
* subscription, and `costUnpriced` marks a row the ingest path could not price
|
|
11
|
+
* (a scheduled card with no recoverable request timestamp).
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatMessageCost(message: Pick<MessageStats, "provider" | "usage" | "costUnpriced">, digits?: number): string;
|
|
9
14
|
export declare function formatPercent(value: number, digits?: number): string;
|
|
10
15
|
export declare function formatDurationMs(value: number | null, digits?: number): string;
|
|
11
16
|
export declare function formatTokensPerSecond(value: number | null): string;
|
|
@@ -21,7 +21,7 @@ export interface TraceTheme {
|
|
|
21
21
|
tick: string;
|
|
22
22
|
/** Span label text drawn inside blocks. */
|
|
23
23
|
spanText: string;
|
|
24
|
-
/** Selection outline (
|
|
24
|
+
/** Selection outline (theme focus accent). */
|
|
25
25
|
selection: string;
|
|
26
26
|
tooltipBackground: string;
|
|
27
27
|
tooltipTitle: string;
|
|
@@ -42,6 +42,8 @@ export interface MessageStats {
|
|
|
42
42
|
stopReason: string;
|
|
43
43
|
errorMessage: string | null;
|
|
44
44
|
usage: Usage;
|
|
45
|
+
/** Ingest found no tariff to price this request from; its zero is unknown spend. */
|
|
46
|
+
costUnpriced?: boolean;
|
|
45
47
|
}
|
|
46
48
|
export interface RequestDetails extends MessageStats {
|
|
47
49
|
messages: unknown[];
|
package/dist/types/db.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
|
-
import type { AgentTypeStats, AggregatedStats, BehaviorModelStats, BehaviorOverallStats, BehaviorTimeSeriesPoint, CostTimeSeriesPoint, DailyActivityPoint, FolderStats, MessageStats, ModelPerformancePoint, ModelStats, ModelTimeSeriesPoint, ProviderAggregate, ProviderHourlyPoint, ProviderTimeSeriesPoint, TimeSeriesPoint, ToolCallStats, ToolModelStats, ToolResultLink, ToolTimeSeriesPoint, ToolUsageStats, UserMessageLink, UserMessageStats } from "./types.js";
|
|
2
|
+
import type { AgentTypeStats, AggregatedStats, BehaviorModelStats, BehaviorOverallStats, BehaviorTimeSeriesPoint, CostTimeSeriesPoint, DailyActivityPoint, FolderStats, MessageStats, MessageStatsInput, ModelPerformancePoint, ModelStats, ModelTimeSeriesPoint, ProviderAggregate, ProviderHourlyPoint, ProviderTimeSeriesPoint, TimeSeriesPoint, ToolCallStats, ToolModelStats, ToolResultLink, ToolTimeSeriesPoint, ToolUsageStats, UserMessageLink, UserMessageStats } from "./types.js";
|
|
3
3
|
/**
|
|
4
4
|
* Initialize the database and create tables.
|
|
5
5
|
*/
|
|
6
6
|
export declare function initDb(): Promise<Database>;
|
|
7
|
+
/** Whether the catalog prices this model on a time-based (scheduled) card. */
|
|
8
|
+
export declare function isScheduledCatalogModel(provider: string, modelId: string): boolean;
|
|
7
9
|
/**
|
|
8
10
|
* Get the stored offset for a session file.
|
|
9
11
|
*/
|
|
@@ -31,7 +33,7 @@ export declare function setFileOffset(sessionFile: string, offset: number, lastM
|
|
|
31
33
|
* stored cost (orchestration-aware) and keeps `premium_requests` monotonic, so
|
|
32
34
|
* a forced re-parse repairs historical `premium_requests` and cost fix-ups.
|
|
33
35
|
*/
|
|
34
|
-
export declare function insertMessageStats(stats:
|
|
36
|
+
export declare function insertMessageStats(stats: MessageStatsInput[]): number;
|
|
35
37
|
/**
|
|
36
38
|
* Get overall aggregated stats.
|
|
37
39
|
*/
|
|
@@ -98,6 +100,7 @@ export interface SessionRollupRow {
|
|
|
98
100
|
endedAt: number;
|
|
99
101
|
totalTokens: number;
|
|
100
102
|
costTotal: number;
|
|
103
|
+
unpricedRequests: number;
|
|
101
104
|
/** Comma-joined DISTINCT models. */
|
|
102
105
|
models: string;
|
|
103
106
|
}
|
|
@@ -121,6 +124,25 @@ export declare function getDailyActivity(days?: number): Promise<DailyActivityPo
|
|
|
121
124
|
* Settle every full-session backfill after a successful sync pass.
|
|
122
125
|
*/
|
|
123
126
|
export declare function markSessionBackfillsComplete(): void;
|
|
127
|
+
export interface PruneOrphanResult {
|
|
128
|
+
/** Distinct session files whose rows were removed. */
|
|
129
|
+
prunedFiles: number;
|
|
130
|
+
/** Total rows removed across all session-keyed tables. */
|
|
131
|
+
deletedRows: number;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Delete every row whose `session_file` no longer exists on disk. Deleted
|
|
135
|
+
* projects (tmp test suites, wiped transcripts) otherwise keep surfacing in
|
|
136
|
+
* the Operational Feed and models list forever. Runs in one transaction; the
|
|
137
|
+
* existing-file set is collected by the caller before any DELETE so the
|
|
138
|
+
* comparison is a single in-memory set probe per stored path.
|
|
139
|
+
*/
|
|
140
|
+
export declare function pruneOrphanSessionsInDb(database: Database, existingFiles: readonly string[]): PruneOrphanResult;
|
|
141
|
+
/**
|
|
142
|
+
* {@link pruneOrphanSessionsInDb} against the module DB handle. No-op when
|
|
143
|
+
* {@link initDb} has not run.
|
|
144
|
+
*/
|
|
145
|
+
export declare function pruneOrphanSessions(existingFiles: readonly string[]): PruneOrphanResult;
|
|
124
146
|
/**
|
|
125
147
|
* Insert user-message stats. Idempotent via UNIQUE(session_file, entry_id).
|
|
126
148
|
* The `WHERE NOT EXISTS` clause matches {@link insertMessageStats}: forks
|
package/dist/types/parser.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentType,
|
|
1
|
+
import type { AgentType, MessageStatsInput, SessionEntry, ToolCallStats, ToolResultLink, UserMessageLink, UserMessageStats } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Classify which agent produced a transcript from its path within the sessions
|
|
4
4
|
* directory. Layout: `<sessionsDir>/<project>/<file>.jsonl` is the `main`
|
|
@@ -16,6 +16,49 @@ export declare function classifyAgentType(sessionPath: string): AgentType;
|
|
|
16
16
|
* The folder part uses -- as path separator.
|
|
17
17
|
*/
|
|
18
18
|
export declare function extractFolderFromPath(sessionPath: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Decode a session project folder name back to its source cwd, mirroring the
|
|
21
|
+
* encoder in the coding agent's session-paths module: home-relative cwds
|
|
22
|
+
* encode as `-<segments>`, temp-relative ones as `-tmp<segments>`, and legacy
|
|
23
|
+
* absolute ones as `--<absolute>--`, with `/`, `\`, and `:` collapsed to `-`.
|
|
24
|
+
* Returns null when the name carries no decodable cwd.
|
|
25
|
+
*/
|
|
26
|
+
export declare function decodeProjectFolderCwd(projectDir: string): string | null;
|
|
27
|
+
/**
|
|
28
|
+
* Whether a session project folder decodes to a cwd inside the OS temp dir.
|
|
29
|
+
* Test suites spawn agents under `os.tmpdir()` and re-create their transcripts
|
|
30
|
+
* on every run; those sessions are throwaway usage that must not reach the
|
|
31
|
+
* stats DB. The `-` collapse in the encoder is lossy (a literal `-` inside a
|
|
32
|
+
* path segment is indistinguishable from a separator), which can only produce
|
|
33
|
+
* false positives here — a skipped ingestion, never a misattributed row.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isTempProjectFolder(projectDir: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Token-bucket view for total derivation. Persisted session payloads are
|
|
38
|
+
* outside-controlled (old versions, foreign producers), so every counter is
|
|
39
|
+
* `unknown` and validated at read time.
|
|
40
|
+
*/
|
|
41
|
+
export interface UsageBucketView {
|
|
42
|
+
totalTokens?: unknown;
|
|
43
|
+
input?: unknown;
|
|
44
|
+
output?: unknown;
|
|
45
|
+
cacheRead?: unknown;
|
|
46
|
+
cacheWrite?: unknown;
|
|
47
|
+
orchestration?: {
|
|
48
|
+
input?: unknown;
|
|
49
|
+
output?: unknown;
|
|
50
|
+
cacheRead?: unknown;
|
|
51
|
+
} | null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Total tokens for one usage payload, per the documented contract: the
|
|
55
|
+
* conversation buckets plus provider-reported orchestration tokens. A present
|
|
56
|
+
* finite provider total stays authoritative; a missing or malformed one
|
|
57
|
+
* (absent, string, NaN) is derived from the buckets, which would otherwise
|
|
58
|
+
* persist a zero total next to real token counts. Shared by ingest and the
|
|
59
|
+
* trace builder so stored and displayed totals cannot disagree.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveUsageTotal(usage: UsageBucketView | null | undefined): number;
|
|
19
62
|
/** Parse every well-formed entry in a transcript buffer (malformed lines skipped). */
|
|
20
63
|
export declare function parseAllSessionEntries(bytes: Uint8Array): SessionEntry[];
|
|
21
64
|
/**
|
|
@@ -35,7 +78,7 @@ export declare function parseAllSessionEntries(bytes: Uint8Array): SessionEntry[
|
|
|
35
78
|
* entries, preserving offset-based memory behavior for large sessions.
|
|
36
79
|
*/
|
|
37
80
|
export interface ParseSessionResult {
|
|
38
|
-
stats:
|
|
81
|
+
stats: MessageStatsInput[];
|
|
39
82
|
userStats: UserMessageStats[];
|
|
40
83
|
userLinks: UserMessageLink[];
|
|
41
84
|
toolCalls: ToolCallStats[];
|
|
@@ -454,6 +454,8 @@ export interface SessionSummary {
|
|
|
454
454
|
subagents: number;
|
|
455
455
|
totalTokens: number;
|
|
456
456
|
costTotal: number;
|
|
457
|
+
/** Requests whose zero cost is unknown spend (scheduled card, no timestamp). */
|
|
458
|
+
unpricedRequests: number;
|
|
457
459
|
models: string[];
|
|
458
460
|
}
|
|
459
461
|
export type TraceSpanKind = "turn" | "model" | "tool" | "subagent" | "background";
|
|
@@ -533,6 +535,8 @@ export interface TraceSummary {
|
|
|
533
535
|
subagents: number;
|
|
534
536
|
totalTokens: number;
|
|
535
537
|
costTotal: number;
|
|
538
|
+
/** Model requests whose zero cost is unknown spend, not free usage. */
|
|
539
|
+
unpricedRequests: number;
|
|
536
540
|
/** Sorted totalMs desc. */
|
|
537
541
|
toolStats: TraceToolStat[];
|
|
538
542
|
}
|
package/dist/types/trace.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export declare class TracePathError extends Error {
|
|
|
17
17
|
* span assembly changes so browsers don't revalidate stale cached bodies
|
|
18
18
|
* against an unchanged transcript mtime.
|
|
19
19
|
*/
|
|
20
|
-
export declare const TRACE_ETAG_VERSION =
|
|
20
|
+
export declare const TRACE_ETAG_VERSION = 3;
|
|
21
21
|
/**
|
|
22
22
|
* Assemble the full span tree for one root session transcript.
|
|
23
23
|
* Throws {@link TracePathError} for paths outside the sessions root; ENOENT
|
package/dist/types/types.d.ts
CHANGED
|
@@ -33,6 +33,27 @@ export interface MessageStats {
|
|
|
33
33
|
usage: Usage;
|
|
34
34
|
/** Which agent produced this message (main agent, task subagent, advisor) */
|
|
35
35
|
agentType: AgentType;
|
|
36
|
+
/**
|
|
37
|
+
* Ingest refused to price this request: a scheduled (time-based) card with no
|
|
38
|
+
* recoverable request timestamp, so `usage.cost.total` of 0 is unknown spend
|
|
39
|
+
* rather than a free request. Always written by `rowToMessageStats`; optional
|
|
40
|
+
* only because session fixtures that stand in for ingest input are typed as
|
|
41
|
+
* `MessageStats` too.
|
|
42
|
+
*/
|
|
43
|
+
costUnpriced?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Session-recorded token usage before pricing: counters are coerced to numbers
|
|
47
|
+
* because their columns are NOT NULL, but `cost` is only present when the
|
|
48
|
+
* session entry actually recorded one. Absence is meaningful — `resolveStoredCost`
|
|
49
|
+
* estimates a request with no recorded price, whereas a recorded zero is a real
|
|
50
|
+
* charge that scheduled cards freeze. `costUnpriced` is omitted because it is a
|
|
51
|
+
* decision of the ingest path, not something the parser or a caller supplies.
|
|
52
|
+
*/
|
|
53
|
+
export interface MessageStatsInput extends Omit<MessageStats, "usage" | "costUnpriced"> {
|
|
54
|
+
usage: Omit<Usage, "cost"> & {
|
|
55
|
+
cost?: Partial<Usage["cost"]>;
|
|
56
|
+
};
|
|
36
57
|
}
|
|
37
58
|
/**
|
|
38
59
|
* Full details of a request, including content.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@linxiraos/pi-stats",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.15",
|
|
5
5
|
"description": "Local observability dashboard for pi AI usage statistics",
|
|
6
6
|
"homepage": "https://linxira-os.github.io/zeta/",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@linxiraos/pi-ai": "1.1.
|
|
43
|
-
"@linxiraos/pi-catalog": "1.1.
|
|
44
|
-
"@linxiraos/pi-utils": "1.1.
|
|
42
|
+
"@linxiraos/pi-ai": "1.1.15",
|
|
43
|
+
"@linxiraos/pi-catalog": "1.1.15",
|
|
44
|
+
"@linxiraos/pi-utils": "1.1.15",
|
|
45
45
|
"@tailwindcss/node": "^4.3.2",
|
|
46
46
|
"chart.js": "^4.5.1",
|
|
47
47
|
"lucide-react": "^1.24.0",
|
package/src/aggregator.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
insertToolCalls,
|
|
30
30
|
insertUserMessageStats,
|
|
31
31
|
markSessionBackfillsComplete,
|
|
32
|
+
pruneOrphanSessions,
|
|
32
33
|
setFileOffset,
|
|
33
34
|
updateToolResults,
|
|
34
35
|
updateUserMessageLinks,
|
|
@@ -223,6 +224,9 @@ export async function syncAllSessions(opts?: SyncOptions): Promise<{ processed:
|
|
|
223
224
|
await initDb();
|
|
224
225
|
|
|
225
226
|
const files = await listAllSessionFiles();
|
|
227
|
+
// Drop rows for transcripts that no longer exist on disk (deleted temp/test
|
|
228
|
+
// projects) before parsing, so every aggregate reflects live files only.
|
|
229
|
+
pruneOrphanSessions(files);
|
|
226
230
|
let totalProcessed = 0;
|
|
227
231
|
let filesProcessed = 0;
|
|
228
232
|
let completed = 0;
|
|
@@ -51,7 +51,7 @@ export function AppLayout({
|
|
|
51
51
|
>
|
|
52
52
|
<div className="stats-mobile-drawer-header">
|
|
53
53
|
<div className="stats-logo-container">
|
|
54
|
-
<span className="stats-logo-text">
|
|
54
|
+
<span className="stats-logo-text">ZETA</span>
|
|
55
55
|
<span className="stats-logo-subtext">Observability</span>
|
|
56
56
|
</div>
|
|
57
57
|
<button
|
|
@@ -11,7 +11,7 @@ export function NavRail({ activeSection, onSectionChange, className = "" }: NavR
|
|
|
11
11
|
<aside className={`stats-nav-rail ${className}`}>
|
|
12
12
|
<div className="stats-nav-rail-header">
|
|
13
13
|
<div className="stats-logo-container">
|
|
14
|
-
<span className="stats-logo-text">
|
|
14
|
+
<span className="stats-logo-text">ZETA</span>
|
|
15
15
|
<span className="stats-logo-subtext">Observability</span>
|
|
16
16
|
</div>
|
|
17
17
|
</div>
|
|
@@ -4,14 +4,14 @@ import { buildAgentTokenShare } from "../data/view-models";
|
|
|
4
4
|
import type { AgentType, AgentTypeStats } from "../types";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Per-agent-type display chrome. Colors
|
|
8
|
-
* (
|
|
9
|
-
*
|
|
7
|
+
* Per-agent-type display chrome. Colors come from the Zeta chart palette
|
|
8
|
+
* (chart-shared.tsx) so the bar reads on both themes without per-theme
|
|
9
|
+
* overrides.
|
|
10
10
|
*/
|
|
11
11
|
const AGENT_META: Record<AgentType, { label: string; color: string }> = {
|
|
12
|
-
main: { label: "Main agent", color: "#
|
|
13
|
-
subagent: { label: "Subagents", color: "#
|
|
14
|
-
advisor: { label: "Advisor", color: "#
|
|
12
|
+
main: { label: "Main agent", color: "#9d9d9d" },
|
|
13
|
+
subagent: { label: "Subagents", color: "#61afef" },
|
|
14
|
+
advisor: { label: "Advisor", color: "#56b6c2" },
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
export interface AgentTokenShareProps {
|