|
| 1 | +//! Query-Lane Tool Parallelization Test |
| 2 | +//! |
| 3 | +//! Demonstrates A3S Code's Query-lane tool parallelization with slow I/O operations. |
| 4 | +//! Parallelization is OPT-IN (default: serial execution). Users control when and how |
| 5 | +//! to parallelize via SessionQueueConfig. |
| 6 | +//! |
| 7 | +//! This test uses web_fetch to demonstrate real performance benefits, as network I/O |
| 8 | +//! is significantly slower than local file operations. |
| 9 | +//! |
| 10 | +//! Run with: cargo run --example test_internal_parallel |
| 11 | +
|
| 12 | +use a3s_code_core::{Agent, SessionOptions, SessionQueueConfig}; |
| 13 | +use a3s_code_core::permissions::PermissionPolicy; |
| 14 | +use a3s_code_core::queue::ParallelizationStrategy; |
| 15 | +use anyhow::Result; |
| 16 | +use std::path::PathBuf; |
| 17 | +use std::sync::Arc; |
| 18 | +use std::time::Instant; |
| 19 | + |
| 20 | +fn find_config() -> Result<PathBuf> { |
| 21 | + let home_config = dirs::home_dir() |
| 22 | + .map(|h| h.join(".a3s/config.hcl")) |
| 23 | + .filter(|p| p.exists()); |
| 24 | + |
| 25 | + if let Some(config) = home_config { |
| 26 | + return Ok(config); |
| 27 | + } |
| 28 | + |
| 29 | + let project_config = PathBuf::from(env!("CARGO_MANIFEST_DIR")) |
| 30 | + .parent() |
| 31 | + .and_then(|p| p.parent()) |
| 32 | + .and_then(|p| p.parent()) |
| 33 | + .map(|p| p.join(".a3s/config.hcl")) |
| 34 | + .filter(|p| p.exists()); |
| 35 | + |
| 36 | + project_config.ok_or_else(|| anyhow::anyhow!("Config file not found")) |
| 37 | +} |
| 38 | + |
| 39 | +#[tokio::main] |
| 40 | +async fn main() -> Result<()> { |
| 41 | + tracing_subscriber::fmt() |
| 42 | + .with_env_filter("info,a3s_code_core=info") |
| 43 | + .init(); |
| 44 | + |
| 45 | + println!("🚀 A3S Code - Query-Lane Tool Parallelization Test\n"); |
| 46 | + println!("{}", "=".repeat(80)); |
| 47 | + |
| 48 | + let config_path = find_config()?; |
| 49 | + println!("📄 Using config: {}", config_path.display()); |
| 50 | + println!("{}", "=".repeat(80)); |
| 51 | + println!(); |
| 52 | + |
| 53 | + let agent = Agent::new(config_path.to_str().unwrap()).await?; |
| 54 | + |
| 55 | + println!("📌 Test Scenario: Fetch 10 web pages"); |
| 56 | + println!(" This demonstrates real performance benefits with slow I/O operations.\n"); |
| 57 | + |
| 58 | + // Test 1: Default behavior (serial execution, no parallelization) |
| 59 | + test_default_serial(&agent).await?; |
| 60 | + |
| 61 | + // Test 2: Enabled parallelization with custom strategy |
| 62 | + test_enabled_parallel(&agent).await?; |
| 63 | + |
| 64 | + println!("\n{}", "=".repeat(80)); |
| 65 | + println!("✅ All parallelization tests completed!"); |
| 66 | + println!("{}", "=".repeat(80)); |
| 67 | + |
| 68 | + Ok(()) |
| 69 | +} |
| 70 | + |
| 71 | +/// Test 1: Default behavior - serial execution (parallelization disabled by default) |
| 72 | +async fn test_default_serial(agent: &Agent) -> Result<()> { |
| 73 | + println!("\n📦 Test 1: Default Behavior (Serial Execution)"); |
| 74 | + println!("{}", "-".repeat(80)); |
| 75 | + println!("Task: Fetch 10 web pages with default configuration\n"); |
| 76 | + |
| 77 | + // Default: enable_parallelization = false (serial execution) |
| 78 | + // Use permissive policy so web_fetch doesn't require HITL confirmation |
| 79 | + let mut opts = SessionOptions::default(); |
| 80 | + opts.permission_checker = Some(Arc::new(PermissionPolicy::permissive())); |
| 81 | + let session = agent.session(".", Some(opts))?; |
| 82 | + |
| 83 | + let start = Instant::now(); |
| 84 | + |
| 85 | + // Construct a task that will trigger >= 8 tool calls in a single LLM turn |
| 86 | + let result = session |
| 87 | + .send( |
| 88 | + "Fetch the following web pages and extract their titles:\n\ |
| 89 | + 1. https://www.rust-lang.org/\n\ |
| 90 | + 2. https://tokio.rs/\n\ |
| 91 | + 3. https://docs.rs/\n\ |
| 92 | + 4. https://crates.io/\n\ |
| 93 | + 5. https://github.com/rust-lang/rust\n\ |
| 94 | + 6. https://blog.rust-lang.org/\n\ |
| 95 | + 7. https://www.rust-lang.org/learn\n\ |
| 96 | + 8. https://www.rust-lang.org/tools\n\ |
| 97 | + 9. https://www.rust-lang.org/governance\n\ |
| 98 | + 10. https://www.rust-lang.org/community\n\ |
| 99 | + \n\ |
| 100 | + Fetch all pages at once using web_fetch tool, don't do them one by one.", |
| 101 | + None, |
| 102 | + ) |
| 103 | + .await?; |
| 104 | + |
| 105 | + let duration = start.elapsed(); |
| 106 | + |
| 107 | + println!("✓ Completed in: {:.2}s", duration.as_secs_f64()); |
| 108 | + println!("✓ Result length: {} chars", result.text.len()); |
| 109 | + println!("✓ Tool calls: {}", result.tool_calls_count); |
| 110 | + println!("\n💡 Default: enable_parallelization = false (serial execution)"); |
| 111 | + println!(" Expected: ~10 * avg_fetch_time (network latency adds up)\n"); |
| 112 | + |
| 113 | + Ok(()) |
| 114 | +} |
| 115 | + |
| 116 | +/// Test 2: Enabled parallelization - tools execute in parallel |
| 117 | +async fn test_enabled_parallel(agent: &Agent) -> Result<()> { |
| 118 | + println!("\n⚡ Test 2: Enabled Parallelization (Parallel Execution)"); |
| 119 | + println!("{}", "-".repeat(80)); |
| 120 | + println!("Task: Fetch 10 web pages in parallel via opt-in configuration\n"); |
| 121 | + |
| 122 | + // Create SessionQueueConfig with parallelization ENABLED |
| 123 | + let mut queue_config = SessionQueueConfig::default(); |
| 124 | + queue_config.enable_parallelization = true; // OPT-IN: explicitly enable |
| 125 | + queue_config.query_max_concurrency = 10; // Allow 10 concurrent web fetches |
| 126 | + |
| 127 | + // Custom strategy: lower threshold, only allow web operations |
| 128 | + let mut strategy = ParallelizationStrategy::default(); |
| 129 | + strategy.min_tool_count = 3; // Lower threshold: 3 tools trigger parallelization |
| 130 | + strategy.allowed_tools = vec![ |
| 131 | + "web_fetch".to_string(), |
| 132 | + "web_search".to_string(), |
| 133 | + ]; |
| 134 | + |
| 135 | + queue_config.parallelization_strategy = Some(strategy); |
| 136 | + |
| 137 | + println!("✓ SessionQueueConfig created"); |
| 138 | + println!(" enable_parallelization: true (OPT-IN)"); |
| 139 | + println!(" Query lane max concurrency: 10"); |
| 140 | + println!(" Custom strategy:"); |
| 141 | + println!(" - min_tool_count: 3 (lower threshold)"); |
| 142 | + println!(" - allowed_tools: [web_fetch, web_search]"); |
| 143 | + println!(" - blocked_tools: [bash, write, edit, patch]\n"); |
| 144 | + |
| 145 | + // Create session with queue config + permissive policy |
| 146 | + let mut opts = SessionOptions::default().with_queue_config(queue_config); |
| 147 | + opts.permission_checker = Some(Arc::new(PermissionPolicy::permissive())); |
| 148 | + let session = agent.session(".", Some(opts))?; |
| 149 | + |
| 150 | + let start = Instant::now(); |
| 151 | + |
| 152 | + // Same task as Test 1 - but now with parallelization enabled |
| 153 | + let result = session |
| 154 | + .send( |
| 155 | + "Fetch the following web pages and extract their titles:\n\ |
| 156 | + 1. https://www.rust-lang.org/\n\ |
| 157 | + 2. https://tokio.rs/\n\ |
| 158 | + 3. https://docs.rs/\n\ |
| 159 | + 4. https://crates.io/\n\ |
| 160 | + 5. https://github.com/rust-lang/rust\n\ |
| 161 | + 6. https://blog.rust-lang.org/\n\ |
| 162 | + 7. https://www.rust-lang.org/learn\n\ |
| 163 | + 8. https://www.rust-lang.org/tools\n\ |
| 164 | + 9. https://www.rust-lang.org/governance\n\ |
| 165 | + 10. https://www.rust-lang.org/community\n\ |
| 166 | + \n\ |
| 167 | + Fetch all pages at once using web_fetch tool, don't do them one by one.", |
| 168 | + None, |
| 169 | + ) |
| 170 | + .await?; |
| 171 | + |
| 172 | + let duration = start.elapsed(); |
| 173 | + |
| 174 | + println!("\n✓ Completed in: {:.2}s", duration.as_secs_f64()); |
| 175 | + println!("✓ Result length: {} chars", result.text.len()); |
| 176 | + println!("✓ Tool calls: {}", result.tool_calls_count); |
| 177 | + println!("\n💡 Parallelization enabled: web_fetch calls execute in parallel"); |
| 178 | + println!(" Expected: ~max(fetch_times) instead of sum(fetch_times)"); |
| 179 | + println!(" Speedup: 3-8x for network I/O operations\n"); |
| 180 | + |
| 181 | + Ok(()) |
| 182 | +} |
0 commit comments