use crate::memory_neuron_array::MemoryNeuronDetail;
use crate::memory_stats_cache::MemoryStatsCache;
use crate::service::{
MemoryCorticalAreaRuntimeInfo, PlasticityCommand, PlasticityConfig, PlasticityService,
};
use std::sync::{Arc, Mutex};
use tracing::{debug, warn};
pub trait PlasticityExecutor: Send + Sync {
fn notify_burst(&self, timestep: u64);
fn drain_commands(&self) -> Vec<PlasticityCommand>;
fn register_memory_area(
&self,
area_idx: u32,
area_name: String,
temporal_depth: u32,
upstream_areas: Vec<u32>,
lifecycle_config: Option<crate::MemoryNeuronLifecycleConfig>,
mp_learning_enabled: bool,
);
fn start(&mut self) {}
fn stop(&mut self) {}
fn is_running(&self) -> bool {
false }
}
pub struct AsyncPlasticityExecutor {
service: Arc<Mutex<Option<PlasticityService>>>,
memory_stats_cache: MemoryStatsCache,
_config: PlasticityConfig,
running: bool,
}
impl AsyncPlasticityExecutor {
pub fn new(
config: PlasticityConfig,
memory_stats_cache: MemoryStatsCache,
npu: Arc<feagi_npu_burst_engine::TracingMutex<feagi_npu_burst_engine::DynamicNPU>>,
) -> Self {
let service = PlasticityService::new(config.clone(), memory_stats_cache.clone(), npu);
Self {
service: Arc::new(Mutex::new(Some(service))),
memory_stats_cache,
_config: config,
running: false,
}
}
pub fn get_memory_stats_cache(&self) -> MemoryStatsCache {
self.memory_stats_cache.clone()
}
pub fn get_service(&self) -> Option<PlasticityService> {
self.service.lock().ok()?.as_ref().cloned()
}
pub fn enqueue_commands_for_test(&self, commands: Vec<crate::PlasticityCommand>) {
if let Some(service) = self.service.lock().unwrap().as_ref() {
service.enqueue_commands_for_test(commands);
}
}
pub fn memory_cortical_area_runtime_info(
&self,
cortical_idx: u32,
) -> Option<MemoryCorticalAreaRuntimeInfo> {
self.service
.lock()
.ok()?
.as_ref()
.map(|s| s.memory_cortical_area_runtime_info(cortical_idx))
}
pub fn memory_neuron_detail(&self, neuron_id: u32) -> Option<MemoryNeuronDetail> {
self.service
.lock()
.ok()?
.as_ref()
.and_then(|s| s.memory_neuron_detail(neuron_id))
}
pub fn paginated_memory_neuron_ids_in_area(
&self,
cortical_idx: u32,
offset: usize,
limit: usize,
) -> Option<(Vec<u32>, usize)> {
let guard = self.service.lock().ok()?;
let service = guard.as_ref()?;
let array_arc = service.get_memory_neuron_array();
let array = array_arc.lock().ok()?;
Some(array.paginated_neuron_ids_in_area(cortical_idx, offset, limit))
}
}
impl PlasticityExecutor for AsyncPlasticityExecutor {
fn notify_burst(&self, timestep: u64) {
if let Some(service) = self.service.lock().unwrap().as_ref() {
service.notify_burst(timestep);
} else {
warn!("[PLASTICITY-EXEC] ⚠️ Service is None, cannot notify");
}
}
fn drain_commands(&self) -> Vec<PlasticityCommand> {
if let Some(service) = self.service.lock().unwrap().as_ref() {
let drained = service.drain_commands();
if !drained.is_empty() {
debug!(
target: "plasticity",
"[PLASTICITY-EXEC] Drained {} command(s) from executor",
drained.len()
);
for command in &drained {
match command {
PlasticityCommand::RegisterMemoryNeuron {
neuron_id,
area_idx,
..
} => {
debug!(
target: "plasticity",
"[PLASTICITY-EXEC] RegisterMemoryNeuron area={} neuron_id={}",
area_idx,
neuron_id
);
}
PlasticityCommand::MemoryNeuronConvertedToLtm {
neuron_id,
area_idx,
..
} => {
debug!(
target: "plasticity",
"[PLASTICITY-EXEC] MemoryNeuronConvertedToLtm area={} neuron_id={}",
area_idx,
neuron_id
);
}
PlasticityCommand::InjectMemoryNeuronToFCL {
neuron_id,
area_idx,
is_reactivation,
replay_frames,
..
} => {
debug!(
target: "plasticity",
"[PLASTICITY-EXEC] InjectMemoryNeuronToFCL area={} neuron_id={} reactivation={} replay_frames={}",
area_idx,
neuron_id,
is_reactivation,
replay_frames.len()
);
}
PlasticityCommand::UpdateWeightsDelta { .. } => {}
PlasticityCommand::UpdateStateCounters { .. } => {}
PlasticityCommand::ResetMemoryNeuronsInArea { cortical_idx } => {
debug!(
target: "plasticity",
"[PLASTICITY-EXEC] ResetMemoryNeuronsInArea cortical_idx={}",
cortical_idx
);
}
}
}
}
drained
} else {
Vec::new()
}
}
fn register_memory_area(
&self,
area_idx: u32,
area_name: String,
temporal_depth: u32,
upstream_areas: Vec<u32>,
lifecycle_config: Option<crate::MemoryNeuronLifecycleConfig>,
mp_learning_enabled: bool,
) {
if let Some(service) = self.service.lock().unwrap().as_mut() {
service.register_memory_area(
area_idx,
area_name,
temporal_depth,
upstream_areas,
lifecycle_config,
mp_learning_enabled,
);
}
}
fn start(&mut self) {
if self.running {
tracing::warn!(target: "plasticity", "⚠️ PlasticityExecutor already running");
return;
}
if let Some(service) = self.service.lock().unwrap().as_ref() {
tracing::info!(target: "plasticity", "🚀 Initializing AsyncPlasticityExecutor...");
service.start();
self.running = true;
tracing::info!(target: "plasticity",
"✅ AsyncPlasticityExecutor started successfully - ready to monitor memory areas and process STDP"
);
} else {
tracing::error!(target: "plasticity", "❌ Failed to start PlasticityExecutor - service not initialized");
}
}
fn stop(&mut self) {
if !self.running {
return;
}
if let Some(service) = self.service.lock().unwrap().as_ref() {
service.stop();
self.running = false;
tracing::info!("Stopped async plasticity executor");
}
}
fn is_running(&self) -> bool {
self.running
}
}
#[cfg(not(feature = "std"))]
pub struct SyncPlasticityExecutor {
_marker: core::marker::PhantomData<()>,
}
#[cfg(not(feature = "std"))]
impl PlasticityExecutor for SyncPlasticityExecutor {
fn notify_burst(&self, _timestep: u64) {
unimplemented!("SyncPlasticityExecutor not yet implemented");
}
fn drain_commands(&self) -> Vec<PlasticityCommand> {
unimplemented!("SyncPlasticityExecutor not yet implemented");
}
fn register_memory_area(
&self,
_area_idx: u32,
_area_name: String,
_temporal_depth: u32,
_upstream_areas: Vec<u32>,
_lifecycle_config: Option<crate::MemoryNeuronLifecycleConfig>,
_mp_learning_enabled: bool,
) {
unimplemented!("SyncPlasticityExecutor not yet implemented");
}
}