@voightxyz/anthropic
v0.1.8
Published
Voight observability for the Anthropic SDK. Wrap your Anthropic client and capture every Messages call — prompts, tokens, costs, cache reads, tool use, latency, errors — surfaced live in the Voight dashboard.
Maintainers
Readme
@voightxyz/anthropic
Voight observability for the Anthropic SDK. Wrap your Anthropic client and capture every Messages call — prompts, tokens, cache reads, cache creations, tool use, costs, latency, errors — surfaced live in the Voight dashboard.
Same backend and dashboard as @voightxyz/openai. Drop in whichever provider your app uses; events from both land side-by-side under the same agent.
Quick setup with the wizard
If your app already imports @anthropic-ai/sdk, the lowest-friction install is the wizard from the main SDK:
cd your-app
npx -y @voightxyz/sdk initIt detects @anthropic-ai/sdk (and openai if present) in your package.json, prompts for a privacy level + Voight key + agent name, and writes a ready-to-import src/lib/voight.ts with the wrapped client. 30 seconds, zero copy-paste. Full walkthrough at docs.voight.xyz/ai-apps/wizard.
Continue below if you'd rather wire it manually.
Install
npm install @anthropic-ai/sdk @voightxyz/anthropicQuick start
import Anthropic from '@anthropic-ai/sdk'
import { wrapAnthropic } from '@voightxyz/anthropic'
const client = wrapAnthropic(new Anthropic(), {
voightApiKey: process.env.VOIGHT_KEY,
agent: 'my-prod-agent',
})
const response = await client.messages.create({
model: 'claude-haiku-4-5',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Hello' }],
})That's it — every call is captured automatically. Visit your Voight dashboard to see them in real time.
Tracing & per-user attribution
For production apps where you want to group every LLM call inside one request into one trace, and attribute cost per end-user with one line of code, wrap each request boundary with withTrace:
import Anthropic from '@anthropic-ai/sdk'
import { wrapAnthropic, withTrace, log } from '@voightxyz/anthropic'
const anthropic = wrapAnthropic(new Anthropic(), {
agent: 'production-chat-api',
privacy: 'standard',
})
app.post('/api/chat', async (req, res) => {
await withTrace(
async () => {
log('chat request received')
const reply = await anthropic.messages.create({
model: 'claude-haiku-4-5',
max_tokens: 1024,
messages: [{ role: 'user', content: req.body.prompt }],
})
res.json({ reply })
},
{
routeTag: 'POST /api/chat',
tags: { userId: req.user.id, plan: req.user.plan },
},
)
})Every wrapped LLM call inside the withTrace block automatically inherits the routeTag and tags. The tags.userId field drives per-user spend tracking — the dashboard's Users sub-tab populates with per-customer cost as soon as your first request lands.
The same withTrace exported here also works in @voightxyz/openai — they share an async-context store, so an app calling both providers inside one request gets one trace, not two.
What's captured
| Signal | Where it lands |
|---|---|
| Model id (with version suffix) | model |
| Prompt messages | input.messages |
| Response text (aggregated from content[].text blocks) | metadata.responseText |
| Token counts (input / output / total) | metadata.tokens |
| Cache reads (cache_read_input_tokens) | metadata.tokens.cache_read |
| Cache creations (cache_creation_input_tokens) | metadata.tokens.cache_creation |
| Tool use (full array) | metadata.toolCalls + toolExecuted |
| Streaming flag | metadata.streaming |
| Trace grouping (auto UUID or explicit) | metadata.sessionId |
| Stop reason | metadata.finishReason |
| Latency (ms) | durationMs |
| Errors (re-thrown to the caller) | errorMessage + outcome: 'failed' |
Supported endpoints
client.messages.create— Messages API (non-streaming + streaming, tool use, cache breakpoints)
The wrapper passes everything else through untouched. Bedrock and Vertex clients are on the 0.2.0 roadmap.
Options
| Option | Type | Default | Purpose |
| --- | --- | --- | --- |
| voightApiKey | string | process.env.VOIGHT_KEY | Your Voight key from the dashboard |
| agent | string | process.env.VOIGHT_AGENT → HOSTNAME → 'unknown-agent' | Stable identifier surfaced in the dashboard |
| apiBase | string | https://api.voight.xyz | Override for self-hosted deployments |
| privacy | 'minimal' \| 'standard' \| 'full' | 'standard' | Capture aggressiveness |
| sessionId | string | auto UUID v4 | Trace grouping. Stable across calls of one wrapper instance |
| enabled | boolean | true | Kill switch — returns the original client untouched |
Privacy
Three levels apply to prompts, response text, and tool-call arguments. The function name in toolExecuted always survives as a tag (not user content).
| Level | Prompts | Response text | Tool arguments | Tokens / timing / model |
| --- | --- | --- | --- | --- |
| minimal | dropped | dropped | dropped | kept |
| standard (default) | scrubbed | scrubbed | scrubbed | kept |
| full | verbatim | verbatim | verbatim | kept |
Standard scrubs 12 patterns: PEM private keys, JWTs, Anthropic / OpenAI / Stripe live / GitHub / AWS / Slack / Voight API keys, emails, E.164 phones, and Luhn-validated credit cards.
See CHANGELOG.md for release notes.
License
Apache 2.0
