Skip to main content
@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 imports ai, the lowest-friction install is the wizard from the main SDK:
It detects 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 when experimental_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,
with the same code path either way. No vendor lock, no custom middleware.

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:

Quick start

1. Register the exporter via instrumentation.ts

2. Enable telemetry on your LLM calls

3. Set VOIGHT_KEY

That’s it — every wrapped 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, pass metadata on experimental_telemetry:
The exporter lifts every 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 other SpanExporter via OTel’s MultiSpanProcessor — common when you already ship to Datadog / Honeycomb / Grafana Tempo and want to add Voight as a second sink.
Each exporter sees the same span batch independently — Voight only consumes spans that carry the GenAI semconv (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 for 0.2.
  • withTrace / log async-context helpers — OTel context propagation already provides equivalent semantics. The helpers may return in 0.2 if 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.

Source

github.com/Voightxyz/voight-vercel-ai — Apache 2.0.