TokenPolice
Docs
SDK reference

Custom & in-house clients: protect()

Bring an unsupported or in-house LLM client under TokenPolice. In Node, objectPath is a string array.

If you call a provider or an in-house wrapper that the auto-instrumentation doesn't already cover, protect() wraps one method so it gets the same pre-flight check and telemetry as a built-in client. Like everything else, it's fail-open: if the wrap fails, your call runs unchanged.

Python

import token_police as tp

tp.protect(
    "my_company.llm",     # module path
    "InHouseClient",      # class name
    "complete",           # method name
    True,                 # is it async?
    manual=True,          # required for the provider hint to take effect
    provider="openai",    # hint how to parse usage
)

Signature:

protect(module_path, class_name, method_name, is_async, *,
        manual=False, provider=None, target_object=None)
  • manual=True — for clients that don't emit OpenTelemetry spans; TokenPolice records usage itself.
  • provider=... — a hint (e.g. "openai", "anthropic") that drives usage and cost parsing.
  • target_object=... — pass a live object to wrap instead of resolving it by import.

provider= only takes effect with manual=True. The two are gated together, so provider="openai" on its own is silently ignored — no error, no warning — and the wrapper falls back to guessing the provider from the module path, which for an in-house module usually guesses nothing. The result is a call that is metered with no usable usage parsing. If you are passing provider=, pass manual=True with it.

Node

The shape matches, with one difference to watch: the second argument, objectPath, is a string[] — the path of property names walked from the module root, not a single string.

import * as tp from "token-police";

tp.protect(
  "my-company-llm",                    // module name
  ["InHouseClient", "prototype"],      // objectPath — an ARRAY, walked from the module root
  "complete",                          // method name
  true,                                // is it async?
  { manual: true, provider: "openai" },// options
);

Signature:

protect(moduleName, objectPath: string[], methodName, isAsync, options?: {
  manual?: boolean;
  provider?: string;
  streaming?: boolean;
  module?: unknown;   // pass a live module/object instead of resolving by name
})

objectPath is an array, and it must end on the object that actually owns the method. TokenPolice walks the path property by property from the module root and then reads obj[methodName] — there is no class-vs-instance magic.

So for class InHouseClient { async complete() {…} }, the method lives on InHouseClient.prototype, and the path is ["InHouseClient", "prototype"]. Passing ["InHouseClient"] looks for a static InHouseClient.complete, doesn't find one, and the wrap is skipped with a startup warning — your calls run, unmetered and unchecked. Use ["InHouseClient"] only when the method really is static, and a bare "InHouseClient" string is never right. A nested target is just a longer walk: ["outer", "Inner", "prototype"].

Next

On this page