@voightxyz/vercel-ai is an OpenTelemetry SpanExporter that consumes the experimental_telemetry spans the Vercel AI SDK emits natively. Register it once next to @vercel/otel, set experimental_telemetry: { isEnabled: true } on your streamText / generateText / streamObject / generateObject calls, and every call lands in your Voight dashboard with prompts, tokens, tool calls, cache reads, latency, and errors.
Same backend and dashboard as @voightxyz/openai + @voightxyz/anthropic. Events from all three packages land side-by-side under the same agent.
Quick setup with the wizard
If your app already importsai, the lowest-friction install is the wizard from the main SDK:
ai in your package.json, prompts for a privacy level + Voight key + agent name, validates the key, and writes a ready-to-load instrumentation.ts that registers VoightExporter with @vercel/otel. 30 seconds, zero copy-paste. Full walkthrough at docs.voight.xyz/ai-apps/wizard.
Continue below if you’d rather wire it manually.
Why an OTel SpanExporter
The Vercel AI SDK emits OpenTelemetry spans natively whenexperimental_telemetry is enabled — the same wire format every other LLM-observability tool listed in the Vercel AI SDK docs consumes (Langfuse, Helicone, Phoenix, Braintrust, Datadog, Sentry, Weights & Biases). We follow the same contract so you can:
- Wire Voight alongside one of those tools via
MultiSpanProcessor, or - Drop in Voight as the sole observability provider,
Install
@voightxyz/vercel-ai declares @opentelemetry/api and @opentelemetry/sdk-trace-base as peer dependencies, and ai as an optional peer. Bring your own provider package (@ai-sdk/openai, @ai-sdk/anthropic, …).
Requirements:
- Node.js 18+
- Vercel AI SDK 6.0.0+
- A Voight API key (
vk_…) — generate one at voight.xyz/dashboard/settings
Quick start
1. Register the exporter via instrumentation.ts
2. Enable telemetry on your LLM calls
3. Set VOIGHT_KEY
streamText / generateText / streamObject / generateObject call is captured automatically. Visit your Voight dashboard to see them in real time.
Per-user attribution
For production apps where you want to attribute cost per end-user with one line of code, passmetadata on experimental_telemetry:
ai.telemetry.metadata.<key> span attribute into the Voight event’s metadata.tags.<key> — which is exactly the shape that drives the per-user spend sub-tab and the per-tag filter pills.
The userId value is the only one Voight treats specially (it powers the Users sub-tab). Everything else is filter-only.
Options
What’s captured
Privacy
Three levels control how aggressively prompts, responses, and tool arguments are scrubbed before they leave the process. The PII catalogue is the same as@voightxyz/openai and @voightxyz/anthropic.
For most apps
'standard' is the right balance — useful debugging signal without leaking secrets.
Pairing with another OTel exporter
The exporter coexists with any otherSpanExporter via OTel’s MultiSpanProcessor — common when you already ship to Datadog / Honeycomb / Grafana Tempo and want to add Voight as a second sink.
gen_ai.*) or Vercel (ai.*) attributes, so non-LLM spans (HTTP, DB) are silently skipped from the Voight stream while still reaching your other exporter.
Pairing with Voight direct wrappers
If your app also uses@voightxyz/openai or @voightxyz/anthropic with otel: true, both packages emit OpenTelemetry spans for every LLM call. Since VoightExporter is registered as an OTel exporter, those wrapper-emitted spans would normally hit the Voight backend twice — once via the wrapper’s direct POST, once via this exporter.
Starting in 0.1.1, the exporter recognises the voight.source: 'wrapper' attribute the wrappers stamp on those spans and skips them cleanly — no POST, callback still SUCCESS. Other OTel exporters in the same process (Langfuse, Datadog, Sentry) still see the spans and forward them normally. The dedup is scoped to the Voight-to-Voight loop.
Spans without that marker — the canonical streamText / generateText / streamObject / generateObject spans the Vercel AI SDK emits — are unaffected.
Out of scope (deferred)
- Direct middleware (
voightMiddleware()) for users who prefer a zero-OTel-setup wrap — planned for0.2. withTrace/logasync-context helpers — OTel context propagation already provides equivalent semantics. The helpers may return in0.2if real usage shows a gap.- Batched / buffered ingest — the per-event POST is sufficient for the workloads we expect at this scale. Batching arrives with real-world failure-mode data to design against.
- Bedrock / Vertex provider-specific paths — the
gen_ai.*normalisation is expected to cover them; will revisit if a user reports a missing attribute.