Skip to main content

Documentation Index

Fetch the complete documentation index at: https://handbook.fiddler.ai/llms.txt

Use this file to discover all available pages before exploring further.

Exporting OTel Traces to Fiddler

Overview

This guide covers the client-side export scenario: your application has already generated OpenTelemetry traces, you manage their storage and processing, and you need to ship them to Fiddler. You are responsible for:
  1. Attribute mapping — translating your OTel span attributes to Fiddler’s schema
  2. Protobuf serialization — building ResourceSpans → ScopeSpans → Span structures
  3. Export — POSTing the compressed payload to Fiddler’s v1/traces endpoint
This differs from live instrumentation, where the OTel SDK exports spans automatically as your agent runs.
When to use this approachUse client-side export when you have:
  • Traces stored in a data warehouse, JSONL files, or a logging pipeline that you want to replay into Fiddler
  • A custom export pipeline that processes spans before sending (e.g., filtering, enrichment, or ID regeneration)
  • Batch backfill of historical trace data
For real-time agent instrumentation, use the OpenTelemetry Integration or a framework SDK instead.

Prerequisites

  • A Fiddler account with a GenAI application created — you will need its Application UUID
  • A valid Fiddler API token (from Settings > Credentials)
  • Python packages:
    pip install opentelemetry-proto httpx
    

The v1/traces Endpoint

Send traces as a gzip-compressed protobuf ExportTraceServiceRequest payload:
PropertyValue
URLhttps://<your-fiddler-instance>/v1/traces
MethodPOST
Content-Typeapplication/x-protobuf
Content-Encodinggzip
AuthorizationBearer <your-api-token>
fiddler-application-id<your-application-uuid>
import gzip
import httpx
from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ExportTraceServiceRequest

payload = ExportTraceServiceRequest(resource_spans=[...]).SerializeToString()

httpx.post(
    "https://your-instance.fiddler.ai/v1/traces",
    content=gzip.compress(payload),
    headers={
        "Authorization": "Bearer <YOUR_TOKEN>",
        "fiddler-application-id": "<YOUR_APP_UUID>",
        "Content-Type": "application/x-protobuf",
        "Content-Encoding": "gzip",
    },
)

Protobuf Structure

Fiddler expects the standard OTLP hierarchy:
ExportTraceServiceRequest
└── ResourceSpans[]
    ├── Resource
    │   └── attributes: [application.id, service.name, ...]
    └── ScopeSpans[]
        └── Span[]
            ├── trace_id, span_id, parent_span_id
            ├── name, kind, status
            ├── start_time_unix_nano, end_time_unix_nano
            └── attributes: [gen_ai.agent.name, fiddler.span.type, ...]
application.id must be set at the Resource level (not on individual spans):
from opentelemetry.proto.resource.v1.resource_pb2 import Resource
from opentelemetry.proto.common.v1.common_pb2 import AnyValue, KeyValue

resource = Resource(attributes=[
    KeyValue(key="application.id", value=AnyValue(string_value="<YOUR_APP_UUID>")),
    KeyValue(key="service.name",   value=AnyValue(string_value="my-agent-service")),
])

Attribute Mapping Reference

Span Structure Fields

The following fields control the span’s structural properties and are not sent as span attributes. Map them to the corresponding protobuf Span fields instead:
Your fieldProtobuf Span field
trace_idtrace_id (bytes, 16 bytes / 32-char hex)
span_idspan_id (bytes, 8 bytes / 16-char hex)
parent_span_idparent_span_id (bytes, same format)
span_name / namename
span_kindkind (SpanKind enum)
status_codestatus.code (StatusCode enum)
start_timestart_time_unix_nano
end_timeend_time_unix_nano

Required Span Attributes

Every span sent to Fiddler must include this attribute:
AttributeDescription
fiddler.span.typeSpan type: llm, tool, chain, or agent
application.id is required at the Resource level (see above).

Span Type: Deriving from gen_ai.operation.name

If your spans follow the GenAI semantic conventions and carry gen_ai.operation.name, map it to fiddler.span.type as follows:
gen_ai.operation.namefiddler.span.type
chatllm
execute_tooltool
invoke_agentchain
(any other value or absent)chain (default)

LLM Semantic Convention Mappings

Your OTel attributeFiddler attributeNotes
gen_ai.input.messagesgen_ai.llm.input.user + gen_ai.llm.contextSee message parsing below
gen_ai.output.messagesgen_ai.llm.output
gen_ai.system_instructionsgen_ai.llm.input.system
gen_ai.response.modelgen_ai.request.modelFiddler normalises to the request-side attribute
gen_ai.usage.input_tokensgen_ai.usage.input_tokensPass through unchanged
gen_ai.usage.output_tokensgen_ai.usage.output_tokensPass through unchanged
gen_ai.usage.total_tokensgen_ai.usage.total_tokensPass through unchanged
gen_ai.systemgen_ai.systemLLM provider identifier (e.g. openai)
gen_ai.request.modelgen_ai.request.modelPass through unchanged

Tool Semantic Convention Mappings

Your OTel attributeFiddler attribute
gen_ai.tool.call.argumentsgen_ai.tool.input
gen_ai.tool.call.resultgen_ai.tool.output
gen_ai.tool.namegen_ai.tool.name

Agent and Conversation Attributes

Your OTel attributeFiddler attributeNotes
gen_ai.agent.namegen_ai.agent.nameOptional
gen_ai.agent.idgen_ai.agent.idOptional
gen_ai.conversation.idgen_ai.conversation.idOptional — used for multi-turn conversation grouping
Set agent attributes on every span. If gen_ai.agent.name or gen_ai.agent.id are provided, set both on every span within the trace. Fiddler uses these attributes to attribute spans to the correct agent — spans missing these fields will be unattributed even if other spans in the same trace carry them.

Legacy / Underscore Field Names

If your traces use older underscore-style field names, map them to the Fiddler dotted equivalents before serialization:
Legacy fieldFiddler attribute
model_namegen_ai.request.model
model_providergen_ai.system
tool_namegen_ai.tool.name
tool_inputgen_ai.tool.input
tool_outputgen_ai.tool.output
llm_input_systemgen_ai.llm.input.system
llm_input_usergen_ai.llm.input.user
llm_outputgen_ai.llm.output
llm_contextgen_ai.llm.context

Custom User Attributes

To attach business-level metadata to spans, prefix your keys with fiddler.span.user.:
# Source field: custom_attributes = {"session_type": "onboarding", "region": "us-west"}
# Maps to:
span.attributes["fiddler.span.user.session_type"] = "onboarding"
span.attributes["fiddler.span.user.region"]       = "us-west"
These attributes are indexed and queryable in Fiddler’s trace explorer.

Parsing gen_ai.input.messages

The gen_ai.input.messages attribute is a JSON array of chat-style message objects. Fiddler expects it split into two separate attributes:
Output attributeContent
gen_ai.llm.input.userText content of the last message with role: user
gen_ai.llm.contextAll other messages (conversation history), formatted as [role]: content
Example input:
[
  {"role": "system",    "content": "You are a helpful assistant."},
  {"role": "user",      "content": "What is the capital of France?"},
  {"role": "assistant", "content": "Paris."},
  {"role": "user",      "content": "And Germany?"}
]
Resulting mapping:
AttributeValue
gen_ai.llm.input.user"And Germany?"
gen_ai.llm.context"[system]: You are a helpful assistant.\n\n[user]: What is the capital of France?\n\n[assistant]: Paris."
If no user message is found, all messages are placed in gen_ai.llm.context and gen_ai.llm.input.user is omitted.

Span Kind and Status Mappings

Span kind (SpanKind enum):
String valueProtobuf value
INTERNALSpanKind.INTERNAL (default)
SERVERSpanKind.SERVER
CLIENTSpanKind.CLIENT
PRODUCERSpanKind.PRODUCER
CONSUMERSpanKind.CONSUMER
Status code (StatusCode enum):
String valueProtobuf value
OKStatusCode.OK (default)
ERRORStatusCode.ERROR
UNSETStatusCode.UNSET

Minimal End-to-End Example

import gzip
import httpx
from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ExportTraceServiceRequest
from opentelemetry.proto.common.v1.common_pb2 import AnyValue, InstrumentationScope, KeyValue
from opentelemetry.proto.resource.v1.resource_pb2 import Resource
from opentelemetry.proto.trace.v1.trace_pb2 import ResourceSpans, ScopeSpans, Span, Status
from opentelemetry.trace import SpanKind
from opentelemetry.trace.status import StatusCode

FIDDLER_URL    = "https://your-instance.fiddler.ai"
APP_UUID       = "550e8400-e29b-41d4-a716-446655440000"
FIDDLER_TOKEN  = "your-api-token"

# Build a single LLM span
span = Span(
    trace_id=bytes.fromhex("4bf92f3577b34da6a3ce929d0e0e4736"),
    span_id=bytes.fromhex("00f067aa0ba902b7"),
    name="chat",
    kind=SpanKind.INTERNAL.value,
    start_time_unix_nano=1_700_000_000_000_000_000,
    end_time_unix_nano=1_700_000_001_000_000_000,
    status=Status(code=StatusCode.OK.value),
    attributes=[
        KeyValue(key="gen_ai.agent.name",    value=AnyValue(string_value="my-agent")),
        KeyValue(key="fiddler.span.type",    value=AnyValue(string_value="llm")),
        KeyValue(key="gen_ai.request.model", value=AnyValue(string_value="gpt-4o")),
        KeyValue(key="gen_ai.llm.input.user",value=AnyValue(string_value="What is the capital of France?")),
        KeyValue(key="gen_ai.llm.output",    value=AnyValue(string_value="Paris.")),
    ],
)

# Wrap in ResourceSpans
resource_spans = ResourceSpans(
    resource=Resource(attributes=[
        KeyValue(key="application.id", value=AnyValue(string_value=APP_UUID)),
        KeyValue(key="service.name",   value=AnyValue(string_value="my-agent-service")),
    ]),
    scope_spans=[ScopeSpans(
        scope=InstrumentationScope(name="my-tracer", version="1.0.0"),
        spans=[span],
    )],
)

# Serialize and compress
payload = ExportTraceServiceRequest(resource_spans=[resource_spans]).SerializeToString()

# Send
response = httpx.post(
    f"{FIDDLER_URL}/v1/traces",
    content=gzip.compress(payload),
    headers={
        "Authorization": f"Bearer {FIDDLER_TOKEN}",
        "fiddler-application-id": APP_UUID,
        "Content-Type": "application/x-protobuf",
        "Content-Encoding": "gzip",
    },
    timeout=30.0,
)
response.raise_for_status()

Attribute Quick Reference

AttributeLevelRequiredType
application.idResourceYesUUID string
gen_ai.agent.nameSpanNostring
fiddler.span.typeSpanYesllm | tool | chain | agent
gen_ai.agent.idSpanNostring
gen_ai.conversation.idSpanNostring
gen_ai.request.modelSpanNostring
gen_ai.systemSpanNostring (provider name)
gen_ai.llm.input.systemSpanNostring
gen_ai.llm.input.userSpanNostring
gen_ai.llm.outputSpanNostring
gen_ai.llm.contextSpanNostring
gen_ai.tool.nameSpanNostring
gen_ai.tool.inputSpanNostring (JSON)
gen_ai.tool.outputSpanNostring (JSON)
gen_ai.usage.input_tokensSpanNoint
gen_ai.usage.output_tokensSpanNoint
gen_ai.usage.total_tokensSpanNoint
fiddler.span.user.*SpanNoany