# Node & ESM (/docs/sdk/node-esm)



`instrumentModules` hands TokenPolice the provider modules your app already imported, so it
patches *your* copy instead of hunting for one. It's Node-only, and it's the fix for every
"the calls run but nothing shows up" shape.

## When you need it [#when-you-need-it]

* **Native ESM** (`"type": "module"`, `.mjs`, Next.js, Vite, Bun, Deno). ESM freezes a
  module's bindings at import time, so a provider client imported before `tp.init()` runs can't
  be patched. It fails open — your app is fine, the calls are just invisible.
* **`openai` 7.x**, on any module system. Without it, chat completions report 0 tokens. See
  [Supported versions](/docs/sdk/supported-versions).
* **Some CommonJS trees too:** `pnpm`, a `file:`/`link:` dependency, npm/yarn/pnpm
  `workspaces`, or more than one copy of a provider package. There, metering can look healthy
  while enforcement never attaches.

If you're on plain CommonJS with a single flat `node_modules`, import order genuinely doesn't
matter and you can skip this page.

<Callout type="info">
  The SDK tells you when it's needed: at startup it names any provider package it can see
  installed but couldn't attach enforcement to. Watch your boot output the first time you run
  with `logErrors: true`.
</Callout>

## The fix [#the-fix]

```typescript
import * as tp from "token-police";
import OpenAI from "openai";

tp.init({
  apiKey: process.env.TOKENPOLICE_API_KEY,
  baseUrl: "https://collect.tokenpolice.ai",
  instrumentModules: { openAI: OpenAI },
});

const client = new OpenAI();
```

Pass only the libraries your app actually uses — a provider you don't name simply isn't hooked
by this route.

## The keys [#the-keys]

`openAI`, `anthropic`, `cohere`, `bedrock`, `googleGenAI`, `openRouter`, `cerebras`,
`together`, `groq`, `mistral`, `huggingFace`, `xai`, `llamaIndex`,
`aiSdkProviders`, `langChain`.

Three of them aren't a plain module reference:

| Key              | Shape                                                                                                                                                                                                                                                                                |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `llamaIndex`     | An object: `{ openaiModule, anthropicModule, geminiModule }` — the namespaces of whichever `@llamaindex/*` providers you use.                                                                                                                                                        |
| `aiSdkProviders` | An **array**. Needed only for community or custom Vercel AI SDK providers, which bundle their own nested `@ai-sdk/*` copy. Pass provider factories, constructed model instances, or package namespaces. First-party `@ai-sdk/*` providers and the AI Gateway are found on their own. |
| `langChain`      | Exists, but it isn't the supported path. For LangChain, install the [`token-police-langchain`](/docs/integrations/langchain) companion instead — there's nothing to call, the base SDK detects it.                                                                                   |

## Which import form to pass [#which-import-form-to-pass]

For `openAI` and `anthropic`, either the default export or a namespace import works.
&#x2A;*Every other key takes a namespace import.**

```typescript
import OpenAI from "openai";                       // ✅ default export
import * as OpenAINs from "openai";                // ✅ namespace import

import * as Anthropic from "@anthropic-ai/sdk";    // ✅ preferred
import AnthropicDefault from "@anthropic-ai/sdk";  // ⚠️ works, but see below

import * as cohere from "cohere-ai";               // namespace — as for every other key
```

<Callout type="warn">
  **For `anthropic`, use the namespace import.** The default export is the `Anthropic` class
  alone, and it leaves out an export the streaming path needs. With it, streamed
  `messages.create` and `beta.messages.create` still meter through a fallback — but the older
  `completions.create` surface is metered by nothing at all. `import * as Anthropic from
    "@anthropic-ai/sdk"` avoids the whole question.
</Callout>

## Next [#next]

<Cards>
  <Card title="ESM instrumentation order" href="/docs/troubleshooting/esm-instrumentation-order" description="Symptoms and fixes when calls don't show up." />

  <Card title="Supported versions" href="/docs/sdk/supported-versions" description="Per-provider capabilities and the openai 7.x note." />

  <Card title="init() parameters" href="/docs/sdk/init" description="Where instrumentModules lives." />
</Cards>
