1use async_trait::async_trait;
2use serde::{Deserialize, Serialize};
3use std::time::Duration;
4
5use crate::error::SpiceError;
6
7#[derive(Debug, Clone, Default, Serialize, Deserialize)]
9pub struct AgentConfig {
10 pub data: serde_json::Value,
11}
12
13impl AgentConfig {
14 pub fn new(data: serde_json::Value) -> Self {
15 Self { data }
16 }
17
18 pub fn empty() -> Self {
19 Self {
20 data: serde_json::Value::Null,
21 }
22 }
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct ToolCall {
28 pub id: String,
29 pub name: String,
30 pub arguments: serde_json::Value,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct Turn {
36 pub index: usize,
37 pub output_text: Option<String>,
38 pub tool_calls: Vec<ToolCall>,
39 pub tool_results: Vec<serde_json::Value>,
40 pub stop_reason: Option<String>,
41 pub duration: Duration,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct AgentOutput {
47 pub final_text: String,
48 pub turns: Vec<Turn>,
49 pub tools_called: Vec<String>,
50 pub duration: Duration,
51 pub error: Option<String>,
52}
53
54impl AgentOutput {
55 pub fn all_tool_calls(&self) -> Vec<&ToolCall> {
57 self.turns
58 .iter()
59 .flat_map(|t| t.tool_calls.iter())
60 .collect()
61 }
62
63 pub fn tool_calls_by_name(&self, name: &str) -> Vec<&ToolCall> {
65 self.all_tool_calls()
66 .into_iter()
67 .filter(|tc| tc.name == name)
68 .collect()
69 }
70}
71
72#[async_trait]
74pub trait AgentUnderTest: Send + Sync {
75 async fn run(&self, user_message: &str, config: &AgentConfig)
77 -> Result<AgentOutput, SpiceError>;
78
79 fn available_tools(&self, config: &AgentConfig) -> Vec<String>;
81
82 fn name(&self) -> &str {
84 "agent"
85 }
86}