Skip to content
Radiant LabsRadiant Labs

Connect your agent

OpenTelemetry

Send existing OpenTelemetry traces through the project endpoint without replacing your instrumentation.

Send your application’s existing OpenTelemetry traces to Radiant through its SDK provider or collector. Choose the provider setup when the application exports directly, or configure the collector when it already receives your traces.

Export configuration preserves the recorded operations and their relationships. If you need to instrument a custom agent, add agent and tool spans after configuring export.

Get the project’s traces endpoint and ingestion key from Set up your project. The examples below use OTLP over HTTP/protobuf.

For a Python application using the OpenTelemetry SDK:

Terminal
python -m pip install opentelemetry-exporter-otlp-proto-http
Application environment
export RADIANT_TRACES_ENDPOINT='https://YOUR_INGESTION_HOST/v1/traces'
export RADIANT_INGESTION_KEY='YOUR_INGESTION_KEY'

Add the processor during application initialization, using the existing SDK TracerProvider as provider:

Existing tracing setup
import os
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import BatchSpanProcessor
provider.add_span_processor(
BatchSpanProcessor(
OTLPSpanExporter(
endpoint=os.environ["RADIANT_TRACES_ENDPOINT"],
headers={"Authorization": "Bearer " + os.environ["RADIANT_INGESTION_KEY"]},
)
)
)

Keep the provider’s existing resource attributes, including service.name, and its other exporters. Configure shutdown or flushing through the lifecycle that already owns the provider. Do not initialize another global provider or create one for every request.

If the application has no SDK provider yet, create a TracerProvider with the application’s resource attributes, add the processor, and register it with trace.set_tracer_provider(provider) before instrumented work begins.

If the application already sends spans to an OpenTelemetry Collector, add a named OTLP/HTTP exporter instead of sending a duplicate stream from the application. Add this exporter definition, then append otlp_http/radiant to the existing traces pipeline’s exporters list. Keep its receivers, processors, and other exporters. Set the same Radiant endpoint and key in the collector’s environment.

Collector exporter
exporters:
otlp_http/radiant:
traces_endpoint: ${env:RADIANT_TRACES_ENDPOINT}
headers:
Authorization: 'Bearer ${env:RADIANT_INGESTION_KEY}'
compression: none

Radiant requires uncompressed requests; the collector exporter enables gzip by default. Current collectors name the component otlp_http. On older collectors, use otlphttp/radiant in both the exporter definition and the pipeline.

Use the collector’s OTLP/HTTP exporter configuration for the exact configuration keys. A traces-specific endpoint contains /v1/traces; a base OTLP endpoint can have that path appended automatically, so do not append it twice.

If your application already emits agent and tool spans, keep that instrumentation. For a custom agent, follow the OpenTelemetry GenAI agent and tool conventions at the existing invocation and tool-execution boundaries.

With your SDK provider registered, obtain a tracer and wrap the application’s existing agent call. Here, agent_name, run_agent and request are the name, invocation function and input already used by your application:

Existing agent invocation
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span(
f"invoke_agent {agent_name}",
attributes={
"gen_ai.operation.name": "invoke_agent",
"gen_ai.agent.name": agent_name,
},
) as agent_span:
result = run_agent(request)

Inside the existing tool dispatcher or tool function, add a span around the call it already makes. tool_name, execute_tool and arguments below represent that existing tool and its input:

Existing tool execution
with tracer.start_as_current_span(
f"execute_tool {tool_name}",
attributes={
"gen_ai.operation.name": "execute_tool",
"gen_ai.tool.name": tool_name,
},
) as span:
result = execute_tool(arguments)

When the tool executes within the active agent span, it becomes that invocation’s child. Keep the parent span open through asynchronous work or stream consumption. Propagate OpenTelemetry context explicitly across process or queue boundaries.

If the application captures tool payloads, record the selected arguments and result as JSON strings on the tool span:

Optional payload capture inside the tool span
import json
span.set_attribute("gen_ai.tool.call.arguments", json.dumps(arguments))
span.set_attribute("gen_ai.tool.call.result", json.dumps(result))

Set these attributes before the tool span ends. Include only the values intended for telemetry. You can also record gen_ai.tool.call.id when the application has a stable call identifier. See the telemetry reference for additional recognized agent, model, and tool fields.

Agent and tool spans do not capture model requests automatically. Add the instrumentation for the model client your application actually uses. For an OpenAI Python client, the upstream instrumentor records Chat Completions and Responses calls, including model identity and token usage:

Terminal
python -m pip install opentelemetry-instrumentation-genai-openai==1.3b0

Enable message content before starting the application, then register the instrumentor at startup, after the provider and before creating model clients:

Application environment
export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT='SPAN_ONLY'
Existing tracing setup
from opentelemetry.instrumentation.genai.openai import OpenAIInstrumentor
OpenAIInstrumentor().instrument()

Existing client calls inside the active agent span now produce child model spans. With content capture enabled, those spans contain the actual request and response messages in gen_ai.input.messages and gen_ai.output.messages. Token usage comes from the model response. Radiant uses the captured messages to render the model record’s Thread.

This configuration applies to the OpenAI client; use the corresponding instrumentation for other model libraries. If you instrument a model client yourself, record its actual messages and usage using the GenAI model fields and message format. Avoid adding another client instrumentor when a framework integration already records the same model calls.

The upstream OpenAI instrumentor documents supported client versions and content-capture controls. Message capture is off by default; enable it for the payloads your application intends to export.

Resource attributes describe the process and apply to all of its spans. Put request-specific identifiers on the operation that owns them.

Inside the agent span, add identifiers that your application already uses. Set these before run_agent(request) in the invocation example:

Inside the agent span
agent_span.set_attribute("gen_ai.conversation.id", conversation_id)
agent_span.set_attribute("gen_ai.agent.version", agent_version)

Here, conversation_id identifies the conversation and agent_version identifies the agent release. These attributes apply to this invocation, not automatically to its child spans. The Conversations view groups agent runs by gen_ai.conversation.id; the version appears in Agents and versions. See Telemetry reference for field definitions.

Run an existing application request and find its service in the project’s Logs. Open the trace and confirm that the expected operations and relationships are present. The exporter sends recorded values; it does not infer missing messages, token usage or agent identity.

Recognized agent invocations also require the agent span attributes. The telemetry reference defines model and tool fields, usage, content formats and correlation. For missing traces, check export and authentication troubleshooting.