Cost
Workspace-scoped cost rollups plus a CRUD surface for the per-model price table. Cost is computed from inference-log rows by joining each (prompt_tokens, completion_tokens) against the active PriceRate for the model — see packages/observability/cost.py for the math. The dashboard at 10 — Cost dashboard is the primary consumer.
The router lives at packages/observability/cost_routes.py.
Endpoints
| Method | Path | Auth | Summary |
|---|---|---|---|
| GET | /admin/cost/by-model | Bearer | Per-(model, adapter) rollup for the workspace. |
| GET | /admin/cost/by-day | Bearer | Daily rollup (sparkline source). |
| GET | /admin/cost/prices | Bearer | List configured per-model rates. |
| PUT | /admin/cost/prices | Bearer | Upsert one model's rate (platform admin only). |
| DELETE | /admin/cost/prices/{model_id} | Bearer | Drop a model's configured rate. |
GET /admin/cost/by-model
Query params: since, until (ISO-8601, optional). Returns {"rows": [CostRollupRow]}:
{
"rows": [
{
"workspace_id": "default",
"model_id": "llama3.2:3b",
"adapter_id": null,
"prompt_tokens": 12480,
"completion_tokens": 4900,
"total_tokens": 17380,
"request_count": 64,
"amount": 0.0034,
"currency": "USD"
}
]
}curl -H "Authorization: Bearer $BIGWAVE_ADMIN_TOKEN" \
"http://localhost:8000/admin/cost/by-model?since=2026-04-01T00:00:00Z"GET /admin/cost/by-day
Same time bounds as by-model. Returns {"rows": [CostDailyRow]}, one row per UTC calendar day. Used to draw the cost sparkline on the overview page.
curl -H "Authorization: Bearer $BIGWAVE_ADMIN_TOKEN" \
"http://localhost:8000/admin/cost/by-day?since=2026-04-01T00:00:00Z&until=2026-05-01T00:00:00Z"GET /admin/cost/prices
Lists the currently-configured per-1 000-token rates. The price table is in-memory on app.state.cost_repo.prices; persisted rates are reloaded at lifespan boot.
{
"rates": [
{
"model_id": "llama3.2:3b",
"prompt_per_1k": 0.0,
"completion_per_1k": 0.0,
"currency": "USD"
},
{
"model_id": "gpt-4o-mini",
"prompt_per_1k": 0.15,
"completion_per_1k": 0.60,
"currency": "USD"
}
]
}PUT /admin/cost/prices
Body schema is the in-route _PriceRateBody (packages/observability/cost_routes.py:22):
{
"model_id": "gpt-4o-mini",
"prompt_per_1k": 0.15,
"completion_per_1k": 0.60,
"currency": "USD"
}Returns {"status": "ok", "model_id": "..."}. Negative rates are rejected with 400.
curl -X PUT -H "Authorization: Bearer $BIGWAVE_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model_id":"gpt-4o-mini","prompt_per_1k":0.15,"completion_per_1k":0.60}' \
http://localhost:8000/admin/cost/pricesDELETE /admin/cost/prices/{model_id}
Removes the configured rate for model_id. Subsequent rollups for that model report amount = 0.0 until a rate is re-uploaded.
Operator notes
- Workspace fence. Rollup routes filter on the bearer principal's workspace; price routes are global (the rate table is shared across tenants — tenants do not see each other's consumption, but do share the catalog of rates). See workspace fence.
- Platform admin vs workspace admin. Until RBAC ships, the bearer token gates everything; the operator-level
BIGWAVE_ADMIN_TOKENis the only role. In intent: workspace admins read rates, platform admins edit them. 503causes.app.state.cost_repois wired in lifespan; routes return503with"cost rollup repository not initialised"when missing.- Cost source of truth. Rollups are computed from
inference_log(see/admin/inference); rows blocked by guardrails (status=blocked) carry zero token counts and therefore contribute zero amount. - Concept page. See 10 — Cost dashboard for the operator-facing tour.