pub mod evaluator_optimizer;
pub mod orchestrator_workers;
pub mod parallelization;
pub mod prompt_chaining;
pub mod routing;
pub use evaluator_optimizer::*;
pub use orchestrator_workers::{
CoordinationMessage, CoordinationStrategy, ExecutionPlan, MessageType, OrchestrationConfig,
OrchestratorWorkers, TaskPriority as OrchestratorTaskPriority, WorkerResult, WorkerRole,
WorkerTask,
};
pub use parallelization::{
AggregationStrategy, ParallelConfig, ParallelTask, ParallelTaskResult, Parallelization,
TaskPriority as ParallelTaskPriority,
};
pub use prompt_chaining::*;
pub use routing::*;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::{AgentId, EvolutionResult, LlmAdapter, TaskId};
#[async_trait]
pub trait WorkflowPattern: Send + Sync {
fn pattern_name(&self) -> &'static str;
async fn execute(&self, input: WorkflowInput) -> EvolutionResult<WorkflowOutput>;
fn is_suitable_for(&self, task_analysis: &TaskAnalysis) -> bool;
fn estimate_execution_time(&self, input: &WorkflowInput) -> std::time::Duration;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowInput {
pub task_id: TaskId,
pub agent_id: AgentId,
pub prompt: String,
pub context: Option<String>,
pub parameters: WorkflowParameters,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowOutput {
pub task_id: TaskId,
pub agent_id: AgentId,
pub result: String,
pub metadata: WorkflowMetadata,
pub execution_trace: Vec<ExecutionStep>,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowParameters {
pub max_steps: Option<usize>,
pub timeout: Option<std::time::Duration>,
pub quality_threshold: Option<f64>,
pub parallel_degree: Option<usize>,
pub retry_attempts: Option<usize>,
}
impl Default for WorkflowParameters {
fn default() -> Self {
Self {
max_steps: Some(10),
timeout: Some(std::time::Duration::from_secs(300)),
quality_threshold: Some(0.8),
parallel_degree: Some(4),
retry_attempts: Some(3),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowMetadata {
pub pattern_used: String,
pub execution_time: std::time::Duration,
pub steps_executed: usize,
pub success: bool,
pub quality_score: Option<f64>,
pub resources_used: ResourceUsage,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceUsage {
pub llm_calls: usize,
pub tokens_consumed: usize,
pub parallel_tasks: usize,
pub memory_peak_mb: f64,
}
impl Default for ResourceUsage {
fn default() -> Self {
Self {
llm_calls: 0,
tokens_consumed: 0,
parallel_tasks: 0,
memory_peak_mb: 0.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionStep {
pub step_id: String,
pub step_type: StepType,
pub input: String,
pub output: String,
pub duration: std::time::Duration,
pub success: bool,
pub metadata: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum StepType {
LlmCall,
Routing,
Aggregation,
Evaluation,
Decomposition,
Parallel,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskAnalysis {
pub complexity: TaskComplexity,
pub domain: String,
pub requires_decomposition: bool,
pub suitable_for_parallel: bool,
pub quality_critical: bool,
pub estimated_steps: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum TaskComplexity {
Simple,
Moderate,
Complex,
VeryComplex,
}
pub struct WorkflowFactory;
impl WorkflowFactory {
pub fn create_for_task(
task_analysis: &TaskAnalysis,
llm_adapter: Arc<dyn LlmAdapter>,
) -> Arc<dyn WorkflowPattern> {
match task_analysis.complexity {
TaskComplexity::Simple => {
if task_analysis.quality_critical {
Arc::new(EvaluatorOptimizer::new(llm_adapter))
} else {
Arc::new(PromptChaining::new(llm_adapter))
}
}
TaskComplexity::Moderate => {
if task_analysis.suitable_for_parallel {
Arc::new(Parallelization::new(llm_adapter))
} else {
Arc::new(Routing::new(llm_adapter))
}
}
TaskComplexity::Complex | TaskComplexity::VeryComplex => {
if task_analysis.requires_decomposition {
Arc::new(OrchestratorWorkers::new(llm_adapter))
} else {
Arc::new(Routing::new(llm_adapter))
}
}
}
}
pub fn create_by_name(
pattern_name: &str,
llm_adapter: Arc<dyn LlmAdapter>,
) -> EvolutionResult<Arc<dyn WorkflowPattern>> {
match pattern_name {
"prompt_chaining" => Ok(Arc::new(PromptChaining::new(llm_adapter))),
"routing" => Ok(Arc::new(Routing::new(llm_adapter))),
"parallelization" => Ok(Arc::new(Parallelization::new(llm_adapter))),
"orchestrator_workers" => Ok(Arc::new(OrchestratorWorkers::new(llm_adapter))),
"evaluator_optimizer" => Ok(Arc::new(EvaluatorOptimizer::new(llm_adapter))),
_ => Err(crate::EvolutionError::WorkflowError(format!(
"Unknown workflow pattern: {}",
pattern_name
))),
}
}
pub fn available_patterns() -> Vec<&'static str> {
vec![
"prompt_chaining",
"routing",
"parallelization",
"orchestrator_workers",
"evaluator_optimizer",
]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_workflow_parameters_default() {
let params = WorkflowParameters::default();
assert_eq!(params.max_steps, Some(10));
assert_eq!(params.timeout, Some(std::time::Duration::from_secs(300)));
assert_eq!(params.quality_threshold, Some(0.8));
}
#[test]
fn test_factory_available_patterns() {
let patterns = WorkflowFactory::available_patterns();
assert_eq!(patterns.len(), 5);
assert!(patterns.contains(&"prompt_chaining"));
assert!(patterns.contains(&"routing"));
assert!(patterns.contains(&"parallelization"));
assert!(patterns.contains(&"orchestrator_workers"));
assert!(patterns.contains(&"evaluator_optimizer"));
}
#[test]
fn test_task_complexity_levels() {
assert_eq!(TaskComplexity::Simple, TaskComplexity::Simple);
assert_ne!(TaskComplexity::Simple, TaskComplexity::Complex);
}
}