use crate::llm::LlmMessage;
use crate::tools::{ToolCall, ToolResult};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrajectoryEntry {
pub id: String,
pub timestamp: DateTime<Utc>,
pub entry_type: EntryType,
pub step: usize,
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum EntryType {
TaskStart {
task: String,
agent_config: serde_json::Value,
},
LlmRequest {
messages: Vec<LlmMessage>,
model: String,
provider: String,
},
LlmResponse {
message: LlmMessage,
usage: Option<crate::llm::Usage>,
finish_reason: Option<String>,
},
ToolCall {
call: ToolCall,
},
ToolResult {
result: ToolResult,
},
StepComplete {
step_summary: String,
success: bool,
},
TaskComplete {
success: bool,
final_result: String,
total_steps: usize,
duration_ms: u64,
},
Error {
error: String,
context: Option<String>,
},
Log {
level: LogLevel,
message: String,
context: Option<HashMap<String, serde_json::Value>>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
Debug,
Info,
Warn,
Error,
}
impl TrajectoryEntry {
pub fn new(entry_type: EntryType, step: usize) -> Self {
Self {
id: Uuid::new_v4().to_string(),
timestamp: Utc::now(),
entry_type,
step,
metadata: None,
}
}
pub fn with_metadata(mut self, metadata: HashMap<String, serde_json::Value>) -> Self {
self.metadata = Some(metadata);
self
}
pub fn task_start(task: String, agent_config: serde_json::Value) -> Self {
Self::new(EntryType::TaskStart { task, agent_config }, 0)
}
pub fn llm_request(messages: Vec<LlmMessage>, model: String, provider: String, step: usize) -> Self {
Self::new(EntryType::LlmRequest { messages, model, provider }, step)
}
pub fn llm_response(
message: LlmMessage,
usage: Option<crate::llm::Usage>,
finish_reason: Option<String>,
step: usize,
) -> Self {
Self::new(EntryType::LlmResponse { message, usage, finish_reason }, step)
}
pub fn tool_call(call: ToolCall, step: usize) -> Self {
Self::new(EntryType::ToolCall { call }, step)
}
pub fn tool_result(result: ToolResult, step: usize) -> Self {
Self::new(EntryType::ToolResult { result }, step)
}
pub fn step_complete(step_summary: String, success: bool, step: usize) -> Self {
Self::new(EntryType::StepComplete { step_summary, success }, step)
}
pub fn task_complete(
success: bool,
final_result: String,
total_steps: usize,
duration_ms: u64,
) -> Self {
Self::new(
EntryType::TaskComplete {
success,
final_result,
total_steps,
duration_ms,
},
total_steps,
)
}
pub fn error(error: String, context: Option<String>, step: usize) -> Self {
Self::new(EntryType::Error { error, context }, step)
}
pub fn log(level: LogLevel, message: String, step: usize) -> Self {
Self::new(EntryType::Log { level, message, context: None }, step)
}
pub fn log_with_context(
level: LogLevel,
message: String,
context: HashMap<String, serde_json::Value>,
step: usize,
) -> Self {
Self::new(EntryType::Log { level, message, context: Some(context) }, step)
}
}