How to Configure Real-Time Cost Tracking and Per-Request Cost Breakdown in AxonHub
AxonHub automatically calculates monetary costs for every LLM request by mapping token usage against configurable per-model pricing, storing granular breakdowns in the usage_logs table and exposing them via GraphQL.
AxonHub is an open-source LLM gateway that provides centralized request management and observability. To configure real-time cost tracking and per-request cost breakdown, you must define model-specific pricing in ChannelModelPrice records, enable the automatic cost computation pipeline, and query the exposed GraphQL fields. This guide walks through the exact configuration steps using the actual source implementation.
Define Model Pricing in ChannelModelPrice
AxonHub stores model-specific pricing in the ChannelModelPrice entity. The server loads these prices at startup from your configuration file or via the business logic layer.
In internal/server/biz/channel_price.go, the CreateChannelModelPrice function persists pricing entries.
Configuration via YAML
Add pricing definitions to your conf/example.yml:
channel_models:
- channel_id: "default"
model_id: "gpt-4o"
price:
prompt_tokens: 0.00001
completion_tokens: 0.00002
reference_id: "openai-2024-04"
Programmatic Configuration
To create pricing entries via the Go SDK, use the CreateChannelModelPrice method in internal/server/biz/channel_price.go:
price := objects.ModelPrice{
PromptTokens: 0.00001,
CompletionTokens: 0.00002,
ReferenceID: "openai-2024-04",
}
ctx := context.Background()
_, err := channelPriceService.CreateChannelModelPrice(ctx, channelID, modelID, price)
if err != nil {
log.Fatalf("cannot create price: %v", err)
}
Enable Automatic Cost Computation
Cost is calculated in internal/server/biz/cost_calc.go using the ComputeUsageCost function:
func ComputeUsageCost(usage *objects.Usage, price objects.ModelPrice) (items []objects.CostItem, total float64)
The function is called from internal/server/biz/usage_log.go after a request finishes:
costItems, totalCost, priceReferenceID = s.computeUsageCost(
ctx, params.ChannelID, params.ActualModelID, params.Usage)
mut = mut.SetCost(totalCost).SetCostItems(costItems).SetCostPriceReferenceID(priceReferenceID)
The server always attempts to find a price for the model. Ensure a ChannelModelPrice exists for the model and channel combination. The cost fields are stored in the usage_logs table columns total_cost, cost_items, and cost_price_reference_id.
Query Per-Request Cost Breakdown via GraphQL
AxonHub exposes cost data through its GraphQL API defined in internal/server/gql/cost.graphql.
The UsageLog type includes:
type UsageLog {
id: ID!
requestID: ID!
totalCost: Float!
costItems: [CostItem!]!
costPriceReferenceID: String
}
Each CostItem provides granular detail:
type CostItem {
name: String!
units: Float!
pricePerUnit: Float!
cost: Float!
}
Fetch a specific request's cost breakdown:
query GetUsageCost($id: ID!) {
usageLog(id: $id) {
totalCost
costItems {
name
units
pricePerUnit
cost
}
costPriceReferenceID
}
}
Real-Time Dashboard Aggregation
For real-time cost tracking across all requests, the dashboard resolver in internal/server/gql/dashboard.resolvers.go aggregates daily totals.
It queries the usage_logs table using:
sql.As(fmt.Sprintf("COALESCE(SUM(%s), 0)", s.C(usagelog.FieldTotalCost)), "total_cost")
The DailyRequestStats GraphQL type returns:
type DailyRequestStats {
period: Date!
requests: Int!
totalTokens: Int!
cost: Float!
}
Query for dashboard data:
query DailyStats($start: Date!, $end: Date!) {
dailyRequestStats(start: $start, end: $end) {
period
requests
totalTokens
cost
}
}
Verify Your Configuration
Follow these steps to confirm real-time cost tracking is active:
- Define pricing for your target model in
conf/example.ymlor via the API usingCreateChannelModelPrice. - Send a test request through AxonHub to the configured model.
- Query the usage log using the GraphQL API to verify
totalCostandcostItemsare populated. - Check the dashboard at
/dashboardto see the aggregated cost reflect in the daily statistics.
Summary
- Define pricing using
ChannelModelPriceentries ininternal/server/biz/channel_price.goor via YAML configuration. - Automatic calculation happens in
internal/server/biz/cost_calc.goviaComputeUsageCost, triggered after each request ininternal/server/biz/usage_log.go. - Granular data is stored in the
usage_logstable columnstotal_cost,cost_items, andcost_price_reference_id. - GraphQL exposure through
internal/server/gql/cost.graphqlprovidestotalCost,costItems, andcostPriceReferenceIDon theUsageLogtype. - Real-time aggregation in
internal/server/gql/dashboard.resolvers.gopowers the dashboard's daily cost totals using SQL sums on thetotal_costcolumn.
Frequently Asked Questions
How does AxonHub calculate costs for different token types?
AxonHub's ComputeUsageCost function in internal/server/biz/cost_calc.go multiplies the token count for each category—such as prompt_tokens, completion_tokens, vision_tokens, or audio_tokens—by the corresponding price defined in the ChannelModelPrice entry. The function returns both the total cost and an itemized list of CostItem objects detailing each calculation.
Can I update pricing without restarting the server?
Yes. While the server loads initial pricing from conf/example.yml at startup, you can create or update ChannelModelPrice records programmatically using the CreateChannelModelPrice method in internal/server/biz/channel_price.go. New requests immediately use the updated pricing without requiring a restart, as the cost calculation logic queries the database for the current price at request time.
Where is the per-request cost data stored?
Per-request cost data is persisted in the usage_logs table with three specific columns: total_cost stores the aggregated monetary value, cost_items contains a JSON array of detailed CostItem objects, and cost_price_reference_id records which ChannelModelPrice version was used for the calculation. This schema is defined in the ent schema files and handled by the persistence layer in internal/server/biz/usage_log.go.
How do I query total daily costs for billing purposes?
Use the dailyRequestStats GraphQL query defined in internal/server/gql/dashboard.resolvers.go. This resolver aggregates the total_cost column from the usage_logs table using SQL's SUM function, grouped by day. The query returns a DailyRequestStats type containing the period, requests, totalTokens, and cost fields, allowing you to generate daily or monthly billing reports directly from the API.
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 →