Supported providers & frameworks
The canonical list: every provider and framework TokenPolice meters, what it costs to install, and the caveats worth knowing.
This is the single support surface. If your provider is in the first table you are done after
Get started — install the SDK, call tp.init(), and your
calls are metered. Frameworks sometimes need one extra step, listed in the second table.
For which library versions are supported, see Supported versions. This page is about what is covered, not which release of it.
Providers
Every provider below is metered on the base install — pip install token-police /
npm i token-police — with the two exceptions marked in the table. In Node, install the provider
package you already use (they are optional peer dependencies) and TokenPolice taps it.
Streaming is metered everywhere in this table. Usage is recorded when the stream finishes, so a streamed call shows up with the same token counts and cost as a non-streamed one.
| Provider | Python | Node | Notes |
|---|---|---|---|
| OpenAI | base | base | Chat, Responses, images, audio and embeddings. On Node with openai 7 or newer, pass the module — see the caveat below |
| Anthropic | base | base | On Node ESM, prefer the namespace import — see Node & ESM |
| Amazon Bedrock | base | base | Converse usage is read from the response |
| Google Gemini | base | base | |
| Cohere | base | base | Chat and embeddings |
| Mistral | base | base | Chat, embeddings, OCR, transcription, FIM, speech (2.x); mistralai 1.x and 2.x — see the import note below |
| Groq | base | base | Groq's own client. Groq's OpenAI-compatible endpoint is metered too — see the note below |
| OpenRouter | base | base | |
| Cerebras | base | base | |
| Together | base | base | Chat and images |
| Hugging Face | base | base | Node: instrument before your app constructs its inference client |
| xAI (Grok) | token-police[xai] | base | The Python extra installs xAI's own client; nothing extra is needed to meter it |
| Voyage | token-police[voyageai] | — | Embeddings only. Python only — there is no Node path today |
Node + openai 7: pass the module
On Node, chat completions made with openai 7 or newer are only metered when you hand the
module to init():
import OpenAI from "openai";
tp.init({ /* … */ instrumentModules: { openAI: OpenAI } });Without it the calls still run and are still checked against your rules, but they are recorded with zero tokens, so budgets never accumulate. The SDK prints a warning at startup when it spots this. Responses-API, image, audio and embedding calls are unaffected. Full detail: Node & ESM.
OpenAI-compatible base URLs are usually already metered. If you reach a provider by
pointing the openai client at its OpenAI-compatible endpoint, the OpenAI instrumentation
meters that call like any other, and the SDK reads the base URL to attribute it to the right
provider. It recognises around two dozen hosts — api.groq.com, openrouter.ai,
api.together.xyz, api.fireworks.ai, api.deepinfra.com, api.deepseek.com, api.x.ai,
api.mistral.ai, api.perplexity.ai, api.cerebras.ai, api.cohere.com, api.moonshot.ai,
api.minimax.io, api.novita.ai, api.z.ai, open.bigmodel.cn, api.studio.nebius.com,
ai-gateway.vercel.sh, plus the Azure OpenAI, Bedrock and Vertex URL patterns. A host it
doesn't know is still metered; it is just recorded under openai, with the sanitized base URL
attached so you can tell it apart.
So reach for protect() only for something we do not instrument at
all: an in-house client, a raw fetch against a provider's REST API, or a provider SDK that
isn't in this table. Never protect() a call that already runs through an instrumented
client — including an OpenAI-compatible one — or it is counted twice.
Beyond chat
Non-chat calls are metered too and land in the dashboard tagged with their own operation, so a
rule can target them separately from chat:
operation | Covered |
|---|---|
embedding | Broad coverage in both SDKs |
image_gen | OpenAI Images, Google Imagen, Together, Hugging Face; Vercel AI SDK image models (Node) |
audio_tts | OpenAI, Hugging Face, Gemini audio output, Mistral speech (mistralai 2.x, Python); Vercel AI SDK speech models (Node) |
audio_stt | OpenAI transcription and translation, Mistral Voxtral, Hugging Face; Vercel AI SDK transcription models (Node) |
video_gen | Google Veo; Vercel AI SDK video models (Node) |
ocr | Mistral |
A rule on operation is embedding is handy for RAG: it stops a runaway re-embed loop
without touching your chat traffic.
Known gaps. Rerank endpoints (Cohere, Voyage), moderation endpoints, and OpenAI
images.edit / images.createVariation are not metered. Cover them with
protect() and a provider= hint if you need them counted.
Frameworks
| Framework | Python | Node | Tool spans | Wiring |
|---|---|---|---|---|
| LangChain / LangGraph | token-police[langchain] | token-police-langchain companion (@langchain/core 0.3.x and 1.x) | automatic | LangChain |
| Vercel AI SDK | — | base (ai + @ai-sdk/*) | manual, always | Vercel AI SDK |
| LlamaIndex | token-police[llamaindex] | base; instrumentModules.llamaIndex on ESM | automatic (Node) | LlamaIndex |
| CrewAI | token-police[crewai] | — | manual | CrewAI |
| Pydantic AI | token-police[pydantic-ai] | — | automatic | Pydantic AI |
| OpenAI Agents SDK | token-police[openai-agents] | base (@openai/agents) | automatic | OpenAI Agents SDK |
| Agno | token-police[agno] | — | manual | install the extra, then tp.init() as usual |
Tool spans say whether the tool calls inside an agent run appear as their own steps in the dashboard. Where the column says manual — as it does for hand-rolled tool loops and MCP tools in any framework — the model calls are still metered and still enforced; the tool steps simply don't get their own entry in the trace.
Reroute does not switch the model on every framework
A Reroute rule does not switch the model on some frameworks — including LangChain, LangGraph, LlamaIndex, Pydantic AI, and the Vercel AI SDK. Those calls still run on the original model. Confirm a swap actually happens in your app before you enforce this in production. Blocking and metering are unaffected. See Actions.
Python extras
pip install "token-police[langchain]" # or llamaindex, pydantic-ai, openai-agents, agno, xai, voyageai
pip install "token-police[crewai]" # CrewAI has its own extra and is not part of [all]
pip install "token-police[all]" # every framework extra except crewai[all] deliberately leaves CrewAI out: CrewAI supports Python 3.10–3.13 but not 3.14 yet, so
keeping it separate is what lets [all] install everywhere. If you use CrewAI, install
token-police[crewai] (on its own, or alongside [all]).
Pins & conflicts to know about
These are environment caveats, not things TokenPolice enforces — but they trip people up at install time:
- Mistral (Python):
mistralai1.x and 2.x are both supported — no pin needed. On 2.x the client import moved:from mistralai.client import Mistral(on 1.x it staysfrom mistralai import Mistral). Onmistralai2.0–2.9.1, pip may install an older OpenTelemetry train to satisfy that release's own cap; it still works, andmistralai>=2.9.2drops the cap. - CrewAI (Python): installs may print a benign resolver warning — safe to ignore. Strict
resolvers (uv, Poetry, pip-tools) sometimes need a two-step install: CrewAI first, then
token-police[crewai]. SetCREWAI_DISABLE_TELEMETRY=trueto quiet CrewAI's own tracing noise. - Don't downgrade TokenPolice's own dependencies. Older versions of them undercount Bedrock Converse usage; the pins that ship with the SDK are the tested ones.
- Never set
OTEL_SDK_DISABLED=true. It turns off tracing globally — including TokenPolice's own instrumentation, so nothing gets metered.

