Skip to main content

codetether_agent/swarm/
mod.rs

1//! Swarm orchestration for parallel sub-agent execution
2//!
3//! Implements the SubAgent/SubTask paradigm for parallel task execution,
4//! similar to Kimi K2.5's Agent Swarm but generalized for the CodeTether ecosystem.
5//!
6//! Key concepts:
7//! - **Orchestrator**: Decomposes complex tasks into parallelizable subtasks
8//! - **SubAgent**: A dynamically instantiated agent for executing a subtask
9//! - **SubTask**: A unit of work that can be executed in parallel
10//! - **Critical Path**: Latency-oriented metric for parallel execution
11
12pub mod executor;
13pub mod orchestrator;
14pub mod subtask;
15
16pub use executor::SwarmExecutor;
17pub use orchestrator::Orchestrator;
18pub use subtask::{SubAgent, SubTask, SubTaskContext, SubTaskResult, SubTaskStatus};
19
20use async_trait::async_trait;
21use anyhow::Result;
22
23/// Actor trait for swarm participants
24/// 
25/// An Actor is an entity that can participate in the swarm by receiving
26/// and processing messages. This is the base trait for all swarm participants.
27#[async_trait]
28pub trait Actor: Send + Sync {
29    /// Get the unique identifier for this actor
30    fn actor_id(&self) -> &str;
31    
32    /// Get the actor's current status
33    fn actor_status(&self) -> ActorStatus;
34    
35    /// Initialize the actor for swarm participation
36    async fn initialize(&mut self) -> Result<()>;
37    
38    /// Shutdown the actor gracefully
39    async fn shutdown(&mut self) -> Result<()>;
40}
41
42/// Handler trait for processing messages in the swarm
43/// 
44/// A Handler can receive and process messages of a specific type.
45/// This enables actors to respond to different message types.
46#[async_trait]
47pub trait Handler<M>: Actor {
48    /// The response type for this handler
49    type Response: Send + Sync;
50    
51    /// Handle a message and return a response
52    async fn handle(&mut self, message: M) -> Result<Self::Response>;
53}
54
55/// Status of an actor in the swarm
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum ActorStatus {
58    /// Actor is initializing
59    Initializing,
60    /// Actor is ready to process messages
61    Ready,
62    /// Actor is currently processing
63    Busy,
64    /// Actor is paused
65    Paused,
66    /// Actor is shutting down
67    ShuttingDown,
68    /// Actor has stopped
69    Stopped,
70}
71
72impl std::fmt::Display for ActorStatus {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        match self {
75            ActorStatus::Initializing => write!(f, "initializing"),
76            ActorStatus::Ready => write!(f, "ready"),
77            ActorStatus::Busy => write!(f, "busy"),
78            ActorStatus::Paused => write!(f, "paused"),
79            ActorStatus::ShuttingDown => write!(f, "shutting_down"),
80            ActorStatus::Stopped => write!(f, "stopped"),
81        }
82    }
83}
84
85/// Message types for swarm coordination
86#[derive(Debug, Clone)]
87pub enum SwarmMessage {
88    /// Execute a task
89    ExecuteTask { task_id: String, instruction: String },
90    /// Report progress
91    Progress { task_id: String, progress: f32, message: String },
92    /// Task completed
93    TaskCompleted { task_id: String, result: String },
94    /// Task failed
95    TaskFailed { task_id: String, error: String },
96    /// Request tool execution
97    ToolRequest { tool_id: String, arguments: serde_json::Value },
98    /// Tool response
99    ToolResponse { tool_id: String, result: crate::tool::ToolResult },
100}
101
102use serde::{Deserialize, Serialize};
103
104/// Maximum number of concurrent sub-agents
105pub const MAX_SUBAGENTS: usize = 100;
106
107/// Maximum total tool calls across all sub-agents
108pub const MAX_TOOL_CALLS: usize = 1500;
109
110/// Swarm execution configuration
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct SwarmConfig {
113    /// Maximum number of concurrent sub-agents
114    pub max_subagents: usize,
115    
116    /// Maximum tool calls per sub-agent
117    pub max_steps_per_subagent: usize,
118    
119    /// Maximum total tool calls across all sub-agents
120    pub max_total_steps: usize,
121    
122    /// Timeout for individual sub-agent execution (seconds)
123    pub subagent_timeout_secs: u64,
124    
125    /// Whether to enable parallel execution
126    pub parallel_enabled: bool,
127    
128    /// Critical path optimization threshold
129    pub critical_path_threshold: usize,
130    
131    /// Model to use for sub-agents (provider/model format)
132    pub model: Option<String>,
133    
134    /// Max concurrent API requests (rate limiting)
135    pub max_concurrent_requests: usize,
136    
137    /// Delay between API calls in ms (rate limiting)
138    pub request_delay_ms: u64,
139}
140
141impl Default for SwarmConfig {
142    fn default() -> Self {
143        Self {
144            max_subagents: MAX_SUBAGENTS,
145            max_steps_per_subagent: 100,
146            max_total_steps: MAX_TOOL_CALLS,
147            subagent_timeout_secs: 600,  // 10 minutes for complex tasks
148            parallel_enabled: true,
149            critical_path_threshold: 10,
150            model: None,
151            max_concurrent_requests: 3,  // V1 tier allows 3 concurrent
152            request_delay_ms: 1000,      // V1 tier: 60 RPM, 3 concurrent = fast
153        }
154    }
155}
156
157/// Swarm execution statistics
158#[derive(Debug, Clone, Default, Serialize, Deserialize)]
159pub struct SwarmStats {
160    /// Total number of sub-agents spawned
161    pub subagents_spawned: usize,
162    
163    /// Number of sub-agents that completed successfully
164    pub subagents_completed: usize,
165    
166    /// Number of sub-agents that failed
167    pub subagents_failed: usize,
168    
169    /// Total tool calls across all sub-agents
170    pub total_tool_calls: usize,
171    
172    /// Critical path length (longest chain of dependent steps)
173    pub critical_path_length: usize,
174    
175    /// Wall-clock execution time (milliseconds)
176    pub execution_time_ms: u64,
177    
178    /// Estimated sequential time (milliseconds)
179    pub sequential_time_estimate_ms: u64,
180    
181    /// Parallelization speedup factor
182    pub speedup_factor: f64,
183    
184    /// Per-stage statistics
185    pub stages: Vec<StageStats>,
186}
187
188/// Statistics for a single execution stage
189#[derive(Debug, Clone, Default, Serialize, Deserialize)]
190pub struct StageStats {
191    /// Stage index
192    pub stage: usize,
193    
194    /// Number of sub-agents in this stage
195    pub subagent_count: usize,
196    
197    /// Maximum steps in this stage (critical path contribution)
198    pub max_steps: usize,
199    
200    /// Total steps across all sub-agents in this stage
201    pub total_steps: usize,
202    
203    /// Execution time for this stage (milliseconds)
204    pub execution_time_ms: u64,
205}
206
207impl SwarmStats {
208    /// Calculate the speedup factor
209    pub fn calculate_speedup(&mut self) {
210        if self.execution_time_ms > 0 {
211            self.speedup_factor = self.sequential_time_estimate_ms as f64 / self.execution_time_ms as f64;
212        }
213    }
214    
215    /// Calculate critical path from stages
216    pub fn calculate_critical_path(&mut self) {
217        self.critical_path_length = self.stages.iter().map(|s| s.max_steps).sum();
218    }
219}
220
221/// Decomposition strategy for breaking down tasks
222#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
223#[serde(rename_all = "snake_case")]
224pub enum DecompositionStrategy {
225    /// Let the AI decide how to decompose
226    Automatic,
227    
228    /// Decompose by domain/specialty
229    ByDomain,
230    
231    /// Decompose by data partition
232    ByData,
233    
234    /// Decompose by workflow stage
235    ByStage,
236    
237    /// Single agent (no decomposition)
238    None,
239}
240
241impl Default for DecompositionStrategy {
242    fn default() -> Self {
243        Self::Automatic
244    }
245}
246
247/// Result of swarm execution
248#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct SwarmResult {
250    /// Overall success status
251    pub success: bool,
252    
253    /// Aggregated result from all sub-agents
254    pub result: String,
255    
256    /// Individual sub-task results
257    pub subtask_results: Vec<SubTaskResult>,
258    
259    /// Execution statistics
260    pub stats: SwarmStats,
261    
262    /// Any artifacts produced
263    pub artifacts: Vec<SwarmArtifact>,
264    
265    /// Error message if failed
266    pub error: Option<String>,
267}
268
269/// An artifact produced by the swarm
270#[derive(Debug, Clone, Serialize, Deserialize)]
271pub struct SwarmArtifact {
272    /// Artifact type
273    pub artifact_type: String,
274    
275    /// Name/identifier
276    pub name: String,
277    
278    /// Content or path
279    pub content: String,
280    
281    /// Which sub-agent produced it
282    pub source_subagent: Option<String>,
283    
284    /// MIME type
285    pub mime_type: Option<String>,
286}