# Supported providers & frameworks (/docs/integrations/matrix)



This is the single support surface. If your provider is in the first table you are done after
[Get started](/docs/get-started/choose-your-path) — 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](/docs/sdk/supported-versions). This page is about *what* is covered, not
which release of it.

## Providers [#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](/docs/sdk/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                                                          |

<Callout type="warn" title="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()`:

  ```typescript
  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](/docs/sdk/node-esm).
</Callout>

<Callout type="info">
  **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()`](/docs/sdk/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.
</Callout>

## Beyond chat [#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()`](/docs/sdk/protect) and a `provider=` hint if you need them counted.

## Frameworks [#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](/docs/integrations/langchain)             |
| Vercel AI SDK         | —                             | base (`ai` + `@ai-sdk/*`)                                            | **manual, always** | [Vercel AI SDK](/docs/integrations/vercel-ai)         |
| LlamaIndex            | `token-police[llamaindex]`    | base; `instrumentModules.llamaIndex` on ESM                          | automatic (Node)   | [LlamaIndex](/docs/integrations/llamaindex)           |
| CrewAI                | `token-police[crewai]`        | —                                                                    | manual             | [CrewAI](/docs/integrations/crewai)                   |
| Pydantic AI           | `token-police[pydantic-ai]`   | —                                                                    | automatic          | [Pydantic AI](/docs/integrations/pydantic-ai)         |
| OpenAI Agents SDK     | `token-police[openai-agents]` | base (`@openai/agents`)                                              | automatic          | [OpenAI Agents SDK](/docs/integrations/openai-agents) |
| 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.

<Callout type="warn" title="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](/docs/rules/actions#reroute).
</Callout>

## Python extras [#python-extras]

```bash
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 [#pins--conflicts-to-know-about]

These are environment caveats, not things TokenPolice enforces — but they trip people up at
install time:

* **Mistral (Python):** `mistralai` 1.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 stays
  `from mistralai import Mistral`). On `mistralai` 2.0–2.9.1, pip may install an older
  OpenTelemetry train to satisfy that release's own cap; it still works, and `mistralai>=2.9.2`
  drops 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]`. Set `CREWAI_DISABLE_TELEMETRY=true` to 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.

## Next [#next]

<Cards>
  <Card title="Supported versions" href="/docs/sdk/supported-versions" description="Which SDK, runtime, and provider-library versions are supported." />

  <Card title="Custom & in-house clients: protect()" href="/docs/sdk/protect" description="Meter a provider or client that isn't in the matrix." />
</Cards>
