# How to Configure Real-Time Cost Tracking and Per-Request Cost Breakdown in AxonHub

> Learn to configure real-time cost tracking and per-request cost breakdown in AxonHub. Map token usage to pricing for granular cost insights and optimize your LLM expenses.

- Repository: [Loop/axonhub](https://github.com/looplj/axonhub)
- Tags: how-to-guide
- Published: 2026-03-06

---

**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`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/channel_price.go), the `CreateChannelModelPrice` function persists pricing entries.

### Configuration via YAML

Add pricing definitions to your [`conf/example.yml`](https://github.com/looplj/axonhub/blob/main/conf/example.yml):

```yaml
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`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/channel_price.go):

```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`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/cost_calc.go) using the `ComputeUsageCost` function:

```go
func ComputeUsageCost(usage *objects.Usage, price objects.ModelPrice) (items []objects.CostItem, total float64)

```

The function is called from [`internal/server/biz/usage_log.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/usage_log.go) after a request finishes:

```go
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:

```graphql
type UsageLog {
  id: ID!
  requestID: ID!
  totalCost: Float!
  costItems: [CostItem!]!
  costPriceReferenceID: String
}

```

Each `CostItem` provides granular detail:

```graphql
type CostItem {
  name: String!
  units: Float!
  pricePerUnit: Float!
  cost: Float!
}

```

Fetch a specific request's cost breakdown:

```graphql
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`](https://github.com/looplj/axonhub/blob/main/internal/server/gql/dashboard.resolvers.go) aggregates daily totals.

It queries the `usage_logs` table using:

```go
sql.As(fmt.Sprintf("COALESCE(SUM(%s), 0)", s.C(usagelog.FieldTotalCost)), "total_cost")

```

The `DailyRequestStats` GraphQL type returns:

```graphql
type DailyRequestStats {
  period: Date!
  requests: Int!
  totalTokens: Int!
  cost: Float!
}

```

Query for dashboard data:

```graphql
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:

1. **Define pricing** for your target model in [`conf/example.yml`](https://github.com/looplj/axonhub/blob/main/conf/example.yml) or via the API using `CreateChannelModelPrice`.
2. **Send a test request** through AxonHub to the configured model.
3. **Query the usage log** using the GraphQL API to verify `totalCost` and `costItems` are populated.
4. **Check the dashboard** at `/dashboard` to see the aggregated cost reflect in the daily statistics.

## Summary

- **Define pricing** using `ChannelModelPrice` entries in [`internal/server/biz/channel_price.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/channel_price.go) or via YAML configuration.
- **Automatic calculation** happens in [`internal/server/biz/cost_calc.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/cost_calc.go) via `ComputeUsageCost`, triggered after each request in [`internal/server/biz/usage_log.go`](https://github.com/looplj/axonhub/blob/main/internal/server/biz/usage_log.go).
- **Granular data** is stored in the `usage_logs` table columns `total_cost`, `cost_items`, and `cost_price_reference_id`.
- **GraphQL exposure** through `internal/server/gql/cost.graphql` provides `totalCost`, `costItems`, and `costPriceReferenceID` on the `UsageLog` type.
- **Real-time aggregation** in [`internal/server/gql/dashboard.resolvers.go`](https://github.com/looplj/axonhub/blob/main/internal/server/gql/dashboard.resolvers.go) powers the dashboard's daily cost totals using SQL sums on the `total_cost` column.

## Frequently Asked Questions

### How does AxonHub calculate costs for different token types?

AxonHub's `ComputeUsageCost` function in [`internal/server/biz/cost_calc.go`](https://github.com/looplj/axonhub/blob/main/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`](https://github.com/looplj/axonhub/blob/main/conf/example.yml) at startup, you can create or update `ChannelModelPrice` records programmatically using the `CreateChannelModelPrice` method in [`internal/server/biz/channel_price.go`](https://github.com/looplj/axonhub/blob/main/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`](https://github.com/looplj/axonhub/blob/main/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`](https://github.com/looplj/axonhub/blob/main/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.