Telemetry
Telemetry integrations receive the lifecycle events of each operation. OpenTelemetry
exports spans using the OpenTelemetry GenAI semantic conventions through a Tracer
you supply. Implement Tracer and Span to bridge to your observability backend.
Enabling telemetry
Section titled “Enabling telemetry”import SwiftAISDKimport OpenAIProvider
let integration = OpenTelemetry(tracer: myTracer)let result = try await generateText( model: openai("gpt-4o"), prompt: "Write a short story about a cat.", telemetry: TelemetryOptions( functionId: "story", integrations: [integration] ))Use registerTelemetry(integration) to register integrations for all calls.
Per-call integrations replace the registered ones. TelemetryOptions(isEnabled: false)
turns telemetry off for that call.
Privacy controls
Section titled “Privacy controls”Inputs and outputs are recorded by default. Disable them independently:
let result = try await generateText( model: openai("gpt-4o"), prompt: "Sensitive user data...", telemetry: TelemetryOptions( recordInputs: false, recordOutputs: false, integrations: [integration] ))recordInputs controls system instructions, input messages, tool definitions and arguments,
embedding values, schema contents and audio inputs. recordOutputs controls output messages,
tool results, embedding vectors and audio outputs. Model identifiers, settings and usage
remain available. Supplemental request headers, runtime context and provider metadata have
separate opt-in controls; input/output flags do not redact them.
Span tree
Section titled “Span tree”generateText and streamText produce:
invoke_agent <model>└─ step 1 ├─ chat <model> │ └─ execute_tool <provider-tool> └─ execute_tool <client-tool>Each additional step is a child of the operation. Client tools are children of their step;
provider-executed tools are children of the inference span. A generation started inside a
tool inherits that tool’s span through OpenTelemetry.activeSpan. Provider calls similarly
run with the inference span active.
generateObject and streamObject create invoke_agent <model> with a chat <model>
child. Both carry gen_ai.output.type = "json".
embed and embedMany create an embeddings <model> operation with an embeddings <model>
child per model call. The child records gen_ai.usage.input_tokens. rerank creates a
rerank <model> operation and model-call child. Speech and transcription operations use
their operation identifier and model in the span name, with gen_ai.output.type set to
speech or text and gen_ai.request.stream indicating streaming transcription.
Attributes
Section titled “Attributes”Operation and inference spans identify the operation, provider and model with
gen_ai.operation.name, gen_ai.provider.name and gen_ai.request.model.
Provider names are mapped to semantic-convention names, for example openai,
gcp.gemini, gcp.vertex_ai, aws.bedrock and azure.ai.openai.
TelemetryOptions.functionId becomes gen_ai.agent.name on generation and audio operations.
Generation attributes include:
gen_ai.request.max_tokens,temperature,top_p,top_k,presence_penalty,frequency_penalty,stop_sequencesandseedwhen supplied.gen_ai.system_instructions: JSON parts withtypeandcontent.gen_ai.input.messages: JSON messages withroleandparts.gen_ai.output.messages: JSON messages withrole,partsandfinish_reason.gen_ai.response.finish_reasons,gen_ai.response.idandgen_ai.response.model.gen_ai.usage.input_tokens,output_tokens,cache_read.input_tokensandcache_creation.input_tokens, where available.gen_ai.tool.definitions: JSON tool definitions on inference spans.
Message parts preserve text, reasoning, tool calls and tool responses. Input files and images
use uri or blob parts with modality and mime_type. Output files use base64 blob parts.
Inference timing is recorded in seconds:
gen_ai.client.operation.durationgen_ai.client.operation.time_to_first_chunkgen_ai.client.operation.time_per_output_chunk
Tool spans record gen_ai.tool.name, gen_ai.tool.call.id, gen_ai.tool.type
(function or extension), gen_ai.tool.call.arguments, gen_ai.tool.call.result
and, for client tools, gen_ai.execute_tool.duration in seconds. Failures record an exception
and error status. Aborts and errors close every open span for the operation.
Supplemental attributes
Section titled “Supplemental attributes”Supplemental groups are disabled by default:
let integration = OpenTelemetry( tracer: myTracer, usage: true, providerMetadata: true, embedding: true, reranking: true, runtimeContext: true, headers: true, toolChoice: true, schema: true)| Option | Additional attributes |
|---|---|
usage | ai.usage.inputTokenDetails.noCacheTokens, ai.usage.outputTokenDetails.textTokens, ai.usage.outputTokenDetails.reasoningTokens; audio ai.response.usage |
providerMetadata | ai.response.providerMetadata |
embedding | ai.value, ai.values, ai.embedding, ai.embeddings |
reranking | ai.documents, ai.ranking.type, ai.ranking |
runtimeContext | ai.settings.context.*, with nested objects flattened |
headers | ai.request.headers.* |
toolChoice | ai.prompt.toolChoice on step spans |
schema | ai.schema, ai.schema.name, ai.schema.description, ai.settings.output |
Audio operations always expose applicable ai.request.text, ai.request.audio.size,
ai.request.audio.media_type, ai.response.text, ai.response.audio.size,
ai.response.audio.media_type and ai.response.audio.format, subject to input/output flags.
Provider-reported audio usage is normalized under gen_ai.usage.*.
Runtime context is delivered to integrations only for keys explicitly allowed by
TelemetryOptions.includeRuntimeContext:
let result = try await generateText( model: openai("gpt-4o"), prompt: "Write a short story.", telemetry: TelemetryOptions( includeRuntimeContext: ["tenant": true], integrations: [OpenTelemetry(tracer: myTracer, runtimeContext: true)] ), runtimeContext: ["tenant": "acme"])Custom span attributes
Section titled “Custom span attributes”enrichSpan receives the span type, operation identifier, call identifier and permitted
runtime context when each span starts. SDK-owned attributes take precedence over custom
attributes. Exceptions thrown by this closure are ignored, and non-finite numeric attributes
are dropped.
let integration = OpenTelemetry(tracer: myTracer, enrichSpan: { event in ["app.span_type": .string(event.spanType.rawValue)]})