本页还没有译文
本页 140 段还没译成한국어,下面显示的是英文原文。Telemetry
还没翻译译自英文(ai-sdk.dev)本页 140 段都还没译
The AI SDK uses OpenTelemetry to collect telemetry data. OpenTelemetry is an open-source observability framework designed to provide standardized instrumentation for collecting telemetry data.
Check out the AI SDK Observability Integrations to see providers that offer monitoring and tracing for AI SDK applications.
Enabling telemetry
Step 1: Register the OpenTelemetry integration
OpenTelemetry span collection requires the @ai-sdk/otel package. Install it and register the integration once at application startup:
pnpm install @ai-sdk/otelimport { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';
registerTelemetry(new OpenTelemetry());Next.js
For Next.js applications, create an instrumentation.ts file in your project root and register the AI SDK telemetry integration alongside your OpenTelemetry provider setup:
import { registerOTel } from '@vercel/otel';
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';
export function register() {
registerOTel({
serviceName: 'my-ai-app',
});
registerTelemetry(new OpenTelemetry());
}See the Next.js OpenTelemetry guide for more details on setting up OpenTelemetry in Next.js.
For Node.js applications (without Next.js), register the integration at the top level of your entry file.
Step 2: Enabling Telemetry
Once a telemetry integration is registered, supported AI SDK calls emit
telemetry events by default. This includes text and object generation,
embeddings, reranking, speech generation, and both non-streaming and streaming
transcription.
You can still pass telemetry to attach metadata (like functionId) or to opt out of a specific call:
const result = await generateText({
model: 'anthropic/claude-sonnet-5.5',
prompt: 'Write a short story about a cat.',
telemetry: {
functionId: `story-agent`,
},
});By default, both inputs and outputs are recorded. You can disable them by setting the recordInputs and recordOutputs options to false.
Disabling the recording of inputs and outputs can be useful for privacy, data transfer, and performance reasons. You might for example want to disable recording inputs if they contain sensitive information.
For generateSpeech, input telemetry contains the source text and output
telemetry contains the generated audio byte length, media type, and format.
For transcribe and experimental_streamTranscribe, telemetry contains the
input audio byte length and media type plus the output transcript. Raw audio is
never included in telemetry events. Streaming transcription reports its final
byte length when the audio stream has been consumed and the transcription
finishes.
Custom integrations receive stable generateSpeech and transcribe
operations through onStart and onEnd. To keep the experimental API isolated,
experimental_streamTranscribe uses
experimental_onStreamTranscriptionStart and
experimental_onStreamTranscriptionEnd; all three operations report failures
through onError.
An integration that wraps or delegates to another integration must explicitly
forward the experimental streaming transcription callbacks. Forwarding only
the stable onStart and onEnd callbacks does not observe
experimental_streamTranscribe.
The built-in OpenTelemetry integrations record numeric provider usage by
default, preserving each reported unit. The modern integration uses attributes
such as gen_ai.usage.characters, gen_ai.usage.seconds, and
gen_ai.usage.input_tokens; LegacyOpenTelemetry uses the corresponding
ai.usage.* attributes. The provider-native usage object remains available to
callbacks, and can also be included as the supplemental ai.response.usage
attribute by enabling usage: true.
Opting out
Telemetry is opt-out. To disable telemetry for a specific call, set isEnabled: false:
const result = await generateText({
model: 'anthropic/claude-sonnet-5.5',
prompt: 'Write a short story about a cat.',
telemetry: { isEnabled: false },
});Not registering integrations with registerTelemetry() prevents delivery to globally registered integrations, but does not disable per-call integrations or Node.js tracing-channel subscribers. To disable telemetry events for a call, including tracing-channel events, pass telemetry: { isEnabled: false }. Apply this setting to every call if you want to opt out throughout your application.
Telemetry Metadata
You can provide a functionId to identify the function that the telemetry data is for,
and runtimeContext to include additional information in the telemetry data.
For the broader context model, see
Runtime and Tool Context.
const result = await generateText({
model: 'anthropic/claude-sonnet-5.5',
prompt: 'Write a short story about a cat.',
runtimeContext: {
userId: 'user_123',
requestId: 'req_abc',
},
telemetry: {
functionId: 'my-awesome-function',
},
});Runtime context telemetry inclusion
runtimeContext can contain values that are useful inside your application but should not be sent to telemetry providers, such as user identifiers, tenant IDs, or credentials. Use telemetry.includeRuntimeContext to mark the top-level runtimeContext properties that should be included in telemetry:
const result = await generateText({
model: 'anthropic/claude-sonnet-5.5',
prompt: 'Write a short story about a cat.',
runtimeContext: {
userId: 'user_123',
requestId: 'req_abc',
},
telemetry: {
includeRuntimeContext: {
requestId: true,
},
},
});In this example, telemetry integrations receive runtimeContext as { requestId: 'req_abc' }. Properties set to false or omitted are excluded. If telemetry.includeRuntimeContext is omitted, no runtime context properties are included. telemetry.includeRuntimeContext is supported by generateText, streamText, ToolLoopAgent, embed, embedMany, rerank, and experimental_decide.
Note
telemetry.includeRuntimeContext only filters telemetry integrations,
including OpenTelemetry integrations. Lifecycle callbacks and returned results
still receive the full runtimeContext.
Tool context
Tool-specific context can also contain values that are useful during execution but should not be sent to telemetry providers, such as API keys or access tokens.
Use telemetry.includeToolsContext to include selected top-level properties from a tool's context in telemetry:
const weatherTool = tool({
inputSchema: z.object({
location: z.string(),
}),
contextSchema: z.object({
weatherApiKey: z.string(),
defaultUnit: z.enum(['celsius', 'fahrenheit']),
}),
execute: async ({ location }, { context }) =>
fetchWeather(location, context.weatherApiKey, context.defaultUnit),
});
const result = await generateText({
model: 'anthropic/claude-sonnet-5.5',
tools: { weather: weatherTool },
toolsContext: {
weather: {
weatherApiKey: 'weather-123',
defaultUnit: 'fahrenheit',
},
},
prompt: 'What is the weather in San Francisco?',
telemetry: {
includeToolsContext: {
weather: {
defaultUnit: true,
},
},
},
});In this example, telemetry integrations receive toolsContext.weather and tool execution telemetry event toolContext as { defaultUnit: 'fahrenheit' }.
Properties set to false or omitted are excluded. If telemetry.includeToolsContext is omitted, no tool context properties are included. telemetry.includeToolsContext is supported by generateText, streamText, and ToolLoopAgent.
Note
telemetry.includeToolsContext only filters telemetry integrations, including
OpenTelemetry integrations. Tool execution, lifecycle callbacks, and returned
results still receive the full tool context. See Runtime and Tool
Context for the difference
between tool execution context and telemetry event context.
Agent span names
agentSpanName controls root text/object generation span names. The strings
'modelId' (default) and 'functionId' select a naming strategy, producing
invoke_agent {modelId} or invoke_agent {telemetry.functionId}:
registerTelemetry(new OpenTelemetry({ agentSpanName: 'functionId' }));
// With telemetry: { functionId: 'travel-planner' }, the span is:
// invoke_agent travel-plannerFor a literal custom name, use a callback returning the complete span name:
registerTelemetry(new OpenTelemetry({ agentSpanName: () => 'my-custom-span' }));The callback receives { modelId, functionId } for dynamic names. If it returns
undefined or throws, the default name is used. The 'functionId' strategy also
falls back to modelId when functionId is absent. Child span names and
attributes are unchanged.
Custom Tracer
If you want your traces to use a TracerProvider other than the one provided by the @opentelemetry/api singleton, pass a custom Tracer to the OpenTelemetry constructor:
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';
const tracerProvider = new NodeTracerProvider();
registerTelemetry(
new OpenTelemetry({
tracer: tracerProvider.getTracer('gen_ai'),
}),
);Telemetry Integrations
Telemetry integrations let you hook into the generation lifecycle to build custom observability — logging, analytics, DevTools, or any other monitoring system. Instead of wiring up individual callbacks on every call, you implement a Telemetry once and register it globally or pass it via telemetry.integrations.
The OpenTelemetry and LegacyOpenTelemetry from @ai-sdk/otel are the built-in integrations for collecting OpenTelemetry spans (see Enabling telemetry above).
Registering integrations globally
Use registerTelemetry to register an integration once for all AI SDK calls:
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';
registerTelemetry(new OpenTelemetry());You can also register multiple integrations in a single call by passing them as additional arguments. They all receive the same lifecycle events:
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';
import { DevToolsTelemetry } from '@ai-sdk/devtools';
registerTelemetry(new OpenTelemetry(), DevToolsTelemetry());Per-call integrations
You can also pass one or more integrations to individual generateText or streamText calls. When per-call integrations are provided, they replace the globally registered integrations for that call:
import { streamText } from 'ai';
import { DevToolsTelemetry } from '@ai-sdk/devtools';
const result = streamText({
model: openai('gpt-6-astra'),
prompt: 'Hello!',
telemetry: {
integrations: [DevToolsTelemetry()],
},
});You can combine multiple integrations — they all receive the same lifecycle events:
telemetry: {
integrations: [DevToolsTelemetry(), customLogger()],
},Errors inside integrations are caught and do not break the generation flow.
Tracing channel
In Node.js, AI SDK telemetry lifecycle and execution events are also traced on
the ai:telemetry tracing channel.
This lets observability providers subscribe to AI SDK telemetry events without
requiring users to register a separate telemetry integration, and bind async
context across provider calls and tool executions.
import { tracingChannel } from 'node:diagnostics_channel';
import {
AI_SDK_TELEMETRY_TRACING_CHANNEL,
type InferTelemetryEvent,
type TelemetryTracingChannelMessage,
} from 'ai';
tracingChannel(AI_SDK_TELEMETRY_TRACING_CHANNEL).subscribe({
start(message) {
const telemetryMessage = message as TelemetryTracingChannelMessage<
InferTelemetryEvent<unknown>
>;
if (
telemetryMessage.type === 'generateText' ||
telemetryMessage.type === 'streamText'
) {
console.log('AI SDK version:', telemetryMessage.event.aiVersion);
// Inspect telemetryMessage.event and forward it to your provider.
}
},
});Tracing-channel events follow the same per-call telemetry settings as other
telemetry integrations. Setting telemetry: { isEnabled: false } disables both
registered integrations and tracing-channel events for that call.
Building a custom integration
Implement the Telemetry interface from the ai package. All methods are optional — implement only the lifecycle events you care about:
import type { Telemetry } from 'ai';
class MyIntegration implements Telemetry {
async onStart(event) {
console.log('Generation started:', event.modelId);
}
async onStepEnd(event) {
console.log(
`Step ${event.stepNumber} done:`,
event.usage.totalTokens,
'tokens',
);
}
async onToolExecutionEnd(event) {
if (event.toolOutput.type === 'tool-result') {
console.log(
`Tool "${event.toolCall.toolName}" took ${event.toolExecutionMs}ms`,
);
} else {
console.error(
`Tool "${event.toolCall.toolName}" failed:`,
event.toolOutput.error,
);
}
}
async onEnd(event) {
console.log('Done. Total tokens:', event.usage.totalTokens);
}
async onAbort(event) {
console.log('Stream aborted after', event.steps.length, 'finished steps');
}
async onError(event) {
console.error('Generation failed:', event.callId, event.error);
}
}
export function myIntegration(): Telemetry {
return new MyIntegration();
}Available lifecycle methods
onStart((event: GenerateTextStartEvent) => void | PromiseLike<void>): Called when the generation operation begins, before any LLM calls.onStepStart((event: GenerateTextStepStartEvent) => void | PromiseLike<void>): Called when a step (LLM call) begins, before the provider is called.onLanguageModelCallStart((event: LanguageModelCallStartEvent) => void | PromiseLike<void>): Called immediately before the provider model call begins. Useful for model-only spans or logging.onLanguageModelCallEnd((event: LanguageModelCallEndEvent) => void | PromiseLike<void>): Called after the model response has been normalized and parsed, but before any client-side tool execution begins. Includes provider-specific metadata when available.onToolExecutionStart((event: ToolExecutionStartEvent) => void | PromiseLike<void>): Called when a tool's execute function is about to run.onToolExecutionEnd((event: ToolExecutionEndEvent) => void | PromiseLike<void>): Called when a tool's execute function completes or errors.onStepEnd((event: GenerateTextStepEndEvent) => void | PromiseLike<void>): Called when a step (LLM call) completes.onStepError((event: GenerateTextStepErrorEvent) => void | PromiseLike<void>): Called when a step that started fails before it produces a step result. Use it to close step-level spans. The call then ends withonError.onEmbedEnd((event: EmbeddingModelCallEndEvent) => void | PromiseLike<void>): Called when an individual embedding model call completes.onRerankEnd((event: RerankingModelCallEndEvent) => void | PromiseLike<void>): Called when an individual reranking model call completes.experimental_onDecideStart((event: Experimental_DecideStartEvent) => void | PromiseLike<void>): Called when an experimental decision operation begins.experimental_onDecisionModelCallStart((event: Experimental_DecisionModelCallStartEvent) => void | PromiseLike<void>): Called when the logical decision model call begins. The call includes any provider retries.experimental_onDecisionModelCallEnd((event: Experimental_DecisionModelCallEndEvent) => void | PromiseLike<void>): Called when the logical decision model call, including any retries, completes.experimental_onDecideEnd((event: Experimental_DecideEndEvent) => void | PromiseLike<void>): Called when an experimental decision operation completes.onEnd((event: GenerateTextEndEvent) => void | PromiseLike<void>): Called when the entire generation completes (all steps finished).onAbort((event: GenerateTextAbortEvent) => void | PromiseLike<void>): Called when a streaming text generation operation is aborted before it completes.onError((event: { callId: string; error: unknown }) => void | PromiseLike<void>): Called when an unrecoverable error occurs. The event identifies the operation and contains the thrown value.
Experimental decision events use the four experimental_* callbacks above.
They are not included in the stable onStart and onEnd event unions.
The event types for each method are the same as the corresponding lifecycle callbacks. See the lifecycle callbacks documentation for the full property reference of each event.
Collected Data
The @ai-sdk/otel package provides two integrations that emit different span formats.
The OpenTelemetry follows the OpenTelemetry GenAI Semantic Conventions and is the recommended integration.
The LegacyOpenTelemetry integration emits legacy AI SDK-specific spans.
GenAI Semantic Conventions
The OpenTelemetry emits spans that follow the OpenTelemetry Semantic Conventions for GenAI.
By default, attributes use the gen_ai.* prefix. Provider names are mapped to well-known values (e.g. openai, anthropic, gcp.vertex_ai).
generateText / streamText
For generateText and streamText, the integration records 3 types of spans:
-
invoke_agent {modelId}(default root span name,INTERNAL): covers the full operation including all steps and tool calls. UseagentSpanNameto customize the name.Initial attributes:
gen_ai.operation.name:"invoke_agent"gen_ai.provider.name: the provider (e.g."openai","anthropic")gen_ai.request.model: the requested model IDgen_ai.agent.name: thefunctionIdfrom telemetry settingsgen_ai.system_instructions: system instructions formatted as a JSON array of parts (whenrecordInputsis enabled)gen_ai.input.messages: the input messages in GenAI SemConv message format (whenrecordInputsis enabled)gen_ai.request.temperature: the temperature settinggen_ai.request.max_tokens: the maximum output tokensgen_ai.request.top_p: the topP settinggen_ai.request.top_k: the topK settinggen_ai.request.frequency_penalty: the frequency penaltygen_ai.request.presence_penalty: the presence penaltygen_ai.request.stop_sequences: the stop sequencesgen_ai.request.seed: the seed value
Attributes set on finish:
gen_ai.response.finish_reasons: array of finish reasons (e.g.["stop"],["tool_call"])gen_ai.usage.input_tokens: the number of input tokens usedgen_ai.usage.output_tokens: the number of output tokens usedgen_ai.usage.cache_read.input_tokens: cached input tokens readgen_ai.usage.cache_creation.input_tokens: cached input tokens createdgen_ai.output.messages: the output in GenAI SemConv message format (whenrecordOutputsis enabled)
-
chat {modelId}(step span,CLIENT): one span per LLM provider call, nested under the root span.Initial attributes:
gen_ai.operation.name:"chat"gen_ai.provider.name: the providergen_ai.request.model: the requested model IDgen_ai.request.temperature,gen_ai.request.max_tokens,gen_ai.request.top_p,gen_ai.request.top_k,gen_ai.request.frequency_penalty,gen_ai.request.presence_penalty,gen_ai.request.stop_sequences: request parametersgen_ai.system_instructions: instructions supplied separately from the chat history, formatted as a JSON array of parts (whenrecordInputsis enabled)gen_ai.input.messages: the prompt messages in GenAI SemConv message format, including system messages that are part of the chat history in their original order (whenrecordInputsis enabled)gen_ai.tool.definitions: the tool definitions as stringified JSON (whenrecordInputsis enabled)
Attributes set on finish:
gen_ai.response.finish_reasons: array of finish reasonsgen_ai.response.id: the response ID from the providergen_ai.response.model: the model that generated the response (may differ from the requested model)gen_ai.usage.input_tokens: input tokens used in this stepgen_ai.usage.output_tokens: output tokens used in this stepgen_ai.usage.cache_read.input_tokens: cached input tokens readgen_ai.usage.cache_creation.input_tokens: cached input tokens createdgen_ai.client.operation.duration: provider call duration, in secondsgen_ai.client.operation.time_to_first_chunk: time to the first streamed output chunk, in seconds (streaming calls only)gen_ai.client.operation.time_per_output_chunk: average time between streamed output chunks, in seconds (streaming calls with multiple output chunks only)gen_ai.output.messages: the output in GenAI SemConv message format (whenrecordOutputsis enabled)
-
execute_tool {toolName}(tool span,INTERNAL): one span per tool execution, nested under the step span. See GenAI tool call spans for details.
Deprecated object APIs (generateObject / streamObject)
Warning
generateObject and streamObject are deprecated. Use generateText and
streamText with the output property instead.
The deprecated object APIs emit the same span hierarchy as generateText/streamText with these additional attributes on the root span:
gen_ai.output.type:"json"
The step spans also include gen_ai.output.type: "json", and gen_ai.output.messages contains the generated object as a text part.
embed / embedMany
For embed and embedMany, the integration records spans with CLIENT kind:
-
embeddings {modelId}(root span): covers the full embedding operation.Initial attributes:
gen_ai.operation.name:"embeddings"gen_ai.provider.name: the providergen_ai.request.model: the requested model ID
-
embeddings {modelId}(inner span): one span per provider request, nested under the root span.embedcreates one inner span.embedManycreates one inner span per provider batch call.Initial attributes:
gen_ai.operation.name:"embeddings"gen_ai.provider.name: the providergen_ai.request.model: the model ID
Attributes set on finish:
gen_ai.usage.input_tokens: the number of tokens used
Usage is recorded only on inner provider-request spans. For embedMany, sum
the usage from the inner spans to get the total usage for the operation.
rerank
For rerank, the integration records spans with CLIENT kind:
-
rerank {modelId}(root span): covers the full rerank operation.Initial attributes:
gen_ai.operation.name:"rerank"gen_ai.provider.name: the providergen_ai.request.model: the requested model ID
-
rerank {modelId}(inner span): one span per provider rerank call, nested under the root span.Initial attributes:
gen_ai.operation.name:"rerank"gen_ai.provider.name: the providergen_ai.request.model: the model ID
generateSpeech / transcribe / experimental_streamTranscribe
Each call records a single CLIENT span named {operationId} {modelId}, where
operationId is ai.generateSpeech, ai.transcribe, or ai.streamTranscribe.
gen_ai.output.type:"speech"for speech generation and"text"for transcription.gen_ai.request.stream:truefor streaming transcription andfalseotherwise.gen_ai.input.messages: speech generation input text as a user message with a text part, whenrecordInputsis enabled.gen_ai.output.messages: the completed transcript as an assistant message with a text part, whenrecordOutputsis enabled. Streaming transcription records this once on completion.
The existing ai.request.text and ai.response.text attributes are also retained.
Audio byte length, media type, and output format remain in the
ai.request.audio.* and ai.response.audio.* attributes, subject to the
corresponding input/output recording controls. Raw audio is not captured, so
transcription input and speech output do not populate message attributes with
audio content or placeholder audio parts.
experimental_decide
For experimental_decide, the integration records two spans with CLIENT
kind: a decide {modelId} root span for the whole operation and one
decide {modelId} child span for decision model work, including
retries. Both include the provider and requested model with
gen_ai.operation.name: "decide". Token
usage is recorded on the child span. When decision attributes are enabled,
answers are recorded on both spans so the operation span contains its output.
decide is an AI SDK-defined custom operation name, not a well-known
OpenTelemetry GenAI operation. OpenTelemetry permits custom operation names
when no well-known value describes the operation.
OpenTelemetry's gen_ai.evaluation.result event describes the result of
evaluating a GenAI output for quality or other characteristics. In contrast,
experimental_decide can use arbitrary JSON state for classification,
routing, scoring, and similar tasks. The integration therefore does not emit a
gen_ai.evaluation.result event.
Decision state, questions, and answers also do not have applicable GenAI
semantic-convention attributes. Enable the experimental_decision
supplemental option to record them under AI SDK-specific
ai.decision.* attributes.
GenAI span details
GenAI message format
The gen_ai.input.messages and gen_ai.output.messages attributes follow the OpenTelemetry GenAI Semantic Conventions message format.
Messages are JSON arrays of objects with a role and a parts array. Each part has a type and type-specific fields:
text:{ type: "text", content: "..." }reasoning:{ type: "reasoning", content: "..." }tool_call:{ type: "tool_call", id: "...", name: "...", arguments: ... }tool_call_response:{ type: "tool_call_response", id: "...", response: ... }blob:{ type: "blob", modality: "image"|"video"|"audio", mime_type: "...", content: "..." }(base64-encoded)uri:{ type: "uri", modality: "image"|"video"|"audio", mime_type: "...", uri: "..." }(for URL-based files)
Output messages also include a finish_reason field (e.g. "stop", "tool_call", "length", "content_filter").
Instructions supplied through instructions (or the deprecated system option) are recorded separately in gen_ai.system_instructions as a JSON array of { type: "text", content: "..." } parts. System messages supplied as chat history with allowSystemInMessages remain in gen_ai.input.messages in their original positions.
GenAI tool call spans
Tool call spans (execute_tool {toolName}) are nested under the step span and contain:
gen_ai.operation.name:"execute_tool"gen_ai.tool.name: the name of the toolgen_ai.tool.call.id: the tool call IDgen_ai.tool.type:"function"gen_ai.tool.call.arguments: the input arguments (stringified JSON, whenrecordInputsis enabled)gen_ai.execute_tool.duration: the tool execution duration, in secondsgen_ai.tool.call.result: the output result (stringified JSON, whenrecordOutputsis enabled). Only set when the tool call succeeds.
Custom OpenTelemetry span attributes
Use enrichSpan to add custom attributes to spans created by the
OpenTelemetry integration. This is useful when an observability backend needs
vendor-specific attributes that are not AI SDK-owned semantics.
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';
registerTelemetry(
new OpenTelemetry({
enrichSpan: ({ spanType, operationId, callId, runtimeContext }) => {
return {
...getCustomAttributes(runtimeContext, spanType),
'my_app.operation_id': operationId,
'my_app.call_id': callId,
};
},
}),
);The callback runs when each span is created and receives:
spanType: the type of span being created (operation,step,languageModel,tool,embedding,reranking, orexperimental_decision).operationId: the AI SDK operation ID for the current call, such asai.generateTextorai.streamText.callId: the unique ID for the current AI SDK call.runtimeContext: the telemetry-filtered runtime context for text generation, embedding, reranking, and decision spans. Text generation spans also reflect updates fromprepareStep.
Custom attributes are merged with AI SDK attributes on the span. AI SDK-owned attributes take precedence when a custom attribute uses the same key.
telemetry.includeRuntimeContext is applied before telemetry integrations receive
runtimeContext, so custom span enrichment only receives top-level runtime context
properties explicitly set to true. If the option is omitted, no runtime context
properties are included.
Embedding and reranking operations can share the same runtime context with text generation to attribute their spans to the same application request:
await embed({
model: 'openai/text-embedding-3-small',
value: 'sunny day at the beach',
runtimeContext: {
requestId: 'req_abc',
userId: 'user_123',
},
telemetry: {
includeRuntimeContext: { requestId: true },
},
});A globally registered OpenTelemetry({ enrichSpan }) integration receives
{ requestId: 'req_abc' } for the operation span and its embedding model span.
The same applies to every chunk in embedMany and to the operation and model
spans in rerank. The onStart and onEnd callbacks receive the full context.
Supplemental AI SDK attributes on OpenTelemetry spans
The GenAI semantic conventions cover the core model, prompt, response, tool call, and usage data. You can opt into additional AI SDK-specific attributes for data that is not represented by GenAI semantics:
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';
registerTelemetry(
new OpenTelemetry({
usage: true,
providerMetadata: true,
runtimeContext: true,
}),
);This does not create additional AI SDK-specific spans such as ai.generateText, ai.generateText.doGenerate, or ai.toolCall. Instead, it adds only the selected supplemental attributes to the spans emitted by OpenTelemetry, such as the default root span invoke_agent {modelId}, step {n}, chat {modelId}, and execute_tool {toolName}.
All supplemental attributes are disabled by default. Enable only the data you want to collect:
registerTelemetry(
new OpenTelemetry({
usage: true,
providerMetadata: true,
embedding: true,
reranking: true,
experimental_decision: true,
runtimeContext: true,
headers: true,
toolChoice: true,
schema: true,
}),
);The available options are:
usage: detailed usage attributes that are not covered by GenAI usage attributes, such as uncached input tokens and output text/reasoning token details.providerMetadata:ai.response.providerMetadataon operation, step, and model-call spans.embedding: embedding inputs and outputs.reranking: rerank input documents and ranking output.experimental_decision: experimental decision state, questions, and answers.runtimeContext:ai.settings.context.*.headers:ai.request.headers.*.toolChoice:ai.prompt.toolChoice.schema: object generation schema and output mode attributes.
The recordInputs and recordOutputs telemetry options are still respected for supplemental input and output attributes.
Legacy AI SDK Spans (LegacyOpenTelemetry)
The LegacyOpenTelemetry integration emits spans using AI SDK-specific ai.* prefixed attributes.
This is the legacy format. Consider migrating to the OpenTelemetry for better compatibility with observability platforms.
generateText function
generateText records 3 types of spans:
-
ai.generateText(span): the full length of the generateText call. It contains 1 or moreai.generateText.doGeneratespans. It contains the basic LLM span information and the following attributes:operation.name:ai.generateTextand the functionId that was set throughtelemetry.functionIdai.operationId:"ai.generateText"ai.prompt: the prompt that was used when callinggenerateTextai.response.text: the text that was generatedai.response.toolCalls: the tool calls that were made as part of the generation (stringified JSON)ai.response.finishReason: the reason why the generation finishedai.settings.maxOutputTokens: the maximum number of output tokens that were set
-
ai.generateText.doGenerate(span): a provider doGenerate call. It can containai.toolCallspans. It contains the call LLM span information and the following attributes:operation.name:ai.generateText.doGenerateand the functionId that was set throughtelemetry.functionIdai.operationId:"ai.generateText.doGenerate"ai.prompt.messages: the messages that were passed into the providerai.prompt.tools: array of stringified tool definitions. The serialized tools can be of typefunctionorprovider. Function tools includeFunctionToolandDynamicTooldefinitions and have aname,description(optional), andinputSchema(JSON schema). Provider tools includeProviderDefinedToolandProviderExecutedTooldefinitions and have aname,id, andargs(Record).ai.prompt.toolChoice: the stringified tool choice setting (JSON). It has atypeproperty (auto,none,required,tool), and if the type istool, atoolNameproperty with the specific tool.ai.response.text: the text that was generatedai.response.toolCalls: the tool calls that were made as part of the generation (stringified JSON)ai.response.finishReason: the reason why the generation finished
-
ai.toolCall(span): a tool call that is made as part of the generateText call. See Legacy tool call spans for more details.
streamText function
streamText records 3 types of spans and 2 types of events:
-
ai.streamText(span): the full length of the streamText call. It contains aai.streamText.doStreamspan. It contains the basic LLM span information and the following attributes:operation.name:ai.streamTextand the functionId that was set throughtelemetry.functionIdai.operationId:"ai.streamText"ai.prompt: the prompt that was used when callingstreamTextai.response.text: the text that was generatedai.response.toolCalls: the tool calls that were made as part of the generation (stringified JSON)ai.response.finishReason: the reason why the generation finishedai.settings.maxOutputTokens: the maximum number of output tokens that were set
-
ai.streamText.doStream(span): a provider doStream call. This span contains anai.stream.firstChunkevent andai.toolCallspans. It contains the call LLM span information and the following attributes:operation.name:ai.streamText.doStreamand the functionId that was set throughtelemetry.functionIdai.operationId:"ai.streamText.doStream"ai.prompt.messages: the messages that were passed into the providerai.prompt.tools: array of stringified tool definitions. The serialized tools can be of typefunctionorprovider. Function tools includeFunctionToolandDynamicTooldefinitions and have aname,description(optional), andinputSchema(JSON schema). Provider tools includeProviderDefinedToolandProviderExecutedTooldefinitions and have aname,id, andargs(Record).ai.prompt.toolChoice: the stringified tool choice setting (JSON). It has atypeproperty (auto,none,required,tool), and if the type istool, atoolNameproperty with the specific tool.ai.response.text: the text that was generatedai.response.toolCalls: the tool calls that were made as part of the generation (stringified JSON)ai.response.msToFirstChunk: the time it took to receive the first chunk in millisecondsai.response.msToFinish: the time it took to receive the finish part of the LLM stream in millisecondsai.response.avgCompletionTokensPerSecond: the average number of completion tokens per secondai.response.finishReason: the reason why the generation finished
-
ai.toolCall(span): a tool call that is made as part of the streamText call. See Legacy tool call spans for more details. -
ai.stream.firstChunk(event): an event that is emitted when the first chunk of the stream is received.ai.response.msToFirstChunk: the time it took to receive the first chunk
-
ai.stream.finish(event): an event that is emitted when the finish part of the LLM stream is received.
Deprecated object APIs
Warning
generateObject and streamObject are deprecated. Use generateText and
streamText with the output property instead.
If you still run deprecated object APIs, you will see legacy span names:
generateObject:ai.generateObject,ai.generateObject.doGeneratestreamObject:ai.streamObject,ai.streamObject.doStream,ai.stream.firstChunk
Legacy object spans include the same core metadata as other LLM spans, plus
object-specific attributes such as ai.schema.*, ai.response.object, and
ai.settings.output.
embed function
embed records 2 types of spans:
-
ai.embed(span): the full length of the embed call. It contains 1ai.embed.doEmbedspans. It contains the basic embedding span information and the following attributes:operation.name:ai.embedand the functionId that was set throughtelemetry.functionIdai.operationId:"ai.embed"ai.value: the value that was passed into theembedfunctionai.embedding: a JSON-stringified embedding
-
ai.embed.doEmbed(span): a provider doEmbed call. It contains the basic embedding span information and the following attributes:operation.name:ai.embed.doEmbedand the functionId that was set throughtelemetry.functionIdai.operationId:"ai.embed.doEmbed"ai.values: the values that were passed into the provider (array)ai.embeddings: an array of JSON-stringified embeddings
embedMany function
embedMany records 2 types of spans:
-
ai.embedMany(span): the full length of the embedMany call. It contains 1 or moreai.embedMany.doEmbedspans. It contains the basic embedding span information and the following attributes:operation.name:ai.embedManyand the functionId that was set throughtelemetry.functionIdai.operationId:"ai.embedMany"ai.values: the values that were passed into theembedManyfunctionai.embeddings: an array of JSON-stringified embedding
-
ai.embedMany.doEmbed(span): a provider doEmbed call. It contains the basic embedding span information and the following attributes:operation.name:ai.embedMany.doEmbedand the functionId that was set throughtelemetry.functionIdai.operationId:"ai.embedMany.doEmbed"ai.values: the values that were sent to the providerai.embeddings: an array of JSON-stringified embeddings for each value
Legacy span details
Basic LLM span information
Many spans that use LLMs (ai.generateText, ai.generateText.doGenerate, ai.streamText, ai.streamText.doStream) contain the following attributes:
resource.name: the functionId that was set throughtelemetry.functionIdai.model.id: the id of the modelai.model.provider: the provider of the modelai.request.headers.*: the request headers that were passed in throughheadersai.response.providerMetadata: provider specific metadata returned with the generation responseai.settings.maxRetries: the maximum number of retries that were setai.telemetry.functionId: the functionId that was set throughtelemetry.functionIdai.settings.runtimeContext.*: the runtime context that was passed in through theruntimeContextoption, filtered to top-level properties marked withtelemetry.includeRuntimeContextwhen configuredai.usage.completionTokens: the number of completion tokens that were usedai.usage.promptTokens: the number of prompt tokens that were used
Call LLM span information
Spans that correspond to individual LLM calls (ai.generateText.doGenerate, ai.streamText.doStream) contain
basic LLM span information and the following attributes:
ai.response.model: the model that was used to generate the response. This can be different from the model that was requested if the provider supports aliases.ai.response.id: the id of the response. Uses the ID from the provider when available.ai.response.timestamp: the timestamp of the response. Uses the timestamp from the provider when available.- Semantic Conventions for GenAI operations
gen_ai.system: the provider that was usedgen_ai.request.model: the model that was requestedgen_ai.request.temperature: the temperature that was setgen_ai.request.max_tokens: the maximum number of tokens that were setgen_ai.request.frequency_penalty: the frequency penalty that was setgen_ai.request.presence_penalty: the presence penalty that was setgen_ai.request.top_k: the topK parameter value that was setgen_ai.request.top_p: the topP parameter value that was setgen_ai.request.stop_sequences: the stop sequencesgen_ai.response.finish_reasons: the finish reasons that were returned by the providergen_ai.response.model: the model that was used to generate the response. This can be different from the model that was requested if the provider supports aliases.gen_ai.response.id: the id of the response. Uses the ID from the provider when available.gen_ai.usage.input_tokens: the number of prompt tokens that were usedgen_ai.usage.output_tokens: the number of completion tokens that were used
Basic embedding span information
Many spans that use embedding models (ai.embed, ai.embed.doEmbed, ai.embedMany, ai.embedMany.doEmbed) contain the following attributes:
ai.model.id: the id of the modelai.model.provider: the provider of the modelai.request.headers.*: the request headers that were passed in throughheadersai.settings.maxRetries: the maximum number of retries that were setai.telemetry.functionId: the functionId that was set throughtelemetry.functionIdai.settings.runtimeContext.*: the runtime context that was passed in through theruntimeContextoptionai.usage.tokens: the number of tokens that were usedresource.name: the functionId that was set throughtelemetry.functionId
Legacy tool call spans
Tool call spans (ai.toolCall) contain the following attributes:
operation.name:"ai.toolCall"ai.operationId:"ai.toolCall"ai.toolCall.name: the name of the toolai.toolCall.id: the id of the tool callai.toolCall.args: the input parameters of the tool callai.toolCall.result: the output result of the tool call. Only available if the tool call is successful and the result is serializable.