TokenOps
Cuts wasted agent spend by up to 65%, governing what the run has already spent before every call.
Toward token governance as a first-class discipline, not an afterthought.
Built by Susheem Koul and Tisha Chawla
|
|
See it stop a run mid-budget in the Quickstart below.
Core features · Quickstart · Quickdeploy · How it compares · Policies · Support · Contributing
🙌 Open to contribution
Token spend deserves the same first-class attention as compute or latency, and we are growing the community working on that. Policies, actuators, and the shared ledger are all open to extension. See CONTRIBUTING.md to get started.
✨ Core features
An AI agent's workflow can run up cost fast: dozens of small, individually cheap steps that quietly add up to a surprisingly large bill. TokenOps sets a single budget for the whole workflow and enforces it before every step, so spending never gets away from you.
Ten policies ship configured in docs/policies/, and you can
add your own.
🚀 Quickstart
Requires Python 3.10+.
1. Put it in your agent
[!TIP] Recommended. Your coding assistant reads
SKILL.md, wires the one enforcement point into your agent, and tells you what to check.
In Claude Code, from a clone:
/integrate-tokenops
Anywhere else (Cursor, Copilot, ...), paste this:
Integrate TokenOps into this agent, following https://github.com/theagentplane/tokenops/blob/main/.claude/skills/integrate-tokenops/SKILL.md
Manual, about ten lines
Wrap your model call once, then hand the wrapped version to your agent.
from tokenops import ControlPlaneClient, tokenops_run
from tokenops.control import Halt, wrap_complete
from tokenops.providers import complete
client = ControlPlaneClient.from_env()
with tokenops_run(client=client, service="my-agent", intent="research",
provider="openai", model="gpt-4o") as bound:
governed = wrap_complete(
bound.governor, bound.controls, bound.attr,
provider="openai", model="gpt-4o",
dispatch=complete, service="my-agent",
)
try:
agent.run(..., complete_fn=governed) # <-- pass `governed`, not `complete`
except Halt as stopped:
print(f"run stopped: {stopped}")
Pass governed to your agent instead of complete; nothing else changes.
wrap_complete checks the budget before each call and raises Halt when the
run is out, even from another process.
2. When you need more
| You want to | Go to |
|---|---|
| Change the budget | Set the budget |
| One budget across several agent processes | Shared plane |
| FastAPI or A2A services | Instrumented app |
| Something other than stopping | The ten policies |
| Cost per agent in a dashboard | Quickdeploy |
| A worked end-to-end example | Field guide |
| Everything else | Onboarding guide |
3. See it in action
|
|
Same task, run twice: ungoverned it completes over budget, governed it halts within the cap, then the Dashboard attributes cost per agent. Full video.
🐳 Quickdeploy
[!TIP] The control plane (
python -m tokenops.server) shares one budget across processes and powers the dashboard. A single-process agent doesn't need it running at all.
One command, plane + dashboard:
git clone https://github.com/theagentplane/tokenops && cd tokenops
docker compose --profile ui up --build
Plane: localhost:7700/health · Dashboard: localhost:8501. Plane only:
docker compose up --build. Details: docs/control-plane-deploy.md.
Without Docker (make)
git clone https://github.com/theagentplane/tokenops && cd tokenops
make install
make run # control plane :7700 + Admin/Dashboard :8501
Then open localhost:8501 to see spend and governance per agent.
Multi-agent benches: watch one budget span several agents
Each is a real multi-agent stack sharing one run ledger. One target starts the plane, the agents, and the Admin UI.
| Bench | Agents | Run |
|---|---|---|
| Two-agent | Research to Summarize | make demo |
| Triad | Planner to Researcher to Writer | make demo-triad |
| Brief | Scout to Analyst to Editor (LangChain) | make demo-brief |
| Bench UI | Chat + Simulator only | make bench-ui |
cp .env.example .env first if you want them to call real models. See
examples/README.md for the bench profiles.
Pointing several processes at one plane
export TOKENOPS_URL=http://localhost:7700
export TOKENOPS_DB=tokenops.db # plane and every agent read the same file
TOKENOPS_EMBEDDED=1overridesTOKENOPS_URL. Leave it unset here, or each process silently falls back to its own local ledger and gets the full budget.
PyPI name is agent-tokenops; the import is tokenops. Extras:
pip install "agent-tokenops[examples]" for the LangChain benches,
".[dev,examples]" from source. Releases: RELEASING.md.
🆚 How TokenOps compares
TokenOps is not a gateway or a tracing dashboard. It governs the run, a full agent workflow, and sits alongside the tools you already use for routing and observability.
| TokenOps | LiteLLM / Portkey / AI Gateway | Langfuse | |
|---|---|---|---|
| Primary focus | Run | Request | Trace |
| Multi-agent workflow as one unit | Yes | No | Partial |
| Budget enforcement in-path | Yes | Yes | No |
| Steer next call (mutate / inject) | Yes | Partial | No |
| Shared ledger across processes | Yes | — | — |
What this does not do: replace your LLM gateway, replace Chronicle-style record-and-replay, or host a SaaS control plane for you.
Longer table with logos: docs/product/comparison.md.
Reference
Things you will want eventually, not now.
Architecture: how the plane and the SDK split the work
TokenOps is two layers that share one artifact, the run: a control plane that registers runs and stores budgets/policies, and an in-process SDK that enforces at every boundary crossing.
flowchart LR
subgraph PLANE["Control plane (:7700)"]
R["POST /v1/runs"] --> DB[("SQLite TOKENOPS_DB<br/>registrations · budgets · policies · ledger")]
UI["Admin + Dashboard"] --> DB
end
subgraph AGENTS["Agent processes (SDK)"]
E["Entry agent<br/>tokenops_run"] -->|"register_run"| R
E -->|"X-TokenOps-Run-Id"| D["Downstream agents<br/>tokenops_run"]
E & D -->|"wrap_complete"| G["Governor<br/>pre_call → detect → decide → apply"]
E & D -->|"@boundary + crossing hook"| G
G --> L["Shared ledger<br/>(same run_id)"]
end
L --> DB
DB -->|"governance_config_for"| G
| Piece | Owns | Does not own |
|---|---|---|
Control plane (python -m tokenops.server) |
POST /v1/runs, shared SQLite, Admin/Dashboard |
Agent loops, LLM calls, tools |
| SDK (in agents) | tokenops_run, wrap_complete, ledger/policies, Chronicle crossing hook |
Ad-hoc run IDs; mounting /v1/runs when TOKENOPS_URL is set |
Chronicle records decision boundaries; TokenOps attaches as the cost/governance observer on live crossings. See Chronicle for record-and-replay.
Environment variables
| Variable | Purpose |
|---|---|
TOKENOPS_URL |
Remote plane base URL (e.g. http://localhost:7700) → HTTP register_run |
TOKENOPS_EMBEDDED |
Set to 1 to force in-process Store (tests / single-process) |
TOKENOPS_DB |
SQLite path shared by plane + agents |
TOKENOPS_CONFIG |
YAML for governance seed (core: src/tokenops/config/default.yaml) |
TOKENOPS_URL also accepts the aliases CONTROL_PLANE_URL and
TOKENOPS_CONTROL_PLANE_URL.
Production / multi-process: set TOKENOPS_URL; agents must not mount /v1/runs. Tests: TOKENOPS_EMBEDDED=1 (or omit URL).
Precedence.
ControlPlaneClient.from_envtakes the HTTP path only when a URL is set andTOKENOPS_EMBEDDEDis not1. Setting both falls back to a local SQLite file with no warning, and every process then gets its own full budget. Check withprint("embedded" if client.embedded else client.url).
Make targets
| Target | Role |
|---|---|
make install |
Editable install with dev + examples extras |
make dist / check-dist |
Build sdist+wheel / twine check |
make control-plane |
Standalone plane (python -m tokenops.server) on :7700 |
make ui |
Admin + Dashboard on :8501 |
make run |
Plane + Admin/Dashboard |
make demo-quick |
python -m tokenops.demo: no API keys, no server |
make demo / demo-triad / demo-brief |
Runnable A2A stacks |
make bench-ui |
Chat + Simulator |
make db-reset |
Clear SQLite + reseed from TOKENOPS_CONFIG |
make stop |
Kill listeners on :7700 / :8501 |
make sync-skills |
Regenerate the editor copies of the integration skill |
Project structure
Only src/tokenops/ is the installable package. Demos and benches stay under examples/.
src/tokenops/ # installable package
├── server/ # control plane (:7700, POST /v1/runs)
├── control/ # SDK: ledger, policies, wrap_complete, crossing hook
├── providers/ # OpenAI / Anthropic complete dispatch
├── config/ # default.yaml governance seed
└── ui/ # Admin + Dashboard (Streamlit)
examples/ # A2A benches (two-agent, triad, brief) + Chat/Simulator
benchmarking/ # MetaGPT / browser-use live harness
docs/ # architecture, policies, guides, product
tests/ # unit + e2e
More documentation
- Onboarding: prereqs, minimum integration, FAQ, current limits
- Field guide: a triad walked through, with screenshots
- Policies: one page per policy
- Run attribution - control plane deploy - status
- Examples - comparison - shared ledger
📰 Talks & press
- Featured by Microsoft Developer: “Who spent all the tokens?” on LinkedIn and X.
- Who spent all the tokens? Real-time, run-scoped cost control for AI agents: Command Line, a Microsoft publication.
- FinOps for AI Agents: Who Spent All the Tokens?: talk at the AI Engineer World's Fair, San Francisco.
🛟 Support
| Need | Where |
|---|---|
| Bug | Open an issue |
| Security issue | SECURITY.md |
| Real-time help | Slack |
| Longer-form discussion | GitHub Discussions |
| Talk it through | Office hours |
| Talks & writing | theagentplane.github.io/media |
Contributors
Thanks to everyone who has contributed.
Saved you tokens? ⭐ Star the repo.
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 agent_tokenops-0.2.1.tar.gz.
File metadata
- Download URL: agent_tokenops-0.2.1.tar.gz
- Upload date:
- Size: 123.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7eaef0da6805b81c7a55d7f64c8be8f61bcbfaf2244063ca07ffe98fe11bab20
|
|
| MD5 |
d37a831a556f2161aaf1b0c5e97fd22d
|
|
| BLAKE2b-256 |
d6d234beff8928aa37f135989f65af0bb96507cac25ae5d4290cd8a05f6cf97a
|
Provenance
The following attestation bundles were made for agent_tokenops-0.2.1.tar.gz:
Publisher:
release.yml on theagentplane/tokenops
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agent_tokenops-0.2.1.tar.gz -
Subject digest:
7eaef0da6805b81c7a55d7f64c8be8f61bcbfaf2244063ca07ffe98fe11bab20 - Sigstore transparency entry: 2712584735
- Sigstore integration time:
-
Permalink:
theagentplane/tokenops@e59e4ec09ea50a7dc00bae46da4dd03a8d0ac807 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/theagentplane
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e59e4ec09ea50a7dc00bae46da4dd03a8d0ac807 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file agent_tokenops-0.2.1-py3-none-any.whl.
File metadata
- Download URL: agent_tokenops-0.2.1-py3-none-any.whl
- Upload date:
- Size: 112.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 |
5d71fe06141bf7077b78230d5060257ebc0948e37ce53c3795a2c2b65ee13569
|
|
| MD5 |
147ce82f7163096411ce85588dba7918
|
|
| BLAKE2b-256 |
be7efb680be6ebcb99b3f7ddaf920383f01f4d60851506e71b0bddbbd9df3014
|
Provenance
The following attestation bundles were made for agent_tokenops-0.2.1-py3-none-any.whl:
Publisher:
release.yml on theagentplane/tokenops
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agent_tokenops-0.2.1-py3-none-any.whl -
Subject digest:
5d71fe06141bf7077b78230d5060257ebc0948e37ce53c3795a2c2b65ee13569 - Sigstore transparency entry: 2712585375
- Sigstore integration time:
-
Permalink:
theagentplane/tokenops@e59e4ec09ea50a7dc00bae46da4dd03a8d0ac807 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/theagentplane
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e59e4ec09ea50a7dc00bae46da4dd03a8d0ac807 -
Trigger Event:
workflow_dispatch
-
Statement type: