Class: ActiveAgent::TelemetryTrace

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb

Overview

Stores telemetry traces from ActiveAgent clients.

Each trace represents a complete generation lifecycle, including prompt preparation, LLM calls, tool invocations, and error handling.

This model supports two modes:

  • Local mode: No account association (single-tenant, self-hosted)

  • Multi-tenant mode: With account association (for activeagents.ai platform)

Examples:

Creating a trace from ingested data (local mode)

ActiveAgent::TelemetryTrace.create_from_payload(trace_payload, sdk_info)

Creating a trace with account (multi-tenant mode)

ActiveAgent::TelemetryTrace.create_from_payload(trace_payload, sdk_info, account: )

Constant Summary collapse

STATUS_OK =

Status values for traces

"OK"
STATUS_ERROR =
"ERROR"
STATUS_UNSET =
"UNSET"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.create_from_payload(trace, sdk_info = {}, account: nil) ⇒ TelemetryTrace

Creates a TelemetryTrace from an ingested trace payload.

Extracts relevant data from the trace payload and stores it in a normalized format for querying and analysis.

Parameters:

  • trace (Hash)

    The trace payload from ActiveAgent::Telemetry

  • sdk_info (Hash) (defaults to: {})

    SDK metadata

  • account (Object, nil) (defaults to: nil)

    Optional account for multi-tenant mode

Returns:



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 53

def self.create_from_payload(trace, sdk_info = {}, account: nil)
  spans = trace["spans"] || []
  root_span = spans.find { |s| s["parent_span_id"].nil? } || spans.first || {}

  # Calculate totals from all spans
  total_duration = root_span["duration_ms"]
  total_input = 0
  total_output = 0
  total_thinking = 0

  spans.each do |span|
    tokens = span["tokens"] || {}
    total_input += (tokens["input"] || 0)
    total_output += (tokens["output"] || 0)
    total_thinking += (tokens["thinking"] || 0)
  end

  # Extract agent info from root span attributes
  attributes = root_span["attributes"] || {}
  agent_class = attributes["agent.class"]
  agent_action = attributes["agent.action"]

  # Find any error message
  error_span = spans.find { |s| s["status"] == STATUS_ERROR }
  error_message = error_span&.dig("attributes", "error.message")

  attrs = {
    trace_id: trace["trace_id"],
    service_name: trace["service_name"],
    environment: trace["environment"],
    timestamp: Time.parse(trace["timestamp"]),
    spans: spans,
    resource_attributes: trace["resource_attributes"],
    sdk_info: sdk_info,
    total_duration_ms: total_duration,
    total_input_tokens: total_input,
    total_output_tokens: total_output,
    total_thinking_tokens: total_thinking,
    status: root_span["status"] || STATUS_UNSET,
    agent_class: agent_class,
    agent_action: agent_action,
    error_message: error_message
  }

  # Add account if in multi-tenant mode
  attrs[:account] =  if ActiveAgent::Dashboard.multi_tenant? && 

  create!(attrs)
end

Instance Method Details

#display_nameString

Returns display name for the trace.

Returns:

  • (String)

    Display name (e.g., “WeatherAgent.forecast”)



141
142
143
144
145
146
147
148
149
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 141

def display_name
  if agent_class && agent_action
    "#{agent_class}.#{agent_action}"
  elsif agent_class
    agent_class
  else
    trace_id&.first(8)
  end
end

#error?Boolean

Returns whether this trace had an error.

Returns:

  • (Boolean)


134
135
136
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 134

def error?
  status == STATUS_ERROR
end

#formatted_durationString

Returns formatted duration.

Returns:

  • (String)

    Duration in ms or s



154
155
156
157
158
159
160
161
162
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 154

def formatted_duration
  return "" unless total_duration_ms

  if total_duration_ms >= 1000
    "#{(total_duration_ms / 1000.0).round(2)}s"
  else
    "#{total_duration_ms.round(0)}ms"
  end
end

#formatted_tokensString

Returns formatted token count.

Returns:

  • (String)

    Token count with K suffix for large numbers



167
168
169
170
171
172
173
174
175
176
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 167

def formatted_tokens
  count = total_tokens
  return "0" if count.zero?

  if count >= 1000
    "#{(count / 1000.0).round(1)}K"
  else
    count.to_s
  end
end

#llm_spansArray<Hash>

Returns all LLM spans in this trace.

Returns:

  • (Array<Hash>)

    LLM spans



113
114
115
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 113

def llm_spans
  spans&.select { |s| s["type"] == "llm" } || []
end

#modelString?

Returns the model used (from LLM spans).

Returns:

  • (String, nil)

    Model name



191
192
193
194
195
196
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 191

def model
  llm_span = llm_spans.first
  return nil unless llm_span

  llm_span.dig("attributes", "llm.model")
end

#providerString?

Returns the provider used (from LLM spans).

Returns:

  • (String, nil)

    Provider name



181
182
183
184
185
186
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 181

def provider
  llm_span = llm_spans.first
  return nil unless llm_span

  llm_span.dig("attributes", "llm.provider")
end

#root_spanHash?

Returns the root span of this trace.

Returns:

  • (Hash, nil)

    The root span or nil



106
107
108
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 106

def root_span
  spans&.find { |s| s["parent_span_id"].nil? }
end

#tool_spansArray<Hash>

Returns all tool call spans in this trace.

Returns:

  • (Array<Hash>)

    Tool spans



120
121
122
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 120

def tool_spans
  spans&.select { |s| s["type"] == "tool" } || []
end

#total_tokensInteger

Returns total token count.

Returns:

  • (Integer)

    Total tokens used



127
128
129
# File 'lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb', line 127

def total_tokens
  (total_input_tokens || 0) + (total_output_tokens || 0) + (total_thinking_tokens || 0)
end