@tokenwisehq/observe
v0.2.3
Published
See your LLM calls in Tokenwise: a tiny, never-blocking wrapper for the OpenAI and Anthropic SDKs.
Maintainers
Readme
@tokenwisehq/observe
See your LLM calls in Tokenwise without rerouting production. It wraps your existing OpenAI or Anthropic client and logs each call in the background. Your requests are never slowed, broken, or changed.
Observe mode shows your calls on every dashboard, with quality scores and evals. To also cut cost automatically (caching and model routing), point your client's base URL at the Tokenwise gateway.
Quick start
Set it up with one command. It finds your package manager and SDK, installs the package, and prints the two lines to add. It does not touch your code or config files.
npx @tokenwisehq/observe initPrefer to do it by hand? Install the package:
npm i @tokenwisehq/observeGet a key (it starts with tw_api_) from Settings, API Keys in Tokenwise, then
add it to your environment. The SDK reads it automatically:
TOKENWISE_OBSERVE_KEY=tw_api_...OpenAI
Also works with OpenAI-compatible providers: Groq, DeepSeek, Mistral, xAI, OpenRouter, and more.
import OpenAI from "openai";
import { observeOpenAI } from "@tokenwisehq/observe";
// reads TOKENWISE_OBSERVE_KEY from your environment
const openai = observeOpenAI(new OpenAI());
// Use it exactly as before. Every call is logged to Tokenwise.
const res = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
});Streaming is handled for you: usage is added up as you read the stream and sent once it finishes.
Anthropic
import Anthropic from "@anthropic-ai/sdk";
import { observeAnthropic } from "@tokenwisehq/observe";
// reads TOKENWISE_OBSERVE_KEY from your environment
const anthropic = observeAnthropic(new Anthropic());
await anthropic.messages.create({
model: "claude-haiku-4-5",
max_tokens: 256,
messages: [{ role: "user", content: "Hello" }],
});Options
| Option | Default | What it does |
| --- | --- | --- |
| apiKey | TOKENWISE_OBSERVE_KEY env var | Your Tokenwise key (starts with tw_api_). Optional when the env var is set. |
| endpoint | https://tokenwisehq.com/api/v1/observe | Where events are sent. Set this only if you self-host. |
| tag | none | A label added to every call, e.g. a feature name. |
| captureContent | true | Set to false to send only metrics, never prompt or response content. |
| redact | none | A function to scrub or drop an event before it is sent. |
| timeoutMs | 3000 | How long to wait when sending an event. |
| fetchImpl | global fetch | Provide your own fetch if you need to. |
| onError | none | Called if sending an event fails. It never affects your request. |
Privacy
By default the SDK includes your prompt and response so you can see them in the dashboard. You can keep all content on your side:
captureContent: falsesends only metrics (model, tokens, cost, latency, status). Your prompts and responses never leave your servers, and cost still works.redactruns right before an event is sent. Return a changed event, ornullto drop it.
observeOpenAI(new OpenAI(), {
captureContent: false, // metrics only
// or scrub selectively:
// redact: (e) => ({ ...e, input: maskPII(e.input) }),
});Any content you do send is encrypted at rest, sent over TLS, and governed by your workspace's payload-storage setting. Keys are stored as hashes, never in plain text.
What it promises
- Your requests are never slowed. Events are sent in the background.
- Your app never breaks because of logging. It never throws.
- Your responses come back unchanged.
Already using OpenTelemetry or OpenLLMetry?
You don't need this package. Point your OTLP exporter at Tokenwise instead:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = https://tokenwisehq.com/api/otel/v1/traces
OTEL_EXPORTER_OTLP_PROTOCOL = http/json
OTEL_EXPORTER_OTLP_HEADERS = Authorization=Bearer tw_api_...License
MIT
