Epic 3 — Model Gateway / Task T7
Cost Tracking
Per-request cost logging and queryable tier analytics for the Model Gateway.
Every completion call is metered, priced against a configurable pricing table,
and aggregated into reports that prove the Judge-Router's cost savings claim.
Test coverage
10/10
All Gherkin scenarios pass
Race detector
0
go test -race clean
External deps
0
context + sync + time
Cost savings
72.8%
vs all-frontier baseline
Ctrl/Cmd + wheel to zoom. Scroll to pan. Drag to pan when zoomed. Double-click to fit.
CostTracker
interface
Records per-request cost data and queries aggregated reports by time period.
Two methods: Record and Report.
type CostTracker interface {
Record(ctx context.Context, record CostRecord) error
Report(ctx context.Context, period TimePeriod) (CostReport, error)
}
InMemoryCostTracker
struct
Thread-safe in-memory implementation. Uses sync.RWMutex —
write lock on Record, read lock on Report and RecordCount. No external deps.
type InMemoryCostTracker struct {
mu sync.RWMutex
records []CostRecord
}
EstimateCost
func
Pure function. Computes USD cost from model ID, token counts, and a pricing table.
Returns 0 for unknown models — no panic, no error.
func EstimateCost(
model string,
inputTokens, outputTokens int,
pricing map[string]TokenCost,
) float64
NewCostRecordFromResponse
func
Bridges gateway completion output to cost tracking. Constructs a CostRecord from
a CompletionResponse, computing estimated cost via the pricing table.
func NewCostRecordFromResponse(
resp CompletionResponse,
tier ModelTier,
pricing map[string]TokenCost,
) CostRecord
DefaultPricing + Model Constants
const/var
Named constants for Claude model IDs eliminate string literals across the codebase.
DefaultPricing serves as the fallback pricing table until the config loader (T5) loads from models.yaml.
const (
ModelHaiku = "claude-haiku-4-5-20251001"
ModelSonnet = "claude-sonnet-4-6"
ModelOpus = "claude-opus-4-6"
)
var DefaultPricing = map[string]TokenCost{
ModelHaiku: {Input: 0.80, Output: 4.00},
ModelSonnet: {Input: 3.00, Output: 15.00},
ModelOpus: {Input: 15.00, Output: 75.00},
}
| Model |
Tier |
Input $/M |
Output $/M |
1K in + 500 out |
| claude-haiku-4-5-20251001 |
cheap |
$0.80 |
$4.00 |
$0.0028 |
| claude-sonnet-4-6 |
mid |
$3.00 |
$15.00 |
$0.0105 |
| claude-opus-4-6 |
frontier |
$15.00 |
$75.00 |
$0.0525 |
Formula. cost = (inputTokens × pricing.Input + outputTokens × pricing.Output) / 1,000,000.
Each record stores its own pre-computed EstimatedCost — Report sums stored values rather than recomputing from raw tokens, avoiding float precision drift in large aggregates.
Tiered routing
$10.87
800 cheap + 150 mid + 50 frontier
All-frontier baseline
$39.98
1000 requests at Opus pricing
Cheap tier total
$1.34
800 × $0.00168 each
Frontier tier total
$7.50
50 × $0.150 each
Write (Record) — exclusive
Read (Report/RecordCount) — shared
100 concurrent goroutines. The test launches 100 goroutines recording simultaneously.
After all complete, RecordCount() must equal exactly 100. Validated with go test -race.
internal/gateway/
gateway.go — +TokenCost, types (T1 base + T7 additions)
errors.go — +ProviderError, FallbackError, 7 sentinels
cost.go — InMemoryCostTracker, EstimateCost, DefaultPricing, NewCostRecordFromResponse
cost_test.go — 10 test functions, 402 lines, table-driven
cost-tracking-spec.md — feature specification with Gherkin scenarios
| Test Function |
Gherkin Rule |
Status |
| TestInMemoryCostTracker_RecordAndReport |
Record and Report basic requests |
PASS |
| TestInMemoryCostTracker_TimeFiltering |
Time filtering |
PASS |
| TestInMemoryCostTracker_PeriodBoundariesInclusive |
Period boundaries inclusive |
PASS |
| TestInMemoryCostTracker_EmptyReport |
Empty period |
PASS |
| TestEstimateCost_KnownPricing |
EstimateCost pure function (4 subtests) |
PASS |
| TestInMemoryCostTracker_ConcurrentSafety |
Concurrent safety (100 goroutines) |
PASS |
| TestInMemoryCostTracker_PerTierBreakdown |
Per-tier breakdown |
PASS |
| TestInMemoryCostTracker_SpecScenario_AggregationByTier |
Spec scenario: 5 records, 3 cheap + 2 mid |
PASS |
| TestNewCostRecordFromResponse |
Constructor bridge: response to record |
PASS |
| TestNewCostRecordFromResponse_UnknownModel |
Unknown model returns zero cost |
PASS |