agent_metering
Language-agnostic LLM cost metering for B2B SaaS — point any OpenAI / Anthropic client at the HTTP proxy (Node, Go, Java, PHP, Python, curl, …).
MIT open source — CONTRIBUTING · SECURITY · Code of Conduct
Install
pip install llm-agent-metering
# optional extras:
# pip install "llm-agent-metering[dashboard]"
# pip install "llm-agent-metering[example]"
From GitHub (latest main):
pip install "git+https://github.com/prantakhandaker/agent_metering.git"
Any language (recommended)
Run the proxy once, then set your SDK base URL to it. No SDK install in the app language required.
pip install llm-agent-metering
python -m uvicorn agent_metering.proxy:app --host 0.0.0.0 --port 8787
| Provider | Base URL / env |
|---|---|
| OpenAI | http://127.0.0.1:8787/proxy/openai/v1 → OPENAI_BASE_URL |
| Anthropic | http://127.0.0.1:8787/proxy/anthropic → ANTHROPIC_BASE_URL |
| Azure | .../proxy/azure/v1 → AZURE_OPENAI_BASE_URL |
Optional per-user / feature headers (stripped before upstream):
X-User-Id(preferred) orX-Customer-IdX-Feature- Or OpenAI body field
user/ Anthropicmetadata.user_id
Defaults: env AGENT_METERING_CUSTOMER_ID / AGENT_METERING_FEATURE, else default.
For OpenAI-compatible streaming (stream: true), the proxy automatically adds
stream_options: {"include_usage": true} when the client omitted it, so the final
SSE chunk includes a real usage block. Anthropic streams already emit usage without
this option. Exact token counts from the usage block are logged (not content estimates).
Optional granularity headers (also stripped before upstream):
X-Call-IdorX-Step— tag a single step inside an agent loop (rolls up underX-Feature)X-Unit-Id— business unit / artifact id for cost-per-unit reportingX-Correlation-Id+ optionalX-Attempt— link retries; prior attempts becomeretry_attempt
Spend is written to local SQLite (agent_metering.db) in WAL mode with batched
flushes (every ~200ms or 50 records). A crash loses at most the current in-memory
batch; committed data survives.
Per-customer allowances
Hard stop when a customer exceeds a monthly spend cap (off by default). In
agent_metering.config.json:
"enforcement": {
"enabled": true,
"status_code": 429,
"allowances": {
"acme_corp": { "max_spend_usd": 50.0, "period": "calendar_month" }
}
}
status_code may be 429 (default) or 402. When exceeded, the proxy returns JSON
{"error":"allowance_exceeded", ...} and does not call the upstream provider.
Checks use an in-memory spend cache refreshed when usage is flushed to SQLite.
Dashboard:
python -m streamlit run examples/dashboard.py # pip install "llm-agent-metering[dashboard]"
Node
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: "http://127.0.0.1:8787/proxy/openai/v1",
defaultHeaders: { "X-User-Id": "user_42" },
});
await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Hi" }],
});
Full script: examples/proxy_node_example.mjs.
curl
curl http://127.0.0.1:8787/proxy/openai/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-H "X-User-Id: user_42" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hi"}]}'
Go
client := openai.NewClient(
option.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
option.WithBaseURL("http://127.0.0.1:8787/proxy/openai/v1"),
option.WithHeader("X-User-Id", "user_42"),
)
Docker sidecar
docker compose -f examples/docker-compose.sidecar.yml up --build
App containers only need OPENAI_BASE_URL / ANTHROPIC_BASE_URL pointing at http://metering-proxy:8787/proxy/....
Or wrap a local process:
python -m agent_metering run --start-proxy -- python your_app.py
Python-only shortcut (optional)
Same venv install auto-patches OpenAI / Anthropic SDKs (no base URL change):
pip install llm-agent-metering
# run your Python app — no import required
Opt out: AGENT_METERING_AUTO=0. Demo: python examples/auto_instrument_example.py.
Also detects FastAPI/Flask/Django request users and OpenAI user= when present.
Who this is for / not for
For: Any stack that can set an LLM HTTP base URL (or env) and needs per-customer / per-user / per-feature spend.
Not for: Full tracing/evals (Langfuse), or replacing multi-provider gateways you already run (LiteLLM / Portkey) unless you put this proxy in front.
Why
Flat API rate limits do not protect margin. Agent workloads are open-ended: tool loops and long contexts can burn tokens quietly. Metering per customer/feature surfaces that before margin disappears.
Project layout
Primary (any language): proxy.py, providers/, cli.py
Python convenience: autoload.py (.pth), instrument.py, user_detect.py, frameworks.py
Shared: config.py, core.py, storage.py, context.py
Tests
pytest
Releasing
Maintainers publish to PyPI via GitHub Actions Trusted Publishing (no API token in secrets).
- One-time on pypi.org: Publishing → Pending publisher
- Project:
llm-agent-metering - Owner:
prantakhandaker - Repository:
agent_metering - Workflow:
publish.yml - Environment: leave empty
- Project:
- Bump
versioninpyproject.tomlto match the release tag. - Tag and release:
# version in pyproject.toml must match the tag
git tag v0.4.0
git push origin v0.4.0
# then GitHub → Releases → Draft release from that tag → Publish
Publishing workflow: .github/workflows/publish.yml.
License
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 llm_agent_metering-0.4.0.tar.gz.
File metadata
- Download URL: llm_agent_metering-0.4.0.tar.gz
- Upload date:
- Size: 44.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb6c598155bbca492b6fe2c690f2015c546cb7237144ae2cecbbb28569ea376d
|
|
| MD5 |
f4eb79822fa1fa09d21569cad233ec23
|
|
| BLAKE2b-256 |
da15c0809e61a5640c4615ffe62793a066256b66cc540ec892119b836c969c23
|
Provenance
The following attestation bundles were made for llm_agent_metering-0.4.0.tar.gz:
Publisher:
publish.yml on prantakhandaker/agent_metering
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llm_agent_metering-0.4.0.tar.gz -
Subject digest:
eb6c598155bbca492b6fe2c690f2015c546cb7237144ae2cecbbb28569ea376d - Sigstore transparency entry: 2755414576
- Sigstore integration time:
-
Permalink:
prantakhandaker/agent_metering@25438be5b7281bdb7455db4942178f2a513a4712 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/prantakhandaker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@25438be5b7281bdb7455db4942178f2a513a4712 -
Trigger Event:
release
-
Statement type:
File details
Details for the file llm_agent_metering-0.4.0-py3-none-any.whl.
File metadata
- Download URL: llm_agent_metering-0.4.0-py3-none-any.whl
- Upload date:
- Size: 36.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
afd58c68a98f09b89fda1ba32c18797901f8967aff4dd213326a90262dafcd60
|
|
| MD5 |
4523f5129856fcad3990eb8408432108
|
|
| BLAKE2b-256 |
503d828bed42f7c2530a7b20fd48a3712bbffaa54c3b5d7696fa5a6e75615910
|
Provenance
The following attestation bundles were made for llm_agent_metering-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on prantakhandaker/agent_metering
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llm_agent_metering-0.4.0-py3-none-any.whl -
Subject digest:
afd58c68a98f09b89fda1ba32c18797901f8967aff4dd213326a90262dafcd60 - Sigstore transparency entry: 2755414581
- Sigstore integration time:
-
Permalink:
prantakhandaker/agent_metering@25438be5b7281bdb7455db4942178f2a513a4712 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/prantakhandaker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@25438be5b7281bdb7455db4942178f2a513a4712 -
Trigger Event:
release
-
Statement type: