use async_trait::async_trait;
use llm_toolkit::agent::{Agent, AgentError, Payload};
use llm_toolkit::orchestrator::{
BlueprintWorkflow, Orchestrator, OrchestratorConfig, StrategyMap, StrategyStep,
};
use std::time::Instant;
#[derive(Debug)]
struct MockAgent {
name: String,
expertise: String,
response_prefix: String,
}
impl MockAgent {
fn new(name: &str, expertise: &str, response_prefix: &str) -> Self {
Self {
name: name.to_string(),
expertise: expertise.to_string(),
response_prefix: response_prefix.to_string(),
}
}
}
#[async_trait]
impl Agent for MockAgent {
type Output = String;
type Expertise = String;
fn expertise(&self) -> &String {
&self.expertise
}
fn name(&self) -> String {
self.name.clone()
}
async fn execute(&self, intent: Payload) -> Result<Self::Output, AgentError> {
let text_intent = intent.to_text();
Ok(format!(
"{}: {}",
self.response_prefix,
text_intent.chars().take(100).collect::<String>()
))
}
}
#[derive(Debug)]
struct MockInternalAgent;
#[async_trait]
impl Agent for MockInternalAgent {
type Output = String;
type Expertise = &'static str;
fn expertise(&self) -> &&'static str {
const EXPERTISE: &str = "Mock internal agent";
&EXPERTISE
}
fn name(&self) -> String {
"MockInternalAgent".to_string()
}
async fn execute(&self, intent: Payload) -> Result<Self::Output, AgentError> {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
Ok(format!(
"🤖 Generated intent: {}",
intent.to_text().chars().take(50).collect::<String>()
))
}
}
#[derive(Debug)]
struct MockJsonAgent;
#[async_trait]
impl Agent for MockJsonAgent {
type Output = StrategyMap;
type Expertise = &'static str;
fn expertise(&self) -> &&'static str {
const EXPERTISE: &str = "Mock strategy generator";
&EXPERTISE
}
fn name(&self) -> String {
"MockJsonAgent".to_string()
}
async fn execute(&self, _intent: Payload) -> Result<Self::Output, AgentError> {
let mut strategy = StrategyMap::new("Process data pipeline".to_string());
strategy.add_step(StrategyStep::new(
"step_1".to_string(),
"Extract data from input".to_string(),
"DataExtractor".to_string(),
"Extract data from: {{task}}".to_string(),
"Extracted data".to_string(),
));
strategy.add_step(StrategyStep::new(
"step_2".to_string(),
"Transform extracted data".to_string(),
"DataTransformer".to_string(),
"Transform this data: {{previous_output}}".to_string(),
"Transformed data".to_string(),
));
strategy.add_step(StrategyStep::new(
"step_3".to_string(),
"Generate final report".to_string(),
"ReportGenerator".to_string(),
"Create report from: {{previous_output}}".to_string(),
"Final report".to_string(),
));
Ok(strategy)
}
}
async fn run_orchestrator_test(
enable_fast_path: bool,
) -> Result<
(
llm_toolkit::orchestrator::OrchestrationResult,
std::time::Duration,
),
Box<dyn std::error::Error>,
> {
let blueprint = BlueprintWorkflow::new(
r#"
Fast Path Test Pipeline:
1. Extract data from input
2. Transform the extracted data
3. Generate comprehensive report
"#
.to_string(),
);
let mut orchestrator = Orchestrator::with_internal_agents(
blueprint,
Box::new(MockInternalAgent),
Box::new(MockJsonAgent),
);
let config = OrchestratorConfig {
enable_fast_path_intent_generation: enable_fast_path,
..Default::default()
};
orchestrator.set_config(config);
orchestrator.add_agent(MockAgent::new(
"DataExtractor",
"Extracts data from various sources",
"📥 Extracted",
));
orchestrator.add_agent(MockAgent::new(
"DataTransformer",
"Transforms and cleans data",
"🔄 Transformed",
));
orchestrator.add_agent(MockAgent::new(
"ReportGenerator",
"Generates reports from processed data",
"📝 Report",
));
let task = "customer feedback data";
let start = Instant::now();
let result = orchestrator.execute(task).await;
let duration = start.elapsed();
Ok((result, duration))
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🚀 Fast Path Intent Generation E2E Test\n");
println!("{}", "=".repeat(60));
println!("\n📊 Test 1: Fast Path ENABLED (optimized)");
println!("{}", "-".repeat(60));
let (result_fast, duration_fast) = run_orchestrator_test(true).await?;
println!("✅ Test completed!");
println!(" Status: {:?}", result_fast.status);
println!(" Steps executed: {}", result_fast.steps_executed);
println!(" Redesigns: {}", result_fast.redesigns_triggered);
println!(" ⚡ Duration: {:?}", duration_fast);
assert_eq!(
result_fast.status,
llm_toolkit::orchestrator::OrchestrationStatus::Success,
"Fast path execution should succeed"
);
assert_eq!(result_fast.steps_executed, 3, "Should execute all 3 steps");
assert_eq!(
result_fast.redesigns_triggered, 0,
"Should have no redesigns"
);
println!("\n📊 Test 2: Fast Path DISABLED (uses LLM)");
println!("{}", "-".repeat(60));
let (result_slow, duration_slow) = run_orchestrator_test(false).await?;
println!("✅ Test completed!");
println!(" Status: {:?}", result_slow.status);
println!(" Steps executed: {}", result_slow.steps_executed);
println!(" Redesigns: {}", result_slow.redesigns_triggered);
println!(" 🐌 Duration: {:?}", duration_slow);
assert_eq!(
result_slow.status,
llm_toolkit::orchestrator::OrchestrationStatus::Success,
"LLM path execution should succeed"
);
assert_eq!(result_slow.steps_executed, 3, "Should execute all 3 steps");
assert_eq!(
result_slow.redesigns_triggered, 0,
"Should have no redesigns"
);
println!("\n📈 Performance Comparison");
println!("{}", "=".repeat(60));
let speedup = duration_slow.as_millis() as f64 / duration_fast.as_millis() as f64;
println!(" Fast Path: {:?}", duration_fast);
println!(" LLM Path: {:?}", duration_slow);
println!(" Speedup: {:.2}x faster", speedup);
if duration_fast >= duration_slow {
println!("\n⚠️ WARNING: Fast path was not faster than LLM path!");
println!(" This might indicate the fast path is not being used.");
println!(" Check that all placeholders are being resolved correctly.");
} else {
let saved_ms = duration_slow.as_millis() - duration_fast.as_millis();
println!(" ✨ Saved: ~{}ms by using fast path", saved_ms);
}
println!("\n🔍 Final Validation");
println!("{}", "=".repeat(60));
assert!(
result_fast.final_output.is_some(),
"Fast path should produce final output"
);
assert!(
result_slow.final_output.is_some(),
"LLM path should produce final output"
);
let output_fast = result_fast.final_output.unwrap();
let output_slow = result_slow.final_output.unwrap();
let output_fast_str = serde_json::to_string(&output_fast)?;
let output_slow_str = serde_json::to_string(&output_slow)?;
assert!(
output_fast_str.contains("📝 Report"),
"Fast path output should contain report marker"
);
assert!(
output_slow_str.contains("📝 Report"),
"LLM path output should contain report marker"
);
println!("✅ Both paths produce valid outputs");
println!("✅ Fast path provides performance improvement");
println!("✅ Configuration toggle works correctly");
println!("\n🎉 E2E Test: PASSED");
println!("{}", "=".repeat(60));
println!("\n💡 Key Takeaways:");
println!(" • Fast path skips LLM calls when placeholders are resolved");
println!(" • Both paths produce functionally equivalent results");
println!(" • Performance improvement: {:.2}x", speedup);
println!(" • Can be toggled via OrchestratorConfig");
Ok(())
}