Skip to content

Commit da791f2

Browse files
committed
feat: surface max mode usage in dashboard and cost alerts
Add max_mode_reqs and avg_cache_read_tokens to user cost breakdown queries. Show max mode % column in user detail Cost Breakdown table with amber highlighting and cache token tooltip. Enrich cost/req spike alert messages with max mode percentage so recipients understand WHY the cost per request is high, not just that it spiked. Made-with: Cursor
1 parent d7d223a commit da791f2

File tree

3 files changed

+36
-4
lines changed

3 files changed

+36
-4
lines changed

src/app/users/[email]/user-detail-client.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ interface UserStats {
4343
overage_reqs: number;
4444
overage_cost_cents: number;
4545
error_reqs: number;
46+
max_mode_reqs: number;
47+
avg_cache_read_tokens: number;
4648
}>;
4749
mcpSummary: Array<{ tool_name: string; server_name: string; total_usage: number }>;
4850
commandsSummary: Array<{ command_name: string; total_usage: number }>;
@@ -317,6 +319,7 @@ export function UserDetailClient({ email, stats }: UserDetailClientProps) {
317319
<th className="text-right px-4 py-2 font-medium">$/Req</th>
318320
<th className="text-right px-4 py-2 font-medium">Total</th>
319321
<th className="text-left px-4 py-2 font-medium">Included in Plan vs Overage</th>
322+
<th className="text-right px-4 py-2 font-medium">Max Mode</th>
320323
<th className="text-right px-4 py-2 font-medium">Errors</th>
321324
</tr>
322325
</thead>
@@ -370,6 +373,18 @@ export function UserDetailClient({ email, stats }: UserDetailClientProps) {
370373
</span>
371374
</div>
372375
</td>
376+
<td className="text-right px-4 py-2 font-mono">
377+
{r.max_mode_reqs > 0 ? (
378+
<span
379+
className="text-amber-400/80"
380+
title={`${r.max_mode_reqs}/${r.requests} requests used max mode (avg ${Math.round(r.avg_cache_read_tokens / 1000).toLocaleString()}k cache tokens/req)`}
381+
>
382+
{Math.round((r.max_mode_reqs / r.requests) * 100)}%
383+
</span>
384+
) : (
385+
<span className="text-zinc-700"></span>
386+
)}
387+
</td>
373388
<td className="text-right px-4 py-2 font-mono">
374389
{r.error_reqs > 0 ? (
375390
<span

src/lib/anomaly/trends.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ function detectCostPerReqSpikes(
136136
const todayCpr = (user.today_cost_per_req / 100).toFixed(2);
137137
const histCpr = (user.hist_avg_cost_per_req / 100).toFixed(2);
138138
const todaySpend = (user.today_spend_cents / 100).toFixed(2);
139+
const maxModePct =
140+
user.today_reqs > 0 ? Math.round((user.today_max_mode_reqs / user.today_reqs) * 100) : 0;
141+
const maxModeNote = maxModePct > 0 ? ` [${maxModePct}% max mode]` : "";
139142

140143
anomalies.push({
141144
userEmail: user.email,
@@ -144,7 +147,7 @@ function detectCostPerReqSpikes(
144147
metric: "cost_per_req",
145148
value: Math.round(user.today_cost_per_req),
146149
threshold: Math.round(user.hist_avg_cost_per_req * spikeMultiplier),
147-
message: `${user.name}: cost/request spiked to $${todayCpr} (${ratio.toFixed(1)}x their avg of $${histCpr}), using ${user.today_top_model || "unknown"}, $${todaySpend} total today across ${user.today_reqs} reqs`,
150+
message: `${user.name}: cost/request spiked to $${todayCpr}/req (${ratio.toFixed(1)}x their avg of $${histCpr}/req), using ${user.today_top_model || "unknown"}${maxModeNote}, $${todaySpend} total today across ${user.today_reqs} reqs`,
148151
detectedAt: now,
149152
resolvedAt: null,
150153
alertedAt: null,

src/lib/data/sqlite.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,6 +2235,8 @@ export function getUserUsageEventsSummary(
22352235
overage_reqs: number;
22362236
overage_cost_cents: number;
22372237
error_reqs: number;
2238+
max_mode_reqs: number;
2239+
avg_cache_read_tokens: number;
22382240
}> {
22392241
const db = getDb();
22402242
return db
@@ -2248,7 +2250,9 @@ export function getUserUsageEventsSummary(
22482250
SUM(CASE WHEN kind LIKE 'Included%' THEN total_cents ELSE 0 END) as plan_cost_cents,
22492251
SUM(CASE WHEN kind = 'Usage-based' THEN 1 ELSE 0 END) as overage_reqs,
22502252
SUM(CASE WHEN kind = 'Usage-based' THEN total_cents ELSE 0 END) as overage_cost_cents,
2251-
SUM(CASE WHEN kind LIKE 'Errored%' THEN 1 ELSE 0 END) as error_reqs
2253+
SUM(CASE WHEN kind LIKE 'Errored%' THEN 1 ELSE 0 END) as error_reqs,
2254+
SUM(CASE WHEN max_mode = 1 THEN 1 ELSE 0 END) as max_mode_reqs,
2255+
ROUND(AVG(cache_read_tokens)) as avg_cache_read_tokens
22522256
FROM usage_events
22532257
WHERE user_email = ? AND CAST(timestamp AS INTEGER) >= ?
22542258
GROUP BY model
@@ -2266,6 +2270,8 @@ export function getUserUsageEventsSummary(
22662270
overage_reqs: number;
22672271
overage_cost_cents: number;
22682272
error_reqs: number;
2273+
max_mode_reqs: number;
2274+
avg_cache_read_tokens: number;
22692275
}>;
22702276
}
22712277

@@ -2945,6 +2951,8 @@ export function getUserCostPerRequest(
29452951
today_top_model: string;
29462952
hist_avg_cost_per_req: number | null;
29472953
hist_days: number;
2954+
today_max_mode_reqs: number;
2955+
today_avg_cache_read: number;
29482956
}> {
29492957
const db = getDb();
29502958
return db
@@ -2954,7 +2962,9 @@ export function getUserCostPerRequest(
29542962
COALESCE(m.name, ue.user_email) as name,
29552963
ROUND(SUM(ue.total_cents)) as spend_cents,
29562964
COUNT(*) as reqs,
2957-
ROUND(SUM(ue.total_cents) / COUNT(*), 2) as cost_per_req
2965+
ROUND(SUM(ue.total_cents) / COUNT(*), 2) as cost_per_req,
2966+
SUM(CASE WHEN ue.max_mode = 1 THEN 1 ELSE 0 END) as max_mode_reqs,
2967+
ROUND(AVG(ue.cache_read_tokens)) as avg_cache_read
29582968
FROM usage_events ue
29592969
LEFT JOIN members m ON ue.user_email = m.email
29602970
WHERE date(ue.timestamp/1000, 'unixepoch') = date('now')
@@ -2990,7 +3000,9 @@ export function getUserCostPerRequest(
29903000
td.cost_per_req as today_cost_per_req,
29913001
COALESCE(tt.model, '') as today_top_model,
29923002
hd.avg_cost_per_req as hist_avg_cost_per_req,
2993-
COALESCE(hd.active_days, 0) as hist_days
3003+
COALESCE(hd.active_days, 0) as hist_days,
3004+
td.max_mode_reqs as today_max_mode_reqs,
3005+
td.avg_cache_read as today_avg_cache_read
29943006
FROM today_data td
29953007
LEFT JOIN today_top tt ON td.email = tt.email
29963008
LEFT JOIN hist_data hd ON td.email = hd.email`,
@@ -3004,6 +3016,8 @@ export function getUserCostPerRequest(
30043016
today_top_model: string;
30053017
hist_avg_cost_per_req: number | null;
30063018
hist_days: number;
3019+
today_max_mode_reqs: number;
3020+
today_avg_cache_read: number;
30073021
}>;
30083022
}
30093023

0 commit comments

Comments
 (0)