use crate::agent::Agent;
use crate::error::Result;
use futures::future::BoxFuture;
use tracing::{debug, info};
pub trait Executor: Send + Sync {
fn execute_step<'a>(
&'a mut self,
step_description: &'a str,
context: &'a str,
) -> BoxFuture<'a, Result<String>>;
}
pub struct ReactExecutor {
agent: Box<dyn Agent>,
}
impl ReactExecutor {
pub fn new(agent: impl Agent + 'static) -> Self {
Self {
agent: Box::new(agent),
}
}
pub fn from_boxed(agent: Box<dyn Agent>) -> Self {
Self { agent }
}
}
impl Executor for ReactExecutor {
fn execute_step<'a>(
&'a mut self,
step_description: &'a str,
context: &'a str,
) -> BoxFuture<'a, Result<String>> {
Box::pin(async move {
let prompt = if context.is_empty() {
step_description.to_string()
} else {
format!("{}\n\n{}", context, step_description)
};
info!(
agent = %self.agent.name(),
step = %step_description,
"⚡ ReactExecutor executing step"
);
let result = self.agent.execute(&prompt).await?;
debug!(
agent = %self.agent.name(),
output_len = result.len(),
"✅ Step execution complete"
);
Ok(result)
})
}
}
pub struct SimpleExecutor {
agent: Box<dyn Agent>,
}
impl SimpleExecutor {
pub fn new(agent: impl Agent + 'static) -> Self {
Self {
agent: Box::new(agent),
}
}
}
impl Executor for SimpleExecutor {
fn execute_step<'a>(
&'a mut self,
step_description: &'a str,
context: &'a str,
) -> BoxFuture<'a, Result<String>> {
Box::pin(async move {
let prompt = if context.is_empty() {
step_description.to_string()
} else {
format!("{}\n\n{}", context, step_description)
};
self.agent.execute(&prompt).await
})
}
}