Metadata-Version: 2.4
Name: powerailabs-tokenguard
Version: 0.6.1
Summary: Budget: pre-flight cost caps, spend attribution, and circuit-breakers for LLM calls.
Author: Raghav Mishra
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Requires-Python: >=3.11
Requires-Dist: powerailabs-core<0.2,>=0.1.1
Description-Content-Type: text/markdown

# powerailabs-tokenguard

Stop runaway LLM bills, and get per-feature / per-user cost attribution for free. One decorator,
one context manager. No dashboard, no account, no infra.

**Caught a $40 runaway loop before it ran away — and told you which feature spent the rest.**

![PyPI](https://img.shields.io/pypi/v/powerailabs-tokenguard) ![license](https://img.shields.io/badge/license-Apache_2.0-blue) · `pip install powerailabs-tokenguard`

```python
from powerailabs.core import instrument
from powerailabs.tokenguard import budget, track, report

client = instrument(openai_client)              # wrap once; tokenguard subscribes, never patches

@budget(usd=0.50, on_exceed="downgrade", downgrade={"gpt-4o": "gpt-4o-mini"})
def answer(q: str) -> str:
    with track(feature="support_bot", user_id="alice"):   # ambient attribution, zero bookkeeping
        resp = client.chat.completions.create(model="gpt-4o", messages=[{"role": "user", "content": q}])
        return resp.choices[0].message.content

for row in report(group_by=["feature", "user_id"]):       # where did the money go?
    print(row["tags"], row["usd"], row["calls"])
```

## Highlights

- **Pre-flight circuit breaker** — `on_exceed="block"` raises **before** an over-budget call runs; `"downgrade"` reroutes to a cheaper model pre-flight; `"truncate"` degrades; `"raise"` stops a runaway loop; or call your own function.
- **Decorator *and* context manager** — budgets **nest** (an inner downgrade never masks an outer hard cap); config is validated at creation (a typo'd `on_exceed` or a map-less `downgrade` is a `ValueError`, never a silent no-op).
- **Cost attribution, free** — `track(feature=…, user_id=…)` tags ambient spend via `contextvars` (sync + async); `report(group_by=[…])` shows where the money went.
- **Cost as a test assertion** — `report().assert_under(usd=0.05, feature="search")`.
- **Pre-flight projection** — `estimate(model, messages)` prices a call *without making it*.
- **Durable + bounded** — pluggable `use_sink(tokenguard.sinks.SQLiteSink / OTelSink)`; FIFO-bounded in-memory buffer (`configure(max_records=…)`, `dropped()`).

**Wrap-around** — it rides the call you already make. Offline and standalone — bundled prices, no account.

See [`docs/tokenguard.md`](https://github.com/PowerAI-Labs/powerailabs/blob/main/docs/tokenguard.md) · [CHANGELOG](https://github.com/PowerAI-Labs/powerailabs/blob/main/packages/powerailabs-tokenguard/CHANGELOG.md). *Part of the PowerAI Labs stack — [github.com/PowerAI-Labs/powerailabs](https://github.com/PowerAI-Labs/powerailabs). Apache-2.0; provided "as is", without warranty — use at your own risk (LICENSE §7–8).*
