# How Apache Maka Tracks Model Usage, Maintains a Usage Ledger, and Calculates Pricing

> Apache Maka tracks LLM usage with a JSONL ledger aggregates data applies pricing rules for per-turn costs and provides a telemetry API for billing and session recovery.

- Repository: [The Apache Software Foundation/maka](https://github.com/apache/maka)
- Tags: how-to-guide
- Published: 2026-08-27

---

**Apache Maka records every LLM interaction in an append-only JSONL ledger stored in SQLite, buckets the data for efficient aggregation, applies configurable pricing rules to compute per-turn costs, and exposes the data through a telemetry API for billing and session recovery.**

Apache Maka implements a comprehensive model usage tracking system that captures every prompt, token count, and tool call in an immutable ledger. This architecture serves as the source-of-truth for billing and enables exact session recovery after crashes. Understanding how Maka handles model usage tracking, ledger maintenance, and pricing calculation is essential for developers integrating custom models and cost controls.

## Recording Model Calls

When the **runtime-host** executes a model call, it creates a `LlmCallRecord` containing metadata required for billing and reconstruction. This record includes the `model_key` (e.g., `gpt-4-turbo`), `session_id`, unique `request_id`, detailed `token_usage` (prompt, completion, and cached tokens), `usageBasis` (raw token counts), and `costBasis` (monetary value computed later).

The record is ingested via `admitLlmCall()` and persisted to the `usage_llm_calls` table by [`packages/storage/src/sqlite-usage-store.ts`](https://github.com/apache/maka/blob/main/packages/storage/src/sqlite-usage-store.ts). The storage layer uses an **append-only** strategy, inserting the JSON-serialized record into SQLite with a unique storage key:

```typescript
// Simplified flow from sqlite-usage-store.ts
const admitted = await admitLlmCall(record);
this.#lease.database
  .prepare(`INSERT INTO usage_llm_calls(storage_key, id, ts, record_json, session_id)
            VALUES(?, ?, ?, ?, ?)`)
  .run(usageIdentityKey(admitted.id), admitted.id, admitted.ts,
       JSON.stringify(admitted), admitted.session_id);

```

This insertion guarantees **exact-once** semantics; once written, the row is read-only and immutable, forming the foundation of the usage ledger.

## The Immutable Usage Ledger

Maka maintains the ledger as an append-only log of JSONL entries within the `usage` SQLite schema. To support efficient querying and UI rendering, raw records are periodically **bucketed** by model, time interval, and session using `usageBucketKey` from [`packages/storage/src/usage-stats-store.ts`](https://github.com/apache/maka/blob/main/packages/storage/src/usage-stats-store.ts).

A bucket aggregates data into a compact structure:

```json
{
  "model_key": "gpt-4-turbo",
  "time_bucket": "2024-08-27T00:00:00Z",
  "tokens_used": 12345,
  "cost_usd": 3.21
}

```

The `mergeUsageBuckets` utility (from `@maka/core/usage-ledger-merge` and utilized in [`packages/runtime-host/src/server/daily-review-coordinator.ts`](https://github.com/apache/maka/blob/main/packages/runtime-host/src/server/daily-review-coordinator.ts)) combines per-session buckets into global summaries. This merging produces the compact usage statistics displayed in the UI, such as "12 k / 100 k tokens" progress indicators.

## Configuring Pricing Rules

Pricing configuration in Maka is dynamic and model-specific. The system stores overrides in the `usage_pricing_overrides` table, which contains **PricingConfig** entries with `model_key`, `price_per_token_usd`, and optional `price_per_output_token_usd`.

The coordinator loads the active pricing authority via [`packages/storage/src/sqlite-usage-store.ts`](https://github.com/apache/maka/blob/main/packages/storage/src/sqlite-usage-store.ts) (querying `SELECT * FROM usage_pricing_authority`). If no override exists for a specific model, Maka falls back to a **default pricing map** defined in `@maka/core/usage-stats/types`, such as OpenAI’s standard rates (e.g., $0.03 per 1,000 prompt tokens for `gpt-4-turbo`).

This hierarchy allows operators to inject custom rates for private or fine-tuned models without modifying core code.

## Calculating Token Costs

Cost calculation occurs when persisting the `LlmCallRecord`. The system projects `usageBasis` into `costBasis` by applying the active **PricingConfig** to token counts. The logic distinguishes between input (prompt) and output (completion) tokens to support divergent pricing tiers:

```typescript
function computeCost(record: PersistedLlmCallRecord, pricing: PricingConfig) {
  const promptCost = (record.promptTokens / 1000) * pricing.price_per_token_usd;
  const completionCost = (record.completionTokens / 1000) *
                        (pricing.price_per_output_token_usd ?? pricing.price_per_token_usd);
  return promptCost + completionCost;
}

```

The resulting `costUsd` is stored alongside the record, enabling **exact per-turn billing** and accurate aggregate session cost tracking.

## Consuming the Usage Ledger

Maka exposes ledger data through multiple interfaces:

- **Telemetry API** – The `readUsageStats(range)` function (exposed by [`settings-store.ts`](https://github.com/apache/maka/blob/main/settings-store.ts)) returns a `UsageStats` object containing bucketed token totals and cost totals for a specified time window.
- **User Interface** – The [`daily-review-coordinator.ts`](https://github.com/apache/maka/blob/main/daily-review-coordinator.ts) merges buckets and feeds a **Usage Summary** panel displaying tokens consumed, total cost, model breakdowns, and tool call counts.
- **Session Recovery** – On startup, `recoverInterruptedSessions()` scans the `usage_llm_calls` ledger to rebuild session state without re-executing model calls, preserving the exact-once execution guarantee.

## Implementation Examples

### Recording a Model Call

The following pattern demonstrates how runtime components record usage within a write transaction:

```typescript
import { admitLlmCall } from '@maka/core/usage-stats';
import { openWriter, closeWriter } from '@maka/core/async';

async function handleModelCall(sessionId: string, requestId: string, usage) {
  const record = {
    model_key: usage.modelKey,
    session_id: sessionId,
    request_id: requestId,
    promptTokens: usage.promptTokens,
    completionTokens: usage.completionTokens,
    usageBasis: usage.promptTokens + usage.completionTokens,
  };
  const admitted = await admitLlmCall(record);   // validates & adds timestamps
  await openWriter(() => openInteractiveUsageStoresForWrite(), async (stores) => {
    await stores.usage.appendLlmCall(admitted); // writes to SQLite ledger
  });
}

```

### Querying Aggregated Usage

Retrieve bucketed statistics for the last 24 hours using the telemetry API:

```typescript
import { readUsageStats } from '@maka/core/settings-store';

async function getUsageSummary() {
  const stats = await readUsageStats('24h'); // returns UsageStats
  console.log('Tokens used:', stats.total.tokens);
  console.log('Cost (USD):', stats.total.costUsd);
}

```

### Overriding Pricing for Custom Models

Operators can inject custom pricing at runtime, which immediately affects subsequent cost calculations:

```typescript
import { setPricingOverride } from '@maka/core/pricing-store';

await setPricingOverride({
  model_key: 'my-custom-model',
  price_per_token_usd: 0.0015,
  price_per_output_token_usd: 0.0020,
});

```

This override persists in `usage_pricing_overrides` and takes precedence over default rates.

## Key Source Files

| Component | Source Path | Responsibility |
|-----------|-------------|----------------|
| **Usage Persistence** | [`packages/storage/src/sqlite-usage-store.ts`](https://github.com/apache/maka/blob/main/packages/storage/src/sqlite-usage-store.ts) | Appends immutable `LlmCallRecord` entries to the SQLite ledger. |
| **Bucket Aggregation** | [`packages/storage/src/usage-stats-store.ts`](https://github.com/apache/maka/blob/main/packages/storage/src/usage-stats-store.ts) | Groups raw records into time-bucketed summaries using `usageBucketKey`. |
| **Pricing Authority** | [`packages/runtime-host/src/protocol/usage-pricing.ts`](https://github.com/apache/maka/blob/main/packages/runtime-host/src/protocol/usage-pricing.ts) | Loads pricing overrides and provides the active `PricingConfig`. |
| **Ledger Merging** | `@maka/core/usage-ledger-merge` | Utility for merging per-session buckets into compact global summaries. |
| **Daily Review** | [`packages/runtime-host/src/server/daily-review-coordinator.ts`](https://github.com/apache/maka/blob/main/packages/runtime-host/src/server/daily-review-coordinator.ts) | Consumes usage data to render UI summaries and reports. |
| **Architecture Docs** | [`docs/session-task-ledger-lifecycle.md`](https://github.com/apache/maka/blob/main/docs/session-task-ledger-lifecycle.md) | Defines ledger invariants, lifecycle, and recovery procedures. |

## Summary

- Maka captures every LLM interaction as an immutable `LlmCallRecord` in an append-only SQLite ledger.
- The **usage ledger** uses time-based bucketing and merging utilities to aggregate raw data for efficient querying.
- **PricingConfig** objects stored in `usage_pricing_overrides` enable per-model rate customization with fallback defaults.
- Costs are calculated per-turn using distinct input and output token rates, ensuring transparent billing.
- The ledger supports session recovery, telemetry APIs, and UI dashboards while guaranteeing exact-once execution semantics.

## Frequently Asked Questions

### How does Maka prevent duplicate billing after a crash?

Maka implements **exact-once semantics** by writing `LlmCallRecord` entries to the immutable ledger before acknowledging a model response to the user. During recovery, `recoverInterruptedSessions()` scans the `usage_llm_calls` table to reconstruct session state without re-executing calls, ensuring no duplicate tokens are recorded or billed.

### Can I set different prices for input and output tokens?

Yes. The **PricingConfig** interface supports `price_per_token_usd` for input (prompt) tokens and an optional `price_per_output_token_usd` for completion tokens. If the output-specific field is omitted, the system defaults to the input price. Set these via `setPricingOverride()` to apply custom rates for specific models.

### Where is the usage ledger physically stored?

The ledger resides in a SQLite database under the **"usage"** schema, managed by [`packages/storage/src/sqlite-usage-store.ts`](https://github.com/apache/maka/blob/main/packages/storage/src/sqlite-usage-store.ts). Records are stored as JSONL in the `usage_llm_calls` table, while pricing overrides live in `usage_pricing_overrides`. This file-backed storage ensures durability and ACID compliance for billing data.

### How does Maka handle pricing for unknown models?

If a model lacks an entry in `usage_pricing_overrides`, Maka falls back to the **default pricing map** defined in `@maka/core/usage-stats/types`. This map contains hardcoded rates for popular providers like OpenAI. For truly unknown models without defaults, the cost calculation yields zero until an operator injects a pricing override.