Skip to main content
Phoenix traces Vercel AI SDK (>= 7) applications through the AI SDK’s OpenTelemetry integration (@ai-sdk/otel). The simplest setup is @arizeai/phoenix-otel, which handles the OpenTelemetry provider, OpenInference span processing, and export to Phoenix in a single register() call.

Version compatibility

AI SDK v7 telemetry requires Node.js 22 or newer.

Installation

Ensure your installed version of @opentelemetry/api matches the version installed by @ai-sdk/otel otherwise the AI SDK will not emit traces to the TracerProvider that you configure. If you install ai before the other packages, then dependency resolution in your package manager should install the correct version.

Setup

Since AI SDK v7, telemetry is emitted once you register a telemetry integration with registerTelemetry(new OpenTelemetry(...)). Pair that with register() from @arizeai/phoenix-otel, which processes the resulting spans and exports them to Phoenix:
Import this file before the rest of your program executes, e.g. node --import ./instrumentation.ts index.ts, or import "./instrumentation.js" at the top of your application’s entrypoint. Once the telemetry integration is registered, AI SDK calls are traced by default; no per-call configuration is required. Use the telemetry option for per-call metadata such as functionId, or to opt out with isEnabled: false.
This applies to agents as well: a ToolLoopAgent run is traced end to end, with the agent, each LLM step, and every tool call captured as spans in a single trace. See the AI SDK agent example for a complete runnable project.
register() uses a batch span processor by default, so spans still queued when a short-lived script exits may not be exported. Call await provider.shutdown() before exit to flush them, or pass batch: false to register() for immediate export.

Decision models

AI SDK decision models, such as OpenAI’s gpt-6-luna through openai.decisionModel() or TypeSafe’s Jev through @ai-sdk/typesafe-ai, answer typed questions about a shared state instead of generating text. Calls made through the AI SDK’s experimental_decide are traced as DECISION spans rather than LLM spans, with no extra instrumentor. This requires @arizeai/openinference-vercel 3.3.0 or later, plus ai 7.0.128 and @ai-sdk/otel 1.0.128 or later. @arizeai/phoenix-otel 2.x depends on @arizeai/openinference-vercel with a caret range, so a fresh install resolves to 3.3.0 or later. In an existing project, check the version in your lockfile and run npm update @arizeai/openinference-vercel if it is older. Two options on the OpenTelemetry integration shape what Phoenix receives:
  • experimental_decision: true records the decision state, questions, and answers. Without it the spans carry the model and token counts but no input or output.
  • enrichSpan: enrichSpanWithOpenInference tells the processor which of the two spans the AI SDK emits per call is the model call. The SDK emits an outer operation span and a child model-call span with identical gen_ai.* attributes, so without this hint both are exported as DECISION spans. enrichSpanWithOpenInference is re-exported from the ESM-only @arizeai/phoenix-otel/vercel subpath.
With that in place, a decision call needs no per-call configuration:
Each call appears in Phoenix as two spans named decide <model>, for example decide gpt-6-luna: a CHAIN span for the decide operation, carrying the state and questions as input and the answers as output, with a child DECISION span for the model call carrying:
  • decision.system and decision.provider, for example openai or typesafe
  • decision.request.model_name, decision.response.model_name, and decision.model_name. The response model needs @ai-sdk/otel 1.0.138 or later; earlier versions do not record it on decide spans.
  • decision.token_count.input and decision.token_count.output. The output count is often 0, since the answers are typed values rather than generated tokens.
Phoenix names the decision model in the DECISION span’s input card. Because the model and its usage are recorded under decision.* rather than llm.*, decision calls stay out of Phoenix’s LLM token and cost reporting.
TypeScript classification evaluators from @arizeai/phoenix-evals take the same path when given a decision model. They enable experimental_decision on their own telemetry integration, so the state, questions, and answers are recorded with no further setup. They do not set enrichSpan, so the outer operation span is also exported as a DECISION span rather than a CHAIN span.

Next.js

In a Next.js project, register the telemetry integration and @vercel/otel from the register() function in instrumentation.ts, using the OpenInference span processor from @arizeai/openinference-vercel, the same processor @arizeai/phoenix-otel uses under the hood:
See Vercel’s instrumentation guide for more details on configuring your instrumentation file and @vercel/otel within a Next.js project.
When instrumenting a Next.js application, traced spans will not be “root spans” when the OpenInference span filter is configured. This is because Next.js parents spans underneath http requests, which do not meet the requirements to be an OpenInference span.

Examples

For details on Vercel AI SDK telemetry see the Vercel AI SDK Telemetry documentation.