1use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16use std::sync::Arc;
17use std::time::{SystemTime, UNIX_EPOCH};
18use tokio::sync::{mpsc, RwLock};
19use uuid::Uuid;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum Topology {
24 Hierarchical,
26 Mesh,
28 Hybrid,
30}
31
32impl Topology {
33 pub fn as_str(&self) -> &'static str {
34 match self {
35 Topology::Hierarchical => "hierarchical",
36 Topology::Mesh => "mesh",
37 Topology::Hybrid => "hybrid",
38 }
39 }
40}
41
42#[derive(Debug, Clone)]
44pub struct SwarmConfig {
45 pub max_agents: usize,
47 pub context_window: usize,
49 pub topology: Topology,
51}
52
53impl Default for SwarmConfig {
54 fn default() -> Self {
55 Self {
56 max_agents: 100,
57 context_window: 256_000,
58 topology: Topology::Mesh,
59 }
60 }
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct TaskMessage {
66 pub id: String,
68 #[serde(rename = "type")]
70 pub msg_type: String,
71 pub payload: serde_json::Value,
73 #[serde(rename = "from")]
75 pub from_agent: String,
76 #[serde(rename = "to")]
78 pub to_agent: String,
79 pub timestamp: u64,
81}
82
83impl TaskMessage {
84 pub fn new(
86 msg_type: impl Into<String>,
87 payload: serde_json::Value,
88 from_agent: impl Into<String>,
89 ) -> Self {
90 let now = SystemTime::now()
91 .duration_since(UNIX_EPOCH)
92 .unwrap()
93 .as_secs();
94
95 Self {
96 id: Uuid::new_v4().to_string(),
97 msg_type: msg_type.into(),
98 payload,
99 from_agent: from_agent.into(),
100 to_agent: String::new(),
101 timestamp: now,
102 }
103 }
104
105 pub fn to(mut self, agent: impl Into<String>) -> Self {
107 self.to_agent = agent.into();
108 self
109 }
110}
111
112#[derive(Debug, Clone)]
114pub struct Agent {
115 pub id: String,
117 pub capabilities: Vec<String>,
119 pub context_used: Arc<RwLock<usize>>,
121}
122
123impl Agent {
124 pub fn new(id: impl Into<String>, capabilities: Vec<String>) -> Self {
126 Self {
127 id: id.into(),
128 capabilities,
129 context_used: Arc::new(RwLock::new(0)),
130 }
131 }
132
133 pub fn dispatch_message(&self, msg_type: &str, payload: serde_json::Value) -> TaskMessage {
135 TaskMessage::new(msg_type, payload, &self.id)
136 }
137}
138
139pub struct AgentSwarm {
141 config: SwarmConfig,
143 agents: Arc<RwLock<HashMap<String, Agent>>>,
145 tokens_used: Arc<RwLock<usize>>,
147 message_tx: mpsc::Sender<TaskMessage>,
149}
150
151impl AgentSwarm {
152 pub fn new(config: SwarmConfig) -> Self {
154 let (message_tx, mut message_rx) = mpsc::channel::<TaskMessage>(1000);
155 let agents = Arc::new(RwLock::new(HashMap::new()));
156 let tokens_used = Arc::new(RwLock::new(0));
157
158 let agents_clone = Arc::clone(&agents);
160 let tokens_clone = Arc::clone(&tokens_used);
161 let context_limit = config.context_window;
162
163 tokio::spawn(async move {
164 while let Some(msg) = message_rx.recv().await {
165 let agents = agents_clone.read().await;
166 if agents.contains_key(&msg.to_agent) {
167 let mut tokens = tokens_clone.write().await;
169 let estimated = Self::estimate_tokens(&msg);
170 *tokens += estimated;
171
172 if *tokens > context_limit {
173 log::warn!(
174 "[AgentSwarm] Context window at {}/{} tokens",
175 *tokens,
176 context_limit
177 );
178 }
179 }
180 }
181 });
182
183 log::info!(
184 "[AgentSwarm] Initialized swarm with {} max agents, {} context window",
185 config.max_agents,
186 config.context_window
187 );
188
189 Self {
190 config,
191 agents,
192 tokens_used,
193 message_tx,
194 }
195 }
196
197 pub async fn spawn_agent(&self, id: impl Into<String>, capabilities: Vec<String>) -> Option<Agent> {
199 let id = id.into();
200 let mut agents = self.agents.write().await;
201
202 if agents.len() >= self.config.max_agents {
203 log::warn!(
204 "[AgentSwarm] Max agents ({}) reached, cannot spawn {}",
205 self.config.max_agents,
206 id
207 );
208 return None;
209 }
210
211 let agent = Agent::new(&id, capabilities);
212 log::info!("[AgentSwarm] Spawned agent {} with capabilities: {:?}", id, agent.capabilities);
213 agents.insert(id.clone(), agent.clone());
214
215 Some(agent)
216 }
217
218 pub async fn broadcast_message(
220 &self,
221 from_agent: &str,
222 msg_type: &str,
223 payload: serde_json::Value,
224 ) -> usize {
225 let agents = self.agents.read().await;
226 let mut count = 0;
227
228 for (id, _) in agents.iter() {
229 if id != from_agent {
230 let msg = TaskMessage::new(msg_type, payload.clone(), from_agent).to(id);
231 let _ = self.message_tx.send(msg).await;
232 count += 1;
233 }
234 }
235
236 log::info!("[AgentSwarm] Broadcasted {} message to {} agents", msg_type, count);
237 count
238 }
239
240 pub async fn get_agent(&self, id: &str) -> Option<Agent> {
242 let agents = self.agents.read().await;
243 agents.get(id).cloned()
244 }
245
246 fn estimate_tokens(msg: &TaskMessage) -> usize {
248 serde_json::to_string(msg)
249 .map(|s| s.len() / 4)
250 .unwrap_or(100)
251 }
252
253 pub async fn get_stats(&self) -> SwarmStats {
255 let agents = self.agents.read().await;
256 let tokens = *self.tokens_used.read().await;
257
258 SwarmStats {
259 agent_count: agents.len(),
260 max_agents: self.config.max_agents,
261 tokens_used: tokens,
262 context_window: self.config.context_window,
263 topology: self.config.topology.as_str().to_string(),
264 utilization_percent: (agents.len() as f64 / self.config.max_agents as f64) * 100.0,
265 }
266 }
267
268 pub async fn shutdown(&self) {
270 log::info!("[AgentSwarm] Shutting down swarm...");
271 let mut agents = self.agents.write().await;
272 agents.clear();
273 log::info!("[AgentSwarm] Shutdown complete");
274 }
275}
276
277#[derive(Debug, Clone, Serialize)]
279pub struct SwarmStats {
280 pub agent_count: usize,
281 pub max_agents: usize,
282 pub tokens_used: usize,
283 pub context_window: usize,
284 pub topology: String,
285 pub utilization_percent: f64,
286}
287
288#[tokio::main]
289async fn main() -> Result<(), Box<dyn std::error::Error>> {
290 println!("=== AgentSwarm Rust Demo ===\n");
291
292 let config = SwarmConfig {
294 max_agents: 100,
295 context_window: 256_000, topology: Topology::Mesh,
297 };
298
299 let swarm = AgentSwarm::new(config);
300
301 let coordinator = swarm
303 .spawn_agent(
304 "coordinator",
305 vec!["routing".to_string(), "scheduling".to_string()],
306 )
307 .await;
308
309 let processor = swarm
310 .spawn_agent(
311 "processor",
312 vec!["data-analysis".to_string(), "transform".to_string()],
313 )
314 .await;
315
316 let _analyzer = swarm
317 .spawn_agent(
318 "analyzer",
319 vec!["pattern-detection".to_string(), "reporting".to_string()],
320 )
321 .await;
322
323 if let Some(agent) = &coordinator {
325 let msg = agent.dispatch_message(
326 "coordinate",
327 serde_json::json!({"task": "initialize-pipeline"}),
328 );
329 let _ = swarm.message_tx.send(msg.to("coordinator")).await;
330 }
331
332 if let Some(agent) = &processor {
333 let msg = agent.dispatch_message("process", serde_json::json!({"data": "sample-input"}));
334 let _ = swarm.message_tx.send(msg.to("processor")).await;
335 }
336
337 swarm
339 .broadcast_message(
340 "system",
341 "swarm-update",
342 serde_json::json!({"status": "operational", "agents": 3}),
343 )
344 .await;
345
346 let stats = swarm.get_stats().await;
348 println!("\nSwarm Stats: {}", serde_json::to_string_pretty(&stats)?);
349
350 if let Some(agent) = swarm.get_agent("processor").await {
352 println!(
353 "\nRetrieved agent: {} with capabilities: {:?}",
354 agent.id, agent.capabilities
355 );
356 }
357
358 swarm.shutdown().await;
360
361 println!("\n=== Demo Complete ===");
362 println!("Inspired by: https://kimik25.com");
363 println!("Kimi K2.5 - 1T-parameter native multimodal model with agent swarm capabilities");
364
365 Ok(())
366}