Class: AiUsage::CostReconciler
- Inherits:
-
Object
- Object
- AiUsage::CostReconciler
- Defined in:
- app/services/ai_usage/cost_reconciler.rb
Overview
Compares Anthropic's real organization-level cost (Cost API) against what the
app recorded in +ai_usage_logs+, surfacing the dashboard-vs-invoice gap per
model. This is the automated form of the manual reconciliation that found
Sunny's Opus usage was only ~43% captured.
Note the asymmetry: the Cost API is org-wide (every API key, including
non-app keys like Claude Code), while +ai_usage_logs+ is app-only. So
100% coverage is only expected when non-app usage is ~zero.
Defined Under Namespace
Instance Method Summary collapse
- #call(since_days: 30, ending_at: Time.current) ⇒ Report
-
#initialize(client: AnthropicAdminClient.new) ⇒ CostReconciler
constructor
A new instance of CostReconciler.
Constructor Details
#initialize(client: AnthropicAdminClient.new) ⇒ CostReconciler
Returns a new instance of CostReconciler.
34 35 36 |
# File 'app/services/ai_usage/cost_reconciler.rb', line 34 def initialize(client: AnthropicAdminClient.new) @client = client end |
Instance Method Details
#call(since_days: 30, ending_at: Time.current) ⇒ Report
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'app/services/ai_usage/cost_reconciler.rb', line 41 def call(since_days: 30, ending_at: Time.current) starting_at = ending_at - since_days.days real = @client.cost_by_model(starting_at: starting_at.utc.iso8601, ending_at: ending_at.utc.iso8601) tracked = tracked_cost_by_model(starting_at, ending_at) rows = (real[:by_model].keys | tracked.keys).map do |model| Row.new(model: model, real_usd: real[:by_model][model] || 0.0, tracked_usd: tracked[model] || 0.0) end.sort_by { |row| -row.real_usd } Report.new( starting_at: starting_at, ending_at: ending_at, real_total_usd: real[:total_usd], tracked_total_usd: tracked.values.sum.round(2), rows: rows ) end |