use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PluginPolicy {
AlwaysOn,
OnDemand,
LazyPersistent,
WorkflowScoped,
Suspendable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PluginState {
Unloaded,
Loading,
Active,
Suspended,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginLifecycle {
pub name: String,
pub policy: PluginPolicy,
pub state: PluginState,
pub capabilities: Vec<Capability>,
pub last_used: Option<chrono::DateTime<chrono::Utc>>,
pub memory_mb: f64,
pub initialized: bool,
pub error: Option<String>,
}
impl PluginLifecycle {
pub fn always_on(name: &str, capabilities: Vec<Capability>) -> Self {
Self {
name: name.to_string(),
policy: PluginPolicy::AlwaysOn,
state: PluginState::Active,
capabilities,
last_used: Some(chrono::Utc::now()),
memory_mb: 0.0,
initialized: true,
error: None,
}
}
pub fn on_demand(name: &str, capabilities: Vec<Capability>, memory_mb: f64) -> Self {
Self {
name: name.to_string(),
policy: PluginPolicy::OnDemand,
state: PluginState::Unloaded,
capabilities,
last_used: None,
memory_mb,
initialized: false,
error: None,
}
}
pub fn can_unload(&self) -> bool {
self.policy != PluginPolicy::AlwaysOn && self.state == PluginState::Active
}
pub fn can_suspend(&self) -> bool {
matches!(
self.policy,
PluginPolicy::Suspendable | PluginPolicy::OnDemand
) && self.state == PluginState::Active
}
}
pub struct AlwaysOnPlugins;
impl AlwaysOnPlugins {
pub fn required() -> Vec<&'static str> {
vec![
"bootstrap", "judgment", "explainability", ]
}
pub fn required_capabilities() -> Vec<Capability> {
vec![Capability::PIIDetection, Capability::TamperEvidentAudit]
}
pub fn is_always_on(plugin_name: &str) -> bool {
Self::required().contains(&plugin_name)
}
pub fn is_required_capability(cap: Capability) -> bool {
Self::required_capabilities().contains(&cap)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum IntentCategory {
Conversation,
MLInference,
MLTraining,
WorkflowOrchestration,
ModelDeployment,
HardwareOptimization,
AdaptiveLearning,
KnowledgeGraph,
}
impl IntentCategory {
pub fn required_capabilities(&self) -> Vec<Capability> {
match self {
Self::Conversation => vec![],
Self::MLInference => vec![Capability::MLInference],
Self::MLTraining => vec![
Capability::MLTraining,
Capability::WorkflowEngine, ],
Self::WorkflowOrchestration => {
vec![Capability::WorkflowEngine, Capability::TaskScheduling]
}
Self::ModelDeployment => vec![
Capability::CascadeInference,
Capability::TensorFlowBackend, ],
Self::HardwareOptimization => vec![Capability::HardwareOptimization],
Self::AdaptiveLearning => vec![Capability::AdaptiveLearning, Capability::HumanFeedback],
Self::KnowledgeGraph => vec![Capability::SourceAttribution],
}
}
pub fn suggested_plugins(&self) -> Vec<&'static str> {
match self {
Self::Conversation => vec![],
Self::MLInference => vec!["ml", "local-llm"],
Self::MLTraining => vec!["ml", "pytorch", "workflow"],
Self::WorkflowOrchestration => vec!["workflow"],
Self::ModelDeployment => vec!["tensorflow", "production"],
Self::HardwareOptimization => vec!["hardware"],
Self::AdaptiveLearning => vec!["adaptive"],
Self::KnowledgeGraph => vec!["knowledge"],
}
}
}
pub struct IntentDetector;
impl IntentDetector {
pub fn detect(text: &str) -> Vec<IntentCategory> {
let text_lower = text.to_lowercase();
let mut intents = Vec::new();
if text_lower.contains("predict")
|| text_lower.contains("classify")
|| text_lower.contains("inference")
|| text_lower.contains("run model")
|| text_lower.contains("analyze with")
{
intents.push(IntentCategory::MLInference);
}
if text_lower.contains("train")
|| text_lower.contains("fine-tune")
|| text_lower.contains("fit model")
|| text_lower.contains("learning rate")
|| text_lower.contains("epoch")
{
intents.push(IntentCategory::MLTraining);
}
if text_lower.contains("workflow")
|| text_lower.contains("pipeline")
|| text_lower.contains("orchestrat")
|| text_lower.contains("schedule")
|| text_lower.contains("automat")
{
intents.push(IntentCategory::WorkflowOrchestration);
}
if text_lower.contains("deploy")
|| text_lower.contains("production")
|| text_lower.contains("serve model")
|| text_lower.contains("scale")
{
intents.push(IntentCategory::ModelDeployment);
}
if text_lower.contains("gpu")
|| text_lower.contains("hardware")
|| text_lower.contains("optimize")
|| text_lower.contains("performance")
{
intents.push(IntentCategory::HardwareOptimization);
}
if text_lower.contains("feedback")
|| text_lower.contains("improve")
|| text_lower.contains("learn from")
|| text_lower.contains("adapt")
{
intents.push(IntentCategory::AdaptiveLearning);
}
if intents.is_empty() {
intents.push(IntentCategory::Conversation);
}
intents
}
pub fn capabilities_for_intents(intents: &[IntentCategory]) -> HashSet<Capability> {
intents
.iter()
.flat_map(|i| i.required_capabilities())
.collect()
}
pub fn plugins_for_intents(intents: &[IntentCategory]) -> HashSet<&'static str> {
intents.iter().flat_map(|i| i.suggested_plugins()).collect()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Capability {
MLCore,
MLInference,
MLTraining,
MLCompression,
MLHyperparameterOptimization,
PyTorchBackend,
TensorFlowBackend,
TFLiteEdge,
DistributedTraining,
WorkflowEngine,
TaskScheduling,
PipelineOrchestration,
CascadeInference,
MemoryManagement,
ComplianceAutomation,
AdaptiveLearning,
HumanFeedback,
KnowledgeDistillation,
ReasoningChains,
SourceAttribution,
TamperEvidentAudit,
HardwareOptimization,
LocalVectorDB,
LocalLLMInference,
PIIDetection,
HIPAACompliance,
}
#[derive(Debug, Default)]
pub struct CapabilityRegistry {
capabilities: HashSet<Capability>,
plugin_capabilities: HashMap<String, Vec<Capability>>,
}
impl CapabilityRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, capability: Capability) {
self.capabilities.insert(capability);
}
pub fn register_from_plugin(&mut self, plugin_name: &str, capability: Capability) {
self.capabilities.insert(capability);
self.plugin_capabilities
.entry(plugin_name.to_string())
.or_default()
.push(capability);
}
pub fn has(&self, capability: Capability) -> bool {
self.capabilities.contains(&capability)
}
pub fn has_all(&self, capabilities: &[Capability]) -> bool {
capabilities.iter().all(|c| self.capabilities.contains(c))
}
pub fn has_any(&self, capabilities: &[Capability]) -> bool {
capabilities.iter().any(|c| self.capabilities.contains(c))
}
pub fn all(&self) -> &HashSet<Capability> {
&self.capabilities
}
pub fn get_plugin_capabilities(&self, plugin_name: &str) -> Option<&Vec<Capability>> {
self.plugin_capabilities.get(plugin_name)
}
pub fn unregister_plugin(&mut self, plugin_name: &str) {
if let Some(caps) = self.plugin_capabilities.remove(plugin_name) {
for cap in caps {
let still_provided = self.plugin_capabilities.values().any(|v| v.contains(&cap));
if !still_provided {
self.capabilities.remove(&cap);
}
}
}
}
}
#[async_trait]
pub trait MLCapable: Send + Sync {
async fn infer(
&self,
model_name: &str,
input: serde_json::Value,
) -> Result<serde_json::Value, String>;
async fn list_models(&self) -> Vec<String>;
async fn model_status(&self, model_name: &str) -> Option<ModelStatus>;
}
#[async_trait]
pub trait WorkflowCapable: Send + Sync {
async fn create_workflow(&self, config: WorkflowConfig) -> Result<String, String>;
async fn execute_workflow(&self, workflow_id: &str) -> Result<WorkflowResult, String>;
async fn workflow_status(&self, workflow_id: &str) -> Option<WorkflowStatus>;
}
#[async_trait]
pub trait HardwareCapable: Send + Sync {
async fn get_hardware_info(&self) -> HardwareInfo;
async fn get_recommendations(&self) -> Vec<OptimizationRecommendation>;
}
#[async_trait]
pub trait ExplainabilityCapable: Send + Sync {
async fn explain(&self, decision_id: &str) -> Option<Explanation>;
async fn record_for_audit(&self, context: AuditContext) -> Result<String, String>;
}
#[async_trait]
pub trait AdaptiveCapable: Send + Sync {
async fn add_example(&self, example: TrainingExample) -> Result<(), String>;
async fn maybe_finetune(&self) -> Option<FinetuneResult>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelStatus {
pub name: String,
pub state: String,
pub framework: String,
pub version: String,
pub metrics: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowConfig {
pub name: String,
pub tasks: Vec<TaskConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskConfig {
pub name: String,
pub task_type: String,
pub depends_on: Vec<String>,
pub params: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowResult {
pub workflow_id: String,
pub status: String,
pub task_results: HashMap<String, serde_json::Value>,
pub execution_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowStatus {
pub id: String,
pub status: String,
pub progress: f64,
pub current_task: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HardwareInfo {
pub cpu: CpuInfo,
pub memory: MemoryInfo,
pub gpu: Option<GpuInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuInfo {
pub cores: usize,
pub threads: usize,
pub architecture: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryInfo {
pub total_gb: f64,
pub available_gb: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GpuInfo {
pub name: String,
pub vram_gb: f64,
pub compute: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizationRecommendation {
pub recommendation_type: String,
pub description: String,
pub impact: String,
pub action: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Explanation {
pub decision_id: String,
pub reasoning_steps: Vec<String>,
pub confidence: f64,
pub sources: Vec<String>,
pub alternatives: Vec<AlternativeOption>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlternativeOption {
pub description: String,
pub rejection_reason: String,
pub hypothetical_confidence: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditContext {
pub decision_type: String,
pub input_hash: String,
pub output_hash: String,
pub agent_id: String,
pub metadata: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrainingExample {
pub input: String,
pub output: String,
pub quality: f64,
pub source: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FinetuneResult {
pub adapter_name: String,
pub examples_used: usize,
pub final_loss: f64,
pub validation_accuracy: Option<f64>,
}
pub struct IntegrationBridge {
registry: Arc<RwLock<CapabilityRegistry>>,
ml_providers: Arc<RwLock<Vec<Arc<dyn MLCapable>>>>,
workflow_providers: Arc<RwLock<Vec<Arc<dyn WorkflowCapable>>>>,
hardware_providers: Arc<RwLock<Vec<Arc<dyn HardwareCapable>>>>,
explainability_providers: Arc<RwLock<Vec<Arc<dyn ExplainabilityCapable>>>>,
adaptive_providers: Arc<RwLock<Vec<Arc<dyn AdaptiveCapable>>>>,
}
impl Default for IntegrationBridge {
fn default() -> Self {
Self::new()
}
}
impl IntegrationBridge {
pub fn new() -> Self {
Self {
registry: Arc::new(RwLock::new(CapabilityRegistry::new())),
ml_providers: Arc::new(RwLock::new(Vec::new())),
workflow_providers: Arc::new(RwLock::new(Vec::new())),
hardware_providers: Arc::new(RwLock::new(Vec::new())),
explainability_providers: Arc::new(RwLock::new(Vec::new())),
adaptive_providers: Arc::new(RwLock::new(Vec::new())),
}
}
pub fn registry(&self) -> Arc<RwLock<CapabilityRegistry>> {
self.registry.clone()
}
pub async fn register_ml_provider(&self, provider: Arc<dyn MLCapable>) {
self.ml_providers.write().await.push(provider);
self.registry.write().await.register(Capability::MLCore);
self.registry
.write()
.await
.register(Capability::MLInference);
}
pub async fn register_workflow_provider(&self, provider: Arc<dyn WorkflowCapable>) {
self.workflow_providers.write().await.push(provider);
self.registry
.write()
.await
.register(Capability::WorkflowEngine);
}
pub async fn register_hardware_provider(&self, provider: Arc<dyn HardwareCapable>) {
self.hardware_providers.write().await.push(provider);
self.registry
.write()
.await
.register(Capability::HardwareOptimization);
}
pub async fn register_explainability_provider(&self, provider: Arc<dyn ExplainabilityCapable>) {
self.explainability_providers.write().await.push(provider);
self.registry
.write()
.await
.register(Capability::ReasoningChains);
self.registry
.write()
.await
.register(Capability::TamperEvidentAudit);
}
pub async fn register_adaptive_provider(&self, provider: Arc<dyn AdaptiveCapable>) {
self.adaptive_providers.write().await.push(provider);
self.registry
.write()
.await
.register(Capability::AdaptiveLearning);
}
pub async fn try_infer(
&self,
model_name: &str,
input: serde_json::Value,
) -> Option<serde_json::Value> {
let providers = self.ml_providers.read().await;
if let Some(provider) = providers.first() {
provider.infer(model_name, input).await.ok()
} else {
tracing::debug!("ML inference not available - no ML provider registered");
None
}
}
pub async fn try_run_workflow(&self, config: WorkflowConfig) -> Option<WorkflowResult> {
let providers = self.workflow_providers.read().await;
if let Some(provider) = providers.first() {
if let Ok(workflow_id) = provider.create_workflow(config).await {
return provider.execute_workflow(&workflow_id).await.ok();
}
} else {
tracing::debug!("Workflow not available - no workflow provider registered");
}
None
}
pub async fn try_get_hardware_recommendations(&self) -> Vec<OptimizationRecommendation> {
let providers = self.hardware_providers.read().await;
if let Some(provider) = providers.first() {
provider.get_recommendations().await
} else {
tracing::debug!("Hardware optimization not available");
Vec::new()
}
}
pub async fn try_record_audit(&self, context: AuditContext) -> Option<String> {
let providers = self.explainability_providers.read().await;
if let Some(provider) = providers.first() {
provider.record_for_audit(context).await.ok()
} else {
tracing::debug!("Audit recording not available - no explainability provider");
None
}
}
pub async fn try_add_training_example(&self, example: TrainingExample) -> bool {
let providers = self.adaptive_providers.read().await;
if let Some(provider) = providers.first() {
provider.add_example(example).await.is_ok()
} else {
tracing::debug!("Adaptive learning not available");
false
}
}
pub async fn integration_summary(&self) -> IntegrationSummary {
let registry = self.registry.read().await;
let capabilities = registry.all().clone();
IntegrationSummary {
ml_available: registry.has(Capability::MLCore),
workflow_available: registry.has(Capability::WorkflowEngine),
hardware_optimization: registry.has(Capability::HardwareOptimization),
explainability_available: registry
.has_any(&[Capability::ReasoningChains, Capability::TamperEvidentAudit]),
adaptive_learning_available: registry.has(Capability::AdaptiveLearning),
total_capabilities: capabilities.len(),
capabilities: capabilities.into_iter().collect(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IntegrationSummary {
pub ml_available: bool,
pub workflow_available: bool,
pub hardware_optimization: bool,
pub explainability_available: bool,
pub adaptive_learning_available: bool,
pub total_capabilities: usize,
pub capabilities: Vec<Capability>,
}
pub struct DynamicPluginManager {
lifecycles: Arc<RwLock<HashMap<String, PluginLifecycle>>>,
active_by_policy: Arc<RwLock<HashMap<PluginPolicy, usize>>>,
memory_budget_mb: f64,
current_memory_mb: Arc<RwLock<f64>>,
bridge: Arc<IntegrationBridge>,
idle_timeout: Duration,
}
impl DynamicPluginManager {
pub fn new(bridge: Arc<IntegrationBridge>, memory_budget_mb: f64) -> Self {
Self {
lifecycles: Arc::new(RwLock::new(HashMap::new())),
active_by_policy: Arc::new(RwLock::new(HashMap::new())),
memory_budget_mb,
current_memory_mb: Arc::new(RwLock::new(0.0)),
bridge,
idle_timeout: Duration::from_secs(300), }
}
pub async fn register_always_on(&self, name: &str, capabilities: Vec<Capability>) {
let lifecycle = PluginLifecycle::always_on(name, capabilities.clone());
for cap in &capabilities {
self.bridge
.registry()
.write()
.await
.register_from_plugin(name, *cap);
}
self.lifecycles
.write()
.await
.insert(name.to_string(), lifecycle);
let mut counts = self.active_by_policy.write().await;
*counts.entry(PluginPolicy::AlwaysOn).or_insert(0) += 1;
tracing::info!(
"Registered always-on plugin: {} with {:?}",
name,
capabilities
);
}
pub async fn register_on_demand(
&self,
name: &str,
capabilities: Vec<Capability>,
memory_mb: f64,
) {
let lifecycle = PluginLifecycle::on_demand(name, capabilities.clone(), memory_mb);
self.lifecycles
.write()
.await
.insert(name.to_string(), lifecycle);
tracing::info!(
"Registered on-demand plugin: {} ({:.1}MB) with {:?}",
name,
memory_mb,
capabilities
);
}
pub async fn ensure_capabilities(
&self,
required: &[Capability],
) -> Result<Vec<String>, String> {
let mut loaded_plugins = Vec::new();
for cap in required {
if self.bridge.registry().read().await.has(*cap) {
continue;
}
let lifecycles = self.lifecycles.read().await;
let provider = lifecycles
.values()
.find(|l| l.capabilities.contains(cap) && l.state != PluginState::Active);
if let Some(lifecycle) = provider {
let plugin_name = lifecycle.name.clone();
drop(lifecycles);
self.load_plugin(&plugin_name).await?;
loaded_plugins.push(plugin_name);
}
}
Ok(loaded_plugins)
}
pub async fn load_plugin(&self, name: &str) -> Result<(), String> {
let (already_active, memory_needed) = {
let mut lifecycles = self.lifecycles.write().await;
let lifecycle = lifecycles
.get_mut(name)
.ok_or_else(|| format!("Plugin not registered: {}", name))?;
if lifecycle.state == PluginState::Active {
lifecycle.last_used = Some(chrono::Utc::now());
return Ok(());
}
(lifecycle.state == PluginState::Active, lifecycle.memory_mb)
};
if already_active {
return Ok(());
}
let current = *self.current_memory_mb.read().await;
if current + memory_needed > self.memory_budget_mb {
self.free_memory(memory_needed).await?;
}
let mut lifecycles = self.lifecycles.write().await;
let lifecycle = lifecycles.get_mut(name).unwrap();
lifecycle.state = PluginState::Loading;
let capabilities = lifecycle.capabilities.clone();
let memory = lifecycle.memory_mb;
let policy = lifecycle.policy;
lifecycle.state = PluginState::Active;
lifecycle.initialized = true;
lifecycle.last_used = Some(chrono::Utc::now());
drop(lifecycles);
for cap in &capabilities {
self.bridge
.registry()
.write()
.await
.register_from_plugin(name, *cap);
}
*self.current_memory_mb.write().await += memory;
let mut counts = self.active_by_policy.write().await;
*counts.entry(policy).or_insert(0) += 1;
tracing::info!("Loaded plugin: {} ({:.1}MB)", name, memory);
Ok(())
}
pub async fn unload_plugin(&self, name: &str) -> Result<(), String> {
let mut lifecycles = self.lifecycles.write().await;
let lifecycle = lifecycles
.get_mut(name)
.ok_or_else(|| format!("Plugin not registered: {}", name))?;
if lifecycle.policy == PluginPolicy::AlwaysOn {
return Err(format!("Cannot unload always-on plugin: {}", name));
}
if lifecycle.state != PluginState::Active {
return Ok(());
}
let memory = lifecycle.memory_mb;
lifecycle.state = PluginState::Unloaded;
self.bridge.registry().write().await.unregister_plugin(name);
*self.current_memory_mb.write().await -= memory;
let mut counts = self.active_by_policy.write().await;
if let Some(count) = counts.get_mut(&lifecycle.policy) {
*count = count.saturating_sub(1);
}
tracing::info!("Unloaded plugin: {} (freed {:.1}MB)", name, memory);
Ok(())
}
pub async fn suspend_plugin(&self, name: &str) -> Result<(), String> {
let mut lifecycles = self.lifecycles.write().await;
let lifecycle = lifecycles
.get_mut(name)
.ok_or_else(|| format!("Plugin not registered: {}", name))?;
if !lifecycle.can_suspend() {
return Err(format!("Cannot suspend plugin: {}", name));
}
let memory = lifecycle.memory_mb;
lifecycle.state = PluginState::Suspended;
*self.current_memory_mb.write().await -= memory;
tracing::info!("Suspended plugin: {} (freed {:.1}MB)", name, memory);
Ok(())
}
pub async fn resume_plugin(&self, name: &str) -> Result<(), String> {
let memory_needed = {
let lifecycles = self.lifecycles.read().await;
let lifecycle = lifecycles
.get(name)
.ok_or_else(|| format!("Plugin not registered: {}", name))?;
if lifecycle.state != PluginState::Suspended {
return Err(format!("Plugin is not suspended: {}", name));
}
lifecycle.memory_mb
};
let current = *self.current_memory_mb.read().await;
if current + memory_needed > self.memory_budget_mb {
self.free_memory(memory_needed).await?;
}
let mut lifecycles = self.lifecycles.write().await;
let lifecycle = lifecycles.get_mut(name).unwrap();
lifecycle.state = PluginState::Active;
lifecycle.last_used = Some(chrono::Utc::now());
*self.current_memory_mb.write().await += lifecycle.memory_mb;
tracing::info!("Resumed plugin: {}", name);
Ok(())
}
async fn free_memory(&self, needed_mb: f64) -> Result<(), String> {
let mut freed = 0.0;
let now = chrono::Utc::now();
let lifecycles = self.lifecycles.read().await;
let mut candidates: Vec<_> = lifecycles
.values()
.filter(|l| l.can_suspend() || l.can_unload())
.filter(|l| {
l.last_used
.map(|t| (now - t).num_seconds() > self.idle_timeout.as_secs() as i64)
.unwrap_or(true)
})
.map(|l| (l.name.clone(), l.memory_mb, l.policy))
.collect();
drop(lifecycles);
candidates.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
for (name, memory, policy) in candidates {
if freed >= needed_mb {
break;
}
if matches!(policy, PluginPolicy::Suspendable) {
if self.suspend_plugin(&name).await.is_ok() {
freed += memory;
}
} else if self.unload_plugin(&name).await.is_ok() {
freed += memory;
}
}
if freed >= needed_mb {
Ok(())
} else {
Err(format!(
"Could not free enough memory: needed {:.1}MB, freed {:.1}MB",
needed_mb, freed
))
}
}
pub async fn process_intent(&self, message: &str) -> ProcessedIntent {
let intents = IntentDetector::detect(message);
let required_caps = IntentDetector::capabilities_for_intents(&intents);
let suggested_plugins = IntentDetector::plugins_for_intents(&intents);
let mut all_caps: Vec<_> = required_caps.into_iter().collect();
for cap in AlwaysOnPlugins::required_capabilities() {
if !all_caps.contains(&cap) {
all_caps.push(cap);
}
}
let loaded = self
.ensure_capabilities(&all_caps)
.await
.unwrap_or_default();
ProcessedIntent {
detected_intents: intents,
required_capabilities: all_caps,
suggested_plugins: suggested_plugins.into_iter().map(String::from).collect(),
plugins_loaded: loaded,
}
}
pub async fn status(&self) -> PluginManagerStatus {
let lifecycles = self.lifecycles.read().await;
let always_on: Vec<_> = lifecycles
.values()
.filter(|l| l.policy == PluginPolicy::AlwaysOn)
.map(|l| l.name.clone())
.collect();
let active: Vec<_> = lifecycles
.values()
.filter(|l| l.state == PluginState::Active && l.policy != PluginPolicy::AlwaysOn)
.map(|l| l.name.clone())
.collect();
let suspended: Vec<_> = lifecycles
.values()
.filter(|l| l.state == PluginState::Suspended)
.map(|l| l.name.clone())
.collect();
let unloaded: Vec<_> = lifecycles
.values()
.filter(|l| l.state == PluginState::Unloaded)
.map(|l| l.name.clone())
.collect();
PluginManagerStatus {
always_on_plugins: always_on,
active_plugins: active,
suspended_plugins: suspended,
unloaded_plugins: unloaded,
memory_used_mb: *self.current_memory_mb.read().await,
memory_budget_mb: self.memory_budget_mb,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessedIntent {
pub detected_intents: Vec<IntentCategory>,
pub required_capabilities: Vec<Capability>,
pub suggested_plugins: Vec<String>,
pub plugins_loaded: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginManagerStatus {
pub always_on_plugins: Vec<String>,
pub active_plugins: Vec<String>,
pub suspended_plugins: Vec<String>,
pub unloaded_plugins: Vec<String>,
pub memory_used_mb: f64,
pub memory_budget_mb: f64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_capability_registry() {
let mut registry = CapabilityRegistry::new();
registry.register(Capability::MLCore);
registry.register(Capability::MLInference);
assert!(registry.has(Capability::MLCore));
assert!(registry.has(Capability::MLInference));
assert!(!registry.has(Capability::WorkflowEngine));
assert!(registry.has_all(&[Capability::MLCore, Capability::MLInference]));
assert!(!registry.has_all(&[Capability::MLCore, Capability::WorkflowEngine]));
assert!(registry.has_any(&[Capability::MLCore, Capability::WorkflowEngine]));
assert!(!registry.has_any(&[Capability::WorkflowEngine, Capability::PyTorchBackend]));
}
#[test]
fn test_plugin_capability_tracking() {
let mut registry = CapabilityRegistry::new();
registry.register_from_plugin("ml", Capability::MLCore);
registry.register_from_plugin("ml", Capability::MLInference);
registry.register_from_plugin("pytorch", Capability::PyTorchBackend);
assert_eq!(registry.get_plugin_capabilities("ml").unwrap().len(), 2);
assert_eq!(
registry.get_plugin_capabilities("pytorch").unwrap().len(),
1
);
registry.unregister_plugin("pytorch");
assert!(!registry.has(Capability::PyTorchBackend));
assert!(registry.has(Capability::MLCore)); }
#[tokio::test]
async fn test_integration_bridge() {
let bridge = IntegrationBridge::new();
let result = bridge.try_infer("test", serde_json::json!({})).await;
assert!(result.is_none());
let summary = bridge.integration_summary().await;
assert!(!summary.ml_available);
assert!(!summary.workflow_available);
assert_eq!(summary.total_capabilities, 0);
}
}