use super::{SharedAgent, StepOutput, Workflow, WorkflowOutput, shared_agent};
use echo_core::agent::Agent;
use echo_core::error::Result;
use futures::future::BoxFuture;
use std::time::Instant;
use tracing::{debug, info};
type TransformFn = Box<dyn Fn(&str) -> String + Send + Sync>;
pub struct WorkflowStep {
agent: SharedAgent,
transform: Option<TransformFn>,
}
pub struct SequentialWorkflow {
steps: Vec<WorkflowStep>,
}
impl SequentialWorkflow {
pub fn builder() -> SequentialWorkflowBuilder {
SequentialWorkflowBuilder { steps: Vec::new() }
}
}
impl Workflow for SequentialWorkflow {
fn run<'a>(&'a mut self, input: &'a str) -> BoxFuture<'a, Result<WorkflowOutput>> {
Box::pin(async move {
let total_start = Instant::now();
let mut current_input = input.to_string();
let mut step_outputs = Vec::with_capacity(self.steps.len());
for (i, step) in self.steps.iter().enumerate() {
let actual_input = if let Some(transform) = &step.transform {
transform(¤t_input)
} else {
current_input.clone()
};
let agent = step.agent.lock().await;
let agent_name = agent.name().to_string();
info!(
workflow = "sequential",
step = i + 1,
agent = %agent_name,
"▶ Executing step"
);
debug!(
workflow = "sequential",
step = i + 1,
input_len = actual_input.len()
);
let step_start = Instant::now();
let output = agent.execute(&actual_input).await?;
let step_elapsed = step_start.elapsed();
info!(
workflow = "sequential",
step = i + 1,
agent = %agent_name,
elapsed_ms = step_elapsed.as_millis(),
"✓ Step completed"
);
step_outputs.push(StepOutput {
agent_name,
input: actual_input,
output: output.clone(),
elapsed: step_elapsed,
});
current_input = output;
}
let result = current_input;
Ok(WorkflowOutput {
result,
steps: step_outputs,
elapsed: total_start.elapsed(),
})
})
}
}
pub struct SequentialWorkflowBuilder {
steps: Vec<WorkflowStep>,
}
impl SequentialWorkflowBuilder {
pub fn step(mut self, agent: impl Agent + 'static) -> Self {
self.steps.push(WorkflowStep {
agent: shared_agent(agent),
transform: None,
});
self
}
pub fn step_with_transform(
mut self,
agent: impl Agent + 'static,
transform: impl Fn(&str) -> String + Send + Sync + 'static,
) -> Self {
self.steps.push(WorkflowStep {
agent: shared_agent(agent),
transform: Some(Box::new(transform)),
});
self
}
pub fn step_shared(mut self, agent: SharedAgent) -> Self {
self.steps.push(WorkflowStep {
agent,
transform: None,
});
self
}
pub fn build(self) -> SequentialWorkflow {
SequentialWorkflow { steps: self.steps }
}
}