forked from getagentseal/codeburn
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathday-aggregator.ts
More file actions
143 lines (126 loc) · 4.78 KB
/
Copy pathday-aggregator.ts
File metadata and controls
143 lines (126 loc) · 4.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import type { DailyEntry } from './daily-cache.js'
import type { PeriodData } from './menubar-json.js'
import { CATEGORY_LABELS, type ProjectSummary, type TaskCategory } from './types.js'
function emptyEntry(date: string): DailyEntry {
return {
date,
cost: 0,
calls: 0,
sessions: 0,
inputTokens: 0,
outputTokens: 0,
cacheReadTokens: 0,
cacheWriteTokens: 0,
editTurns: 0,
oneShotTurns: 0,
models: {},
categories: {},
providers: {},
}
}
export function dateKey(iso: string): string {
const d = new Date(iso)
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
export function aggregateProjectsIntoDays(projects: ProjectSummary[]): DailyEntry[] {
const byDate = new Map<string, DailyEntry>()
const ensure = (date: string): DailyEntry => {
let d = byDate.get(date)
if (!d) { d = emptyEntry(date); byDate.set(date, d) }
return d
}
for (const project of projects) {
for (const session of project.sessions) {
const sessionDate = dateKey(session.firstTimestamp)
ensure(sessionDate).sessions += 1
for (const turn of session.turns) {
if (turn.assistantCalls.length === 0) continue
const turnDate = dateKey(turn.assistantCalls[0]!.timestamp)
const turnDay = ensure(turnDate)
const editTurns = turn.hasEdits ? 1 : 0
const oneShotTurns = turn.hasEdits && turn.retries === 0 ? 1 : 0
const turnCost = turn.assistantCalls.reduce((s, c) => s + c.costUSD, 0)
turnDay.editTurns += editTurns
turnDay.oneShotTurns += oneShotTurns
const cat = turnDay.categories[turn.category] ?? { turns: 0, cost: 0, editTurns: 0, oneShotTurns: 0 }
cat.turns += 1
cat.cost += turnCost
cat.editTurns += editTurns
cat.oneShotTurns += oneShotTurns
turnDay.categories[turn.category] = cat
for (const call of turn.assistantCalls) {
const callDate = dateKey(call.timestamp)
const callDay = ensure(callDate)
callDay.cost += call.costUSD
callDay.calls += 1
callDay.inputTokens += call.usage.inputTokens
callDay.outputTokens += call.usage.outputTokens
callDay.cacheReadTokens += call.usage.cacheReadInputTokens
callDay.cacheWriteTokens += call.usage.cacheCreationInputTokens
const model = callDay.models[call.model] ?? {
calls: 0, cost: 0,
inputTokens: 0, outputTokens: 0,
cacheReadTokens: 0, cacheWriteTokens: 0,
}
model.calls += 1
model.cost += call.costUSD
model.inputTokens += call.usage.inputTokens
model.outputTokens += call.usage.outputTokens
model.cacheReadTokens += call.usage.cacheReadInputTokens
model.cacheWriteTokens += call.usage.cacheCreationInputTokens
callDay.models[call.model] = model
const provider = callDay.providers[call.provider] ?? { calls: 0, cost: 0 }
provider.calls += 1
provider.cost += call.costUSD
callDay.providers[call.provider] = provider
}
}
}
}
return [...byDate.values()].sort((a, b) => a.date.localeCompare(b.date))
}
export function buildPeriodDataFromDays(days: DailyEntry[], label: string): PeriodData {
let cost = 0, calls = 0, sessions = 0
let inputTokens = 0, outputTokens = 0, cacheReadTokens = 0, cacheWriteTokens = 0
const catTotals: Record<string, { turns: number; cost: number; editTurns: number; oneShotTurns: number }> = {}
const modelTotals: Record<string, { calls: number; cost: number }> = {}
for (const d of days) {
cost += d.cost
calls += d.calls
sessions += d.sessions
inputTokens += d.inputTokens
outputTokens += d.outputTokens
cacheReadTokens += d.cacheReadTokens
cacheWriteTokens += d.cacheWriteTokens
for (const [name, m] of Object.entries(d.models)) {
const acc = modelTotals[name] ?? { calls: 0, cost: 0 }
acc.calls += m.calls
acc.cost += m.cost
modelTotals[name] = acc
}
for (const [cat, c] of Object.entries(d.categories)) {
const acc = catTotals[cat] ?? { turns: 0, cost: 0, editTurns: 0, oneShotTurns: 0 }
acc.turns += c.turns
acc.cost += c.cost
acc.editTurns += c.editTurns
acc.oneShotTurns += c.oneShotTurns
catTotals[cat] = acc
}
}
return {
label,
cost,
calls,
sessions,
inputTokens,
outputTokens,
cacheReadTokens,
cacheWriteTokens,
categories: Object.entries(catTotals)
.sort(([, a], [, b]) => b.cost - a.cost)
.map(([cat, d]) => ({ name: CATEGORY_LABELS[cat as TaskCategory] ?? cat, ...d })),
models: Object.entries(modelTotals)
.sort(([, a], [, b]) => b.cost - a.cost)
.map(([name, d]) => ({ name, ...d })),
}
}