Span attributes and events
Braintrust maps GenAI attributes and events, OpenInference attributes, and explicitbraintrust.* attributes to span fields.
GenAI attributes
Braintrust implements the OpenTelemetry GenAI semantic conventions. When you send traces with these attributes, they are automatically mapped to Braintrust fields.gen_ai.evaluation.name supplies the name in scores.<name>, and gen_ai.evaluation.score.value supplies its value. The name must be nonempty. The score must be a finite number from 0 to 1, inclusive. Numeric strings are accepted. Invalid values are not mapped to scores.
For self-hosted deployments, this score validation requires data plane v2.16.0 or later.
GenAI events
Braintrust also extracts messages and scores from GenAI span events. These events follow the OpenTelemetry GenAI semantic conventions for events:
The message events listed above are processed in the order they appear in the span. When they produce a nonempty input or output message list, that list takes precedence over the corresponding attribute-derived field.
Each event is also stored as a separate log row associated with the span. Mapping a GenAI event to span fields does not remove the event record.
OpenInference attributes
Spans withopeninference.span.kind are mapped using OpenInference semantic conventions. These include traces from OpenInference instrumentors for LangChain, LangGraph, LlamaIndex, DSPy, and the OpenAI Agents SDK.
For self-hosted deployments, OpenInference mapping requires data plane v2.16.0 or later.
Collections use flattened, indexed attributes, such as llm.input_messages.0.message.role and llm.input_messages.0.message.content.
Span fields
When converting input messages, Braintrust prepends a system prompt from
llm.invocation_parameters if no system or developer message is present. If media cannot be represented as chat messages, Braintrust preserves the original OpenInference messages in input or output instead.
Metadata
Unmapped attributes remain in
metadata. Mapped attributes follow the attribute preservation settings.
Token usage
Token metrics are extracted only fromLLM and EMBEDDING spans to avoid counting aggregate usage on chain or agent spans twice. prompt_tokens includes cached tokens.
Scores
Braintrust maps span-scoped annotations and evaluations to scores on the span carrying the attributes:
Each entry must have a nonempty name and a finite score from 0 to 1, inclusive. Numeric strings are accepted. Missing, nonnumeric, or out-of-range values are not mapped to
scores. Names, labels, and explanations remain in metadata.
Trace- and session-scoped feedback, such as trace.annotations.* or session.evaluations.*, remains in metadata and is not mapped to span scores.
Braintrust attributes
You can also use thebraintrust namespace to set fields in Braintrust directly:
Fields mapped from
braintrust.* attributes are removed from metadata and stored in the corresponding Braintrust fields. Nonempty input or output arrays whose elements have valid chat roles are recognized as LLM message arrays.
Attribute example
This example sets GenAI and Braintrust attributes using flattened names and JSON-encoded values:Python
Why are some attributes missing from metadata?
Why are some attributes missing from metadata?
For Braintrust-hosted organizations, source attributes such as
ai.prompt, gen_ai.input.messages, and llm.input_messages are removed from metadata once Braintrust has mapped them to a structured field (input, output, metrics, etc.). The data itself can be found on the structured field it was mapped into.Self-hosted deployments behave the same way when the STRIP_OTEL_ATTRIBUTES_FROM_METADATA environment variable is set on the api-ts service.To keep the raw attributes on metadata for a specific span, set the braintrust.otel.preserve_attributes attribute to true on that span.For details, see Strip OTel attributes from metadata.Log records
Each OTLP log record becomes a separate row withspan_attributes.type set to log:
Log attributes do not undergo the GenAI mapping used for trace spans. For example, a
gen_ai.usage.input_tokens attribute on a log remains in metadata rather than becoming a token metric. Records from Claude Code are the exception: Braintrust maps their prompts, responses, and errors to input, output, and error. Claude Code API request records get token metrics only when they have no trace ID. Traced requests already carry their tokens on the matching LLM span. See Trace Claude Code with OpenTelemetry.
Each record has a unique row id. Its trace and span IDs determine how it connects to a trace:
- Valid trace and span IDs: Shares its owning span’s
span_idand trace’sroot_span_id. - Trace ID only: Joins that trace with a generated span ID.
- No trace ID: Receives its own span and root IDs.
Span events
OpenTelemetry span events received through/otel/v1/traces, including GenAI events, are also stored as separate log rows. Each row shares its span’s span_id and root_span_id and has its own row id.
The event name is stored in output, and its attributes are stored in metadata. Recognized GenAI events can also populate fields on the owning span. Exception details populate the owning span’s error field, not the event row’s.
Braintrust derives span_attributes.log_level for event rows in this order:
- Map a
severity_numberevent attribute from 1 to 24 using the log record severity ranges. - Otherwise, use the first of
severity_text,log.level, orlevelthat matches a log level, following the same text rules as log records. - If no attribute matches, use
errorfor events namedexception. Other events leavespan_attributes.log_levelunset.
tracing-opentelemetry sets level on every event.
For self-hosted deployments, span event log-level inference requires data plane v2.16.0 or later.
Log levels and exceptions
The severity number maps tospan_attributes.log_level:
Filter on
span_attributes.log_level to find messages by severity, or on error to find records with exception details. An error-level message alone does not populate error.
What if the severity number is missing or invalid?
What if the severity number is missing or invalid?
If the severity number is absent,
0, or outside the ranges above, Braintrust checks the severity text:- Braintrust log levels match regardless of case.
Warningmaps towarn, andCriticalmaps tofatal.- Unrecognized text, such as
verbose, leaves the log level unset.
context.otel.log.What populates the error field?
What populates the error field?
Braintrust populates
error from nonempty exception attributes:exception.stacktraceexception.typeexception.message
For self-hosted deployments, numeric severity mapping and exception-only
error behavior require data plane v2.15.0 or later, and severity-text fallback requires data plane v2.16.0 or later.