1use serde::{Deserialize, Serialize};
2use std::any::Any;
3use std::collections::HashMap;
4use std::sync::Arc;
5use tokio::sync::RwLock;
6
7pub type PluginResult<T> = anyhow::Result<T>;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum ReloadStrategy {
17 Immediate,
19 Debounced(std::time::Duration),
21 Manual,
23 OnIdle,
25}
26
27impl Default for ReloadStrategy {
28 fn default() -> Self {
29 Self::Debounced(std::time::Duration::from_secs(1))
30 }
31}
32
33#[derive(Debug, Clone)]
35pub struct HotReloadConfig {
36 pub strategy: ReloadStrategy,
38 pub preserve_state: bool,
40 pub auto_rollback: bool,
42 pub max_reload_attempts: u32,
44 pub reload_cooldown: std::time::Duration,
46}
47
48impl Default for HotReloadConfig {
49 fn default() -> Self {
50 Self {
51 strategy: ReloadStrategy::default(),
52 preserve_state: true,
53 auto_rollback: true,
54 max_reload_attempts: 3,
55 reload_cooldown: std::time::Duration::from_secs(5),
56 }
57 }
58}
59
60impl HotReloadConfig {
61 pub fn new() -> Self {
63 Self::default()
64 }
65
66 pub fn with_strategy(mut self, strategy: ReloadStrategy) -> Self {
68 self.strategy = strategy;
69 self
70 }
71
72 pub fn with_preserve_state(mut self, preserve: bool) -> Self {
74 self.preserve_state = preserve;
75 self
76 }
77
78 pub fn with_auto_rollback(mut self, auto_rollback: bool) -> Self {
80 self.auto_rollback = auto_rollback;
81 self
82 }
83
84 pub fn with_max_attempts(mut self, max_attempts: u32) -> Self {
86 self.max_reload_attempts = max_attempts;
87 self
88 }
89
90 pub fn with_reload_cooldown(mut self, cooldown: std::time::Duration) -> Self {
92 self.reload_cooldown = cooldown;
93 self
94 }
95}
96
97#[derive(Debug, Clone)]
99pub enum ReloadEvent {
100 ReloadStarted {
102 plugin_id: String,
103 path: std::path::PathBuf,
104 },
105 ReloadCompleted {
107 plugin_id: String,
108 path: std::path::PathBuf,
109 success: bool,
110 duration: std::time::Duration,
111 },
112 ReloadFailed {
114 plugin_id: String,
115 path: std::path::PathBuf,
116 error: String,
117 attempt: u32,
118 },
119 RollbackTriggered { plugin_id: String, reason: String },
121 PluginDiscovered { path: std::path::PathBuf },
123 PluginRemoved {
125 plugin_id: String,
126 path: std::path::PathBuf,
127 },
128 StatePreserved { plugin_id: String },
130 StateRestored { plugin_id: String },
132}
133
134#[async_trait::async_trait]
136pub trait HotReloadable: Send + Sync {
137 async fn refresh(&self) -> PluginResult<()>;
139
140 async fn save_state(&self) -> PluginResult<()> {
142 Ok(())
143 }
144
145 async fn restore_state(&self) -> PluginResult<()> {
147 Ok(())
148 }
149}
150
151#[async_trait::async_trait]
153pub trait AgentPlugin: Send + Sync {
154 fn metadata(&self) -> &PluginMetadata;
156
157 fn plugin_id(&self) -> &str {
159 &self.metadata().id
160 }
161
162 fn plugin_type(&self) -> PluginType {
164 self.metadata().plugin_type.clone()
165 }
166
167 fn state(&self) -> PluginState;
169
170 async fn load(&mut self, ctx: &PluginContext) -> PluginResult<()>;
172
173 async fn init_plugin(&mut self) -> PluginResult<()>;
175
176 async fn start(&mut self) -> PluginResult<()>;
178
179 async fn pause(&mut self) -> PluginResult<()> {
181 Ok(())
182 }
183
184 async fn resume(&mut self) -> PluginResult<()> {
186 Ok(())
187 }
188
189 async fn stop(&mut self) -> PluginResult<()>;
191
192 async fn unload(&mut self) -> PluginResult<()>;
194
195 async fn execute(&mut self, input: String) -> PluginResult<String>;
197
198 async fn health_check(&self) -> PluginResult<bool> {
200 Ok(self.state() == PluginState::Running)
201 }
202
203 fn stats(&self) -> HashMap<String, serde_json::Value> {
205 HashMap::new()
206 }
207
208 fn as_any(&self) -> &dyn Any;
210
211 fn as_any_mut(&mut self) -> &mut dyn Any;
213
214 fn into_any(self: Box<Self>) -> Box<dyn Any>;
216}
217
218#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
224pub enum PluginType {
225 LLM,
227 Tool,
229 Storage,
231 Memory,
233 VectorDB,
235 Communication,
237 Monitor,
239 Skill,
241 Custom(String),
243}
244
245#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
247pub enum PluginState {
248 Unloaded,
250 Loading,
252 Loaded,
254 Running,
256 Paused,
258 Error(String),
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
264pub enum PluginPriority {
265 Low = 0,
266 #[default]
267 Normal = 50,
268 High = 100,
269 Critical = 200,
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct PluginMetadata {
279 pub id: String,
281 pub name: String,
283 pub version: String,
285 pub description: String,
287 pub plugin_type: PluginType,
289 pub priority: PluginPriority,
291 pub dependencies: Vec<String>,
293 pub capabilities: Vec<String>,
295 pub author: Option<String>,
297}
298
299impl PluginMetadata {
300 pub fn new(id: &str, name: &str, plugin_type: PluginType) -> Self {
301 Self {
302 id: id.to_string(),
303 name: name.to_string(),
304 version: "1.0.0".to_string(),
305 description: String::new(),
306 plugin_type,
307 priority: PluginPriority::Normal,
308 dependencies: Vec::new(),
309 capabilities: Vec::new(),
310 author: None,
311 }
312 }
313
314 pub fn with_version(mut self, version: &str) -> Self {
315 self.version = version.to_string();
316 self
317 }
318
319 pub fn with_description(mut self, desc: &str) -> Self {
320 self.description = desc.to_string();
321 self
322 }
323
324 pub fn with_priority(mut self, priority: PluginPriority) -> Self {
325 self.priority = priority;
326 self
327 }
328
329 pub fn with_dependency(mut self, dep_id: &str) -> Self {
330 self.dependencies.push(dep_id.to_string());
331 self
332 }
333
334 pub fn with_capability(mut self, cap: &str) -> Self {
335 self.capabilities.push(cap.to_string());
336 self
337 }
338}
339
340#[derive(Debug, Clone, Default, Serialize, Deserialize)]
346pub struct PluginConfig {
347 pub settings: HashMap<String, serde_json::Value>,
349 pub enabled: bool,
351 pub auto_start: bool,
353}
354
355impl PluginConfig {
356 pub fn new() -> Self {
357 Self {
358 settings: HashMap::new(),
359 enabled: true,
360 auto_start: true,
361 }
362 }
363
364 pub fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
365 self.settings
366 .get(key)
367 .and_then(|v| serde_json::from_value(v.clone()).ok())
368 }
369
370 pub fn set<T: Serialize>(&mut self, key: &str, value: T) {
371 if let Ok(v) = serde_json::to_value(value) {
372 self.settings.insert(key.to_string(), v);
373 }
374 }
375
376 pub fn get_string(&self, key: &str) -> Option<String> {
377 self.get(key)
378 }
379
380 pub fn get_bool(&self, key: &str) -> Option<bool> {
381 self.get(key)
382 }
383
384 pub fn get_i64(&self, key: &str) -> Option<i64> {
385 self.get(key)
386 }
387}
388
389#[derive(Debug, Default)]
395pub struct PluginContext {
396 pub agent_id: String,
398 shared_state: Arc<RwLock<HashMap<String, Box<dyn Any + Send + Sync>>>>,
400 pub config: PluginConfig,
402 event_tx: Option<tokio::sync::mpsc::Sender<PluginEvent>>,
404}
405
406impl PluginContext {
407 pub fn new(agent_id: &str) -> Self {
408 Self {
409 agent_id: agent_id.to_string(),
410 shared_state: Arc::new(RwLock::new(HashMap::new())),
411 config: PluginConfig::new(),
412 event_tx: None,
413 }
414 }
415
416 pub fn with_config(mut self, config: PluginConfig) -> Self {
417 self.config = config;
418 self
419 }
420
421 pub fn with_event_sender(mut self, tx: tokio::sync::mpsc::Sender<PluginEvent>) -> Self {
422 self.event_tx = Some(tx);
423 self
424 }
425
426 pub async fn get_state<T: Clone + Send + Sync + 'static>(&self, key: &str) -> Option<T> {
428 let state = self.shared_state.read().await;
429 state.get(key).and_then(|v| v.downcast_ref::<T>().cloned())
430 }
431
432 pub async fn set_state<T: Clone + Send + Sync + 'static>(&self, key: &str, value: T) {
434 let mut state = self.shared_state.write().await;
435 state.insert(key.to_string(), Box::new(value));
436 }
437
438 pub async fn emit_event(&self, event: PluginEvent) -> anyhow::Result<()> {
440 if let Some(ref tx) = self.event_tx {
441 tx.send(event)
442 .await
443 .map_err(|e| anyhow::anyhow!("Failed to send event: {}", e))?;
444 }
445 Ok(())
446 }
447}
448
449impl Clone for PluginContext {
450 fn clone(&self) -> Self {
451 Self {
452 agent_id: self.agent_id.clone(),
453 shared_state: self.shared_state.clone(),
454 config: self.config.clone(),
455 event_tx: self.event_tx.clone(),
456 }
457 }
458}
459
460#[derive(Debug, Clone)]
466pub enum PluginEvent {
467 PluginLoaded { plugin_id: String },
469 PluginUnloaded { plugin_id: String },
471 StateChanged {
473 plugin_id: String,
474 old_state: PluginState,
475 new_state: PluginState,
476 },
477 PluginError { plugin_id: String, error: String },
479 Custom {
481 plugin_id: String,
482 event_type: String,
483 data: Vec<u8>,
484 },
485}