GNL
Docs menu
Core · Free@gnldev/durable

Cost & pricing observability

Computes per-run token/cost/trace from the journal; reads the pricing table (__pricing__) from the journal, versioned.

What it's for / when to use it#

GNL writes model calls' usage info (input/output/cached tokens) to the journal. getRunCost scans this journal POST-HOC to produce an exact, deterministic per-run cost — not a live/estimated "cost guard", but a literal sum of what actually happened. Use it when you need to answer "how much did this conversation cost me, how much did each model consume" after a run finishes, when generating a billing/usage report, or when feeding an OTel-compatible observability chain (trace exporter).

Setup / import#

No separate sub-package is needed; it comes from the @gnldev/durable root export.

import { getRunCost, toTraceSpans } from '@gnldev/durable';

Step-by-step usage#

getRunCost takes a JournalReader and a runId; it sums the usage of all model entries in the run and computes cost from the pricing table (default DEFAULT_PRICING):

const cost = await getRunCost(reader, runId);
// { runId, inputTokens, outputTokens, cachedTokens, totalTokens,
//   modelCalls, toolCalls, costUsd, byModel: { 'claude-sonnet-5': { calls, tokens, costUsd }, ... } }

If you want to pass your own pricing table, you can override it with opts.pricing; if the journal has no model id (e.g. a mock model), assign a default with opts.modelId:

const cost = await getRunCost(reader, runId, { modelId: 'claude-sonnet-5' });

To feed an OTel exporter or for a waterfall visualization, convert the journal to a gen_ai.* semantic-compatible span list:

const spans = await toTraceSpans(reader, runId);
// [{ name: 'llm.generate' | 'tool.execute', kind: 'model' | 'tool', runId, seq, attributes }, ...]

If you want to update the pricing table without a deploy, you can read the __pricing__ document from the journal to get the effective table (falls back to DEFAULT_PRICING if there's no record in the journal):

import { effectivePricingTable } from '@gnldev/durable';
const table = await effectivePricingTable(journal); // __pricing__ (varsa) > DEFAULT_PRICING

Studio serves the same computation over REST — you don't need to re-implement it in your own backend:

Studio REST
GET /runs/:id/cost   → getRunCost(reader, id)
GET /runs/:id/trace  → journal'dan waterfall span'leri (maliyet dahil)

API reference#

fngetRunCost

Computes total/per-model token and USD cost for a run from the journal (async).

fntoTraceSpans

Converts journal entries into a TraceSpan list compatible with OTel gen_ai semantics (async).

typeRunCost

getRunCost return type: inputTokens/outputTokens/cachedTokens/totalTokens/modelCalls/toolCalls/costUsd/byModel.

typeTraceSpan

A single span record with name/kind/runId/seq/attributes fields.

constDEFAULT_PRICING

An approximate model pricing table ($/1M tokens) for the current Anthropic and OpenAI families. Lookup is longest-prefix, so a single entry prices a whole family, and the journal’s own pricing document layers over it (effectivePricingTable).

fnpriceFor

Resolves the price for a modelId: exact match first, otherwise the longest (most specific) prefix match.

fncostOf

Computes the USD cost from a usage record; cached tokens are priced separately.

fnreadPricing

Reads the __pricing__ PricingDoc live from the journal (undefined if absent).

fneffectivePricingTable

Effective pricing table: journal __pricing__ (if present) > DEFAULT_PRICING.

constPRICING_KEY

The journal key for the pricing document: '__pricing__'.

typeModelPricing

A pricing record with inputPer1M / outputPer1M / cachedInputPer1M (optional) fields.

Note
The figures in the DEFAULT_PRICING table are approximate — you can switch to a live, versioned pricing table tailored to your own agreement by writing a __pricing__ document to the journal (via Studio or directly); every update increments PricingDoc.version and keeps a full history via the audit log.
Related
See the budget-quota page if you want to enforce quotas/limits, and the studio-inspector page to inspect this cost/trace data visually in Studio's timeline.