use crate::tools::{ToolCall, ToolResult};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub struct NullOutput;
#[async_trait]
impl AgentOutput for NullOutput {
async fn emit_event(
&self,
_event: AgentEvent,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Ok(())
}
}
pub mod events {
pub use super::NullOutput;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ToolExecutionStatus {
Executing,
Success,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolExecutionInfo {
pub execution_id: String,
pub tool_name: String,
pub parameters: HashMap<String, serde_json::Value>,
pub status: ToolExecutionStatus,
pub result: Option<ToolResult>,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub metadata: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentStepInfo {
pub step_number: usize,
pub task: String,
pub thinking: Option<String>,
pub tool_executions: Vec<ToolExecutionInfo>,
pub completed: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TokenUsage {
pub input_tokens: u32,
pub output_tokens: u32,
pub total_tokens: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentExecutionContext {
pub agent_id: String,
pub task: String,
pub project_path: String,
pub max_steps: usize,
pub current_step: usize,
pub execution_time: std::time::Duration,
pub token_usage: TokenUsage,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AgentEvent {
ExecutionStarted { context: AgentExecutionContext },
ExecutionCompleted {
context: AgentExecutionContext,
success: bool,
summary: String,
},
StepStarted { step_info: AgentStepInfo },
StepCompleted { step_info: AgentStepInfo },
ToolExecutionStarted { tool_info: ToolExecutionInfo },
ToolExecutionUpdated { tool_info: ToolExecutionInfo },
ToolExecutionCompleted { tool_info: ToolExecutionInfo },
AgentThinking {
step_number: usize,
thinking: String,
},
TokenUsageUpdated { token_usage: TokenUsage },
StatusUpdate {
status: String,
metadata: HashMap<String, serde_json::Value>,
},
Message {
level: MessageLevel,
content: String,
metadata: HashMap<String, serde_json::Value>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MessageLevel {
Debug,
Info,
Normal,
Warning,
Error,
}
#[async_trait]
pub trait AgentOutput: Send + Sync {
async fn emit_event(
&self,
event: AgentEvent,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
async fn emit_message(
&self,
level: MessageLevel,
content: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.emit_event(AgentEvent::Message {
level,
content: content.to_string(),
metadata: HashMap::new(),
})
.await
}
async fn debug(&self, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.emit_message(MessageLevel::Debug, content).await
}
async fn emit_token_update(
&self,
token_usage: TokenUsage,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.emit_event(AgentEvent::TokenUsageUpdated { token_usage })
.await
}
async fn info(&self, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.emit_message(MessageLevel::Info, content).await
}
async fn warning(&self, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.emit_message(MessageLevel::Warning, content).await
}
async fn error(&self, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.emit_message(MessageLevel::Error, content).await
}
async fn normal(&self, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.emit_message(MessageLevel::Normal, content).await
}
async fn emit_status_update(
&self,
status: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.emit_event(AgentEvent::StatusUpdate {
status: status.to_string(),
metadata: HashMap::new(),
})
.await
}
fn supports_realtime_updates(&self) -> bool {
false
}
async fn flush(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Ok(())
}
}
pub trait ToolExecutionInfoBuilder {
fn create_tool_execution_info(
tool_call: &ToolCall,
status: ToolExecutionStatus,
result: Option<&ToolResult>,
) -> ToolExecutionInfo;
}
impl ToolExecutionInfoBuilder for ToolExecutionInfo {
fn create_tool_execution_info(
tool_call: &ToolCall,
status: ToolExecutionStatus,
result: Option<&ToolResult>,
) -> ToolExecutionInfo {
let parameters = if let serde_json::Value::Object(map) = &tool_call.parameters {
map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
} else {
let mut map = HashMap::new();
map.insert("raw_parameters".to_string(), tool_call.parameters.clone());
map
};
ToolExecutionInfo {
execution_id: tool_call.id.clone(),
tool_name: tool_call.name.clone(),
parameters,
status,
result: result.cloned(),
timestamp: chrono::Utc::now(),
metadata: HashMap::new(),
}
}
}