Implementation:Truera Trulens Otel CreateTreeFromCalls
| Knowledge Sources | |
|---|---|
| Domains | Dashboard, Observability, OpenTelemetry |
| Last Updated | 2026-02-14 08:00 GMT |
Overview
createTreeFromCalls is the core tree-building algorithm that transforms a flat array of OTEL spans into a hierarchical StackTreeNode tree structure, handling orphaned nodes gracefully.
Description
This module contains the primary data transformation logic for the OTEL record viewer. It takes raw span data from the backend and produces a rooted tree that the UI components (RecordTree, RecordTable, etc.) can render.
The algorithm proceeds in two phases:
Phase 1 -- createTreeFromCalls:
- Filter: Evaluation-related spans (those with
SpanType.EVALorSpanType.EVAL_ROOT) are removed from the input, since they are displayed separately in the dashboard. - Map: Each remaining
Spanobject is converted into aStackTreeNode, extracting the name, start/end timestamps, attributes, span ID, and parent ID. - Delegate: The resulting node array is passed to
createSpanTreeFromNodes.
Phase 2 -- createSpanTreeFromNodes:
- Index: Two maps are built -- a
nodeMapkeyed by node ID for O(1) lookup, and achildrenMapgrouping nodes by their parent IDs. - Find root: The unique node with
SpanType.RECORD_ROOTis identified. If zero or multiple roots exist, an error is thrown. - Build tree:
buildTreeRecursivewalks down from the root, assigning children (sorted chronologically bystartTime) to each node. - Handle orphans: After building the main tree, the algorithm collects all node IDs that were placed into the tree. Any remaining nodes whose parent ID does not exist in the node map are treated as orphaned subtree roots. Each orphaned subtree is recursively built and then attached under a synthetic "Orphaned nodes" container that is appended as a child of the root.
Helper functions:
buildTreeRecursive-- Recursively assigns sorted children fromchildrenMapto each node.collectNodeIds-- Recursively collects all node IDs in a subtree into aSet, used to detect orphans.
Usage
Call createTreeFromCalls(spans) with the array of OTEL Span objects fetched from the backend. The function returns the root StackTreeNode with all children properly linked. Pass this root (and a derived nodeMap) to the RecordInfo component for rendering.
Code Reference
Source Location
- Repository: Truera_Trulens
- File: src/dashboard/react_components/record_viewer_otel/src/functions/createTreeFromCalls.ts
- Lines: 1-126
Signature
export const createTreeFromCalls = (spans: Span[]): StackTreeNode;
export const createSpanTreeFromNodes = (nodes: StackTreeNode[]): StackTreeNode;
const buildTreeRecursive = (
node: StackTreeNode,
childrenMap: Map<string, StackTreeNode[]>
): void;
const collectNodeIds = (
node: StackTreeNode,
idSet: Set<string>
): void;
Import
import { createTreeFromCalls, createSpanTreeFromNodes } from '@/functions/createTreeFromCalls';
I/O Contract
Inputs
createTreeFromCalls
| Name | Type | Required | Description |
|---|---|---|---|
| spans | Span[] |
yes | A flat array of OTEL span objects as returned from the backend. Each span contains record (name, parent_span_id, status), record_attributes (key-value map of OTEL attributes), start_timestamp, timestamp, and trace (trace_id, parent_id, span_id).
|
createSpanTreeFromNodes
| Name | Type | Required | Description |
|---|---|---|---|
| nodes | StackTreeNode[] |
yes | A flat array of pre-constructed StackTreeNode objects. Must contain exactly one node with SpanType.RECORD_ROOT in its attributes.
|
Outputs
| Name | Type | Description |
|---|---|---|
| root | StackTreeNode |
The root node of the fully built span tree. All descendant nodes are linked via their children arrays. If orphaned nodes were detected, they appear under a synthetic "Orphaned nodes" child of the root.
|
Error Conditions
| Condition | Error Message |
|---|---|
Empty spans array passed to createTreeFromCalls |
'No spans provided'
|
Empty nodes array passed to createSpanTreeFromNodes |
'No nodes provided'
|
No node found with SpanType.RECORD_ROOT attribute |
'No root node found'
|
Multiple nodes found with SpanType.RECORD_ROOT attribute |
'Multiple root nodes found'
|
Algorithm Details
Tree Construction Steps
- Build a
nodeMap: Map<string, StackTreeNode>for O(1) node lookups by ID. - Build a
childrenMap: Map<string, StackTreeNode[]>grouping nodes by theirparentId. - Locate the single
RECORD_ROOTnode as the root. - Call
buildTreeRecursive(root, childrenMap):- For each node, fetch its children from
childrenMap. - Sort children by
startTimeascending (chronological order). - Assign to
node.childrenand recurse.
- For each node, fetch its children from
- Collect all IDs reachable from the root using
collectNodeIds. - Identify orphaned root candidates: nodes not in the collected set whose
parentIdis not innodeMap. - For each orphan root, call
buildTreeRecursiveto build its subtree. - If any orphans exist, create a synthetic
StackTreeNodewithid = ORPHANED_NODES_PARENT_IDandname = 'Orphaned nodes', attach all orphan subtrees as its children, and append it toroot.children.
Complexity
- Time: O(n) where n is the number of spans, since each span is visited a constant number of times across mapping, indexing, and tree building.
- Space: O(n) for the node map, children map, and processed ID set.
Usage Examples
import { createTreeFromCalls } from '@/functions/createTreeFromCalls';
import { Span } from '@/types/Span';
// Assume spans is an array of OTEL Span objects from the backend
const spans: Span[] = [
{
event_id: 'evt-1',
record: { name: 'my_app.pipeline', parent_span_id: '', status: 'OK' },
record_attributes: { 'ai.observability.span_type': 'record_root' },
start_timestamp: 1000,
timestamp: 5000,
trace: { trace_id: 'trace-1', parent_id: '', span_id: 'span-root' },
},
{
event_id: 'evt-2',
record: { name: 'my_app.pipeline.retriever', parent_span_id: 'span-root', status: 'OK' },
record_attributes: { 'ai.observability.span_type': 'retrieval' },
start_timestamp: 1100,
timestamp: 2500,
trace: { trace_id: 'trace-1', parent_id: 'span-root', span_id: 'span-retriever' },
},
];
const root = createTreeFromCalls(spans);
// root.name === 'my_app.pipeline'
// root.children[0].name === 'my_app.pipeline.retriever'
Related Pages
- Environment:Truera_Trulens_Streamlit_Dashboard_Environment
- Truera_Trulens_Otel_StackTreeNode -- The node class instantiated and linked by this algorithm.
- Truera_Trulens_Otel_Span_Constants --
SpanTypeandSpanAttributesconstants used to filter and classify spans. - Truera_Trulens_Otel_RecordInfo_Component -- The component that consumes the tree produced by this function.