The AgentPlugin trait is the foundational interface for all plugins in the MoFA framework. It defines the complete plugin lifecycle from loading to execution and unloading.
Trait Definition
#[async_trait]
pub trait AgentPlugin: Send + Sync {
fn metadata(&self) -> &PluginMetadata;
fn plugin_id(&self) -> &str;
fn plugin_type(&self) -> PluginType;
fn state(&self) -> PluginState;
async fn load(&mut self, ctx: &PluginContext) -> PluginResult<()>;
async fn init_plugin(&mut self) -> PluginResult<()>;
async fn start(&mut self) -> PluginResult<()>;
async fn pause(&mut self) -> PluginResult<()>;
async fn resume(&mut self) -> PluginResult<()>;
async fn stop(&mut self) -> PluginResult<()>;
async fn unload(&mut self) -> PluginResult<()>;
async fn execute(&mut self, input: String) -> PluginResult<String>;
async fn health_check(&self) -> PluginResult<bool>;
fn stats(&self) -> HashMap<String, serde_json::Value>;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>;
}
Lifecycle Methods
Plugins follow a strict lifecycle with the following states:
load
Allocate resources and prepare the plugin for initialization.
Plugin execution context containing configuration and shared state
async fn load(&mut self, ctx: &PluginContext) -> PluginResult<()> {
// Allocate resources, read configuration
self.config = ctx.config.clone();
self.state = PluginState::Loaded;
Ok(())
}
init_plugin
Initialize plugin-specific configuration and state.
async fn init_plugin(&mut self) -> PluginResult<()> {
// Initialize internal state
// Call any setup methods
self.state = PluginState::Running;
Ok(())
}
start
Start the plugin and make it ready for execution.
async fn start(&mut self) -> PluginResult<()> {
self.state = PluginState::Running;
Ok(())
}
pause
Temporarily suspend plugin execution (optional).
async fn pause(&mut self) -> PluginResult<()> {
self.state = PluginState::Paused;
Ok(())
}
resume
Resume a paused plugin (optional).
async fn resume(&mut self) -> PluginResult<()> {
self.state = PluginState::Running;
Ok(())
}
stop
Stop plugin execution.
async fn stop(&mut self) -> PluginResult<()> {
self.state = PluginState::Paused;
Ok(())
}
unload
Release all resources and clean up.
async fn unload(&mut self) -> PluginResult<()> {
// Release resources
self.state = PluginState::Unloaded;
Ok(())
}
Execution Methods
execute
Execute the plugin’s core functionality.
Input data as a string (typically JSON)
Execution result as a string (typically JSON)
async fn execute(&mut self, input: String) -> PluginResult<String> {
// Parse input
let data: InputType = serde_json::from_str(&input)?;
// Execute plugin logic
let result = self.process(data).await?;
// Return result as JSON
Ok(serde_json::to_string(&result)?)
}
health_check
Perform a health check on the plugin.
async fn health_check(&self) -> PluginResult<bool> {
Ok(self.state() == PluginState::Running)
}
stats
Return plugin runtime statistics.
fn stats(&self) -> HashMap<String, serde_json::Value> {
let mut stats = HashMap::new();
stats.insert("calls_total".to_string(), json!(self.call_count));
stats.insert("state".to_string(), json!(format!("{:?}", self.state)));
stats
}
Return plugin metadata.
fn metadata(&self) -> &PluginMetadata {
&self.metadata
}
plugin_id
Convenience method to get the plugin ID.
fn plugin_id(&self) -> &str {
&self.metadata().id
}
plugin_type
Return the plugin type.
fn plugin_type(&self) -> PluginType {
self.metadata().plugin_type.clone()
}
Type Casting Methods
These methods enable downcasting to concrete plugin types:
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
Plugin States
pub enum PluginState {
Unloaded, // Not initialized
Loading, // Currently loading
Loaded, // Loaded and ready
Running, // Active and executing
Paused, // Temporarily suspended
Error(String), // Error state with message
}
Plugin Types
pub enum PluginType {
LLM, // LLM capability plugin
Tool, // Tool calling plugin
Storage, // Storage plugin
Memory, // Memory management plugin
VectorDB, // Vector database plugin
Communication, // Communication plugin
Monitor, // Monitor plugin
Skill, // Agent Skills plugin
Custom(String),// Custom plugin type
}
Example Implementation
use mofa_kernel::plugin::{AgentPlugin, PluginMetadata, PluginState, PluginType};
use std::any::Any;
struct MyPlugin {
metadata: PluginMetadata,
state: PluginState,
call_count: u64,
}
impl MyPlugin {
pub fn new() -> Self {
let metadata = PluginMetadata::new(
"my-plugin",
"My Custom Plugin",
PluginType::Custom("example".to_string()),
)
.with_description("An example plugin implementation");
Self {
metadata,
state: PluginState::Unloaded,
call_count: 0,
}
}
}
#[async_trait]
impl AgentPlugin for MyPlugin {
fn metadata(&self) -> &PluginMetadata {
&self.metadata
}
fn state(&self) -> PluginState {
self.state.clone()
}
async fn load(&mut self, ctx: &PluginContext) -> PluginResult<()> {
self.state = PluginState::Loaded;
Ok(())
}
async fn init_plugin(&mut self) -> PluginResult<()> {
self.state = PluginState::Running;
Ok(())
}
async fn start(&mut self) -> PluginResult<()> {
self.state = PluginState::Running;
Ok(())
}
async fn stop(&mut self) -> PluginResult<()> {
self.state = PluginState::Paused;
Ok(())
}
async fn unload(&mut self) -> PluginResult<()> {
self.state = PluginState::Unloaded;
Ok(())
}
async fn execute(&mut self, input: String) -> PluginResult<String> {
self.call_count += 1;
Ok(format!("Processed: {}", input))
}
fn stats(&self) -> HashMap<String, serde_json::Value> {
let mut stats = HashMap::new();
stats.insert("calls_total".to_string(), json!(self.call_count));
stats
}
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn into_any(self: Box<Self>) -> Box<dyn Any> { self }
}