Attribute AI cost by customer and feature
Build a provider-neutral cost ledger that explains which product behavior generated spend.
What you will build
An internal cost event emitted for every provider attempt. It joins product context—customer, workspace, feature, workflow, and retry—with canonical deployment identity and an ALLM quote.
1. Define a provider-neutral usage event
generation_idstringrequired
Stable ID for the logical product generation.
attempt_idstringrequired
Unique ID for each provider attempt, including retries and fallbacks.
customer_idstringrequired
Tenant charged or analyzed.
featurestringrequired
Product feature or workflow stage.
deployment_idstringrequired
Concrete ALLM deployment used by the provider attempt.
input_tokensintegerrequired
Total input tokens reported by the provider.
cached_input_tokensintegerrequired
Cached subset of input tokens.
output_tokensintegerrequired
Generated output tokens.
2. Normalize each attempt with ALLM
TypeScript
async function priceAttempt(event: UsageEvent) {
const [deployment, quote] = await Promise.all([
allm.deployments.retrieve(event.deployment_id),
allm.pricing.calculate({
deployment: event.deployment_id,
inputTokens: event.input_tokens,
cachedInputTokens: event.cached_input_tokens,
outputTokens: event.output_tokens,
}),
]);
return {
...event,
canonical_model_id: deployment.model_id,
provider_id: deployment.provider_id,
provider_model_id: deployment.provider_model_id,
cost_usd: quote.totalUsd,
rate_observed_at: quote.rateCard.observed_at,
rate_source_url: quote.rateCard.source_url,
};
}3. Persist attempts idempotently
SQL
create table ai_cost_events (
attempt_id text primary key,
generation_id text not null,
customer_id text not null,
feature text not null,
deployment_id text not null,
canonical_model_id text not null,
provider_id text not null,
input_tokens bigint not null,
cached_input_tokens bigint not null,
output_tokens bigint not null,
cost_usd numeric(20, 6) not null,
rate_observed_at timestamptz not null,
occurred_at timestamptz not null
);Use attempt_id as the idempotency key. One logical generation may have multiple paid attempts after a timeout, retry, or failover.
4. Aggregate in product terms
SQL
select
customer_id,
feature,
provider_id,
count(distinct generation_id) as generations,
count(*) as provider_attempts,
sum(cost_usd) as cost_usd,
sum(cost_usd) / nullif(count(distinct generation_id), 0) as cost_per_generation
from ai_cost_events
where occurred_at >= date_trunc('month', now())
group by customer_id, feature, provider_id
order by cost_usd desc;Example report
JSON
[
{
"customer_id": "org_acme",
"feature": "document_extraction",
"provider_id": "openai",
"generations": 18420,
"provider_attempts": 18703,
"cost_usd": "1284.372500",
"cost_per_generation": "0.069727"
}
]Accounting rules
- Count every paid provider attempt, including failures and fallbacks.
- Keep estimated preflight cost separate from settled cost based on actual usage.
- Retain the rate observation time and source used by each quote.
- Reconcile provider invoices, but do not overwrite historical normalized events with today’s rate card.
- Alert on cost per successful generation, retry rate, and customer-level margin—not only total spend.