Skip to content

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:

uv run python -m scripts.backfill_cost_records

Dashboard

A Grafana dashboard is provisioned at docker/grafana/provisioning/dashboards/redhound-cost.json. See Monitoring & Metrics for the observability stack.