Crux
GuidesObservability

Cost tracking

Attribute model spend to prompts, models, sessions, flows, and steps.

Cost tracking answers a different question than token budgeting. Token budgets protect prompt context windows. Cost tracking tells you what the model call actually cost, where that cost came from, and when a tenant, session, flow, or prompt crossed a spend threshold.

Use withCostTracking() when you want model spend to show up in devtools, the CLI, the TUI, and OpenTelemetry without writing provider-specific accounting code.

import { config } from '@crux/core'
import { modelPricing, withCostTracking } from '@crux/core/cost'

const costs = withCostTracking({
  pricing: modelPricing({
    'gpt-4o': { input: 2.5, output: 10 },
    'claude-sonnet-4-20250514': { input: 3, output: 15 },
  }),
  budget: {
    warn: 1,
    limit: 5,
  },
})

config({
  plugins: [costs.asPlugin()],
})

Actual cost vs estimates

Crux records provider-reported cost when the adapter exposes _meta.cost. That is the preferred path because it reflects provider-side billing, routing, and model aliases. OpenRouter responses are the main reference case.

When _meta.cost is missing, Crux estimates from token usage and the pricing table:

modelPricing({
  'gpt-4o': {
    input: 2.5, // USD per 1M input tokens
    output: 10, // USD per 1M output tokens
  },
})

Estimates are useful for dashboards and guardrails, but they are still estimates. If your provider changes pricing, update the table.

Reports

The tracker keeps an in-memory report for the current runtime process:

const report = costs.getReport()

report.total.cost
report.byPrompt['support-reply']
report.byModel['gpt-4o']
report.byFlow['flow_123']
report.bySession['session_123']

Use costs.reset() to clear the process report, or costs.reset(sessionId) to clear one session.

Budgets

warn emits a cost:warn event once. limit emits cost:limit and throws CostLimitError after the call has been attributed.

const costs = withCostTracking({
  budget: {
    warn: 1,
    limit: 5,
    onLimit(report) {
      console.error('cost limit exceeded', report.total.cost)
    },
  },
})

The failed call is still recorded. That makes debugging straightforward: devtools and OTel can show the exact call that crossed the limit.

Where it appears

The same cost events power all Crux observability surfaces:

  • crux cost shows the latest report and model/prompt breakdown.
  • crux dev --tui shows cost totals and budget events in the terminal dashboard.
  • Web devtools shows cost totals and budget status on the dashboard.
  • @crux/otel emits crux.cost.report, crux.cost.warn, and crux.cost.limit spans with cost attributes.

Scope

Cost tracking observes model execution. It does not decide which model to call, retry a cheaper model, or split tenant billing storage. Those belong in routing, application policy, or your billing system. The cost tracker gives those layers a reliable signal.

On this page