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 context
  • Currency: 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 rendering
  • BillingMode: PAYG (pay-as-you-go) or equivalent subscription handling
  • DisplayRequest: 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 versions
  • billing.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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →