use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use crate::error::SpiceError;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AgentConfig {
pub data: serde_json::Value,
}
impl AgentConfig {
pub fn new(data: serde_json::Value) -> Self {
Self { data }
}
pub fn empty() -> Self {
Self {
data: serde_json::Value::Null,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
pub id: String,
pub name: String,
pub arguments: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Turn {
pub index: usize,
pub output_text: Option<String>,
pub tool_calls: Vec<ToolCall>,
pub tool_results: Vec<serde_json::Value>,
pub stop_reason: Option<String>,
pub duration: Duration,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentOutput {
pub final_text: String,
pub turns: Vec<Turn>,
pub tools_called: Vec<String>,
pub duration: Duration,
pub error: Option<String>,
}
impl AgentOutput {
pub fn all_tool_calls(&self) -> Vec<&ToolCall> {
self.turns
.iter()
.flat_map(|t| t.tool_calls.iter())
.collect()
}
pub fn tool_calls_by_name(&self, name: &str) -> Vec<&ToolCall> {
self.all_tool_calls()
.into_iter()
.filter(|tc| tc.name == name)
.collect()
}
}
#[async_trait]
pub trait AgentUnderTest: Send + Sync {
async fn run(&self, user_message: &str, config: &AgentConfig)
-> Result<AgentOutput, SpiceError>;
fn available_tools(&self, config: &AgentConfig) -> Vec<String>;
fn name(&self) -> &str {
"agent"
}
}