use super::traits::{Event, EventType};
use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};
fn generate_id() -> String {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
format!("evt_{:x}", now)
}
fn now_ms() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolResult {
pub tool_call_id: String,
pub content: serde_json::Value,
#[serde(default)]
pub is_error: bool,
}
impl ToolResult {
pub fn success(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
Self {
tool_call_id: tool_call_id.into(),
content: serde_json::Value::String(content.into()),
is_error: false,
}
}
pub fn success_json(tool_call_id: impl Into<String>, content: serde_json::Value) -> Self {
Self {
tool_call_id: tool_call_id.into(),
content,
is_error: false,
}
}
pub fn error(tool_call_id: impl Into<String>, error: impl Into<String>) -> Self {
Self {
tool_call_id: tool_call_id.into(),
content: serde_json::Value::String(error.into()),
is_error: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolResultEvent {
pub event_id: String,
pub session_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub project_hash: Option<String>,
pub timestamp_ms: u64,
pub sequence: u32,
pub tool_call_event_id: String,
pub result: ToolResult,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
impl ToolResultEvent {
pub fn success(
session_id: impl Into<String>,
sequence: u32,
tool_call_event_id: impl Into<String>,
tool_call_id: impl Into<String>,
content: serde_json::Value,
) -> Self {
Self {
event_id: generate_id(),
session_id: session_id.into(),
project_hash: None,
timestamp_ms: now_ms(),
sequence,
tool_call_event_id: tool_call_event_id.into(),
result: ToolResult {
tool_call_id: tool_call_id.into(),
content,
is_error: false,
},
duration_ms: None,
error: None,
}
}
pub fn error(
session_id: impl Into<String>,
sequence: u32,
tool_call_event_id: impl Into<String>,
tool_call_id: impl Into<String>,
error: impl Into<String>,
) -> Self {
let error_str = error.into();
Self {
event_id: generate_id(),
session_id: session_id.into(),
project_hash: None,
timestamp_ms: now_ms(),
sequence,
tool_call_event_id: tool_call_event_id.into(),
result: ToolResult {
tool_call_id: tool_call_id.into(),
content: serde_json::Value::String(error_str.clone()),
is_error: true,
},
duration_ms: None,
error: Some(error_str),
}
}
pub fn with_project(mut self, project_hash: impl Into<String>) -> Self {
self.project_hash = Some(project_hash.into());
self
}
pub fn with_duration_ms(mut self, duration_ms: u64) -> Self {
self.duration_ms = Some(duration_ms);
self
}
pub fn with_event_id(mut self, event_id: impl Into<String>) -> Self {
self.event_id = event_id.into();
self
}
}
impl Event for ToolResultEvent {
fn event_id(&self) -> &str {
&self.event_id
}
fn event_type(&self) -> EventType {
EventType::ToolResult
}
fn session_id(&self) -> &str {
&self.session_id
}
fn timestamp_ms(&self) -> u64 {
self.timestamp_ms
}
fn sequence(&self) -> u32 {
self.sequence
}
fn to_json(&self) -> serde_json::Value {
serde_json::to_value(self).unwrap()
}
}