Skip to main content

lc_callbacks/
run_tree.rs

1// lc-callbacks/src/run_tree.rs
2//! Run tree data structure for tracing
3
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use uuid::Uuid;
8
9use super::RunType;
10
11/// Run tree node for tracing
12///
13/// Each run is a node in a tree, with optional parent and children.
14/// The entire trace forms a tree structure, with the root being the top-level call.
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct RunTree {
17    /// Unique run ID (UUID v7 with timestamp)
18    pub id: Uuid,
19
20    /// Run name
21    pub name: String,
22
23    /// Run type
24    pub run_type: RunType,
25
26    /// Input data
27    pub inputs: serde_json::Value,
28
29    /// Output data (set when run ends)
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub outputs: Option<serde_json::Value>,
32
33    /// Error message (if run failed)
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub error: Option<String>,
36
37    /// Parent run ID
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub parent_run_id: Option<Uuid>,
40
41    /// Trace ID (ID of the root run)
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub trace_id: Option<Uuid>,
44
45    /// Start time
46    pub start_time: DateTime<Utc>,
47
48    /// End time
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub end_time: Option<DateTime<Utc>>,
51
52    /// Metadata
53    #[serde(default)]
54    pub metadata: HashMap<String, serde_json::Value>,
55
56    /// Tags
57    #[serde(default)]
58    pub tags: Vec<String>,
59
60    /// Project name (LangSmith project)
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub project_name: Option<String>,
63
64    /// Serialized representation of the component
65    #[serde(default)]
66    pub serialized: serde_json::Value,
67}
68
69impl RunTree {
70    /// Create a new run
71    pub fn new(name: impl Into<String>, run_type: RunType, inputs: serde_json::Value) -> Self {
72        Self {
73            id: Uuid::now_v7(),
74            name: name.into(),
75            run_type,
76            inputs,
77            outputs: None,
78            error: None,
79            parent_run_id: None,
80            trace_id: None,
81            start_time: Utc::now(),
82            end_time: None,
83            metadata: HashMap::new(),
84            tags: Vec::new(),
85            project_name: None,
86            serialized: serde_json::Value::Null,
87        }
88    }
89
90    /// Create a child run from this run
91    pub fn create_child(
92        &self,
93        name: impl Into<String>,
94        run_type: RunType,
95        inputs: serde_json::Value,
96    ) -> Self {
97        let mut child = Self::new(name, run_type, inputs);
98        child.parent_run_id = Some(self.id);
99        child.trace_id = self.trace_id.or(Some(self.id));
100        child.project_name = self.project_name.clone();
101        child
102    }
103
104    /// End the run with outputs
105    pub fn end(&mut self, outputs: serde_json::Value) {
106        self.outputs = Some(outputs);
107        self.end_time = Some(Utc::now());
108    }
109
110    /// End the run with an error
111    pub fn end_with_error(&mut self, error: impl Into<String>) {
112        self.error = Some(error.into());
113        self.end_time = Some(Utc::now());
114    }
115
116    /// Add a tag
117    pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
118        self.tags.push(tag.into());
119        self
120    }
121
122    /// Add metadata
123    pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
124        self.metadata.insert(key.into(), value);
125        self
126    }
127
128    /// Set project name
129    pub fn with_project(mut self, project: impl Into<String>) -> Self {
130        self.project_name = Some(project.into());
131        self
132    }
133
134    /// Calculate run duration in milliseconds
135    pub fn duration_ms(&self) -> Option<i64> {
136        self.end_time
137            .map(|end| (end - self.start_time).num_milliseconds())
138    }
139}
140
141/// Simplified run structure for API requests
142#[derive(Debug, Serialize)]
143pub struct RunCreate {
144    /// Run ID.
145    pub id: String,
146    /// Run name.
147    pub name: String,
148    /// Run type as a string.
149    pub run_type: String,
150    /// Inputs for the run.
151    pub inputs: serde_json::Value,
152    /// Outputs of the run.
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub outputs: Option<serde_json::Value>,
155    /// Error message, if the run failed.
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub error: Option<String>,
158    /// ID of the parent run, if any.
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub parent_run_id: Option<String>,
161    /// Start time as an RFC 3339 timestamp.
162    pub start_time: String,
163    /// End time as an RFC 3339 timestamp, if the run finished.
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub end_time: Option<String>,
166    /// Session/project name.
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub session_name: Option<String>,
169    /// Tags attached to the run.
170    #[serde(default, skip_serializing_if = "Vec::is_empty")]
171    pub tags: Vec<String>,
172    /// Arbitrary metadata for the run.
173    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
174    pub metadata: HashMap<String, serde_json::Value>,
175}
176
177impl From<&RunTree> for RunCreate {
178    fn from(run: &RunTree) -> Self {
179        Self {
180            id: run.id.to_string(),
181            name: run.name.clone(),
182            run_type: run.run_type.as_str().to_string(),
183            inputs: run.inputs.clone(),
184            outputs: run.outputs.clone(),
185            error: run.error.clone(),
186            parent_run_id: run.parent_run_id.map(|id| id.to_string()),
187            start_time: run.start_time.to_rfc3339(),
188            end_time: run.end_time.map(|t| t.to_rfc3339()),
189            session_name: run.project_name.clone(),
190            tags: run.tags.clone(),
191            metadata: run.metadata.clone(),
192        }
193    }
194}
195
196/// Run update structure for PATCH requests
197#[derive(Debug, Serialize)]
198pub struct RunUpdate {
199    /// Updated outputs of the run.
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub outputs: Option<serde_json::Value>,
202    /// Updated error message.
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub error: Option<String>,
205    /// Updated end time as an RFC 3339 timestamp.
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub end_time: Option<String>,
208}
209
210impl From<&RunTree> for RunUpdate {
211    fn from(run: &RunTree) -> Self {
212        Self {
213            outputs: run.outputs.clone(),
214            error: run.error.clone(),
215            end_time: run.end_time.map(|t| t.to_rfc3339()),
216        }
217    }
218}