KDCube
← Our Journal

Three layers stack, and keeping them apart is the whole trick. Tracking asks what did this call use? — a tracker decorator emits an accounting event. Pricing asks what did that usage cost? — a reported dollar figure or a descriptor price table turns usage into USD. Enforcement asks may it run, and who pays? — the guard reserves and settles that cost. Define the first two, and the paid work you do is countable and chargeable exactly like a model call.

TRACK · PRICE · ENFORCE Tracking what did this call use? a tracker decorator emits one AccountingEvent ServiceUsage + context Pricing what did that usage cost? reported cost_usd, or the descriptor price table usage → USD Enforcement may it run, and who pays? the guard reserves, then settles the actual cost verify · reserve · settle Define the first two, and the paid work your application does is countable and chargeable exactly like a model call.
Track, price, enforce — define the first two and your paid work charges like a model call.

The anatomy of a tracked call

A tracked call is an ordinary function wearing a tracker decorator. When it runs inside a bound accounting context, the decorator reads four extractors off the result and arguments, builds a ServiceUsage, and writes one AccountingEvent carrying a snapshot of who/where/which-turn. The core primitive is AccountingTracker; track_llm, track_embedding, and track_web_search are thin factories over it.

from kdcube_ai_app.infra.accounting import AccountingTracker, ServiceType, ServiceUsage


def track_document_ocr(**extractors):
    return AccountingTracker(ServiceType.VISION, **extractors)


def _ocr_usage(result, *args, **kwargs) -> ServiceUsage:
    pages = int(result.get("pages_processed", 0))
    return ServiceUsage(
        document_pages=pages,
        requests=1,
        cost_usd=round(pages * 0.01, 6),   # the pricing lever — $0.01 / page
    )


@track_document_ocr(usage_extractor=_ocr_usage)
async def ocr_document(path: str) -> dict:
    return await call_acme_ocr(path)

Two facts shape the rest. The event is emitted only when accounting storage is bound — outside a bound context the decorator is a transparent no-op; a chat turn binds it in the processor, a standalone flow binds it through the economics guard. And the event carries a context snapshot, so attribution rides along without threading it through arguments.

ANATOMY OF A TRACKED CALL Decorated function @track_...(extractors) async def ocr_document(...) four extractors read result + arguments: provider · model usage · metadata no-op if storage unbound ServiceUsage unit counts — pages, tokens, queries, requests cost_usd — the lever AccountingEvent service_type · provider · usage · context snapshot into the bound scope → guard settles builds emits The event carries who / where / which-turn from the accounting context, so attribution rides along without extra arguments.
Decorator and four extractors build a ServiceUsage inside an AccountingEvent, emitted into the bound scope.

Making it charge

Tracking records the usage; pricing turns it into the dollars economics reserves and settles. Two bundle-friendly levers, no platform edits: report the cost directly — set ServiceUsage.cost_usd to what the provider billed, and the per-turn calculator honors it as the charge; or price from the descriptor — the economics descriptor economics.yaml carries a price_tables section, read live, a whole-table replacement whose llm section must keep the reference model, so you replace the table and add your provider/model to the right service section.

One honest caveat: the per-turn cost calculator prices llm, embedding, and web_search today, each honoring a reported cost_usd. A tracked call under a new service type shows up in usage analytics but is priced at zero by the turn calculator — so to actually charge a user, emit under the closest priced type and carry a real cost_usd.

This is not hypothetical: the built-in Claude Code runtime runs on exactly this path. A Claude Code turn is accounted as ordinary LLM usage — same @track_llm tracker, provider="anthropic", tokens and a cost_usd parsed from the CLI's stream-json output — and the calculator prices it two ways: an alias-aware price-table lookup when the model is priced, and the reported cost_usd as the fallback otherwise. Report the cost under a priced type and your own service is accounted the same way the platform accounts its runtimes.

Then it settles under the guard

Tracking and pricing make the cost visible; the economics guard makes it authorized and paid. Run the tracked call inside EconomicsGuard, and the guard verifies the user can afford it, reserves the estimate, and settles the reported cost when the block exits — one ledger entry, no double counting.

async with EconomicsGuard(self, subject=subject, scope_id=f"ocr_{doc_id}",
                          flow="reports.ocr",
                          estimate=EconomicsEstimate(reservation_usd=0.20)):
    result = await ocr_document(path)     # emits into the guard's scope; settles on exit

That is the whole arc: define the service so it counts, price it so it costs, run it under the guard so it pays its way — the same treatment the platform gives its own model calls.