polaris_dashboard 0.1.3

Opinionated read-only dashboard for Polaris sessions.
Documentation
//! Output contract served on `/v1/tracing/*`.
//!
//! These structs serialize to exactly the JSON shapes the dashboard SPA
//! already consumes — `RunSummary`, `SpanTree`, `SpanNode`, `SpanEvent` in
//! `@polaris/types`. Keeping the field names in lockstep (`snake_case`, the same
//! optionality) means the `OTel` collector is a drop-in source for the existing
//! runs pane: the trace tree renders identically and only the derived
//! "Source" column differs.

use serde::Serialize;
use serde_json::{Map, Value};

/// One row in the runs list (`GET /v1/tracing/runs`).
#[derive(Debug, Clone, Serialize)]
pub struct RunSummary {
    /// Trace id — one trace is treated as one run.
    pub run_id: String,
    /// Owning agent, when known (native Polaris runs). `None` for generic
    /// `OTel` sources that carry no agent attribute.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub agent_name: Option<String>,
    /// ISO-8601 UTC start of the earliest span, if any span reported a start.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub started_at: Option<String>,
    /// Wall-clock duration in milliseconds, derived from the root span (or the
    /// trace's overall span envelope when there is no closed root).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub duration_ms: Option<f64>,
    /// `"success"` or `"error"`, or `None` while any span is still open.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub outcome: Option<String>,
    /// Sum of `gen_ai.usage.input_tokens` across the trace's spans.
    pub input_tokens: u64,
    /// Sum of `gen_ai.usage.output_tokens` across the trace's spans.
    pub output_tokens: u64,
    /// Sum of per-span USD cost across the trace's spans.
    pub cost_usd: f64,
    /// Resource attributes (`service.name`, etc.) as a flat string map.
    pub labels: Map<String, Value>,
}

/// Envelope for the runs list.
#[derive(Debug, Clone, Serialize)]
pub struct RunsResponse {
    /// The retained runs, newest first.
    pub items: Vec<RunSummary>,
}

/// Hierarchical view of one run (`GET /v1/tracing/runs/{id}`).
#[derive(Debug, Clone, Serialize)]
pub struct SpanTree {
    /// Trace id this tree belongs to.
    pub run_id: String,
    /// Owning agent, when known; `None` for generic `OTel` sources.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub agent_name: Option<String>,
    /// ISO-8601 UTC start of the earliest span, if known.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub started_at: Option<String>,
    /// Wall-clock duration in milliseconds (see [`RunSummary::duration_ms`]).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub duration_ms: Option<f64>,
    /// `"success"` or `"error"`, or `None` while any span is still open.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub outcome: Option<String>,
    /// Resource attributes (`service.name`, etc.) as a flat string map.
    pub labels: Map<String, Value>,
    /// Top-level spans (no parent), sorted by start time.
    pub roots: Vec<SpanNode>,
    /// Spans whose declared parent isn't present in this trace, surfaced as
    /// additional roots so nothing is dropped.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub orphans: Vec<SpanNode>,
}

/// One node in a [`SpanTree`].
#[derive(Debug, Clone, Serialize)]
pub struct SpanNode {
    /// This span's id.
    pub span_id: String,
    /// Parent span id, if the span declared one.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parent_span_id: Option<String>,
    /// Span name.
    pub name: String,
    /// `"info"` or `"error"`, derived from the OTLP status code.
    pub level: String,
    /// Instrumentation scope name (the producing library).
    pub target: String,
    /// ISO-8601 UTC start, if known.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub started_at: Option<String>,
    /// ISO-8601 UTC end, if the span has closed.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub closed_at: Option<String>,
    /// Span duration in milliseconds when both endpoints are known.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub duration_ms: Option<f64>,
    /// Span attributes, copied verbatim — no source-specific interpretation.
    pub fields: Map<String, Value>,
    /// Events recorded on this span.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub events: Vec<SpanEvent>,
    /// Child spans, sorted by start time.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub children: Vec<SpanNode>,
}

/// One event nested inside a [`SpanNode`].
#[derive(Debug, Clone, Serialize)]
pub struct SpanEvent {
    /// ISO-8601 UTC timestamp, or empty when the event carried no time.
    pub ts: String,
    /// Severity — always `"info"` for OTLP events.
    pub level: String,
    /// Instrumentation scope name (the producing library).
    pub target: String,
    /// Event name.
    pub name: String,
    /// Optional human-readable message.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message: Option<String>,
    /// Event attributes, copied verbatim.
    pub fields: Map<String, Value>,
}