1use crate::llm::LlmMessage;
4use crate::tools::{ToolCall, ToolResult};
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use uuid::Uuid;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct TrajectoryEntry {
13 pub id: String,
15
16 pub timestamp: DateTime<Utc>,
18
19 pub entry_type: EntryType,
21
22 pub step: usize,
24
25 pub metadata: Option<HashMap<String, serde_json::Value>>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(tag = "type", rename_all = "snake_case")]
32pub enum EntryType {
33 TaskStart {
35 task: String,
36 agent_config: serde_json::Value,
37 },
38
39 LlmRequest {
41 messages: Vec<LlmMessage>,
42 model: String,
43 provider: String,
44 },
45
46 LlmResponse {
48 message: LlmMessage,
49 usage: Option<crate::llm::Usage>,
50 finish_reason: Option<String>,
51 },
52
53 ToolCall { call: ToolCall },
55
56 ToolResult { result: ToolResult },
58
59 StepComplete { step_summary: String, success: bool },
61
62 TaskComplete {
64 success: bool,
65 final_result: String,
66 total_steps: usize,
67 duration_ms: u64,
68 },
69
70 Error {
72 error: String,
73 context: Option<String>,
74 },
75
76 Log {
78 level: LogLevel,
79 message: String,
80 context: Option<HashMap<String, serde_json::Value>>,
81 },
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86#[serde(rename_all = "lowercase")]
87pub enum LogLevel {
88 Debug,
89 Info,
90 Warn,
91 Error,
92}
93
94impl TrajectoryEntry {
95 pub fn new(entry_type: EntryType, step: usize) -> Self {
97 Self {
98 id: Uuid::new_v4().to_string(),
99 timestamp: Utc::now(),
100 entry_type,
101 step,
102 metadata: None,
103 }
104 }
105
106 pub fn with_metadata(mut self, metadata: HashMap<String, serde_json::Value>) -> Self {
108 self.metadata = Some(metadata);
109 self
110 }
111
112 pub fn task_start(task: String, agent_config: serde_json::Value) -> Self {
114 Self::new(EntryType::TaskStart { task, agent_config }, 0)
115 }
116
117 pub fn llm_request(
119 messages: Vec<LlmMessage>,
120 model: String,
121 provider: String,
122 step: usize,
123 ) -> Self {
124 Self::new(
125 EntryType::LlmRequest {
126 messages,
127 model,
128 provider,
129 },
130 step,
131 )
132 }
133
134 pub fn llm_response(
136 message: LlmMessage,
137 usage: Option<crate::llm::Usage>,
138 finish_reason: Option<String>,
139 step: usize,
140 ) -> Self {
141 Self::new(
142 EntryType::LlmResponse {
143 message,
144 usage,
145 finish_reason,
146 },
147 step,
148 )
149 }
150
151 pub fn tool_call(call: ToolCall, step: usize) -> Self {
153 Self::new(EntryType::ToolCall { call }, step)
154 }
155
156 pub fn tool_result(result: ToolResult, step: usize) -> Self {
158 Self::new(EntryType::ToolResult { result }, step)
159 }
160
161 pub fn step_complete(step_summary: String, success: bool, step: usize) -> Self {
163 Self::new(
164 EntryType::StepComplete {
165 step_summary,
166 success,
167 },
168 step,
169 )
170 }
171
172 pub fn task_complete(
174 success: bool,
175 final_result: String,
176 total_steps: usize,
177 duration_ms: u64,
178 ) -> Self {
179 Self::new(
180 EntryType::TaskComplete {
181 success,
182 final_result,
183 total_steps,
184 duration_ms,
185 },
186 total_steps,
187 )
188 }
189
190 pub fn error(error: String, context: Option<String>, step: usize) -> Self {
192 Self::new(EntryType::Error { error, context }, step)
193 }
194
195 pub fn log(level: LogLevel, message: String, step: usize) -> Self {
197 Self::new(
198 EntryType::Log {
199 level,
200 message,
201 context: None,
202 },
203 step,
204 )
205 }
206
207 pub fn log_with_context(
209 level: LogLevel,
210 message: String,
211 context: HashMap<String, serde_json::Value>,
212 step: usize,
213 ) -> Self {
214 Self::new(
215 EntryType::Log {
216 level,
217 message,
218 context: Some(context),
219 },
220 step,
221 )
222 }
223}