Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Cap spend and steer behavior across a whole agent workflow — not per request

TokenOps is a **control plane + SDK** for agent stacks. Entry agents register a run; every LLM and tool crossing shares one `run_id` and one ledger. Policies can halt, mutate, or inject before the next call executes — so a research → summarize → review pipeline stays inside a single budget even across processes.

**[Why](#why-tokenops) · [Architecture](#architecture) · [Install](#install) · [Quick start](#quick-start) · [Demos](#demos) · [Comparison](#how-tokenops-compares) · [Make targets](#make-targets) · [Roadmap](#roadmap)**
**[Why](#why-tokenops) · [Architecture](#architecture) · [Install](#install) · [Quick start](#quick-start) · [Onboarding](docs/guides/onboarding.md) · [Demos](#demos) · [Comparison](#how-tokenops-compares) · [Make targets](#make-targets) · [Roadmap](#roadmap)**

## Why TokenOps

Expand Down Expand Up @@ -74,8 +74,13 @@ pip install "agent-tokenops[examples]"
pip install -e ".[dev,examples]"
```

Requires Python 3.10+. PyPI name is `agent-tokenops`; import is still `tokenops`
(same pattern as Chronicle). See [`RELEASING.md`](RELEASING.md) for releases.
**Prerequisites:** Python 3.10+; `agent-tokenops`; either a running control plane
(`TOKENOPS_URL` + shared `TOKENOPS_DB`) or `TOKENOPS_EMBEDDED=1` for single-process /
tests. LLM API keys only for real model calls. FastAPI only if you use
`instrument_app`.

PyPI name is `agent-tokenops`; import is still `tokenops` (same pattern as Chronicle).
See [`RELEASING.md`](RELEASING.md) for releases.

## Quick start

Expand Down Expand Up @@ -124,7 +129,9 @@ make control-plane # :7700
make ui # Admin + Dashboard :8501
```

Full integration checklist: [`.cursor/skills/integrate-tokenops/SKILL.md`](.cursor/skills/integrate-tokenops/SKILL.md) · field guide: [`docs/guides/field-guide-add-tokenops.md`](docs/guides/field-guide-add-tokenops.md).
New here? [Onboarding guide](docs/guides/onboarding.md) (prereqs, bare-min integrate, FAQ, current limits).

Full integration checklist: [`.cursor/skills/integrate-tokenops/SKILL.md`](.cursor/skills/integrate-tokenops/SKILL.md) · triad deep dive: [`docs/guides/field-guide-add-tokenops.md`](docs/guides/field-guide-add-tokenops.md).

## Demos

Expand Down Expand Up @@ -230,11 +237,12 @@ Status of each control-plane job: [`docs/control-plane-status.md`](docs/control-

## Documentation

- [Onboarding](docs/guides/onboarding.md) — prereqs, bare-min integrate, FAQ, current limits
- [Field guide](docs/guides/field-guide-add-tokenops.md) — triad deep dive + screenshots
- [Control plane status](docs/control-plane-status.md)
- [Architecture](docs/architecture.md)
- [Run attribution](docs/run-attribution.md)
- [Control plane deploy](docs/control-plane-deploy.md)
- [Field guide](docs/guides/field-guide-add-tokenops.md)
- [Examples](examples/README.md)
- [Product: comparison](docs/product/comparison.md) · [shared ledger](docs/product/shared-ledger.md)

Expand Down
2 changes: 1 addition & 1 deletion docs/control-plane-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Governance for agent stacks: **register → measure → record → detect → de
The agent (data plane) stays vanilla; the control plane taps boundary crossings and enforces on
**cost** (micro-USD integers).

Runnable A2A demos live in-repo under [`examples/`](examples/) (`make demo` / `demo-triad` / `demo-brief`).
Newcomers: [onboarding guide](guides/onboarding.md). Runnable A2A demos live in-repo under [`examples/`](../examples/) (`make demo` / `demo-triad` / `demo-brief`).

## What it does (jobs → status)

Expand Down
3 changes: 3 additions & 0 deletions docs/guides/field-guide-add-tokenops.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ This walkthrough mirrors how TokenOps was wired into the **Planner → Researche
bench under [`examples/triad/`](../../examples/triad/). Code screenshots below were generated with
`python scripts/render_field_guide_snippets.py` → [`docs/guides/assets/`](assets/).

New to TokenOps? Start with the [onboarding guide](onboarding.md) (prereqs, bare-min
integrate, FAQ, current limits), then return here for the triad deep dive.

Related docs: [control-plane status](../control-plane-status.md),
[control-plane-deploy.md](../control-plane-deploy.md),
[run-attribution.md](../run-attribution.md).
Expand Down
166 changes: 166 additions & 0 deletions docs/guides/onboarding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Onboarding: TokenOps in five minutes of concepts

New to TokenOps? Start here. This page covers the mental model, prerequisites,
bare-minimum integration, FAQ, and what TokenOps is **not** doing today.

| Go deeper | Doc |
|-----------|-----|
| Triad walkthrough + screenshots | [Field guide](field-guide-add-tokenops.md) |
| Job-by-job plane status | [Control plane status](../control-plane-status.md) |
| Copilot / agent checklist | [`.cursor/skills/integrate-tokenops/SKILL.md`](../../.cursor/skills/integrate-tokenops/SKILL.md) |
| Architecture | [Architecture](../architecture.md) · [Run attribution](../run-attribution.md) |

---

## Mental model

- **Govern the run, not the request.** One `run_id` spans every LLM call, tool, and agent hop in a workflow.
- **Two layers.** The **control plane** registers runs and holds shared SQLite (budgets, policies, ledger). The **SDK** in each agent process enforces at crossings (`wrap_complete`, Chronicle hook).
- **`tokenops_run` is register-or-join.** Entry opens a new run; downstream hops with `X-TokenOps-Run-Id` join the same run. Same API either way.
- **`wrap_complete` is in-path.** Detect → decide → apply runs *before* the next model call (halt, mutate, inject), not as post-hoc analytics.
- **One shared ledger.** Child spend hits that ledger once — **no parent cost rollup**. Do not re-bill delegated work on the parent.

Chronicle records decision boundaries; TokenOps observes them for cost/governance. See [Chronicle](https://github.com/theagentplane/chronicle).

---

## Prerequisites

| Need | Notes |
|------|--------|
| **Python 3.10+** | Required |
| **`pip install agent-tokenops`** | Import is still `tokenops`. Pulls Chronicle ≥0.1.3, FastAPI, httpx, provider clients, etc. |
| **Control plane + shared DB** *(multi-process)* | `TOKENOPS_URL` (e.g. `http://localhost:7700`) and `TOKENOPS_DB` shared by plane + agents. Seed governance once (`make db-reset`). |
| **Or embedded Store** *(single-process / tests)* | Omit `TOKENOPS_URL` or set `TOKENOPS_EMBEDDED=1`. |
| **LLM API keys** | Only for real model calls — not required for TokenOps itself or offline tests. |
| **FastAPI** | Only if you use `instrument_app`. Non-FastAPI: pass kwargs / `RequestContext` to `tokenops_run`. |
| **Chronicle `@boundary`** | Only if tools should be governed; LLM-only stacks can stop at `wrap_complete`. |

You do **not** need A2A, `create_a2a_app`, LangChain, or the Admin UI for governance to work.

---

## Bare-minimum integrate

### 1. Start the plane (multi-process)

```bash
export TOKENOPS_URL=http://localhost:7700
export TOKENOPS_DB=tokenops.db
make db-reset # seed budgets/policies once
make control-plane # :7700
```

### 2. Instrument the app once (FastAPI)

```python
from tokenops import ControlPlaneClient, instrument_app, tokenops_run
from tokenops.control import wrap_complete, with_governance_errors
from tokenops.providers import complete

client = ControlPlaneClient.from_env()

# After you build your FastAPI app (A2A helper or otherwise):
instrument_app(
app,
service="myagent",
intent="my_intent", # agent-owned; UI sends task only
provider="openai", # defaults for crossings without their own
model="gpt-4o-mini",
)
```

`instrument_app` only needs a FastAPI `app`. It does **not** assume `create_a2a_app`.

### 3. Per request: open the run + wrap the LLM

```python
async def handler(payload: dict, headers: Mapping[str, str]) -> dict:
with tokenops_run(client=client) as bound:
governed = wrap_complete(
bound.governor, bound.controls, bound.attr,
provider="openai", model="gpt-4o-mini",
dispatch=complete, service="myagent",
)
return agent.run(..., complete_fn=governed)

app = create_a2a_app(..., handler=with_governance_errors(handler))
# then instrument_app(app, ...)
```

Use `bound.*` from `tokenops_run` — do not hand-roll attribution or a separate governance scope.

### 4. Multi-agent: propagate the run

Forward `X-TokenOps-Run-Id` (and parent span when you have one) on every hop.
A2A `post_task` merges ambient headers when you are already inside `tokenops_run`.
Downstream agents use the **same** `tokenops_run` + `wrap_complete` pattern; they join, they do not mint a new run.

### Non-FastAPI

```python
from tokenops import RequestContext, bind_request_context, tokenops_run

bind_request_context(RequestContext(
headers=dict(headers), payload=payload, service="myagent", intent="my_intent",
))
with tokenops_run(client=client) as bound:
...
```

Or pass `headers=`, `payload=`, `service=`, `intent=` explicitly to `tokenops_run(...)`.

---

## FAQ

### Why do I pass `provider` / `model` to `instrument_app`?

They are **agent defaults** stored on `RequestContext`, then copied into the governance scope as a **fallback** when a crossing does not carry its own provider/model (typical for Chronicle **tool** boundaries). LLM calls already pass provider/model on `wrap_complete`, so empty defaults are fine for LLM-only setups.

### Does `instrument_app` require A2A or `create_a2a_app`?

No. It registers FastAPI HTTP middleware + installs the Chronicle crossing hook. Any FastAPI app works. `create_a2a_app` is only the demo/A2A helper that builds that app.

### Entry vs downstream — different APIs?

No. Both use `with tokenops_run(...) as bound:`. Entry registers when there is no run header; downstream joins via `X-TokenOps-Run-Id`.

### Who owns `intent` / governance `mode`?

The **agent** (via `instrument_app` / `tokenops_run` kwargs), not the UI. Clients should send **task only**. Optional caller identity (e.g. `user_id`) may still flow from the payload under allow-listed merge rules.

### When do I need Chronicle `@boundary`?

When tools (search, fetch, etc.) should appear on the ledger / be governed. Without `@boundary` + the crossing hook, TokenOps only sees LLM calls you put through `wrap_complete`.

### Embedded Store vs `TOKENOPS_URL`?

| Mode | When |
|------|------|
| `TOKENOPS_URL` set | Production / multi-process: register via HTTP; share `TOKENOPS_DB` with the plane. Agents must **not** mount `/v1/runs`. |
| `TOKENOPS_EMBEDDED=1` or no URL | Tests / single process: in-process `Store`. |

### Do I construct `Store(...)` in the agent?

Prefer `ControlPlaneClient.from_env()` and `tokenops_run`. Happy path does not require user-facing `Store(...)` construction.

### Tools usually cost $0 — why govern them?

Ledger cost may be zero, but policies still care about steps, concurrency, tool frequency, output size, and progress. Spend is one dimension among several.

---

## What TokenOps is not doing today

TokenOps is **0.x / draft**. Honest limits (details and status matrix: [control-plane status](../control-plane-status.md); high-level plan: [README Roadmap](../../README.md#roadmap)):

- **Not a full remote observe/decide plane yet.** Agents still enforce in-process with a shared SQLite ledger; register is remote when `TOKENOPS_URL` is set. A fatter plane (remote observe/decide) is on the roadmap.
- **Not automatic for arbitrary frameworks.** FastAPI gets `instrument_app`; other stacks wire `tokenops_run` / `RequestContext` yourself — no Flask/Django/etc. middleware ships today.
- **Not automatic tool wrapping.** Tools are not governed unless you use Chronicle `@boundary` (and the crossing hook, installed by `instrument_app` / `tokenops.init`).
- **Not per-user / tag budget seed by default.** Registration stores `user_dims`, but seeded governance is **run-scoped** today; segment-scoped budgets are WIP.
- **Not parent rollup of child spend.** By design: one ledger, one booking. Do not double-count delegated work on the parent.
- **Not a replacement for your agent runtime.** TokenOps does not own prompts, planners, or tool loops — it governs crossings you expose.
- **Not production-hardened multi-host without shared storage.** Cross-process correctness assumes a shared `TOKENOPS_DB` (or future remote ledger). See also [concurrency](../concurrency.md).

What **is** working today for the happy path: run registration, `tokenops_run` + `wrap_complete`, shared ledger halt/spend, seeded policies, Admin/Dashboard, and the in-repo demos (`make demo` / `demo-triad` / `demo-brief`).
Loading