1use crate::output::AgentOutput;
2use crate::sandbox::SandboxConfig;
3use anyhow::Result;
4use async_trait::async_trait;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ModelSize {
9 Small,
11 Medium,
13 Large,
15}
16
17impl std::str::FromStr for ModelSize {
18 type Err = ();
19
20 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
21 match s.to_lowercase().as_str() {
22 "small" | "s" => Ok(ModelSize::Small),
23 "medium" | "m" | "default" => Ok(ModelSize::Medium),
24 "large" | "l" | "max" => Ok(ModelSize::Large),
25 _ => Err(()),
26 }
27 }
28}
29
30#[async_trait]
31#[allow(dead_code)]
32pub trait Agent {
33 fn name(&self) -> &str;
34
35 fn default_model() -> &'static str
36 where
37 Self: Sized;
38
39 fn model_for_size(size: ModelSize) -> &'static str
41 where
42 Self: Sized;
43
44 fn resolve_model(model_input: &str) -> String
49 where
50 Self: Sized,
51 {
52 if let Ok(size) = model_input.parse::<ModelSize>() {
53 Self::model_for_size(size).to_string()
54 } else {
55 model_input.to_string()
56 }
57 }
58
59 fn available_models() -> &'static [&'static str]
61 where
62 Self: Sized;
63
64 fn validate_model(model: &str, agent_name: &str) -> Result<()>
68 where
69 Self: Sized,
70 {
71 let available = Self::available_models();
72 if available.contains(&model) {
73 Ok(())
74 } else {
75 let small = Self::model_for_size(ModelSize::Small);
77 let medium = Self::model_for_size(ModelSize::Medium);
78 let large = Self::model_for_size(ModelSize::Large);
79
80 let mut models = vec![
81 format!("{} (small)", small),
82 format!("{} (medium)", medium),
83 format!("{} (large)", large),
84 ];
85
86 for m in available {
88 if m != &small && m != &medium && m != &large {
89 models.push(m.to_string());
90 }
91 }
92
93 anyhow::bail!(
94 "Invalid model '{}' for {}. Available models: {}",
95 model,
96 agent_name,
97 models.join(", ")
98 )
99 }
100 }
101
102 fn system_prompt(&self) -> &str;
103
104 fn set_system_prompt(&mut self, prompt: String);
105
106 fn get_model(&self) -> &str;
107
108 fn set_model(&mut self, model: String);
109
110 fn set_root(&mut self, root: String);
111
112 fn set_skip_permissions(&mut self, skip: bool);
113
114 fn set_output_format(&mut self, format: Option<String>);
115
116 fn set_capture_output(&mut self, _capture: bool) {}
122
123 fn set_max_turns(&mut self, _turns: u32) {}
125
126 fn set_sandbox(&mut self, _config: SandboxConfig) {}
128
129 fn set_add_dirs(&mut self, dirs: Vec<String>);
131
132 fn as_any_ref(&self) -> &dyn std::any::Any;
134
135 fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
137
138 async fn run(&self, prompt: Option<&str>) -> Result<Option<AgentOutput>>;
143
144 async fn run_interactive(&self, prompt: Option<&str>) -> Result<()>;
145
146 async fn run_resume(&self, session_id: Option<&str>, last: bool) -> Result<()>;
152
153 async fn run_resume_with_prompt(
158 &self,
159 _session_id: &str,
160 _prompt: &str,
161 ) -> Result<Option<AgentOutput>> {
162 anyhow::bail!("Resume with prompt is not supported by this agent")
163 }
164
165 async fn cleanup(&self) -> Result<()>;
166}
167
168#[cfg(test)]
169#[path = "agent_tests.rs"]
170mod tests;