Usage
Introduction
Section titled “Introduction”Usage is how you find out what the models cost and who used them. Use it to feed your own metrics, to show users their consumption, or to enforce caps such as the model gateway’s. Every model call reports tokens. The kit gives you two ways to use them: a sink that streams events to your own metrics, and a store that keeps every call so the server can report on it.
Sending events to your metrics
Section titled “Sending events to your metrics”import type { UsageSink } from '@pilotoo/agent-runtime';
const usage: UsageSink = { record: (event) => metrics.emit(event.kind, event.attributes) };Pass it as the usageSink option of PilotooModule.forRoot (token USAGE_SINK). The default is a debug log line per run.
- You get one
model_callevent per model call that reported tokens, a subagent’s included, and onerunevent when the run settles. - Attribute names follow OpenTelemetry GenAI where one exists.
gen_ai.usage.input_tokensincludes the cached tokens thatgen_ai.usage.cache_read.input_tokensandgen_ai.usage.cache_creation.input_tokensalso report. - Events carry no credentials and no message text.
- The sink is never awaited, and a failing one is warned about once per run.
Reporting tokens and cost
Section titled “Reporting tokens and cost”Every model call is also kept in a UsageStore, beside whatever your sink does, and read back at GET /usage?from&to[&scope=own|tenant]. Add prices to get cost.
import { createUsagePriceList, PilotooModule } from '@pilotoo/server';
PilotooModule.forRoot({ // Per million tokens; a cached read or write without its own price is charged as input. usagePrices: createUsagePriceList({ currency: 'USD', models: { 'anthropic:claude-sonnet-4-6': { input: 3, output: 15, cacheRead: 0.3, cacheCreation: 3.75 } }, }),});models is keyed as a profile names its model, provider:model_id. currency is an ISO 4217 code, and input and output are required for each model. Instead of code, point USAGE_PRICES_FILE at a JSON file of the same shape ({ currency, models }). Unset reports tokens only.
To let some users see the whole tenant, set the roles:
USAGE_ADMIN_ROLES=adminscope=tenant answers 403 without a usage-admin role. Unset means nobody.
Showing usage in your client
Section titled “Showing usage in your client”createUsageReader(apiUrl, fetch, { credentials }) from @pilotoo/chat-core reads the report, and useUsageReport(reader, query) holds it for a view. UsageSection from @pilotoo/ui/settings is the settings section the demo web app mounts, with its strings in SettingsLabels.
usage: <UsageSection reader={usage} />,Options
Section titled “Options”| Setting | Default |
|---|---|
usageSink |
A debug log line per run. |
usageStore |
PostgreSQL (tables created on first use; see Storage) when the module uses it. null without a database, and /usage answers 501. |
usagePrices |
USAGE_PRICES_FILE, a JSON file of the same shape. Unset reports tokens only. |
USAGE_ADMIN_ROLES |
Comma-separated roles that may ask for scope=tenant. Unset means nobody. |
What the report guarantees
Section titled “What the report guarantees”scope=own(the default) reads only the caller’s calls.scope=tenantreads the caller’s tenant split by user, never another tenant.fromis inclusive andtoexclusive, at most 366 days apart, and days are UTC days.runscounts distinct runs in each figure. A run whose calls span two days counts on both days but once in the totals, its agent and its user. It is absent from the split by model, since one run can call several.- Cost is computed when the report is read, so a new price list reprices the past. The calls of an unpriced model are counted in
unpricedCallsinstead of costing 0. - Usage is accounting: deleting a conversation keeps its calls.
The model gateway records the calls it lends under the agent id model-gateway, and its monthly caps read this store.
Next steps
Section titled “Next steps”- Model gateway for caps built on this store.
- Schedules: a run’s usage is under its
run_id. - Models for the
provider:model_idnames prices use.