Skip to content
Pilotoo
v0.1

Usage

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.

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_call event per model call that reported tokens, a subagent’s included, and one run event when the run settles.
  • Attribute names follow OpenTelemetry GenAI where one exists. gen_ai.usage.input_tokens includes the cached tokens that gen_ai.usage.cache_read.input_tokens and gen_ai.usage.cache_creation.input_tokens also report.
  • Events carry no credentials and no message text.
  • The sink is never awaited, and a failing one is warned about once per run.

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:

.env
USAGE_ADMIN_ROLES=admin

scope=tenant answers 403 without a usage-admin role. Unset means nobody.

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.

apps/web/src/features/settings/settings-screen.tsx
usage: <UsageSection reader={usage} />,
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.
  • scope=own (the default) reads only the caller’s calls. scope=tenant reads the caller’s tenant split by user, never another tenant.
  • from is inclusive and to exclusive, at most 366 days apart, and days are UTC days.
  • runs counts 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 unpricedCalls instead 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.

  • Model gateway for caps built on this store.
  • Schedules: a run’s usage is under its run_id.
  • Models for the provider:model_id names prices use.