OpenAI Agents SDK
The OpenAI Agents SDK traces its runs with its own tracing API. The OpenInference instrumentation for it turns those traces into OpenTelemetry spans with openinference.span.kind and llm.* attributes, which Spanlight reads for the kind, the model, the token counts and the messages. Send them to Spanlight’s OTLP endpoint with an OpenTelemetry tracer provider.
Before you start
- A running Spanlight instance that your application can reach
- A project API key with the ingest:write scope (the default). Creating one needs the member, admin or owner role
- Python 3.13 (the version this page was tested with)
Install
Section titled “Install”pip install "openinference-instrumentation-openai-agents==2.5.3" "openai-agents==0.24.0" "openai==3.35.1" "opentelemetry-sdk==1.45.1" "opentelemetry-exporter-otlp-proto-http==1.45.1"Point the OpenTelemetry exporter at Spanlight with the standard variables:
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:8080/v1/otlp/tracesexport OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobufexport OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20spl_live_..."Use the traces-specific endpoint variable with the full path. Configure an exporter explains why.
Set up tracing
Section titled “Set up tracing”Create a tracer provider that exports over OTLP and pass it to the instrumentor before you run an agent. The recorded agent used the Chat Completions model:
import asyncio
from agents import Agent, OpenAIChatCompletionsModel, Runner, function_toolfrom openai import AsyncOpenAIfrom openinference.instrumentation.openai_agents import OpenAIAgentsInstrumentorfrom opentelemetry import tracefrom opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporterfrom opentelemetry.sdk.trace import TracerProviderfrom opentelemetry.sdk.trace.export import BatchSpanProcessor
provider = TracerProvider()provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))trace.set_tracer_provider(provider)OpenAIAgentsInstrumentor().instrument(tracer_provider=provider)
@function_tooldef get_weather(city: str, unit: str = "celsius") -> str: """Get the current weather for a city.""" return "18 degrees and sunny"
agent = Agent( name="weather_agent", instructions="Answer weather questions with the get_weather tool.", model=OpenAIChatCompletionsModel(model="gpt-4.1-mini", openai_client=AsyncOpenAI()), tools=[get_weather],)asyncio.run(Runner.run(agent, "What is the weather in Paris and Tokyo? Use the tool."))provider.force_flush()By default the instrumentor replaces the SDK’s trace processors with its own, so the run is no longer sent to OpenAI’s trace dashboard. A long-running server does not need provider.force_flush(); a short script does, or its last spans are lost.
Check it worked
Section titled “Check it worked”Open Traces in the dashboard and pick the project that owns your key. The recorded run gives a trace named Agent workflow with 9 spans: the workflow, the weather_agent agent, one turn per model turn, a generation span for each model call and a get_weather span for each tool call. Open a generation span: it shows the messages, the answer, the token counts and the cost. The trace’s token total is the sum of its two model calls.
What is recorded
Section titled “What is recorded”| Span in the recorded run | OpenInference span kind | Spanlight kind |
|---|---|---|
Agent workflow (the root), weather_agent |
AGENT |
agent |
The inner Agent workflow span, turn |
CHAIN |
chain |
get_weather |
TOOL |
tool |
generation |
LLM |
llm |
On each model call:
- Provider and model from
llm.systemandllm.model_name. The instrumentation sends the model name the agent asked for, such asgpt-4.1-mini, not the snapshot OpenAI billed; Spanlight prices it at that model’s price. - Input and output tokens from
llm.token_count.promptandllm.token_count.completion. Only model calls carry token counts, so each call is counted once and the agent and turn spans add nothing to the totals. - Messages and answer from the flattened
llm.input_messages.*andllm.output_messages.*attributes, including the tool calls the model asked for and the tool results sent back. - Cost, computed by Spanlight from the model and the token counts; see How cost is calculated.
Tool spans keep the tool’s arguments and result (input.value and output.value) as their input and output; the recorded agent and turn spans carry neither.
Which attribute fills which field is listed in OpenInference.
What is not recorded
Section titled “What is not recorded”- Cached tokens. Version 2.5.3 sends only the prompt and completion counts on
generationspans, so cached input is priced at the full input rate. - Finish reason. The
generationspans carry nollm.finish_reason. - Time to first token. The recorded run did not stream.
- The billed model snapshot. See the model note above.
- The Responses API model, streaming, handoffs, guardrails and the realtime API. They were not recorded, so this page makes no claim about them.
- Exporter-computed cost. Spanlight never reads one; it prices every span itself.
OTLP compatibility shows the recorded scenario field by field.
Troubleshooting
Section titled “Troubleshooting”- No trace arrives. Call
instrument()before the firstRunner.run, pass it the tracer provider, and flush the provider before a script exits. Then work through No trace arrives. - The exporter gets
422 VALIDATION_ERROR. It sent to/v1/traces. SetOTEL_EXPORTER_OTLP_TRACES_ENDPOINTto the full/v1/otlp/tracesURL. - Runs stopped showing in OpenAI’s trace dashboard. That is the instrumentor’s default; see Set up tracing.