Cost Tracking¶
All operational cost lives in the cost_records TimescaleDB hypertable. It is the single source of truth for spend reporting.
Schema¶
cost_records (hypertable) columns:
| Column | Meaning |
|---|---|
user_id |
Owner of the spend |
model |
LLM model name |
vendor |
Provider (e.g. openai) |
cost_amount |
Estimated cost |
token_count |
Tokens consumed |
session_id |
Analysis session the cost belongs to |
How rows are written¶
LLM cost rows are written per (session, model) by _persist_session_costs in backend/api/workers_event_consumer.py when an AnalysisCompletedEvent is processed. The write is idempotent on session_id, so replaying an event will not double-count.
flowchart LR
RC[result-consumer] -->|AnalysisCompletedEvent| K[Kafka analysis.completed]
K --> WEC[workers_event_consumer\n_persist_session_costs]
WEC -->|idempotent on session_id| CR[(cost_records)]
See Event-Driven Workers for the surrounding event flow.
Do not read cost from sessions.result['_stats']
sessions.result['_stats'] is a debug breadcrumb, not a source of truth. All cost reporting must go through cost_records.
Reporting¶
Reporting aggregates go through:
CostService(backend/services/cost_service.py)AnalyticsService.get_usage_stats(backend/services/analytics_service.py) — per-user, per-model aggregates.
Backfilling historical sessions¶
Sessions that predate cost tracking (or were created before a fix) can be backfilled:
Dashboard¶
A Grafana dashboard is provisioned at docker/grafana/provisioning/dashboards/redhound-cost.json. See Monitoring & Metrics for the observability stack.