use chrono::Utc;
use serde::{Deserialize, Serialize};
pub type SpanId = String;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SpanKind {
Llm,
Chain,
Tool,
Retriever,
Agent,
Custom(String),
}
impl std::fmt::Display for SpanKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SpanKind::Llm => write!(f, "llm"),
SpanKind::Chain => write!(f, "chain"),
SpanKind::Tool => write!(f, "tool"),
SpanKind::Retriever => write!(f, "retriever"),
SpanKind::Agent => write!(f, "agent"),
SpanKind::Custom(name) => write!(f, "custom:{}", name),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SpanTokenUsage {
pub prompt_tokens: usize,
pub completion_tokens: usize,
pub total_tokens: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum SpanStatus {
Ok,
Error(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraceSpan {
pub id: SpanId,
pub parent_id: Option<SpanId>,
pub name: String,
pub kind: SpanKind,
pub start_time: Option<String>,
pub end_time: Option<String>,
pub tokens: Option<SpanTokenUsage>,
pub cost: Option<f64>,
pub latency_ms: Option<u64>,
pub metadata: serde_json::Value,
pub status: SpanStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraceNode {
pub span: TraceSpan,
pub children: Vec<TraceNode>,
}
pub(crate) fn build_tree(root: &TraceSpan, all_spans: &[TraceSpan]) -> TraceNode {
let children: Vec<TraceNode> = all_spans
.iter()
.filter(|s| s.parent_id.as_deref() == Some(root.id.as_str()))
.map(|child| build_tree(child, all_spans))
.collect();
TraceNode {
span: root.clone(),
children,
}
}
pub(crate) fn make_span(
id: String,
parent_id: Option<SpanId>,
name: &str,
kind: SpanKind,
) -> TraceSpan {
TraceSpan {
id,
parent_id,
name: name.to_string(),
kind,
start_time: Some(Utc::now().to_rfc3339()),
end_time: None,
tokens: None,
cost: None,
latency_ms: None,
metadata: serde_json::Value::Object(serde_json::Map::new()),
status: SpanStatus::Ok,
}
}