How Paperclip Tracks Costs by Aggregating Token Usage: A Deep Dive into the ACP Pipeline
Paperclip tracks costs by capturing token usage at the AI-agent runtime level, persisting each event to a cost_events table, and aggregating those records with SQL to power dashboards, billing reports, and daily token caps.
Paperclip is an open-source AI compute platform (ACP) that provides infrastructure for running AI agents at scale. Understanding how it tracks costs by aggregating token usage reveals a well-engineered pipeline that transforms raw LLM consumption into actionable financial metrics. This article breaks down the complete data flow from runtime emission to UI display, citing the exact source files and functions that make it work.
Runtime Emission: Where Token Tracking Begins
Every AI-agent execution in Paperclip starts with precise measurement. The Tool Runtime Metrics service (server/src/services/tool-runtime-metrics.ts) emits an ACP UsageUpdate message for each agent run. This message contains:
inputTokens– tokens sent to the LLMoutputTokens– tokens received from the LLMused– total bytes consumedcostUsd– derived monetary cost
These fields form the raw data layer for all downstream cost calculations. The runtime captures usage at the moment of inference, ensuring no token goes uncounted.
Persisting Usage Events to the Database
Once emitted, usage data lands in the Run-Log Store (server/src/services/run-log-store.ts). This service writes a row to the cost_events table on every heartbeat of a running job.
Key columns in cost_events include:
| Column | Type | Purpose |
|---|---|---|
inputTokens |
integer | Tokens sent to the model |
outputTokens |
integer | Tokens received from the model |
costCents |
integer | Cost stored as integer cents for precision |
companyId |
string | Organization grouping for aggregations |
recordedAt |
timestamp | When the usage occurred |
Storing cost in cents as integers avoids floating-point rounding errors—a critical detail for financial accuracy at scale.
SQL Aggregation: Computing Cost Summaries
The Status-Cards service (server/src/services/status-cards.ts) performs the heavy lifting of aggregating token usage into meaningful metrics. It uses SQL SUM() operations to compute totals across multiple dimensions.
Per-Day Aggregation
// server/src/services/status-cards.ts
export async function getDailyCost(companyId: string) {
const rows = await db
.select({
tokens: sql<number>`coalesce(sum(${costEvents.inputTokens} + ${costEvents.outputTokens}), 0)::int`,
costCents: sql<number>`coalesce(sum(${costEvents.costCents}), 0)::int`,
})
.from(costEvents)
.where(eq(costEvents.companyId, companyId))
.groupBy(sql`date_trunc('day', ${costEvents.recordedAt})`);
return rows;
}
This query:
- Sums
inputTokens + outputTokensfor total token consumption - Sums
costCentsfor total spend - Groups results by calendar day using PostgreSQL's
date_trunc
The same pattern extends to per-run and per-company aggregations that feed the Cost Summary APIs.
REST API Layer: Typed Cost Endpoints
Aggregated data surfaces through the Costs API (server/src/api/costs.ts), consumed by the UI via ui/src/api/costs.ts. The API returns strongly-typed objects:
CostSummary– high-level spend overviewCostByAgent– usage broken down by individual agentsFinanceSummary– billing-ready totals
Fetching Cost Data Client-Side
import { costsApi } from '@/api/costs';
// Get the cost-summary for a company over the last 30 days
const summary = await costsApi.summary('company-123', {
from: '2024-07-01',
to: '2024-07-31',
});
console.log(summary); // → { tokens: 123456, costCents: 487, … }
The API accepts date ranges, enabling flexible reporting periods for dashboards and invoices.
UI Display: Formatting and Daily Cap Enforcement
The final layer transforms raw numbers into human-readable metrics. The Status Cards formatting module (ui/src/pages/StatusCards/format.ts) provides two essential helpers:
formatTokens()– converts token counts to compact notation (e.g., 96,000 → "96.0k")formatCents()– converts cents to dollar strings (e.g., 48 → "$0.48")
Displaying Token/Cost Pairs
import { formatTokens, formatCents } from '@/pages/StatusCards/format';
const tokens = 96_000; // from the API
const cents = 48; // $0.48
const label = `${formatCents(cents)} · ${formatTokens(tokens)}`;
// label => "$0.48 · 96.0k tok"
This concise format appears throughout Paperclip's dashboards, giving users immediate cost visibility.
Daily Token Cap Enforcement
Paperclip also enforces spending limits at the UI layer. It checks aggregated daily totals against configured caps and pauses auto-updates when thresholds are reached. The test file ui/src/pages/StatusCards/format.test.ts validates this cap logic alongside formatting functions.
Key Source Files in the Cost Pipeline
| Component | File Path | Role in Cost Tracking |
|---|---|---|
| Runtime metrics emission | server/src/services/tool-runtime-metrics.ts |
Emits ACP UsageUpdate with token counts and USD cost |
| Event persistence | server/src/services/run-log-store.ts |
Writes to cost_events table |
| SQL aggregation | server/src/services/status-cards.ts |
Computes daily/company totals with SUM() |
| REST API | server/src/api/costs.ts |
Exposes typed cost endpoints |
| UI API client | ui/src/api/costs.ts |
Calls server cost-summary endpoints |
| Formatting utilities | ui/src/pages/StatusCards/format.ts |
Renders tokens and cents for display |
| Cap enforcement tests | ui/src/pages/StatusCards/format.test.ts |
Validates daily token limit logic |
Summary
Paperclip's cost tracking system demonstrates a clear separation of concerns across five pipeline stages:
- Capture – Runtime metrics service records every token at inference time
- Persist – Run-Log Store writes normalized cost events to PostgreSQL
- Aggregate – Status-Cards service uses SQL
SUM()for efficient grouping - Serve – Costs API exposes typed endpoints for flexible querying
- Display – UI formatting helpers render readable metrics with cap enforcement
This architecture scales from individual agent runs to enterprise billing reports while maintaining cent-level financial precision throughout.
Frequently Asked Questions
How does Paperclip avoid rounding errors in cost calculations?
Paperclip stores all monetary values as integer cents in the costCents column rather than floating-point dollars. This approach eliminates rounding errors during aggregation and ensures billing accuracy. The conversion to display format ($0.48) only happens at the UI layer.
Can I query cost data for custom date ranges?
Yes. The Costs API accepts from and to parameters in ISO date format. The costsApi.summary() method in ui/src/api/costs.ts passes these directly to the server endpoint, enabling arbitrary reporting periods for dashboards or invoices.
What happens when a company hits its daily token cap?
The UI enforces caps by checking aggregated daily totals against configured limits. When thresholds are reached, auto-updates pause to prevent additional spend. This logic is tested in ui/src/pages/StatusCards/format.test.ts alongside the formatting utilities.
Where does the initial USD cost value come from?
The costUsd field originates in the Tool Runtime Metrics service (server/src/services/tool-runtime-metrics.ts), which calculates cost based on model-specific pricing at runtime. This derived value is then converted to integer cents before database storage.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →