Skip to main content

The Performance vs Flexibility Dilemma

Traditional frameworks force you to choose:
  • Compiled languages (C++, Rust): Fast but inflexible, requires recompilation
  • Scripting languages (Python, JavaScript): Flexible but slow, runtime overhead
MoFA solves this with a dual-layer plugin architecture that delivers both:

Compile-time Plugins

Rust/WASM
  • Zero runtime overhead
  • Type safety
  • Native integration
  • 10-100x faster

Runtime Plugins

Rhai Scripts
  • Hot reload
  • No recompilation
  • User-defined logic
  • Instant deployment

Architecture Overview

Compile-time Plugins (Rust/WASM)

When to Use

Use compile-time plugins for:
  • Performance-Critical Paths: LLM inference, embedding generation
  • System Integration: Database connections, file I/O, network calls
  • Type Safety Required: Financial calculations, cryptography
  • Native Libraries: Bindings to C/C++ libraries

Creating a Compile-time Plugin

1

Implement the AgentPlugin trait

mofa-plugins/src/my_plugin.rs
use mofa_kernel::{
    AgentPlugin, PluginMetadata, PluginState, PluginType,
    PluginContext, PluginResult,
};
use async_trait::async_trait;

pub struct MyCompileTimePlugin {
    metadata: PluginMetadata,
    state: PluginState,
    // Your plugin fields
}

impl MyCompileTimePlugin {
    pub fn new() -> Self {
        let metadata = PluginMetadata::new(
            "my_plugin",
            "My Plugin",
            PluginType::Custom("analytics".to_string())
        )
        .with_description("High-performance analytics plugin")
        .with_capability("data_analysis");
        
        Self {
            metadata,
            state: PluginState::Unloaded,
        }
    }
}

#[async_trait]
impl AgentPlugin for MyCompileTimePlugin {
    fn metadata(&self) -> &PluginMetadata {
        &self.metadata
    }
    
    fn state(&self) -> PluginState {
        self.state.clone()
    }
    
    async fn load(&mut self, ctx: &PluginContext) -> PluginResult<()> {
        self.state = PluginState::Loading;
        // Initialize resources (DB connections, etc.)
        self.state = PluginState::Loaded;
        Ok(())
    }
    
    async fn init_plugin(&mut self) -> PluginResult<()> {
        // Plugin initialization logic
        Ok(())
    }
    
    async fn start(&mut self) -> PluginResult<()> {
        self.state = PluginState::Running;
        Ok(())
    }
    
    async fn stop(&mut self) -> PluginResult<()> {
        self.state = PluginState::Loaded;
        Ok(())
    }
    
    async fn unload(&mut self) -> PluginResult<()> {
        // Clean up resources
        self.state = PluginState::Unloaded;
        Ok(())
    }
    
    async fn execute(&mut self, input: String) -> PluginResult<String> {
        // Zero-cost execution
        Ok(format!("Processed: {}", input))
    }
    
    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 }
}
2

Register the plugin

use mofa_plugins::PluginManager;

let manager = PluginManager::new("my-agent");
let plugin = MyCompileTimePlugin::new();

manager.register(plugin).await?;
manager.load_all().await?;
manager.start_all().await?;
3

Use the plugin

let result = manager.execute("my_plugin", "input data".to_string()).await?;
println!("Result: {}", result);

WASM Plugins

For sandboxed security, use WASM plugins:
use mofa_plugins::wasm_runtime::{WasmRuntime, WasmPlugin};

let runtime = WasmRuntime::new()?;
let plugin = WasmPlugin::from_file("plugin.wasm")?;

runtime.register(plugin).await?;
let result = runtime.execute("plugin_id", input).await?;
Benefits of WASM:
  • Sandboxed execution: Cannot access host resources unless permitted
  • Memory safety: Cannot corrupt memory outside sandbox
  • Language agnostic: Write in Rust, C, AssemblyScript, etc.
  • Near-native performance: ~95% of native speed
WASM plugins have startup overhead (~1-10ms). Use for untrusted code or isolation requirements, not for hot paths.

Runtime Plugins (Rhai Scripts)

When to Use

Use runtime plugins for:
  • Business Rules: Discount logic, approval workflows
  • Configurable Workflows: User-defined data pipelines
  • A/B Testing: Experiment logic without redeployment
  • Dynamic Tools: Register new capabilities at runtime
  • Hot Fixes: Emergency updates without downtime

Rhai Script Engine

MoFA integrates the Rhai scripting language:
// Rhai script syntax (JavaScript-like)
fn calculate_discount(price, customer_tier) {
    if customer_tier == "gold" {
        return price * 0.8;  // 20% discount
    } else if customer_tier == "silver" {
        return price * 0.9;  // 10% discount
    } else {
        return price;        // No discount
    }
}

Creating a Runtime Plugin

scripts/order_processor.rhai
// Order processing logic
fn process_order(order) {
    let total = order.quantity * order.price;
    
    // Apply business rules
    if total > 1000.0 {
        // Large order - requires approval
        return #{
            status: "pending_approval",
            reason: "High value order",
            next_agent: "manager"
        };
    }
    
    // Auto-approve
    return #{
        status: "approved",
        order_id: order.id,
        total: total
    };
}

// Entry point
process_order

Dynamic Tool Registration

Register tools at runtime using Rhai:
tools/weather.rhai
// Tool metadata
let tool = #{
    name: "get_weather",
    description: "Get current weather for a city",
    parameters: #{
        type: "object",
        properties: #{
            city: #{
                type: "string",
                description: "City name"
            }
        },
        required: ["city"]
    }
};

// Tool implementation
fn execute(args) {
    let city = args.city;
    
    // In production, call real API
    // For demo, return mock data
    return #{
        city: city,
        temperature: 72,
        condition: "Sunny",
        humidity: 45
    };
}

// Export tool
#{ tool: tool, execute: execute }
Register the tool:
use mofa_plugins::rhai_runtime::RhaiToolRegistry;

let registry = RhaiToolRegistry::new();
registry.load_from_file("tools/weather.rhai").await?;

// Now LLM can call this tool
let tools = registry.get_all_tools();

Rule Engine

Build decision engines with Rhai:
rules/content_moderation.rhai
let rules = [
    #{
        name: "spam_filter",
        priority: "critical",
        condition: |msg| msg.contains("BUY NOW") || msg.contains("CLICK HERE"),
        action: |msg| #{ decision: "block", reason: "spam" }
    },
    #{
        name: "profanity_filter",
        priority: "high",
        condition: |msg| has_profanity(msg),
        action: |msg| #{ decision: "flag", reason: "profanity" }
    },
    #{
        name: "allow_default",
        priority: "low",
        condition: |msg| true,
        action: |msg| #{ decision: "allow" }
    }
];

fn evaluate(message) {
    // Check rules by priority
    for rule in rules {
        if (rule.condition)(message) {
            return (rule.action)(message);
        }
    }
}

Performance Comparison

OperationCompile-time PluginRuntime PluginPure Python
JSON Parsing10 μs50 μs500 μs
Regex Match1 μs5 μs20 μs
LLM Inference100 msN/A120 ms
Rule Evaluation0.1 μs5 μs10 μs
HTTP Request5 msN/A10 ms
Use compile-time plugins for operations under 1ms. Use runtime plugins for business logic that changes frequently.

Security Considerations

Rust Plugins:
  • ✅ Memory safe (no buffer overflows)
  • ✅ Type safe (compile-time checks)
  • ⚠️ Full system access (must trust code)
WASM Plugins:
  • ✅ Sandboxed execution
  • ✅ Capability-based security
  • ✅ Memory isolation
  • ✅ Can run untrusted code
Rhai Script Sandbox:
use mofa_plugins::rhai_runtime::RhaiConfig;

let config = RhaiConfig::default()
    .max_operations(10_000)           // Limit CPU
    .max_call_stack_depth(32)         // Prevent stack overflow
    .max_string_size(1_000_000)       // Limit memory
    .max_array_size(10_000)
    .max_map_size(10_000);
Built-in Protections:
  • ✅ No file system access (unless explicitly allowed)
  • ✅ No network access
  • ✅ No process spawning
  • ✅ Operation limits prevent DoS

Interoperability Between Layers

Compile-time and runtime plugins can work together:
// Compile-time plugin provides capability
pub struct DatabasePlugin {
    pool: sqlx::PgPool,
}

#[async_trait]
impl AgentPlugin for DatabasePlugin {
    async fn execute(&mut self, query: String) -> PluginResult<String> {
        // Execute SQL with native performance
        let result = sqlx::query(&query)
            .fetch_all(&self.pool)
            .await?;
        Ok(serde_json::to_string(&result)?)
    }
}

// Runtime plugin uses it
// scripts/data_analyzer.rhai
fn analyze_sales() {
    // Call compile-time plugin
    let data = call_plugin("database", "SELECT * FROM sales WHERE date > '2024-01-01'");
    let parsed = parse_json(data);
    
    // Process with Rhai (business logic)
    let total = 0.0;
    for row in parsed {
        total += row.amount;
    }
    
    return #{ total_sales: total };
}

Hot Reload Configuration

use mofa_plugins::hot_reload::{
    HotReloadManager, HotReloadConfig, ReloadStrategy
};

let config = HotReloadConfig::new()
    .with_strategy(ReloadStrategy::Debounced(
        std::time::Duration::from_secs(1)
    ))
    .with_preserve_state(true)      // Keep plugin state
    .with_auto_rollback(true)       // Rollback on failure
    .with_max_attempts(3);          // Retry limit

let manager = HotReloadManager::new("./plugins", config);
manager.start().await?;

// Listen for reload events
let mut events = manager.events();
while let Some(event) = events.recv().await {
    match event {
        ReloadEvent::ReloadCompleted { plugin_id, .. } => {
            println!("✅ Reloaded: {}", plugin_id);
        }
        ReloadEvent::ReloadFailed { plugin_id, error, .. } => {
            eprintln!("❌ Failed to reload {}: {}", plugin_id, error);
        }
        _ => {}
    }
}

Best Practices

Performance

  • Use compile-time plugins for <1ms operations
  • Use runtime plugins for >10ms business logic
  • Cache compiled Rhai scripts
  • Profile before optimizing

Security

  • Use WASM for untrusted code
  • Set resource limits on Rhai scripts
  • Validate all inputs
  • Audit plugin permissions

Maintainability

  • Keep plugins focused (single responsibility)
  • Version plugin APIs
  • Document hot reload behavior
  • Test both layers independently

Deployment

  • Compile-time: Ship in binary
  • Runtime: Deploy as files
  • Use hot reload in staging first
  • Monitor reload success rates

Example: Real-World Usage

// High-performance LLM inference (compile-time)
let llm_plugin = LLMPlugin::new("openai");
manager.register(llm_plugin).await?;

// Flexible business rules (runtime)
let rules_plugin = RhaiPlugin::from_file("rules/approval.rhai")?;
manager.register(rules_plugin).await?;

// Workflow combines both
async fn process_request(request: &str) -> Result<String> {
    // Fast LLM call (compile-time plugin)
    let analysis = manager.execute("llm", request.to_string()).await?;
    
    // Business logic (runtime plugin, hot-reloadable)
    let decision = manager.execute("rules", analysis).await?;
    
    Ok(decision)
}

Next Steps

Agent Coordination

Learn multi-agent collaboration patterns

Workflow Engine

Build stateful workflows with plugins

Build docs developers (and LLMs) love