Braintrust.Span (Braintrust v0.1.0)

View Source

Represents a span in a Braintrust trace.

Spans are the core data structure for logging AI interactions. They capture input/output pairs, scores, metrics, and metadata for observability.

Structure

Traces in Braintrust form a directed acyclic graph (DAG) of spans:

  • A trace corresponds to a single request/interaction
  • A span is a unit of work within a trace (e.g., single LLM call, tool invocation)
  • Each span can have multiple parents (supporting DAG structure)
  • Most executions form a simple tree

Fields

Core Fields

  • :id - Unique identifier for the span (UUID, auto-generated if not provided)
  • :span_id - Span identifier for tracing (SDK-managed)
  • :root_span_id - Root span of the trace (SDK-managed)
  • :span_parents - Parent span IDs (SDK-managed, supports DAG structure)

Data Fields

  • :input - Input data (OpenAI message format recommended for UI support)
  • :output - Output/response from the task
  • :expected - Expected output for scoring (optional)
  • :error - Error information if applicable

Scoring Fields

  • :scores - Score values normalized to 0-1 range, keyed by score name
  • :metrics - Raw numeric values that get summed during aggregation

Metadata Fields

  • :metadata - String keys with JSON-serializable values
  • :tags - String tags (only on top-level spans/traces)
  • :created_at - ISO 8601 timestamp

Input Format

For best UI integration, format input as OpenAI message format:

%Braintrust.Span{
  input: %{
    messages: [
      %{role: "system", content: "You are helpful."},
      %{role: "user", content: "Hello!"}
    ]
  },
  output: "Hi there!"
}

Scores vs Metrics

  • Scores: Values normalized to [0, 1] range (e.g., accuracy, relevance)
  • Metrics: Raw numbers that cannot be normalized (e.g., latency_ms, token_count)

Examples

# Basic span
span = %Braintrust.Span{
  input: %{messages: [%{role: "user", content: "What is 2+2?"}]},
  output: "4",
  scores: %{accuracy: 1.0}
}

# Span with metadata and metrics
span = %Braintrust.Span{
  input: %{messages: [%{role: "user", content: "Hello"}]},
  output: "Hi there!",
  scores: %{quality: 0.9, relevance: 0.85},
  metadata: %{model: "gpt-4", environment: "production"},
  metrics: %{latency_ms: 250, input_tokens: 50, output_tokens: 25}
}

Auto-Managed Fields

The following fields are typically managed by the SDK and should not be set manually:

  • span_id, root_span_id, span_parents - Trace hierarchy
  • project_id, experiment_id, dataset_id, log_id - Context IDs

Summary

Functions

Converts a Span struct to a map suitable for API submission.

Types

t()

@type t() :: %Braintrust.Span{
  created_at: DateTime.t() | String.t() | nil,
  error: String.t() | nil,
  expected: any(),
  id: String.t() | nil,
  input: map() | nil,
  metadata: map() | nil,
  metrics: map() | nil,
  output: any(),
  root_span_id: String.t() | nil,
  scores: map() | nil,
  span_id: String.t() | nil,
  span_parents: [String.t()] | nil,
  tags: [String.t()] | nil
}

Functions

to_map(span)

@spec to_map(t()) :: map()

Converts a Span struct to a map suitable for API submission.

Removes nil values to avoid sending empty fields to the API.

Examples

iex> span = %Braintrust.Span{
...>   input: %{query: "test"},
...>   output: "result",
...>   scores: %{quality: 0.9}
...> }
iex> map = Braintrust.Span.to_map(span)
iex> map[:input]
%{query: "test"}
iex> Map.has_key?(map, :id)
false