swiftide_core/
agent_traits.rs1use std::{path::PathBuf, sync::Arc};
2
3use crate::chat_completion::ChatMessage;
4use anyhow::Result;
5use async_trait::async_trait;
6use thiserror::Error;
7
8#[async_trait]
10pub trait ToolExecutor: Send + Sync {
11 async fn exec_cmd(&self, cmd: &Command) -> Result<CommandOutput, CommandError>;
12}
13
14#[async_trait]
15impl<T: ToolExecutor> ToolExecutor for &T {
16 async fn exec_cmd(&self, cmd: &Command) -> Result<CommandOutput, CommandError> {
17 (*self).exec_cmd(cmd).await
18 }
19}
20
21#[async_trait]
22impl ToolExecutor for Arc<dyn ToolExecutor> {
23 async fn exec_cmd(&self, cmd: &Command) -> Result<CommandOutput, CommandError> {
24 (**self).exec_cmd(cmd).await
25 }
26}
27
28#[async_trait]
29impl ToolExecutor for Box<dyn ToolExecutor> {
30 async fn exec_cmd(&self, cmd: &Command) -> Result<CommandOutput, CommandError> {
31 (**self).exec_cmd(cmd).await
32 }
33}
34
35#[async_trait]
36impl ToolExecutor for &dyn ToolExecutor {
37 async fn exec_cmd(&self, cmd: &Command) -> Result<CommandOutput, CommandError> {
38 (**self).exec_cmd(cmd).await
39 }
40}
41
42#[derive(Debug, Error)]
43pub enum CommandError {
44 #[error("executor error: {0:#}")]
46 ExecutorError(#[from] anyhow::Error),
47
48 #[error("command failed with NonZeroExit: {0}")]
50 NonZeroExit(CommandOutput),
51}
52
53impl From<std::io::Error> for CommandError {
54 fn from(err: std::io::Error) -> Self {
55 CommandError::NonZeroExit(err.to_string().into())
56 }
57}
58
59#[non_exhaustive]
67#[derive(Debug, Clone)]
68pub enum Command {
69 Shell(String),
70 ReadFile(PathBuf),
71 WriteFile(PathBuf, String),
72}
73
74impl Command {
75 pub fn shell<S: Into<String>>(cmd: S) -> Self {
76 Command::Shell(cmd.into())
77 }
78
79 pub fn read_file<P: Into<PathBuf>>(path: P) -> Self {
80 Command::ReadFile(path.into())
81 }
82
83 pub fn write_file<P: Into<PathBuf>, S: Into<String>>(path: P, content: S) -> Self {
84 Command::WriteFile(path.into(), content.into())
85 }
86}
87
88#[derive(Debug, Clone)]
90pub struct CommandOutput {
91 pub output: String,
92 }
95
96impl CommandOutput {
97 pub fn empty() -> Self {
98 CommandOutput {
99 output: String::new(),
100 }
101 }
102
103 pub fn new(output: impl Into<String>) -> Self {
104 CommandOutput {
105 output: output.into(),
106 }
107 }
108 pub fn is_empty(&self) -> bool {
109 self.output.is_empty()
110 }
111}
112
113impl std::fmt::Display for CommandOutput {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 self.output.fmt(f)
116 }
117}
118
119impl<T: Into<String>> From<T> for CommandOutput {
120 fn from(value: T) -> Self {
121 CommandOutput {
122 output: value.into(),
123 }
124 }
125}
126
127#[async_trait]
129pub trait AgentContext: Send + Sync {
130 async fn next_completion(&self) -> Option<Vec<ChatMessage>>;
140
141 async fn current_new_messages(&self) -> Vec<ChatMessage>;
143
144 async fn add_messages(&self, item: Vec<ChatMessage>);
146
147 async fn add_message(&self, item: ChatMessage);
149
150 async fn exec_cmd(&self, cmd: &Command) -> Result<CommandOutput, CommandError>;
152
153 async fn history(&self) -> Vec<ChatMessage>;
154
155 async fn redrive(&self);
160}
161
162#[async_trait]
163impl AgentContext for Box<dyn AgentContext> {
164 async fn next_completion(&self) -> Option<Vec<ChatMessage>> {
165 (**self).next_completion().await
166 }
167
168 async fn current_new_messages(&self) -> Vec<ChatMessage> {
169 (**self).current_new_messages().await
170 }
171
172 async fn add_messages(&self, item: Vec<ChatMessage>) {
173 (**self).add_messages(item).await;
174 }
175
176 async fn add_message(&self, item: ChatMessage) {
177 (**self).add_message(item).await;
178 }
179
180 async fn exec_cmd(&self, cmd: &Command) -> Result<CommandOutput, CommandError> {
181 (**self).exec_cmd(cmd).await
182 }
183
184 async fn history(&self) -> Vec<ChatMessage> {
185 (**self).history().await
186 }
187
188 async fn redrive(&self) {
189 (**self).redrive().await;
190 }
191}
192
193#[async_trait]
194impl AgentContext for Arc<dyn AgentContext> {
195 async fn next_completion(&self) -> Option<Vec<ChatMessage>> {
196 (**self).next_completion().await
197 }
198
199 async fn current_new_messages(&self) -> Vec<ChatMessage> {
200 (**self).current_new_messages().await
201 }
202
203 async fn add_messages(&self, item: Vec<ChatMessage>) {
204 (**self).add_messages(item).await;
205 }
206
207 async fn add_message(&self, item: ChatMessage) {
208 (**self).add_message(item).await;
209 }
210
211 async fn exec_cmd(&self, cmd: &Command) -> Result<CommandOutput, CommandError> {
212 (**self).exec_cmd(cmd).await
213 }
214
215 async fn history(&self) -> Vec<ChatMessage> {
216 (**self).history().await
217 }
218
219 async fn redrive(&self) {
220 (**self).redrive().await;
221 }
222}
223
224#[async_trait]
225impl AgentContext for &dyn AgentContext {
226 async fn next_completion(&self) -> Option<Vec<ChatMessage>> {
227 (**self).next_completion().await
228 }
229
230 async fn current_new_messages(&self) -> Vec<ChatMessage> {
231 (**self).current_new_messages().await
232 }
233
234 async fn add_messages(&self, item: Vec<ChatMessage>) {
235 (**self).add_messages(item).await;
236 }
237
238 async fn add_message(&self, item: ChatMessage) {
239 (**self).add_message(item).await;
240 }
241
242 async fn exec_cmd(&self, cmd: &Command) -> Result<CommandOutput, CommandError> {
243 (**self).exec_cmd(cmd).await
244 }
245
246 async fn history(&self) -> Vec<ChatMessage> {
247 (**self).history().await
248 }
249
250 async fn redrive(&self) {
251 (**self).redrive().await;
252 }
253}