Runaway-cost circuit breaker for LLM and metered API calls: rolling-median anomaly detection, absolute per-call ceiling, monthly budget
Project description
rolling-cost-cap
A runaway-cost circuit breaker for LLM and metered API calls. Judges every observed cost against three independent layers: a rolling-median anomaly cap, an absolute per-call ceiling, and a cumulative monthly budget. Zero dependencies, thread-safe, fully typed, 27 tests.
pip install rolling-cost-cap
Problem
A single malformed request can make an LLM call cost 50x the usual amount: a prompt that triggers maximum-length output, a retry loop that compounds, a model misconfiguration that routes traffic to a tier 5x more expensive. Provider billing alerts fire hours later, and a fixed per-call dollar limit is either too loose to catch anomalies or too tight for legitimate heavy calls.
The core difficulty is that "expensive" is relative. $0.10 is an anomaly when your median call costs $0.02, and completely normal when it costs $0.08. A static threshold cannot express that.
Solution
CostCap keeps a rolling window of recent call costs and trips when a new cost exceeds a multiple of the window's median. The median self-calibrates to your real traffic, so the same configuration works whether your calls cost fractions of a cent or dollars. Two fixed layers back it up: an absolute per-call ceiling that catches runaways before the median has warmed up, and a monthly budget that bounds total damage.
Decision precedence: absolute ceiling, then monthly budget, then rolling median. The median layer stays dormant until min_samples costs have been recorded, so one cheap first call cannot set a hair-trigger baseline. A median of zero (free calls) also keeps it dormant rather than tripping on the first nonzero cost.
This design was extracted from the cost-control layer of InvoiceFlow, where it runs in production as a post-call ceiling on Anthropic API spend.
Usage
from rolling_cost_cap import CostCap
cap = CostCap(
multiplier=3.0, # trip when cost > 3x rolling median
window=50, # median over the last 50 calls
min_samples=5, # median layer dormant until 5 costs recorded
absolute_ceiling=1.00, # never allow a single call over $1
monthly_budget=25.00, # stop everything past $25/month
)
# After each API call, compute the observed cost and evaluate it
usage = response.usage
cost = (usage.input_tokens / 1e6) * 3.00 + (usage.output_tokens / 1e6) * 15.00
decision = cap.evaluate(cost)
if not decision.allowed:
match decision.trip:
case "absolute-ceiling":
alert(f"single call cost ${decision.observed:.2f}")
case "rolling-median":
alert(f"cost {decision.observed / decision.median:.0f}x the median")
case "monthly-budget":
disable_feature_until_next_month()
evaluate judges the cost against the history that preceded it, then records it. Recording happens even when the decision trips, because the money is already spent and hiding real spend would make the monthly total under-count. For pre-call gating with an estimate, use check, which never records:
if not cap.check(estimated_cost).allowed:
return err_budget_exceeded()
The monthly window follows the wall clock (UTC by default) and resets on calendar-month rollover. Inject a clock callable to control time in tests:
cap = CostCap(monthly_budget=25.0, clock=lambda: datetime(2026, 8, 1, tzinfo=timezone.utc))
API
| Member | Behavior |
|---|---|
evaluate(cost) -> Decision |
Judge against prior history, then record. The common post-call path. |
check(cost) -> Decision |
Judge without recording. For pre-call estimates. |
record(cost) -> None |
Record without judging. For backfilling history. |
median |
Current rolling median, or None with empty history. |
monthly_spend |
Cumulative recorded spend this calendar month. |
reset() |
Clear window and monthly state. |
Decision is a frozen dataclass: allowed, trip ("absolute-ceiling" | "monthly-budget" | "rolling-median" | None), observed, cap, median, monthly_spend. All fields reflect state at decision time, before the cost is recorded.
Costs must be non-negative finite numbers; NaN, infinity, negatives, and non-numeric types raise instead of being silently ignored.
Benchmarks
evaluate throughput on Apple Silicon (M-series), CPython 3.14, single thread, via python bench.py:
| Window | Throughput | Latency |
|---|---|---|
| 50 (default) | 72,800 ops/sec | 13.7 us |
| 200 | 45,400 ops/sec | 22.0 us |
| 1,000 | 10,700 ops/sec | 93.0 us |
The median is recomputed from a window snapshot on every decision, which is O(n log n). At realistic API-call rates (tens of calls per second) this is noise; the naive recompute was chosen over an incremental two-heap median because it is simpler to verify and the window is small.
Known limitations
- State is in-process. Each worker, container, or serverless instance caps independently. A fleet of N workers can collectively spend up to N times the monthly budget. Acceptable as an anomaly detector and defense-in-depth; not a substitute for a provider-side billing cap.
- The rolling median can drift under a slow ramp. Costs that grow gradually (each within
multiplierof the current median) raise the baseline without ever tripping. The absolute ceiling is the backstop for this case. evaluateis post-call. It detects that money was spent; it does not prevent the spend. Real prevention lives upstream: bounded retries,max_tokenslimits, andcheckwith estimates where estimation is feasible.- Monthly rollover is calendar-based and UTC by default. There is no proration and no billing-cycle alignment beyond what a custom
clockgives you. - No persistence. A process restart forgets both the window and the month-to-date spend.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rolling_cost_cap-0.1.0.tar.gz.
File metadata
- Download URL: rolling_cost_cap-0.1.0.tar.gz
- Upload date:
- Size: 7.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
28bb927df48088a6bca2317b01d131c80b777dbc61307e51de635f4cd0707f76
|
|
| MD5 |
791c6c60010a5f3778f770d42a433885
|
|
| BLAKE2b-256 |
9f4bcbddedf72bfc5991499dbf094bb5fbd0de427860a9227c628dc2603eea1d
|
Provenance
The following attestation bundles were made for rolling_cost_cap-0.1.0.tar.gz:
Publisher:
publish.yml on coreystevensdev/rolling-cost-cap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rolling_cost_cap-0.1.0.tar.gz -
Subject digest:
28bb927df48088a6bca2317b01d131c80b777dbc61307e51de635f4cd0707f76 - Sigstore transparency entry: 2075648351
- Sigstore integration time:
-
Permalink:
coreystevensdev/rolling-cost-cap@c0ae7cfb4f50a40f40c67556542e29df4a4777fc -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/coreystevensdev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c0ae7cfb4f50a40f40c67556542e29df4a4777fc -
Trigger Event:
release
-
Statement type:
File details
Details for the file rolling_cost_cap-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rolling_cost_cap-0.1.0-py3-none-any.whl
- Upload date:
- Size: 6.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a613d3c164cbbabd60d12fd3e61622da71421672d52a2c36ed7d4b1c15275cae
|
|
| MD5 |
ff596c9245f6ad5980b2882dffb5523b
|
|
| BLAKE2b-256 |
a7bd33d596c9cd1403018f86832e0270d2be88c5c8e100e4aa3397782f76bcd1
|
Provenance
The following attestation bundles were made for rolling_cost_cap-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on coreystevensdev/rolling-cost-cap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rolling_cost_cap-0.1.0-py3-none-any.whl -
Subject digest:
a613d3c164cbbabd60d12fd3e61622da71421672d52a2c36ed7d4b1c15275cae - Sigstore transparency entry: 2075648840
- Sigstore integration time:
-
Permalink:
coreystevensdev/rolling-cost-cap@c0ae7cfb4f50a40f40c67556542e29df4a4777fc -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/coreystevensdev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c0ae7cfb4f50a40f40c67556542e29df4a4777fc -
Trigger Event:
release
-
Statement type: