@voightxyz/anthropic instruments the official Anthropic Node SDK. Wrap your client once and every messages.create call — non-streaming or streaming — lands in Voight with prompts, tokens, cache reads, cache creations, tool use, latency, and errors.
Same backend, same dashboard as @voightxyz/openai and library mode — events from all three land side-by-side under the same agent.
Quick setup (recommended)
From the root of your app:@anthropic-ai/sdk in your package.json, prompts for your Voight key + privacy level, validates the key, and writes a ready-to-import src/lib/voight.ts with the wrapped client. 30 seconds, zero copy-paste.
Continue below if you’d rather wire it manually.
Install
- Node.js 18+ (uses global
fetch) @anthropic-ai/sdk0.30.0+
Quick start
Tracing & per-user tags
For production apps, wrap each request boundary withwithTrace to group every LLM call inside one request into one trace, and to attribute cost per end-user with one line of code:
withTrace block gets stamped with metadata.tags = { userId, plan, ... } automatically. The dashboard’s AI Apps section then surfaces:
- Users sub-tab — per-user spend, traces, tokens (driven by
tags.userId) - Trace cards — every
withTraceblock as one drillable card with cost, latency, and event timeline - User filter pill — narrow Overview / Models / Tools to one user
Options
A missing or empty API key is non-fatal: the wrapper prints a one-line warning and returns the original client. Production keeps running.
What’s captured
Streaming
Streaming works without setup. Anthropic emits a typed event sequence (message_start, content_block_start, content_block_delta, content_block_stop, message_delta, message_stop); the wrapper’s state machine reacts to the events that drive capture and passes the rest through unchanged.
message_start carries input_tokens plus the two cache fields. Final output_tokens lands on message_delta and merges into the emitted event so the full breakdown is captured.
Tool use
tool_use blocks inside response.content[]. The wrapper flattens them into the same { id, name, arguments } shape @voightxyz/openai produces — dashboards render tool calls from either provider identically. Anthropic’s tool input is a parsed object on the wire; we JSON-stringify it so the captured arguments field is a string (matching the openai package).
On the captured event:
toolExecuted: 'get_weather'— first tool’s namemetadata.toolCalls: [{ id, name, arguments }]— full array
input_json_delta.partial_json fragments across content_block_delta events are concatenated per index.
Path-A cache pricing
Anthropic distinguishes two cache operations:- Cache creation (
cache_creation_input_tokens) — writing context to the cache. Billed at 1.25× input rate. - Cache read (
cache_read_input_tokens) — reading cached context on subsequent turns. Billed at 0.10× input rate.
metadata.tokens only when strictly positive. The Voight backend pricing engine applies the multipliers automatically — your spend reflects the real cost, not a flat-rate over-estimate.
OpenTelemetry side-channel
By default the wrapper POSTs each captured call directly toapi.voight.xyz. Set otel: true to additionally emit each call as an OpenTelemetry span — useful when the host process already runs an OTel pipeline (Langfuse, Phoenix, Datadog, Sentry, or @voightxyz/vercel-ai) and you want Voight events to appear there too.
voight.anthropic.messages and carries the standard gen_ai.* semantic-convention attributes (gen_ai.system resolves to 'anthropic', plus gen_ai.request.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, gen_ai.usage.cache_read_input_tokens, gen_ai.response.finish_reasons) plus the parallel Vercel-style ai.* namespace. The direct ingest path is unchanged — otel: true is purely additive.
Dedup marker
Every emitted span carriesvoight.source: 'wrapper'. If you also use @voightxyz/vercel-ai ≥ 0.1.1 in the same process, that exporter skips wrapper-emitted spans automatically — no duplicate events in your dashboard.
Optional peer dependency
@opentelemetry/api is now an optional peer dependency. If you never set otel: true, nothing changes. If you set otel: true but the package isn’t installed, the wrapper logs a single warning and falls back to direct ingest only.
Privacy
Three levels apply to prompts, response text, and tool-call arguments. The function name intoolExecuted is treated as a tag (not user content) and survives all levels.
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 PII patterns for the full catalogue.
How it compares
The packages coexist — wrap your Anthropic client AND call
voight.log() for your own domain events under the same agent. Adding withTrace on top groups them all per-request.
Source
Roadmap
- Bedrock and Vertex Anthropic clients
- Batch API (when GA)