How the Billing and Cost Tracking System Works in Reasonix
Reasonix calculates session costs by converting provider token usage into monetary values using rate cards, aggregating results in a Ledger, and normalizing currencies for display while preserving immutable billing currencies.
The billing and cost tracking system in Reasonix is built around a core internal package that bridges raw LLM provider usage with user-facing cost visibility. This article explains the complete data flow—from token counting through quote generation to session aggregation—based on the actual source code implementation.
Core Billing Components
Reasonix defines several key types in its internal billing package that work together to track costs accurately:
| Component | Purpose |
|---|---|
| CostQuote | Immutable snapshot of cost calculation for a single usage event |
| Ledger | Running aggregation of multiple quotes with running totals |
| RateCard | Provider-specific pricing (input/output rates per token, currency) |
| Money | Currency-aware monetary value with proper rounding |
| UsageTokens | Structured token counts (prompt + completion) |
| QuoteContext | Display settings and billing mode configuration |
These types appear throughout the codebase in [internal/serve/broadcaster.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/serve/broadcaster.go), [internal/event/costquote_sink.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/event/costquote_sink.go), and related files.
Step 1: Recording Provider Usage
Every interaction with an LLM provider generates a provider.Usage struct containing token counts. The stats recorder in [internal/stats/recorder.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/stats/recorder.go) captures this data and initiates cost calculation:
// internal/stats/recorder.go
func (r *Recorder) recordProviderUsage(
modelRef string,
usage *provider.Usage,
quote *billing.CostQuote,
usageSource string,
) {
// Records timestamped usage metrics and associated cost quote
// for downstream analytics and billing aggregation
}
The usage parameter contains PromptTokens and CompletionTokens—the raw inputs for cost calculation.
Step 2: Building a CostQuote with Rate Cards
The billing.BuildQuote function transforms token usage into a monetary CostQuote. It requires:
- Usage data (
billing.UsageTokens) - Rate card (
billing.RateCard) from the provider's pricing catalog
Example from test code in [internal/eventwire/costquote_compat_test.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/eventwire/costquote_compat_test.go):
q := billing.BuildQuote(billing.QuoteInput{
Usage: billing.UsageTokens{
PromptTokens: 1_000_000,
CompletionTokens: 1_000_000,
},
Rates: billing.RateCard{
CacheHit: 0.02,
Input: 1,
Output: 2,
Currency: "CNY",
},
})
Rate cards typically include:
Input: Cost per input token (prompt)Output: Cost per output token (completion)CacheHit: Discounted rate for cached contextCurrency: Immutable billing currency from provider
Step 3: Ensuring Events Carry Cost Quotes
Before events propagate through the system, [internal/event/costquote_sink.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/event/costquote_sink.go) ensures every event has a valid CostQuote via EnsureCostQuote:
// internal/event/costquote_sink.go
func EnsureCostQuote(e Event, ctx *QuoteContext) *billing.CostQuote {
// Attaches or validates CostQuote on event using context settings
// Applies display currency normalization if needed
}
The QuoteContext parameter carries:
DisplayCurrency: User's preferred currency for UI renderingBillingMode:PAYG(pay-as-you-go) or equivalent subscription handlingDisplayRequest: Explicit user request for currency display
Step 4: Aggregating Quotes in the Session Ledger
The broadcaster in [internal/serve/broadcaster.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/serve/broadcaster.go) maintains a central Ledger that accumulates all costs for an active session:
// internal/serve/broadcaster.go
type Broadcaster struct {
ledger *billing.Ledger
// ... other fields
}
// Adding a quote to the running total
b.ledger.Add(*e.CostQuote, billing.UsageTokens{
PromptTokens: event.Usage.PromptTokens,
CompletionTokens: event.Usage.CompletionTokens,
})
The ledger provides SessionCostQuote() which calls billing.AggregateQuotes to compute running totals across all events.
Step 5: Currency Handling and Display Normalization
Reasonix strictly separates two currency concepts:
| Concept | Description | Example |
|---|---|---|
| Billing Currency | Immutable currency from provider's list price | USD, CNY |
| Display Currency | User-selected currency for UI rendering | Converted from billing currency |
The billing.NormalizeCurrency function standardizes currency codes. Configuration in [internal/config/render.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/config/render.go) persists these settings:
// internal/config/render.go
fmt.Fprintf(&b, "[billing]\n")
fmt.Fprintf(&b, "display_currency = %q\n", cfg.DisplayCurrency)
fmt.Fprintf(&b, "billing_currency = %q # frozen list-price currency\n",
billing.NormalizeCurrency(providerCurrency))
fmt.Fprintf(&b, "billing_mode = %q\n", cfg.BillingMode)
Step 6: Retrieving Balance via Control API
Client applications can query remaining credit through the controller:
// Example client usage against internal/control
balance, err := controller.Balance(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Remaining credit: %s %.2f\n",
balance.Currency, balance.Amount)
This interfaces with the underlying ledger state managed by the broadcaster.
Step 7: Diagnostic Billing Reports
The reasonix doctor billing command ([internal/doctor/billing.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/doctor/billing.go)) provides operational visibility:
// internal/doctor/billing.go
func CollectBilling() {
// Gathers pricing data from all configured providers
}
func RenderBillingText() {
// Human-readable report with normalized currencies
}
Key diagnostic functions include:
billing.PricingFingerprint: Identifies provider pricing versionsbilling.MatchesCatalog: Validates rate cards against cached pricing data
Complete Usage Example
Here's how components fit together in practice:
package main
import (
"reasonix/internal/billing"
"reasonix/internal/provider"
"reasonix/internal/serve"
)
func trackSessionCost() {
// Initialize broadcaster with fresh ledger
broadcaster := serve.NewBroadcaster()
// Simulate provider response
usage := &provider.Usage{
PromptTokens: 5000,
CompletionTokens: 2000,
}
// Build rate card from provider pricing
rateCard := billing.RateCard{
Input: 0.0005, // $0.50 per 1M tokens
Output: 0.0015, // $1.50 per 1M tokens
Currency: "USD",
}
// Generate cost quote
quote := billing.BuildQuote(billing.QuoteInput{
Usage: billing.UsageTokens{
PromptTokens: usage.PromptTokens,
CompletionTokens: usage.CompletionTokens,
},
Rates: rateCard,
})
// Add to session ledger
broadcaster.Ledger().Add(quote, billing.UsageTokens{
PromptTokens: usage.PromptTokens,
CompletionTokens: usage.CompletionTokens,
})
// Retrieve aggregated session cost
sessionCost := broadcaster.SessionCostQuote()
println("Session total:", sessionCost.Total().String())
}
Summary
Reasonix implements billing and cost tracking through a layered architecture:
- Usage collection captures raw token counts from providers via the stats recorder
- Quote generation converts tokens to monetary values using provider rate cards
- Event enrichment ensures every event carries complete cost metadata
- Ledger aggregation maintains running session totals with currency precision
- Display normalization separates immutable billing currencies from user-facing display
- Diagnostic tooling provides operational visibility into pricing configurations
Frequently Asked Questions
How does Reasonix handle currency conversion for display purposes?
Reasonix stores costs in the provider's billing currency (immutable) and applies conversion at display time using billing.NormalizeCurrency. The QuoteContext tracks the user's DisplayCurrency separately from the frozen billing currency, ensuring accurate historical records while allowing flexible UI presentation.
What billing modes does Reasonix support?
The system supports PAYG (pay-as-you-go) as the primary mode, configured via the [billing] section in billing_mode. The mode affects how quotes are generated and validated but does not change the core token-to-cost calculation logic.
Where are rate cards sourced from?
Rate cards originate from provider pricing catalogs, validated through billing.MatchesCatalog and fingerprinted via billing.PricingFingerprint. The doctor billing command ([internal/doctor/billing.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/doctor/billing.go)) can refresh and verify these against live provider data.
Can I query real-time session costs during inference?
Yes. The broadcaster's SessionCostQuote() method returns the current aggregated cost from the active ledger. For programmatic access, use the control API's Balance(ctx) method as implemented in [internal/control/controller.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/control/controller.go).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →