@oh-my-pi/omp-stats 16.0.4 → 16.0.6

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/build.ts +11 -0
  3. package/dist/client/index.css +1 -1
  4. package/dist/client/index.html +11 -0
  5. package/dist/client/index.js +108 -108
  6. package/dist/client/styles.css +1070 -631
  7. package/dist/types/client/api.d.ts +19 -10
  8. package/dist/types/client/app/AppLayout.d.ts +16 -0
  9. package/dist/types/client/app/NavRail.d.ts +7 -0
  10. package/dist/types/client/app/RangeControl.d.ts +7 -0
  11. package/dist/types/client/app/SyncButton.d.ts +14 -0
  12. package/dist/types/client/app/ThemeToggle.d.ts +1 -0
  13. package/dist/types/client/app/TopBar.d.ts +15 -0
  14. package/dist/types/client/app/routes.d.ts +12 -0
  15. package/dist/types/client/components/chart-shared.d.ts +26 -40
  16. package/dist/types/client/components/models-table-shared.d.ts +20 -40
  17. package/dist/types/client/data/charts.d.ts +1 -0
  18. package/dist/types/client/data/formatters.d.ts +7 -0
  19. package/dist/types/client/data/useHashRoute.d.ts +8 -0
  20. package/dist/types/client/data/useResource.d.ts +13 -0
  21. package/dist/types/client/data/view-models.d.ts +37 -0
  22. package/dist/types/client/index.d.ts +1 -0
  23. package/dist/types/client/routes/BehaviorRoute.d.ts +7 -0
  24. package/dist/types/client/routes/CostsRoute.d.ts +7 -0
  25. package/dist/types/client/routes/ErrorsRoute.d.ts +8 -0
  26. package/dist/types/client/routes/ModelsRoute.d.ts +7 -0
  27. package/dist/types/client/routes/OverviewRoute.d.ts +8 -0
  28. package/dist/types/client/routes/ProjectsRoute.d.ts +7 -0
  29. package/dist/types/client/routes/RequestsRoute.d.ts +8 -0
  30. package/dist/types/client/routes/index.d.ts +7 -0
  31. package/dist/types/client/ui/AsyncBoundary.d.ts +12 -0
  32. package/dist/types/client/ui/DataTable.d.ts +17 -0
  33. package/dist/types/client/ui/EmptyState.d.ts +7 -0
  34. package/dist/types/client/ui/ErrorState.d.ts +6 -0
  35. package/dist/types/client/ui/JsonBlock.d.ts +7 -0
  36. package/dist/types/client/ui/MetricCluster.d.ts +5 -0
  37. package/dist/types/client/ui/Panel.d.ts +7 -0
  38. package/dist/types/client/ui/RequestDrawer.d.ts +5 -0
  39. package/dist/types/client/ui/SegmentedControl.d.ts +12 -0
  40. package/dist/types/client/ui/Skeleton.d.ts +8 -0
  41. package/dist/types/client/ui/StatusPill.d.ts +7 -0
  42. package/dist/types/client/ui/index.d.ts +11 -0
  43. package/dist/types/client/useSystemTheme.d.ts +9 -0
  44. package/package.json +4 -4
  45. package/src/aggregator.ts +4 -3
  46. package/src/client/App.tsx +89 -207
  47. package/src/client/api.ts +55 -37
  48. package/src/client/app/AppLayout.tsx +93 -0
  49. package/src/client/app/NavRail.tsx +44 -0
  50. package/src/client/app/RangeControl.tsx +39 -0
  51. package/src/client/app/SyncButton.tsx +75 -0
  52. package/src/client/app/ThemeToggle.tsx +37 -0
  53. package/src/client/app/TopBar.tsx +73 -0
  54. package/src/client/app/routes.ts +50 -0
  55. package/src/client/components/chart-shared.tsx +28 -91
  56. package/src/client/components/models-table-shared.tsx +9 -29
  57. package/src/client/components/range-meta.ts +3 -2
  58. package/src/client/data/charts.ts +14 -0
  59. package/src/client/data/formatters.ts +38 -0
  60. package/src/client/data/useHashRoute.ts +85 -0
  61. package/src/client/data/useResource.ts +154 -0
  62. package/src/client/data/view-models.ts +178 -0
  63. package/src/client/index.tsx +4 -0
  64. package/src/client/routes/BehaviorRoute.tsx +623 -0
  65. package/src/client/routes/CostsRoute.tsx +234 -0
  66. package/src/client/routes/ErrorsRoute.tsx +118 -0
  67. package/src/client/routes/ModelsRoute.tsx +430 -0
  68. package/src/client/routes/OverviewRoute.tsx +332 -0
  69. package/src/client/routes/ProjectsRoute.tsx +163 -0
  70. package/src/client/routes/RequestsRoute.tsx +123 -0
  71. package/src/client/routes/index.ts +7 -0
  72. package/src/client/styles.css +1242 -225
  73. package/src/client/ui/AsyncBoundary.tsx +54 -0
  74. package/src/client/ui/DataTable.tsx +122 -0
  75. package/src/client/ui/EmptyState.tsx +16 -0
  76. package/src/client/ui/ErrorState.tsx +25 -0
  77. package/src/client/ui/JsonBlock.tsx +75 -0
  78. package/src/client/ui/MetricCluster.tsx +67 -0
  79. package/src/client/ui/Panel.tsx +24 -0
  80. package/src/client/ui/RequestDrawer.tsx +208 -0
  81. package/src/client/ui/SegmentedControl.tsx +36 -0
  82. package/src/client/ui/Skeleton.tsx +17 -0
  83. package/src/client/ui/StatusPill.tsx +15 -0
  84. package/src/client/ui/index.ts +11 -0
  85. package/src/client/useSystemTheme.ts +73 -17
  86. package/dist/types/client/components/BehaviorChart.d.ts +0 -6
  87. package/dist/types/client/components/BehaviorModelsTable.d.ts +0 -7
  88. package/dist/types/client/components/BehaviorSummary.d.ts +0 -7
  89. package/dist/types/client/components/ChartsContainer.d.ts +0 -7
  90. package/dist/types/client/components/CostChart.d.ts +0 -6
  91. package/dist/types/client/components/CostSummary.d.ts +0 -6
  92. package/dist/types/client/components/Header.d.ts +0 -12
  93. package/dist/types/client/components/ModelsTable.d.ts +0 -8
  94. package/dist/types/client/components/RequestDetail.d.ts +0 -6
  95. package/dist/types/client/components/RequestList.d.ts +0 -8
  96. package/dist/types/client/components/StatsGrid.d.ts +0 -6
  97. package/src/client/components/BehaviorChart.tsx +0 -189
  98. package/src/client/components/BehaviorModelsTable.tsx +0 -342
  99. package/src/client/components/BehaviorSummary.tsx +0 -95
  100. package/src/client/components/ChartsContainer.tsx +0 -221
  101. package/src/client/components/CostChart.tsx +0 -171
  102. package/src/client/components/CostSummary.tsx +0 -53
  103. package/src/client/components/Header.tsx +0 -72
  104. package/src/client/components/ModelsTable.tsx +0 -265
  105. package/src/client/components/RequestDetail.tsx +0 -172
  106. package/src/client/components/RequestList.tsx +0 -73
  107. package/src/client/components/StatsGrid.tsx +0 -135
@@ -1,172 +0,0 @@
1
- import { Clock, Coins, FileJson, Gauge, Hash, Star, X, Zap } from "lucide-react";
2
- import { useEffect, useState } from "react";
3
- import { getRequestDetails } from "../api";
4
- import type { RequestDetails } from "../types";
5
-
6
- interface RequestDetailProps {
7
- id: number;
8
- onClose: () => void;
9
- }
10
-
11
- export function RequestDetail({ id, onClose }: RequestDetailProps) {
12
- const [details, setDetails] = useState<RequestDetails | null>(null);
13
- const [loading, setLoading] = useState(true);
14
-
15
- useEffect(() => {
16
- getRequestDetails(id)
17
- .then(setDetails)
18
- .catch(console.error)
19
- .finally(() => setLoading(false));
20
- }, [id]);
21
-
22
- if (!details && loading) {
23
- return (
24
- <div className="fixed inset-0 bg-[var(--bg-overlay)] flex justify-center items-center z-[100]">
25
- <div className="surface px-8 py-6">
26
- <div className="flex items-center gap-3 text-[var(--text-secondary)]">
27
- <div className="w-5 h-5 border-2 border-[var(--border-default)] border-t-[var(--accent-cyan)] rounded-full spin" />
28
- <span>Loading...</span>
29
- </div>
30
- </div>
31
- </div>
32
- );
33
- }
34
-
35
- if (!details) return null;
36
-
37
- return (
38
- <div
39
- role="presentation"
40
- className="fixed inset-0 bg-[var(--bg-overlay)] backdrop-blur-sm flex justify-end z-[100] animate-fade-in"
41
- onClick={onClose}
42
- >
43
- <div
44
- role="dialog"
45
- aria-modal="true"
46
- className="w-[600px] max-w-full bg-[var(--bg-page)] h-full overflow-y-auto border-l border-[var(--border-subtle)] animate-slide-up"
47
- onClick={e => e.stopPropagation()}
48
- >
49
- {/* Header */}
50
- <div className="sticky top-0 bg-[var(--bg-page)]/95 backdrop-blur border-b border-[var(--border-subtle)] px-6 py-4 flex justify-between items-center z-10">
51
- <div className="flex items-center gap-3">
52
- <div className="w-8 h-8 rounded-[var(--radius-sm)] bg-gradient-to-br from-[var(--accent-pink)]/20 to-[var(--accent-cyan)]/20 flex items-center justify-center">
53
- <FileJson size={16} className="text-[var(--accent-cyan)]" />
54
- </div>
55
- <h2 className="text-lg font-semibold text-[var(--text-primary)]">Request Details</h2>
56
- </div>
57
- <button
58
- type="button"
59
- onClick={onClose}
60
- className="p-2 rounded-[var(--radius-sm)] text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] transition-colors"
61
- >
62
- <X size={20} />
63
- </button>
64
- </div>
65
-
66
- <div className="p-6 space-y-6">
67
- {/* Model Info */}
68
- <div className="surface p-5">
69
- <div className="flex items-center justify-between mb-4">
70
- <div>
71
- <div className="text-2xl font-bold text-[var(--text-primary)]">{details.model}</div>
72
- <div className="text-sm text-[var(--text-muted)]">{details.provider}</div>
73
- </div>
74
- {details.errorMessage ? (
75
- <span className="badge badge-error">Error</span>
76
- ) : (
77
- <span className="badge badge-success">Success</span>
78
- )}
79
- </div>
80
- </div>
81
-
82
- {/* Stats Grid */}
83
- <div className="grid grid-cols-2 gap-4">
84
- <div className="surface p-4">
85
- <div className="flex items-center gap-2 text-[var(--text-muted)] mb-2">
86
- <Coins size={14} />
87
- <span className="text-xs uppercase tracking-wide">Cost</span>
88
- </div>
89
- <div className="text-xl font-semibold text-[var(--text-primary)]">
90
- ${details.usage.cost.total.toFixed(4)}
91
- </div>
92
- </div>
93
-
94
- <div className="surface p-4">
95
- <div className="flex items-center gap-2 text-[var(--text-muted)] mb-2">
96
- <Star size={14} />
97
- <span className="text-xs uppercase tracking-wide">Premium Reqs</span>
98
- </div>
99
- <div className="text-xl font-semibold text-[var(--text-primary)]">
100
- {(details.usage.premiumRequests ?? 0).toLocaleString()}
101
- </div>
102
- </div>
103
- <div className="surface p-4">
104
- <div className="flex items-center gap-2 text-[var(--text-muted)] mb-2">
105
- <Hash size={14} />
106
- <span className="text-xs uppercase tracking-wide">Tokens</span>
107
- </div>
108
- <div className="text-xl font-semibold text-[var(--text-primary)]">
109
- {details.usage.totalTokens.toLocaleString()}
110
- </div>
111
- <div className="text-xs text-[var(--text-muted)] mt-1">
112
- {details.usage.input.toLocaleString()} in · {details.usage.output.toLocaleString()} out
113
- </div>
114
- </div>
115
-
116
- <div className="surface p-4">
117
- <div className="flex items-center gap-2 text-[var(--text-muted)] mb-2">
118
- <Clock size={14} />
119
- <span className="text-xs uppercase tracking-wide">Duration</span>
120
- </div>
121
- <div className="text-xl font-semibold text-[var(--text-primary)]">
122
- {details.duration ? `${(details.duration / 1000).toFixed(2)}s` : "-"}
123
- </div>
124
- </div>
125
-
126
- <div className="surface p-4">
127
- <div className="flex items-center gap-2 text-[var(--text-muted)] mb-2">
128
- <Zap size={14} />
129
- <span className="text-xs uppercase tracking-wide">TTFT</span>
130
- </div>
131
- <div className="text-xl font-semibold text-[var(--text-primary)]">
132
- {details.ttft ? `${(details.ttft / 1000).toFixed(2)}s` : "-"}
133
- </div>
134
- </div>
135
- </div>
136
-
137
- {/* Tokens/Sec */}
138
- {details.duration && details.usage.output > 0 && (
139
- <div className="surface p-4">
140
- <div className="flex items-center justify-between">
141
- <div className="flex items-center gap-2 text-[var(--text-muted)]">
142
- <Gauge size={14} />
143
- <span className="text-xs uppercase tracking-wide">Throughput</span>
144
- </div>
145
- <span className="text-2xl font-bold gradient-text">
146
- {((details.usage.output * 1000) / details.duration).toFixed(1)}
147
- </span>
148
- </div>
149
- <div className="text-xs text-[var(--text-muted)] mt-1 text-right">tokens/second</div>
150
- </div>
151
- )}
152
-
153
- {/* Output */}
154
- <div>
155
- <h3 className="text-sm font-semibold text-[var(--text-primary)] mb-3">Output</h3>
156
- <pre className="surface bg-[var(--bg-elevated)] p-4 rounded-[var(--radius-md)] text-sm font-mono text-[var(--text-secondary)] overflow-x-auto">
157
- {JSON.stringify(details.output, null, 2)}
158
- </pre>
159
- </div>
160
-
161
- {/* Raw Metadata */}
162
- <div>
163
- <h3 className="text-sm font-semibold text-[var(--text-primary)] mb-3">Raw Metadata</h3>
164
- <pre className="surface bg-[var(--bg-elevated)] p-4 rounded-[var(--radius-md)] text-xs font-mono text-[var(--text-muted)] overflow-x-auto">
165
- {JSON.stringify(details, null, 2)}
166
- </pre>
167
- </div>
168
- </div>
169
- </div>
170
- </div>
171
- );
172
- }
@@ -1,73 +0,0 @@
1
- import { formatDistanceToNow } from "date-fns";
2
- import { CheckCircle2, XCircle } from "lucide-react";
3
- import type { MessageStats } from "../types";
4
-
5
- interface RequestListProps {
6
- requests: MessageStats[];
7
- onSelect: (req: MessageStats) => void;
8
- title: string;
9
- }
10
-
11
- export function RequestList({ requests, onSelect, title }: RequestListProps) {
12
- return (
13
- <div className="surface overflow-hidden flex flex-col h-full">
14
- <div className="px-5 py-4 border-b border-[var(--border-subtle)]">
15
- <h3 className="text-sm font-semibold text-[var(--text-primary)]">{title}</h3>
16
- </div>
17
- <div className="overflow-auto flex-1">
18
- <table className="w-full">
19
- <thead className="bg-[var(--bg-elevated)] sticky top-0 z-10">
20
- <tr>
21
- <th className="text-left py-3 px-4 table-header">Model</th>
22
- <th className="text-left py-3 px-4 table-header">Time</th>
23
- <th className="text-right py-3 px-4 table-header">Tokens</th>
24
- <th className="text-right py-3 px-4 table-header">Cost</th>
25
- <th className="text-right py-3 px-4 table-header">Duration</th>
26
- <th className="text-center py-3 px-4 table-header">Status</th>
27
- </tr>
28
- </thead>
29
- <tbody>
30
- {requests.map(req => (
31
- <tr
32
- key={`${req.sessionFile}-${req.entryId}`}
33
- onClick={() => onSelect(req)}
34
- className="table-row cursor-pointer border-b border-[var(--border-subtle)] last:border-b-0"
35
- >
36
- <td className="py-3 px-4">
37
- <div className="font-medium text-[var(--text-primary)] text-sm">{req.model}</div>
38
- <div className="text-xs text-[var(--text-muted)]">{req.provider}</div>
39
- </td>
40
- <td className="py-3 px-4 text-sm text-[var(--text-secondary)]">
41
- {formatDistanceToNow(req.timestamp, { addSuffix: true })}
42
- </td>
43
- <td className="py-3 px-4 text-right text-sm text-[var(--text-secondary)] font-mono">
44
- {req.usage.totalTokens.toLocaleString()}
45
- </td>
46
- <td className="py-3 px-4 text-right text-sm text-[var(--text-secondary)] font-mono">
47
- ${req.usage.cost.total.toFixed(4)}
48
- </td>
49
- <td className="py-3 px-4 text-right text-sm text-[var(--text-secondary)] font-mono">
50
- {req.duration ? `${(req.duration / 1000).toFixed(1)}s` : "-"}
51
- </td>
52
- <td className="py-3 px-4 text-center">
53
- {req.errorMessage ? (
54
- <XCircle size={16} className="text-[var(--accent-red)] mx-auto" />
55
- ) : (
56
- <CheckCircle2 size={16} className="text-[var(--accent-green)] mx-auto" />
57
- )}
58
- </td>
59
- </tr>
60
- ))}
61
- {requests.length === 0 && (
62
- <tr>
63
- <td colSpan={6} className="py-12 text-center text-[var(--text-muted)] text-sm">
64
- No requests found
65
- </td>
66
- </tr>
67
- )}
68
- </tbody>
69
- </table>
70
- </div>
71
- </div>
72
- );
73
- }
@@ -1,135 +0,0 @@
1
- import { Activity, AlertCircle, BarChart3, Database, Download, Server, Star, Upload, Zap } from "lucide-react";
2
- import type { AggregatedStats } from "../types";
3
-
4
- interface StatsGridProps {
5
- stats: AggregatedStats;
6
- }
7
-
8
- const compactNumberFormatter = new Intl.NumberFormat(undefined, {
9
- notation: "compact",
10
- maximumFractionDigits: 1,
11
- });
12
-
13
- function formatCompactNumber(value: number): string {
14
- return compactNumberFormatter.format(value);
15
- }
16
-
17
- function formatExactNumber(value: number): string {
18
- return value.toLocaleString();
19
- }
20
-
21
- const totalPromptCompletionTokens = (stats: AggregatedStats) => stats.totalInputTokens + stats.totalOutputTokens;
22
-
23
- const statConfig = [
24
- {
25
- key: "requests",
26
- title: "Total Requests",
27
- icon: Server,
28
- color: "var(--accent-violet)",
29
- getValue: (s: AggregatedStats) => s.totalRequests.toLocaleString(),
30
- getDetail: (s: AggregatedStats) =>
31
- `${s.successfulRequests.toLocaleString()} success · ${s.failedRequests.toLocaleString()} errors`,
32
- },
33
- {
34
- key: "cost",
35
- title: "Total Cost",
36
- icon: Activity,
37
- color: "var(--accent-pink)",
38
- getValue: (s: AggregatedStats) => `$${s.totalCost.toFixed(2)}`,
39
- getDetail: (s: AggregatedStats) =>
40
- s.totalRequests > 0 ? `$${(s.totalCost / s.totalRequests).toFixed(4)} avg/req` : "-",
41
- },
42
- {
43
- key: "premiumRequests",
44
- title: "Premium Reqs",
45
- icon: Star,
46
- color: "var(--accent-amber)",
47
- getValue: (s: AggregatedStats) => formatExactNumber(s.totalPremiumRequests),
48
- getDetail: (s: AggregatedStats) =>
49
- s.totalRequests > 0 ? `${((s.totalPremiumRequests / s.totalRequests) * 100).toFixed(1)}% of requests` : "-",
50
- },
51
- {
52
- key: "cache",
53
- title: "Cache Rate",
54
- icon: Database,
55
- color: "var(--accent-cyan)",
56
- getValue: (s: AggregatedStats) => `${(s.cacheRate * 100).toFixed(1)}%`,
57
- getDetail: (s: AggregatedStats) => `${formatCompactNumber(s.totalCacheReadTokens)} cached tokens`,
58
- },
59
- {
60
- key: "inputTokens",
61
- title: "Input Tokens",
62
- icon: Download,
63
- color: "var(--accent-violet)",
64
- getValue: (s: AggregatedStats) => formatExactNumber(s.totalInputTokens),
65
- getDetail: (s: AggregatedStats) =>
66
- totalPromptCompletionTokens(s) > 0
67
- ? `${((s.totalInputTokens / totalPromptCompletionTokens(s)) * 100).toFixed(1)}% of prompt+completion`
68
- : "-",
69
- },
70
- {
71
- key: "outputTokens",
72
- title: "Output Tokens",
73
- icon: Upload,
74
- color: "var(--accent-pink)",
75
- getValue: (s: AggregatedStats) => formatExactNumber(s.totalOutputTokens),
76
- getDetail: (s: AggregatedStats) =>
77
- totalPromptCompletionTokens(s) > 0
78
- ? `${((s.totalOutputTokens / totalPromptCompletionTokens(s)) * 100).toFixed(1)}% of prompt+completion`
79
- : "-",
80
- },
81
- {
82
- key: "errors",
83
- title: "Error Rate",
84
- icon: AlertCircle,
85
- color: "var(--accent-red)",
86
- getValue: (s: AggregatedStats) => `${(s.errorRate * 100).toFixed(1)}%`,
87
- getDetail: (s: AggregatedStats) => `${s.failedRequests.toLocaleString()} failed requests`,
88
- },
89
- {
90
- key: "tokens",
91
- title: "Tokens/Sec",
92
- icon: BarChart3,
93
- color: "var(--accent-green)",
94
- getValue: (s: AggregatedStats) => s.avgTokensPerSecond?.toFixed(1) ?? "-",
95
- getDetail: (s: AggregatedStats) =>
96
- `${formatCompactNumber(totalPromptCompletionTokens(s))} total prompt+completion`,
97
- },
98
- {
99
- key: "ttft",
100
- title: "TTFT",
101
- icon: Zap,
102
- color: "var(--accent-amber)",
103
- getValue: (s: AggregatedStats) => (s.avgTtft ? `${(s.avgTtft / 1000).toFixed(2)}s` : "-"),
104
- getDetail: () => "Time to first token",
105
- },
106
- ];
107
-
108
- export function StatsGrid({ stats }: StatsGridProps) {
109
- return (
110
- <div className="grid grid-cols-2 lg:grid-cols-3 xl:grid-cols-9 gap-4 mb-8">
111
- {statConfig.map(stat => {
112
- const Icon = stat.icon;
113
- return (
114
- <div key={stat.key} className="stat-card group">
115
- <div className="flex items-center justify-between mb-3">
116
- <span className="text-sm font-medium text-[var(--text-secondary)]">{stat.title}</span>
117
- <div
118
- className="p-2 rounded-[var(--radius-sm)] transition-colors"
119
- style={{ backgroundColor: `${stat.color}15` }}
120
- >
121
- <Icon
122
- size={18}
123
- style={{ color: stat.color }}
124
- className="transition-transform group-hover:scale-110"
125
- />
126
- </div>
127
- </div>
128
- <div className="text-2xl font-bold text-[var(--text-primary)] mb-1">{stat.getValue(stats)}</div>
129
- <div className="text-xs text-[var(--text-muted)] truncate">{stat.getDetail(stats)}</div>
130
- </div>
131
- );
132
- })}
133
- </div>
134
- );
135
- }