Cost and Token Observability
AI and cloud costs can grow quietly when usage is disconnected from teams, services, and deployment changes
Token spend visible before finance week
Andrey Lesnikov · Senior DevOps Platform Engineer · 2026-10-22 · sources checked 2026-10-22
Part of LLM inference on Kubernetes
Most llm cost optimization work starts after the invoice: scrapes, dashboards, weekly reviews. An llm token budget that matters operationally sits before the GPU call — reserve estimated spend, deny when the window is full, settle when usage returns.
This guide is based only on files in ai-runtime-platform: docs/budget-guard.md, docs/budget-guard.policy.json, docs/EVIDENCE.md, docs/budget_guard_results.json, docs/maturity-matrix.md, and the gateway modules those docs describe (app/gateway/budget_guard.py and related tests). Numbers below are copied from those evidence files on this test suite. They are not a multi-region billing guarantee.
| Source | Status / wording |
|---|---|
docs/maturity-matrix.md |
Inference budget guard — Supported (atomic reserve/settle via Redis Lua; fail-closed; CI evidence) |
docs/budget-guard.md |
Guard is off by default (INFERENCE_BUDGET_ENABLED unset): no reservation, no budget headers, no behaviour change |
docs/EVIDENCE.md |
Budget claims: Tested locally; re-run in CI |
When enabled without REDIS_URL, the in-process backend is used — budgets are then per gateway process, not shared across replicas. Set REDIS_URL for a shared budget (docs/budget-guard.md).
From docs/budget-guard.md:
drain check
→ global admission
→ tenant / fleet concurrency admission
→ inference budget reserve ← this guard
→ tenant attribution pre-reserve (signals)
→ Control Plane governance
→ route / execute
→ settle budget + reconcile attribution
On DENY, the gateway returns 429 (inference_budget_exceeded) or 503 (budget_store_* / budget_policy_invalid) and does not call the model backend.
It is integrated into the gateway path beside fleet concurrency admission — not a standalone sidecar.
Spend is tracked per scope (tenant:<id> or key:<tenant>:<sha256(apiKey)[:16]>; the raw key never appears in Redis key names, metric labels or error bodies) inside a fixed window. Money is integer micro-USD. Estimates and actual charges round up; the budget cap rounds down (Decimal arithmetic).
Invariant enforced atomically (Redis Lua or in-process lock):
committed + reserved + estimate ≤ budget
Redis script keys: arp:budget:{scope}:window|reservations|amounts. Window rollover resets committed_micros only; in-flight reservations carry into the new window.
| Condition | HTTP | Reason (from docs) |
|---|---|---|
| Would exceed budget | 429 | budget_exceeded |
| Store unavailable | 503 | store_unavailable |
| Store timeout | 503 | store_timeout |
| Corrupt store payload | 503 | store_corrupt |
| Invalid estimate (NaN / infinite / negative) | 503 | store_corrupt |
| Malformed / missing policy while enabled | 503 | malformed_policy |
There is no silent allow when the guard is enabled and the store or policy fails.
Enable with INFERENCE_BUDGET_ENABLED=true and either INFERENCE_BUDGET_POLICY (inline JSON) or INFERENCE_BUDGET_POLICY_FILE. Example file in the repo: docs/budget-guard.policy.json (version budget-guard-1.0), with fields such as budgetUsd, windowSeconds, softWarningRatio, reservationTtlSeconds, per-tenant / per-key budgets, and a prices table (* / default required per the doc table).
INFERENCE_BUDGET_STORE_TIMEOUT_SECONDS (default 1.0) bounds the Redis exchange.
Each reservation is settled exactly once. Highlights from docs/budget-guard.md:
usage block → actual priced usageusage (most streams) → charged at the estimate0)Actual spend above the estimate is committed as-is, so committed spend can exceed the cap by at most the estimate error of in-flight requests; later requests are then denied until the window rolls over.
docs/EVIDENCE.md (section Inference budget guard (this suite)), regenerated via python scripts/generate_budget_evidence.py, records for commit SHA fee974a25d4d939e2d170ffa737b74ca0de2b0d3:
budget-guard-1.0sha256:eff951135b69d4ff1a86da7f40775c60b3459a3462d46f4e88674d9a11e10d6c1580 passed, 0 failed, 0 skipped (real Redis: True)00[142] (budget allows 142), max reserved 994000 of 1000000 micro-USD over 10 rounds10/10 rounds, leaked reserved micro-USD 0Claim rows in the same file mark these as Tested locally; re-run in CI, with limits: one Redis — not multi-region billing; estimate may differ from actual usage; settlement can lag.
Decision envelopes are ASC-shaped (schema_version 1.0.0) with decision = ALLOW | DENY (plus verdict / acs_disposition for compatibility with agent-security-contract). Scenario ids listed in evidence include legitimate_within_budget, over_budget_denied, store_down_fail_closed, malformed_policy_fail_closed, and others — see the table in docs/EVIDENCE.md.
CI entrypoints named there: .github/workflows/ci.yaml, .github/workflows/budget-guard.yml.
From docs/budget-guard.md and docs/EVIDENCE.md:
Operating cost and token telemetry next door: Cost and Token Observability and OpenTelemetry LLM observability. Serving context: LLM inference on Kubernetes.
Cases
AI and cloud costs can grow quietly when usage is disconnected from teams, services, and deployment changes
Token spend visible before finance week
AI workloads often start as API calls but become production systems that need identity, policy, scaling, telemetry, and clear ownership
Every model call: policy + trace + budget