Skip to content

Commit b71d397

Browse files
committed
feat: Add cost estimation to stats dashboard
Display estimated API costs based on token usage with Sonnet 4 pricing ($3/MTok input, $15/MTok output). - Add cost.go with pricing constants and CalculateCost function - Extend StatsData, TimePoint, ProjectStat with cost fields - Add cost summary card to stats page UI - Cost updates dynamically with time-range and project filters - Include cost aggregation in buildTimeSeriesWithCost function
1 parent 5d14952 commit b71d397

12 files changed

Lines changed: 1924 additions & 122 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
id: cost-estimation
3+
title: Cost Estimation on Stats Dashboard
4+
status: in_progress
5+
created: 2026-01-22
6+
---
7+
8+
# Intent: Cost Estimation on Stats Dashboard
9+
10+
## Goal
11+
12+
Add estimated API cost tracking to the stats dashboard, showing cost breakdowns by project and over time based on token usage.
13+
14+
## Users
15+
16+
Developers using Claude Code who want to understand and monitor their API spending patterns.
17+
18+
## Problem
19+
20+
Currently the stats page shows token counts but doesn't translate these into dollar costs. Users have to manually calculate costs using Anthropic's pricing, which is tedious and error-prone.
21+
22+
## Success Criteria
23+
24+
- Total estimated cost displayed on stats page
25+
- Cost breakdown by project (matches existing project filtering)
26+
- Cost over time chart (daily costs)
27+
- Costs update when time-range filter changes
28+
- Currency formatted as USD ($X.XX)
29+
30+
## Constraints
31+
32+
- Stats page only (no per-session cost display)
33+
- Hardcoded pricing defaults (Sonnet 4 as default model)
34+
- Combined input+output cost (not separated)
35+
- Integrates with existing stats infrastructure
36+
37+
## Pricing Reference
38+
39+
| Model | Input | Output |
40+
|-------|-------|--------|
41+
| Claude Sonnet 4/4.5 | $3/MTok | $15/MTok |
42+
| Claude Opus 4.5 | $5/MTok | $25/MTok |
43+
| Claude Opus 4/4.1 | $15/MTok | $75/MTok |
44+
| Claude Haiku 4.5 | $1/MTok | $5/MTok |
45+
| Claude Haiku 3.5 | $0.80/MTok | $4/MTok |
46+
47+
Default: Sonnet 4 pricing ($3 input, $15 output per million tokens)
48+
49+
## Notes
50+
51+
- Token counts already tracked in existing stats infrastructure
52+
- Client-side cost calculation matches existing time-range filtering pattern
53+
- Future enhancement: detect model from JSONL if available
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
id: 01-cost-calculation-logic
3+
title: Cost Calculation Logic
4+
intent: cost-estimation
5+
complexity: low
6+
mode: autopilot
7+
status: pending
8+
depends_on: []
9+
created: 2026-01-22
10+
---
11+
12+
# Work Item: Cost Calculation Logic
13+
14+
## Description
15+
16+
Add cost calculation functions with Anthropic pricing constants. Create a dedicated `cost.go` file with pricing data and a function to calculate cost from token counts.
17+
18+
## Acceptance Criteria
19+
20+
- [ ] New `cost.go` file with pricing constants for Claude models
21+
- [ ] `CalculateCost(inputTokens, outputTokens int64) float64` function
22+
- [ ] Default to Sonnet 4 pricing ($3/MTok input, $15/MTok output)
23+
- [ ] Returns cost in USD as float64
24+
- [ ] Unit tests for cost calculation
25+
26+
## Technical Notes
27+
28+
Pricing constants (per million tokens):
29+
```go
30+
const (
31+
SonnetInputPrice = 3.0 // $/MTok
32+
SonnetOutputPrice = 15.0 // $/MTok
33+
)
34+
35+
func CalculateCost(inputTokens, outputTokens int64) float64 {
36+
inputCost := float64(inputTokens) / 1_000_000 * SonnetInputPrice
37+
outputCost := float64(outputTokens) / 1_000_000 * SonnetOutputPrice
38+
return inputCost + outputCost
39+
}
40+
```
41+
42+
## Dependencies
43+
44+
None - this is the foundation work item.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
id: 02-stats-cost-aggregation
3+
title: Stats Cost Aggregation
4+
intent: cost-estimation
5+
complexity: low
6+
mode: autopilot
7+
status: pending
8+
depends_on:
9+
- 01-cost-calculation-logic
10+
created: 2026-01-22
11+
---
12+
13+
# Work Item: Stats Cost Aggregation
14+
15+
## Description
16+
17+
Extend the stats data model to include cost fields and update the stats computation to aggregate costs alongside existing token metrics.
18+
19+
## Acceptance Criteria
20+
21+
- [ ] `StatsData` struct extended with `TotalCost float64`
22+
- [ ] `DataPoint` struct extended with `Cost float64` field (for daily cost)
23+
- [ ] `ProjectStat` struct extended with `Cost float64` field
24+
- [ ] `ComputeStats()` calculates costs during aggregation
25+
- [ ] `/api/stats` returns cost data in response
26+
- [ ] Existing stats functionality unchanged (backward compatible)
27+
28+
## Technical Notes
29+
30+
Extend structs in `stats.go`:
31+
```go
32+
type StatsData struct {
33+
// ... existing fields
34+
TotalCost float64 `json:"totalCost"`
35+
}
36+
37+
type DataPoint struct {
38+
// ... existing fields
39+
Cost float64 `json:"cost"`
40+
}
41+
42+
type ProjectStat struct {
43+
// ... existing fields
44+
Cost float64 `json:"cost"`
45+
}
46+
```
47+
48+
In `ComputeStats()`:
49+
- Calculate cost for each day using `CalculateCost()`
50+
- Sum costs per project
51+
- Aggregate total cost
52+
53+
## Dependencies
54+
55+
- 01-cost-calculation-logic (needs `CalculateCost` function)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
id: 03-stats-page-cost-ui
3+
title: Stats Page Cost UI
4+
intent: cost-estimation
5+
complexity: medium
6+
mode: confirm
7+
status: pending
8+
depends_on:
9+
- 02-stats-cost-aggregation
10+
created: 2026-01-22
11+
---
12+
13+
# Work Item: Stats Page Cost UI
14+
15+
## Description
16+
17+
Add cost display elements to the stats page: a cost summary card showing total estimated cost, and integrate cost data into the existing charts or add a dedicated cost chart.
18+
19+
## Acceptance Criteria
20+
21+
- [ ] Cost summary card displaying total estimated cost
22+
- [ ] Cost formatted as currency ($X.XX)
23+
- [ ] Cost updates when time-range filter changes
24+
- [ ] Cost updates when project filter changes
25+
- [ ] Cost by project visible (in project activity section or separate)
26+
- [ ] Optional: Cost over time chart line
27+
28+
## Technical Notes
29+
30+
In `templates_stats.go`:
31+
32+
1. Add cost summary card:
33+
```html
34+
<div class="stat-card">
35+
<div class="stat-value" id="total-cost">$0.00</div>
36+
<div class="stat-label">Estimated Cost</div>
37+
</div>
38+
```
39+
40+
2. Update JavaScript to:
41+
- Read `totalCost` from API response
42+
- Format as currency: `cost.toFixed(2)`
43+
- Update on filter changes (reuse existing filter logic)
44+
45+
3. Project costs:
46+
- Show in project activity breakdown
47+
- Format: "project-name: X messages, Y tokens, $Z.ZZ"
48+
49+
4. Optional enhancement:
50+
- Add cost line to tokens chart (secondary Y-axis)
51+
- Or separate "Cost per Day" chart
52+
53+
## Dependencies
54+
55+
- 02-stats-cost-aggregation (needs cost data in API response)

.specs-fire/runs/run-007/plan.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Implementation Plan — Run 007
2+
3+
---
4+
5+
## Work Item 3: Stats Page Cost UI
6+
7+
**Mode**: confirm
8+
**Intent**: cost-estimation
9+
10+
### Approach
11+
12+
Add a cost summary card to the stats page and update the JavaScript to display and filter costs. Keep it minimal - one card showing total estimated cost.
13+
14+
### Files to Modify
15+
16+
| File | Changes |
17+
|------|---------|
18+
| `templates_stats.go` | Add cost card HTML, add formatCurrency JS function, update display logic |
19+
20+
### Implementation Details
21+
22+
1. **Add Cost Card** (after Tokens card in stats-summary):
23+
```html
24+
<div class="stat-card">
25+
<div class="stat-value" id="stat-cost">$0.00</div>
26+
<div class="stat-label">Est. Cost</div>
27+
</div>
28+
```
29+
30+
2. **Add formatCurrency Function**:
31+
```javascript
32+
function formatCurrency(amount) {
33+
return '$' + amount.toFixed(2);
34+
}
35+
```
36+
37+
3. **Update Display Logic**:
38+
- Read `totalCost` from API response
39+
- Display in cost card with currency formatting
40+
- Update when time-range filter changes
41+
- Update when project filter changes
42+
- Aggregate costs from project data when filtering
43+
44+
4. **Update aggregateProjectsData**:
45+
- Sum `cost` field from filtered projects
46+
- Include cost in tokensPerDay aggregation
47+
48+
### Acceptance Criteria Validation
49+
50+
- [x] Cost summary card displaying total estimated cost
51+
- [x] Cost formatted as currency ($X.XX)
52+
- [x] Cost updates when time-range filter changes
53+
- [x] Cost updates when project filter changes
54+
- [x] Cost by project visible (via project filtering)
55+
56+
---
57+
58+
## Work Item 2: Stats Cost Aggregation
59+
60+
**Mode**: autopilot
61+
**Intent**: cost-estimation
62+
63+
### Approach
64+
65+
Extend existing stats structs with cost fields and update ComputeStats() to calculate costs using the CalculateCost function from work item 1.
66+
67+
### Files to Modify
68+
69+
| File | Changes |
70+
|------|---------|
71+
| `stats.go` | Add cost fields to structs, update ComputeStats() |
72+
73+
### Implementation Details
74+
75+
1. **Struct Extensions**:
76+
- `StatsData`: Add `TotalCost float64`
77+
- `TimePoint`: Add `Cost float64` (for daily cost tracking)
78+
- `ProjectStat`: Add `Cost float64`
79+
80+
2. **ComputeStats() Updates**:
81+
- Track daily costs alongside daily tokens
82+
- Use estimateTokens() result split as rough 80/20 input/output ratio
83+
- Sum costs per project
84+
- Aggregate total cost
85+
86+
3. **Backward Compatibility**:
87+
- All existing fields unchanged
88+
- New fields default to 0 if not populated
89+
90+
---
91+
92+
## Work Item 1: Cost Calculation Logic
93+
94+
**Mode**: autopilot
95+
**Intent**: cost-estimation
96+
97+
### Approach
98+
99+
Create a dedicated `cost.go` file with Anthropic pricing constants and a simple cost calculation function. Follow existing codebase patterns (package main, similar file structure to stats.go).
100+
101+
### Files to Create
102+
103+
| File | Purpose |
104+
|------|---------|
105+
| `cost.go` | Pricing constants and CalculateCost function |
106+
| `cost_test.go` | Unit tests for cost calculation |
107+
108+
### Files to Modify
109+
110+
None for this work item.
111+
112+
### Implementation Details
113+
114+
1. **Pricing Constants** (per million tokens):
115+
- Sonnet 4 Input: $3.00/MTok
116+
- Sonnet 4 Output: $15.00/MTok
117+
118+
2. **CalculateCost Function**:
119+
- Takes inputTokens and outputTokens as int64
120+
- Returns cost in USD as float64
121+
- Formula: `(input/1M * $3) + (output/1M * $15)`
122+
123+
3. **Test Cases**:
124+
- Zero tokens → $0.00
125+
- 1 million input tokens → $3.00
126+
- 1 million output tokens → $15.00
127+
- Mixed tokens calculation
128+
- Small token counts (realistic usage)
129+
130+
### Acceptance Criteria Validation
131+
132+
- [x] New `cost.go` file with pricing constants
133+
- [x] `CalculateCost(inputTokens, outputTokens int64) float64` function
134+
- [x] Default to Sonnet 4 pricing
135+
- [x] Returns cost in USD as float64
136+
- [x] Unit tests for cost calculation

0 commit comments

Comments
 (0)