@oh-my-pi-zen/omp-stats 16.3.6-zen.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 +197 -0
- package/README.md +82 -0
- package/build.ts +95 -0
- package/dist/client/index.css +1 -0
- package/dist/client/index.html +24 -0
- package/dist/client/index.js +257 -0
- package/dist/client/styles.css +1656 -0
- package/dist/types/aggregator.d.ts +87 -0
- package/dist/types/client/App.d.ts +1 -0
- package/dist/types/client/api.d.ts +21 -0
- package/dist/types/client/app/AppLayout.d.ts +16 -0
- package/dist/types/client/app/NavRail.d.ts +7 -0
- package/dist/types/client/app/RangeControl.d.ts +7 -0
- package/dist/types/client/app/SyncButton.d.ts +14 -0
- package/dist/types/client/app/ThemeToggle.d.ts +1 -0
- package/dist/types/client/app/TopBar.d.ts +15 -0
- package/dist/types/client/app/routes.d.ts +12 -0
- package/dist/types/client/components/AgentTokenShare.d.ts +5 -0
- package/dist/types/client/components/chart-shared.d.ts +173 -0
- package/dist/types/client/components/models-table-shared.d.ts +175 -0
- package/dist/types/client/components/range-meta.d.ts +21 -0
- package/dist/types/client/data/charts.d.ts +1 -0
- package/dist/types/client/data/formatters.d.ts +8 -0
- package/dist/types/client/data/useHashRoute.d.ts +8 -0
- package/dist/types/client/data/useResource.d.ts +13 -0
- package/dist/types/client/data/view-models.d.ts +67 -0
- package/dist/types/client/index.d.ts +2 -0
- package/dist/types/client/routes/BehaviorRoute.d.ts +7 -0
- package/dist/types/client/routes/CostsRoute.d.ts +7 -0
- package/dist/types/client/routes/ErrorsRoute.d.ts +8 -0
- package/dist/types/client/routes/GainRoute.d.ts +7 -0
- package/dist/types/client/routes/ModelsRoute.d.ts +7 -0
- package/dist/types/client/routes/OverviewRoute.d.ts +8 -0
- package/dist/types/client/routes/ProjectsRoute.d.ts +7 -0
- package/dist/types/client/routes/RequestsRoute.d.ts +8 -0
- package/dist/types/client/routes/ToolsRoute.d.ts +7 -0
- package/dist/types/client/routes/index.d.ts +9 -0
- package/dist/types/client/types.d.ts +63 -0
- package/dist/types/client/ui/AsyncBoundary.d.ts +12 -0
- package/dist/types/client/ui/DataTable.d.ts +17 -0
- package/dist/types/client/ui/EmptyState.d.ts +7 -0
- package/dist/types/client/ui/ErrorState.d.ts +6 -0
- package/dist/types/client/ui/JsonBlock.d.ts +7 -0
- package/dist/types/client/ui/MetricCluster.d.ts +5 -0
- package/dist/types/client/ui/Panel.d.ts +7 -0
- package/dist/types/client/ui/RequestDrawer.d.ts +5 -0
- package/dist/types/client/ui/SegmentedControl.d.ts +12 -0
- package/dist/types/client/ui/Skeleton.d.ts +8 -0
- package/dist/types/client/ui/StatusPill.d.ts +7 -0
- package/dist/types/client/ui/index.d.ts +11 -0
- package/dist/types/client/useSystemTheme.d.ts +11 -0
- package/dist/types/db.d.ts +144 -0
- package/dist/types/embedded-client.d.ts +18 -0
- package/dist/types/gain-aggregator.d.ts +26 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/parser.d.ts +53 -0
- package/dist/types/server.d.ts +7 -0
- package/dist/types/shared-types.d.ts +301 -0
- package/dist/types/sync-worker.d.ts +31 -0
- package/dist/types/types.d.ts +164 -0
- package/dist/types/user-metrics.d.ts +72 -0
- package/package.json +95 -0
- package/src/aggregator.ts +501 -0
- package/src/client/App.tsx +109 -0
- package/src/client/api.ts +102 -0
- package/src/client/app/AppLayout.tsx +93 -0
- package/src/client/app/NavRail.tsx +44 -0
- package/src/client/app/RangeControl.tsx +39 -0
- package/src/client/app/SyncButton.tsx +75 -0
- package/src/client/app/ThemeToggle.tsx +37 -0
- package/src/client/app/TopBar.tsx +73 -0
- package/src/client/app/routes.ts +69 -0
- package/src/client/components/AgentTokenShare.tsx +68 -0
- package/src/client/components/chart-shared.tsx +257 -0
- package/src/client/components/models-table-shared.tsx +255 -0
- package/src/client/components/range-meta.ts +73 -0
- package/src/client/css.d.ts +1 -0
- package/src/client/data/charts.ts +14 -0
- package/src/client/data/formatters.ts +45 -0
- package/src/client/data/useHashRoute.ts +87 -0
- package/src/client/data/useResource.ts +154 -0
- package/src/client/data/view-models.ts +251 -0
- package/src/client/index.tsx +10 -0
- package/src/client/routes/BehaviorRoute.tsx +623 -0
- package/src/client/routes/CostsRoute.tsx +234 -0
- package/src/client/routes/ErrorsRoute.tsx +118 -0
- package/src/client/routes/GainRoute.tsx +226 -0
- package/src/client/routes/ModelsRoute.tsx +430 -0
- package/src/client/routes/OverviewRoute.tsx +342 -0
- package/src/client/routes/ProjectsRoute.tsx +163 -0
- package/src/client/routes/RequestsRoute.tsx +123 -0
- package/src/client/routes/ToolsRoute.tsx +463 -0
- package/src/client/routes/index.ts +9 -0
- package/src/client/styles.css +1330 -0
- package/src/client/types.ts +80 -0
- package/src/client/ui/AsyncBoundary.tsx +54 -0
- package/src/client/ui/DataTable.tsx +122 -0
- package/src/client/ui/EmptyState.tsx +16 -0
- package/src/client/ui/ErrorState.tsx +25 -0
- package/src/client/ui/JsonBlock.tsx +75 -0
- package/src/client/ui/MetricCluster.tsx +67 -0
- package/src/client/ui/Panel.tsx +24 -0
- package/src/client/ui/RequestDrawer.tsx +208 -0
- package/src/client/ui/SegmentedControl.tsx +36 -0
- package/src/client/ui/Skeleton.tsx +17 -0
- package/src/client/ui/StatusPill.tsx +15 -0
- package/src/client/ui/index.ts +11 -0
- package/src/client/useSystemTheme.ts +87 -0
- package/src/db.ts +1530 -0
- package/src/embedded-client.generated.txt +0 -0
- package/src/embedded-client.ts +26 -0
- package/src/gain-aggregator.ts +281 -0
- package/src/index.ts +194 -0
- package/src/parser.ts +450 -0
- package/src/server.ts +352 -0
- package/src/shared-types.ts +323 -0
- package/src/sync-worker.ts +40 -0
- package/src/types.ts +171 -0
- package/src/user-metrics.ts +685 -0
- package/tailwind.config.js +40 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { BehaviorDashboardStats, DashboardStats, MessageStats, RequestDetails, ToolDashboardStats } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Progress event emitted after each session file is fully processed.
|
|
4
|
+
* `current` is the number of files completed (skipped + parsed),
|
|
5
|
+
* `total` is the size of the work set. `processed` is the running total
|
|
6
|
+
* of inserted rows.
|
|
7
|
+
*/
|
|
8
|
+
export interface SyncProgress {
|
|
9
|
+
current: number;
|
|
10
|
+
total: number;
|
|
11
|
+
processed: number;
|
|
12
|
+
sessionFile: string;
|
|
13
|
+
}
|
|
14
|
+
export interface SyncOptions {
|
|
15
|
+
/** Called after each file completes. Synchronous; keep it cheap. */
|
|
16
|
+
onProgress?: (event: SyncProgress) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Worker pool size. Defaults to a sensible value derived from the host
|
|
19
|
+
* (capped to avoid drowning a small machine in workers). Set to `1` to
|
|
20
|
+
* force serial parsing without spawning workers.
|
|
21
|
+
*/
|
|
22
|
+
workers?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Smoke test: spawns one sync worker, pings it, asserts the pong response,
|
|
26
|
+
* then terminates. Used by `omp --smoke-test` so the install-method CI jobs
|
|
27
|
+
* catch the silent worker-load failure that hit compiled binaries in #1011
|
|
28
|
+
* and #1027 — neither `--version` nor `stats --summary` exercises the worker
|
|
29
|
+
* spawn path on a fresh install (no session files = early return), so a
|
|
30
|
+
* dedicated probe is the only reliable signal.
|
|
31
|
+
*
|
|
32
|
+
* No-op on darwin: `syncAllSessions` keeps macOS on the serial parser path
|
|
33
|
+
* (see {@link defaultWorkerCount}) so the worker spawn surface is unreachable
|
|
34
|
+
* from the CLI, and probing it under the hardened runtime in
|
|
35
|
+
* `scripts/ci-macos-sign.sh` would re-enter the Bun-worker abort surface that
|
|
36
|
+
* motivated the darwin serial default in the first place.
|
|
37
|
+
*
|
|
38
|
+
* Rejects on transport error, error response, or timeout.
|
|
39
|
+
*/
|
|
40
|
+
export declare function smokeTestSyncWorker({ timeoutMs }?: {
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Sync all session files to the database.
|
|
45
|
+
*
|
|
46
|
+
* `workers: 1` parses inline. Larger pools fan parsing out across workers
|
|
47
|
+
* (one in-flight job per worker) while DB writes and offset bookkeeping stay on
|
|
48
|
+
* the calling thread so the single SQLite handle stays uncontended.
|
|
49
|
+
* `onProgress` fires once per completed file (skipped files included so the
|
|
50
|
+
* bar walks at a steady rate).
|
|
51
|
+
*/
|
|
52
|
+
export declare function syncAllSessions(opts?: SyncOptions): Promise<{
|
|
53
|
+
processed: number;
|
|
54
|
+
files: number;
|
|
55
|
+
}>;
|
|
56
|
+
interface TimeRangeConfig {
|
|
57
|
+
timeSeriesHours: number;
|
|
58
|
+
timeSeriesBucketMs: number;
|
|
59
|
+
modelSeriesDays: number;
|
|
60
|
+
modelSeriesBucketMs: number;
|
|
61
|
+
modelPerformanceDays: number;
|
|
62
|
+
modelPerformanceBucketMs: number;
|
|
63
|
+
costSeriesDays: number;
|
|
64
|
+
cutoff: number | null;
|
|
65
|
+
}
|
|
66
|
+
export declare function getTimeRangeConfig(range?: string | null): TimeRangeConfig;
|
|
67
|
+
/**
|
|
68
|
+
* Get all dashboard stats.
|
|
69
|
+
*/
|
|
70
|
+
export declare function getDashboardStats(range?: string | null): Promise<DashboardStats>;
|
|
71
|
+
export declare function getOverviewStats(range?: string | null): Promise<Pick<DashboardStats, "overall" | "byAgentType" | "timeSeries">>;
|
|
72
|
+
export declare function getModelDashboardStats(range?: string | null): Promise<Pick<DashboardStats, "byModel" | "modelSeries" | "modelPerformanceSeries">>;
|
|
73
|
+
export declare function getCostDashboardStats(range?: string | null): Promise<Pick<DashboardStats, "costSeries">>;
|
|
74
|
+
export declare function getRecentRequests(limit?: number): Promise<MessageStats[]>;
|
|
75
|
+
export declare function getRecentErrors(limit?: number): Promise<MessageStats[]>;
|
|
76
|
+
export declare function getRequestDetails(id: number): Promise<RequestDetails | null>;
|
|
77
|
+
/**
|
|
78
|
+
* Get the current message count in the database.
|
|
79
|
+
*/
|
|
80
|
+
export declare function getTotalMessageCount(): Promise<number>;
|
|
81
|
+
export declare function getBehaviorDashboardStats(range?: string | null): Promise<BehaviorDashboardStats>;
|
|
82
|
+
/**
|
|
83
|
+
* Get the tools dashboard payload: per-tool totals, per-(tool, model)
|
|
84
|
+
* breakdown, and the call time series (bucketed like the model series).
|
|
85
|
+
*/
|
|
86
|
+
export declare function getToolDashboardStats(range?: string | null): Promise<ToolDashboardStats>;
|
|
87
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function App(): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BehaviorDashboardStats, CostDashboardStats, FolderStats, GainDashboardStats, MessageStats, ModelDashboardStats, OverviewStats, RequestDetails, TimeRange, ToolDashboardStats } from "./types";
|
|
2
|
+
export declare class ApiError extends Error {
|
|
3
|
+
status: number;
|
|
4
|
+
endpoint: string;
|
|
5
|
+
constructor(status: number, endpoint: string, message: string);
|
|
6
|
+
}
|
|
7
|
+
export declare function getOverviewStats(range?: TimeRange, signal?: AbortSignal): Promise<OverviewStats>;
|
|
8
|
+
export declare function getModelDashboardStats(range?: TimeRange, signal?: AbortSignal): Promise<ModelDashboardStats>;
|
|
9
|
+
export declare function getCostDashboardStats(range?: TimeRange, signal?: AbortSignal): Promise<CostDashboardStats>;
|
|
10
|
+
export declare function getRecentRequests(limit?: number, signal?: AbortSignal): Promise<MessageStats[]>;
|
|
11
|
+
export declare function getRecentErrors(limit?: number, signal?: AbortSignal): Promise<MessageStats[]>;
|
|
12
|
+
export declare function getRequestDetails(id: number, signal?: AbortSignal): Promise<RequestDetails>;
|
|
13
|
+
export declare function sync(signal?: AbortSignal): Promise<{
|
|
14
|
+
processed: number;
|
|
15
|
+
files: number;
|
|
16
|
+
totalMessages: number;
|
|
17
|
+
}>;
|
|
18
|
+
export declare function getBehaviorDashboardStats(range?: TimeRange, signal?: AbortSignal): Promise<BehaviorDashboardStats>;
|
|
19
|
+
export declare function getFolderStats(range?: TimeRange, signal?: AbortSignal): Promise<FolderStats[]>;
|
|
20
|
+
export declare function getGainDashboardStats(range?: TimeRange, project?: string | null, signal?: AbortSignal): Promise<GainDashboardStats>;
|
|
21
|
+
export declare function getToolDashboardStats(range?: TimeRange, signal?: AbortSignal): Promise<ToolDashboardStats>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import type { TimeRange } from "../types";
|
|
3
|
+
import type { DashboardSection } from "./routes";
|
|
4
|
+
export interface AppLayoutProps {
|
|
5
|
+
activeSection: DashboardSection;
|
|
6
|
+
onSectionChange: (section: DashboardSection) => void;
|
|
7
|
+
range: TimeRange;
|
|
8
|
+
onRangeChange: (range: TimeRange) => void;
|
|
9
|
+
updatedAt: number | null;
|
|
10
|
+
onSyncStart?: () => void;
|
|
11
|
+
onSyncComplete?: (result: {
|
|
12
|
+
success: boolean;
|
|
13
|
+
}) => void;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export declare function AppLayout({ activeSection, onSectionChange, range, onRangeChange, updatedAt, onSyncStart, onSyncComplete, children, }: AppLayoutProps): React.JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type DashboardSection } from "./routes";
|
|
2
|
+
export interface NavRailProps {
|
|
3
|
+
activeSection: DashboardSection;
|
|
4
|
+
onSectionChange: (section: DashboardSection) => void;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function NavRail({ activeSection, onSectionChange, className }: NavRailProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TimeRange } from "../types";
|
|
2
|
+
export interface RangeControlProps {
|
|
3
|
+
value: TimeRange;
|
|
4
|
+
onChange: (value: TimeRange) => void;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function RangeControl({ value, onChange, className }: RangeControlProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface SyncButtonProps {
|
|
2
|
+
onSyncStart?: () => void;
|
|
3
|
+
onSyncComplete?: (result: {
|
|
4
|
+
success: boolean;
|
|
5
|
+
data?: {
|
|
6
|
+
processed: number;
|
|
7
|
+
files: number;
|
|
8
|
+
totalMessages: number;
|
|
9
|
+
};
|
|
10
|
+
error?: string;
|
|
11
|
+
}) => void;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function SyncButton({ onSyncStart, onSyncComplete, className }: SyncButtonProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ThemeToggle(): import("react").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TimeRange } from "../types";
|
|
2
|
+
import type { DashboardSection } from "./routes";
|
|
3
|
+
export interface TopBarProps {
|
|
4
|
+
activeSection: DashboardSection;
|
|
5
|
+
range: TimeRange;
|
|
6
|
+
onRangeChange: (range: TimeRange) => void;
|
|
7
|
+
updatedAt: number | null;
|
|
8
|
+
onSyncStart?: () => void;
|
|
9
|
+
onSyncComplete?: (result: {
|
|
10
|
+
success: boolean;
|
|
11
|
+
}) => void;
|
|
12
|
+
onMenuToggle?: () => void;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function TopBar({ activeSection, range, onRangeChange, updatedAt, onSyncStart, onSyncComplete, onMenuToggle, className, }: TopBarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
export type DashboardSection = "overview" | "requests" | "errors" | "models" | "tools" | "costs" | "behavior" | "projects" | "gain";
|
|
3
|
+
export interface DashboardRoute {
|
|
4
|
+
id: DashboardSection;
|
|
5
|
+
label: string;
|
|
6
|
+
shortLabel?: string;
|
|
7
|
+
icon: React.ComponentType<{
|
|
8
|
+
size?: number;
|
|
9
|
+
className?: string;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export declare const routes: DashboardRoute[];
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared chart primitives for the dashboard timeline charts: the OMP color
|
|
3
|
+
* palette, light/dark chart chrome, legend/tooltip + scale plumbing, dataset
|
|
4
|
+
* styling, and the top-N-by-model / aggregate bucketing used by the cost and
|
|
5
|
+
* behavior series.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MODEL_COLORS: string[];
|
|
8
|
+
export declare const CHART_THEMES: {
|
|
9
|
+
readonly dark: {
|
|
10
|
+
readonly legendLabel: "#a89fb3";
|
|
11
|
+
readonly tooltipBackground: "#241a2e";
|
|
12
|
+
readonly tooltipTitle: "#eae5ef";
|
|
13
|
+
readonly tooltipBody: "#a89fb3";
|
|
14
|
+
readonly tooltipBorder: "rgba(255, 255, 255, 0.12)";
|
|
15
|
+
readonly grid: "rgba(255, 255, 255, 0.06)";
|
|
16
|
+
readonly tick: "#867a93";
|
|
17
|
+
};
|
|
18
|
+
readonly light: {
|
|
19
|
+
readonly legendLabel: "#5a5462";
|
|
20
|
+
readonly tooltipBackground: "#ffffff";
|
|
21
|
+
readonly tooltipTitle: "#241a2e";
|
|
22
|
+
readonly tooltipBody: "#5a5462";
|
|
23
|
+
readonly tooltipBorder: "rgba(20, 12, 28, 0.15)";
|
|
24
|
+
readonly grid: "rgba(20, 12, 28, 0.08)";
|
|
25
|
+
readonly tick: "#6a6275";
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export type ChartTheme = (typeof CHART_THEMES)[keyof typeof CHART_THEMES];
|
|
29
|
+
export interface ChartSeries {
|
|
30
|
+
labels: string[];
|
|
31
|
+
datasets: Array<{
|
|
32
|
+
label: string;
|
|
33
|
+
data: number[];
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
interface TooltipItem {
|
|
37
|
+
parsed: {
|
|
38
|
+
y: number | null;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Tooltip + legend config common to bar and line variants of the time charts. */
|
|
42
|
+
export declare function buildSharedPlugins(opts: {
|
|
43
|
+
chartTheme: ChartTheme;
|
|
44
|
+
showLegend: boolean;
|
|
45
|
+
defaultLabel: string;
|
|
46
|
+
formatValue: (n: number) => string;
|
|
47
|
+
footer?: (items: TooltipItem[]) => string | undefined;
|
|
48
|
+
}): {
|
|
49
|
+
legend: {
|
|
50
|
+
display: boolean;
|
|
51
|
+
position: "top";
|
|
52
|
+
align: "start";
|
|
53
|
+
labels: {
|
|
54
|
+
color: "#5a5462" | "#a89fb3";
|
|
55
|
+
usePointStyle: boolean;
|
|
56
|
+
padding: number;
|
|
57
|
+
font: {
|
|
58
|
+
size: number;
|
|
59
|
+
};
|
|
60
|
+
boxWidth: number;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
tooltip: {
|
|
64
|
+
backgroundColor: "#241a2e" | "#ffffff";
|
|
65
|
+
titleColor: "#241a2e" | "#eae5ef";
|
|
66
|
+
bodyColor: "#5a5462" | "#a89fb3";
|
|
67
|
+
borderColor: "rgba(20, 12, 28, 0.15)" | "rgba(255, 255, 255, 0.12)";
|
|
68
|
+
borderWidth: number;
|
|
69
|
+
padding: number;
|
|
70
|
+
cornerRadius: number;
|
|
71
|
+
callbacks: {
|
|
72
|
+
label: (ctx: {
|
|
73
|
+
dataset: {
|
|
74
|
+
label?: string;
|
|
75
|
+
};
|
|
76
|
+
parsed: {
|
|
77
|
+
y: number | null;
|
|
78
|
+
};
|
|
79
|
+
}) => string;
|
|
80
|
+
footer?: ((items: TooltipItem[]) => string | undefined) | undefined;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
/** Y-axis tick formatter + grid/tick styling shared by both charts. */
|
|
85
|
+
export declare function buildSharedScales(opts: {
|
|
86
|
+
chartTheme: ChartTheme;
|
|
87
|
+
formatY: (n: number) => string;
|
|
88
|
+
}): {
|
|
89
|
+
sharedScaleBase: {
|
|
90
|
+
grid: {
|
|
91
|
+
color: "rgba(20, 12, 28, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
92
|
+
drawBorder: boolean;
|
|
93
|
+
};
|
|
94
|
+
ticks: {
|
|
95
|
+
color: "#6a6275" | "#867a93";
|
|
96
|
+
font: {
|
|
97
|
+
size: number;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
yScale: {
|
|
102
|
+
grid: {
|
|
103
|
+
color: "rgba(20, 12, 28, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
104
|
+
drawBorder: boolean;
|
|
105
|
+
};
|
|
106
|
+
ticks: {
|
|
107
|
+
color: "#6a6275" | "#867a93";
|
|
108
|
+
font: {
|
|
109
|
+
size: number;
|
|
110
|
+
};
|
|
111
|
+
callback: (value: number | string) => string;
|
|
112
|
+
};
|
|
113
|
+
min: number;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
/** Stylistic defaults for a single line dataset in a stacked/by-model chart. */
|
|
117
|
+
export declare function lineDatasetStyle(color: string): {
|
|
118
|
+
borderColor: string;
|
|
119
|
+
backgroundColor: string;
|
|
120
|
+
fill: boolean;
|
|
121
|
+
tension: number;
|
|
122
|
+
pointRadius: number;
|
|
123
|
+
pointHoverRadius: number;
|
|
124
|
+
borderWidth: number;
|
|
125
|
+
};
|
|
126
|
+
/** Stylistic defaults for a single bar dataset in a stacked chart. */
|
|
127
|
+
export declare function barDatasetStyle(color: string): {
|
|
128
|
+
backgroundColor: string;
|
|
129
|
+
borderColor: string;
|
|
130
|
+
borderWidth: number;
|
|
131
|
+
borderRadius: number;
|
|
132
|
+
maxBarThickness: number;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Map a generic ChartSeries' datasets through a per-index style function so
|
|
136
|
+
* callers can supply line or bar styling without repeating the label/data
|
|
137
|
+
* spread at every chart site.
|
|
138
|
+
*/
|
|
139
|
+
export declare function styleDatasets(series: ChartSeries, styleFor: (index: number) => Record<string, unknown>): {
|
|
140
|
+
label: string;
|
|
141
|
+
data: number[];
|
|
142
|
+
}[];
|
|
143
|
+
/**
|
|
144
|
+
* Bucket points by day into a single aggregate series. Caller supplies the
|
|
145
|
+
* per-bucket accumulator + final value extractor; mirrors the shape of
|
|
146
|
+
* `buildTopNByModelSeries` for the non-by-model variant of each time chart.
|
|
147
|
+
*/
|
|
148
|
+
export declare function buildAggregateTimeSeries<T extends {
|
|
149
|
+
timestamp: number;
|
|
150
|
+
}, B>(points: T[], label: string, opts: {
|
|
151
|
+
initBucket: () => B;
|
|
152
|
+
accumulate: (bucket: B, point: T) => void;
|
|
153
|
+
bucketToValue: (bucket: B) => number;
|
|
154
|
+
}): ChartSeries;
|
|
155
|
+
interface ModelKeyedPoint {
|
|
156
|
+
timestamp: number;
|
|
157
|
+
model: string;
|
|
158
|
+
provider: string;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Bucket points by day and by top-N model (with an "Other" rollup), producing
|
|
162
|
+
* a ChartSeries. Caller controls how points contribute to ranking and to each
|
|
163
|
+
* day-bucket value via the `rankWeight`/`accumulate`/`bucketToValue` callbacks
|
|
164
|
+
* — keeps the behavior chart's rate math separate from the cost chart's sum.
|
|
165
|
+
*/
|
|
166
|
+
export declare function buildTopNByModelSeries<T extends ModelKeyedPoint, B>(points: T[], opts: {
|
|
167
|
+
topN?: number;
|
|
168
|
+
rankWeight: (point: T) => number;
|
|
169
|
+
initBucket: () => B;
|
|
170
|
+
accumulate: (bucket: B, point: T) => void;
|
|
171
|
+
bucketToValue: (bucket: B) => number;
|
|
172
|
+
}): ChartSeries;
|
|
173
|
+
export {};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared primitives for the per-model breakdown tables. Each table owns its
|
|
3
|
+
* column definitions, sort order, and chart type — this module owns the
|
|
4
|
+
* surface chrome, expand-row plumbing, theme palette, the mini-sparkline, and
|
|
5
|
+
* the shared plugin/scale config consumed by multi-line detail charts.
|
|
6
|
+
*/
|
|
7
|
+
import type { ChartTheme } from "./chart-shared";
|
|
8
|
+
export { CHART_THEMES as TABLE_CHART_THEMES, MODEL_COLORS } from "./chart-shared";
|
|
9
|
+
export type TableChartTheme = ChartTheme;
|
|
10
|
+
/** Style defaults for one line in a non-stacked detail chart. */
|
|
11
|
+
export declare function lineSeriesStyle(color: string): {
|
|
12
|
+
borderColor: string;
|
|
13
|
+
backgroundColor: string;
|
|
14
|
+
tension: number;
|
|
15
|
+
pointRadius: number;
|
|
16
|
+
borderWidth: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* No-axis, no-legend single-series sparkline used in the trend cell of every
|
|
20
|
+
* model row. Caller supplies the already-extracted numeric series so this
|
|
21
|
+
* stays agnostic of the row's underlying data shape.
|
|
22
|
+
*/
|
|
23
|
+
export declare function MiniSparkline({ timestamps, values, color, }: {
|
|
24
|
+
timestamps: number[];
|
|
25
|
+
values: number[];
|
|
26
|
+
color: string;
|
|
27
|
+
}): import("react").JSX.Element;
|
|
28
|
+
/**
|
|
29
|
+
* Plugin block (legend + tooltip) shared by every multi-series detail chart
|
|
30
|
+
* in the table expanded views.
|
|
31
|
+
*/
|
|
32
|
+
export declare function detailChartPlugins(chartTheme: TableChartTheme): {
|
|
33
|
+
legend: {
|
|
34
|
+
display: boolean;
|
|
35
|
+
position: "top";
|
|
36
|
+
labels: {
|
|
37
|
+
color: "#5a5462" | "#a89fb3";
|
|
38
|
+
usePointStyle: boolean;
|
|
39
|
+
padding: number;
|
|
40
|
+
font: {
|
|
41
|
+
size: number;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
tooltip: {
|
|
46
|
+
backgroundColor: "#241a2e" | "#ffffff";
|
|
47
|
+
titleColor: "#241a2e" | "#eae5ef";
|
|
48
|
+
bodyColor: "#5a5462" | "#a89fb3";
|
|
49
|
+
borderColor: "rgba(20, 12, 28, 0.15)" | "rgba(255, 255, 255, 0.12)";
|
|
50
|
+
borderWidth: number;
|
|
51
|
+
cornerRadius: number;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Single-Y-axis scales for a detail chart (used when every series shares a
|
|
56
|
+
* unit, e.g. behavior counts). Min anchored at 0.
|
|
57
|
+
*/
|
|
58
|
+
export declare function detailChartScalesSingleAxis(chartTheme: TableChartTheme): {
|
|
59
|
+
x: {
|
|
60
|
+
grid: {
|
|
61
|
+
color: "rgba(20, 12, 28, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
62
|
+
};
|
|
63
|
+
ticks: {
|
|
64
|
+
color: "#6a6275" | "#867a93";
|
|
65
|
+
font: {
|
|
66
|
+
size: number;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
y: {
|
|
71
|
+
grid: {
|
|
72
|
+
color: "rgba(20, 12, 28, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
73
|
+
};
|
|
74
|
+
ticks: {
|
|
75
|
+
color: "#6a6275" | "#867a93";
|
|
76
|
+
font: {
|
|
77
|
+
size: number;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
min: number;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Dual-Y-axis scales for a detail chart with mixed units (e.g. TTFT seconds
|
|
85
|
+
* on left, tokens/s on right). Right-axis grid is suppressed so it doesn't
|
|
86
|
+
* collide with the left.
|
|
87
|
+
*/
|
|
88
|
+
export declare function detailChartScalesDualAxis(chartTheme: TableChartTheme): {
|
|
89
|
+
x: {
|
|
90
|
+
grid: {
|
|
91
|
+
color: "rgba(20, 12, 28, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
92
|
+
};
|
|
93
|
+
ticks: {
|
|
94
|
+
color: "#6a6275" | "#867a93";
|
|
95
|
+
font: {
|
|
96
|
+
size: number;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
y: {
|
|
101
|
+
type: "linear";
|
|
102
|
+
display: boolean;
|
|
103
|
+
position: "left";
|
|
104
|
+
grid: {
|
|
105
|
+
color: "rgba(20, 12, 28, 0.08)" | "rgba(255, 255, 255, 0.06)";
|
|
106
|
+
};
|
|
107
|
+
ticks: {
|
|
108
|
+
color: "#6a6275" | "#867a93";
|
|
109
|
+
font: {
|
|
110
|
+
size: number;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
y1: {
|
|
115
|
+
type: "linear";
|
|
116
|
+
display: boolean;
|
|
117
|
+
position: "right";
|
|
118
|
+
grid: {
|
|
119
|
+
drawOnChartArea: boolean;
|
|
120
|
+
};
|
|
121
|
+
ticks: {
|
|
122
|
+
color: "#6a6275" | "#867a93";
|
|
123
|
+
font: {
|
|
124
|
+
size: number;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
export interface TableColumn {
|
|
130
|
+
label: string;
|
|
131
|
+
align?: "left" | "right" | "center";
|
|
132
|
+
}
|
|
133
|
+
/** Outer card + section title used by every model table. */
|
|
134
|
+
export declare function ModelTableShell({ title, subtitle, children, }: {
|
|
135
|
+
title: string;
|
|
136
|
+
subtitle?: string;
|
|
137
|
+
children: React.ReactNode;
|
|
138
|
+
}): import("react").JSX.Element;
|
|
139
|
+
/** Sticky column-header row for a model table. */
|
|
140
|
+
export declare function ModelTableHeader({ columns, gridTemplate }: {
|
|
141
|
+
columns: TableColumn[];
|
|
142
|
+
gridTemplate: string;
|
|
143
|
+
}): import("react").JSX.Element;
|
|
144
|
+
/** Scroll wrapper for the row stack — capped to fit the dashboard viewport. */
|
|
145
|
+
export declare function ModelTableBody({ children }: {
|
|
146
|
+
children: React.ReactNode;
|
|
147
|
+
}): import("react").JSX.Element;
|
|
148
|
+
/**
|
|
149
|
+
* Two-line model identity cell (model name + provider) shared by every
|
|
150
|
+
* per-model table. Kept as a stable named contract so callers don't restate
|
|
151
|
+
* the same two divs and font-utility classes.
|
|
152
|
+
*/
|
|
153
|
+
export declare function ModelNameCell({ model, provider }: {
|
|
154
|
+
model: string;
|
|
155
|
+
provider: string;
|
|
156
|
+
}): import("react").JSX.Element;
|
|
157
|
+
/**
|
|
158
|
+
* One expandable model row. `cells` matches the column order from
|
|
159
|
+
* `ModelTableHeader` plus the trend cell at the end (caller controls the
|
|
160
|
+
* sparkline / placeholder). `expandedContent` is the panel revealed on toggle.
|
|
161
|
+
*/
|
|
162
|
+
export declare function ExpandableModelRow({ gridTemplate, cells, trendCell, isExpanded, onToggle, expandedContent, }: {
|
|
163
|
+
gridTemplate: string;
|
|
164
|
+
cells: React.ReactNode[];
|
|
165
|
+
trendCell: React.ReactNode;
|
|
166
|
+
isExpanded: boolean;
|
|
167
|
+
onToggle: () => void;
|
|
168
|
+
expandedContent: React.ReactNode;
|
|
169
|
+
}): import("react").JSX.Element;
|
|
170
|
+
/** Placeholder shown in the trend cell when a model has no time-series data. */
|
|
171
|
+
export declare function TrendEmpty(): import("react").JSX.Element;
|
|
172
|
+
/** Placeholder shown in the expanded detail-chart slot when data is missing. */
|
|
173
|
+
export declare function DetailChartEmpty({ message }: {
|
|
174
|
+
message?: string;
|
|
175
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display metadata for a `TimeRange` — keeps chart labels, sparkline bucket
|
|
3
|
+
* counts, and x-axis date formatting in sync with the server-side bucketing
|
|
4
|
+
* defined in `aggregator.ts`.
|
|
5
|
+
*/
|
|
6
|
+
import type { TimeRange } from "../types";
|
|
7
|
+
export interface RangeMeta {
|
|
8
|
+
/** Human label used in chart subtitles ("the last 24 hours"). */
|
|
9
|
+
windowLabel: string;
|
|
10
|
+
/** Short prefix used in compact column headers ("24h Trend"). */
|
|
11
|
+
trendLabel: string;
|
|
12
|
+
/** Bucket size matching the server query for this range. */
|
|
13
|
+
bucketMs: number;
|
|
14
|
+
/** Number of buckets the server is expected to return for this range. */
|
|
15
|
+
bucketCount: number;
|
|
16
|
+
/** date-fns format string for x-axis labels and tooltip headings. */
|
|
17
|
+
tickFormat: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function rangeMeta(range: TimeRange): RangeMeta;
|
|
20
|
+
/** Format a bucket timestamp using the active range's tick format. */
|
|
21
|
+
export declare function formatRangeTick(timestamp: number, range: TimeRange): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function formatInteger(value: number): string;
|
|
2
|
+
export declare function formatCompact(value: number): string;
|
|
3
|
+
export declare function formatCost(value: number, digits?: number): string;
|
|
4
|
+
export declare function formatPercent(value: number, digits?: number): string;
|
|
5
|
+
export declare function formatDurationMs(value: number | null, digits?: number): string;
|
|
6
|
+
export declare function formatTokensPerSecond(value: number | null): string;
|
|
7
|
+
export declare function formatRelativeTime(timestamp: number): string;
|
|
8
|
+
export declare function formatBytes(value: number): string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DashboardSection } from "../app/routes";
|
|
2
|
+
import type { TimeRange } from "../types";
|
|
3
|
+
export declare function useHashRoute(): {
|
|
4
|
+
section: DashboardSection;
|
|
5
|
+
setSection: (newSection: DashboardSection) => void;
|
|
6
|
+
range: TimeRange;
|
|
7
|
+
setRange: (newRange: string) => void;
|
|
8
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ResourceResult<T> {
|
|
2
|
+
data: T | null;
|
|
3
|
+
error: Error | null;
|
|
4
|
+
loading: boolean;
|
|
5
|
+
refreshing: boolean;
|
|
6
|
+
refetch: () => Promise<void>;
|
|
7
|
+
updatedAt: number | null;
|
|
8
|
+
}
|
|
9
|
+
export interface ResourceOptions {
|
|
10
|
+
pollMs?: number;
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function useResource<T>(key: readonly unknown[], fetcher: (signal: AbortSignal) => Promise<T>, options?: ResourceOptions): ResourceResult<T>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { AgentType, AgentTypeStats, BehaviorOverallStats, BehaviorTimeSeriesPoint, CostTimeSeriesPoint, FolderStats, ModelPerformancePoint, TimeRange, ToolUsageStats } from "../types";
|
|
2
|
+
export interface AgentTokenSegment {
|
|
3
|
+
agentType: AgentType;
|
|
4
|
+
/** input + output + cache read + cache write — the displayed denominator. */
|
|
5
|
+
tokens: number;
|
|
6
|
+
requests: number;
|
|
7
|
+
cost: number;
|
|
8
|
+
/** Fraction (0-1) of total tokens across all present agent types. */
|
|
9
|
+
share: number;
|
|
10
|
+
}
|
|
11
|
+
export interface AgentTokenShareView {
|
|
12
|
+
totalTokens: number;
|
|
13
|
+
totalCost: number;
|
|
14
|
+
segments: AgentTokenSegment[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build the "token usage by agent" breakdown: one segment per agent type that
|
|
18
|
+
* appears in the data, ordered main -> subagents -> advisor, each carrying its
|
|
19
|
+
* token total and share of the grand total. Token counts sum the same four
|
|
20
|
+
* columns the overview renders (input + output + cache read + cache write) so a
|
|
21
|
+
* segment's share never disagrees with the count beside it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildAgentTokenShare(stats: AgentTypeStats[]): AgentTokenShareView;
|
|
24
|
+
export interface CostSummaryView {
|
|
25
|
+
totalCost: number;
|
|
26
|
+
avgDailyCost: number;
|
|
27
|
+
topModelName: string;
|
|
28
|
+
topModelCost: number;
|
|
29
|
+
}
|
|
30
|
+
export interface ModelPerformanceDataPoint {
|
|
31
|
+
timestamp: number;
|
|
32
|
+
avgTtftSeconds: number | null;
|
|
33
|
+
avgTokensPerSecond: number | null;
|
|
34
|
+
requests: number;
|
|
35
|
+
}
|
|
36
|
+
export interface ModelPerformanceSeries {
|
|
37
|
+
label: string;
|
|
38
|
+
data: ModelPerformanceDataPoint[];
|
|
39
|
+
}
|
|
40
|
+
export interface BehaviorSummaryView {
|
|
41
|
+
totalMessages: number;
|
|
42
|
+
totalYelling: number;
|
|
43
|
+
totalProfanity: number;
|
|
44
|
+
totalAnguish: number;
|
|
45
|
+
totalFrustration: number;
|
|
46
|
+
highestFrictionModel: {
|
|
47
|
+
model: string;
|
|
48
|
+
provider: string;
|
|
49
|
+
score: number;
|
|
50
|
+
} | null;
|
|
51
|
+
}
|
|
52
|
+
export interface FolderRowView extends FolderStats {
|
|
53
|
+
costPercentage: number;
|
|
54
|
+
requestsPercentage: number;
|
|
55
|
+
}
|
|
56
|
+
export declare function buildCostSummary(costSeries: CostTimeSeriesPoint[]): CostSummaryView;
|
|
57
|
+
export declare function buildModelPerformanceLookup(points: ModelPerformancePoint[], range: TimeRange): Map<string, ModelPerformanceSeries>;
|
|
58
|
+
export declare function buildBehaviorSummary(overall: BehaviorOverallStats, series: BehaviorTimeSeriesPoint[]): BehaviorSummaryView;
|
|
59
|
+
export declare function buildFolderRows(folders: FolderStats[]): FolderRowView[];
|
|
60
|
+
/** Table row for the Tools route: usage stats plus derived rates/shares. */
|
|
61
|
+
export interface ToolRowView extends ToolUsageStats {
|
|
62
|
+
/** errors / calls (0 for zero calls). */
|
|
63
|
+
errorRate: number;
|
|
64
|
+
/** Calls relative to the busiest tool, 0-100, for the share bar. */
|
|
65
|
+
callsPercentage: number;
|
|
66
|
+
}
|
|
67
|
+
export declare function buildToolRows(tools: ToolUsageStats[]): ToolRowView[];
|