feagi_services/impls/
neuron_service_impl.rs1use crate::traits::NeuronService;
12use crate::types::*;
13use async_trait::async_trait;
14use feagi_brain_development::ConnectomeManager;
15use feagi_structures::genomic::cortical_area::CorticalID;
16use parking_lot::RwLock;
17use std::sync::Arc;
18use tracing::debug;
19
20pub struct NeuronServiceImpl {
22 connectome: Arc<RwLock<ConnectomeManager>>,
23}
24
25impl NeuronServiceImpl {
26 pub fn new(connectome: Arc<RwLock<ConnectomeManager>>) -> Self {
27 Self { connectome }
28 }
29}
30
31#[async_trait]
32impl NeuronService for NeuronServiceImpl {
33 async fn create_neuron(&self, params: CreateNeuronParams) -> ServiceResult<NeuronInfo> {
34 debug!(target: "feagi-services", "Creating neuron in area {} at {:?}", params.cortical_id, params.coordinates);
35
36 let cortical_id_typed = CorticalID::try_from_base_64(¶ms.cortical_id)
38 .map_err(|e| ServiceError::InvalidInput(format!("Invalid cortical ID: {}", e)))?;
39
40 let mut manager = self.connectome.write();
41
42 let props = params.properties.as_ref();
44
45 let firing_threshold = props
46 .and_then(|p| p.get("firing_threshold"))
47 .and_then(|v| v.as_f64())
48 .unwrap_or(1.0) as f32;
49
50 let leak_coefficient = props
51 .and_then(|p| p.get("leak_coefficient"))
52 .and_then(|v| v.as_f64())
53 .unwrap_or(0.0) as f32;
54
55 let resting_potential = props
56 .and_then(|p| p.get("resting_potential"))
57 .and_then(|v| v.as_f64())
58 .unwrap_or(0.0) as f32;
59
60 let is_inhibitory = props
61 .and_then(|p| p.get("is_inhibitory"))
62 .and_then(|v| v.as_bool())
63 .unwrap_or(false);
64
65 let refractory_period = props
66 .and_then(|p| p.get("refractory_period"))
67 .and_then(|v| v.as_i64())
68 .unwrap_or(0) as u16;
69
70 let excitability = props
71 .and_then(|p| p.get("excitability"))
72 .and_then(|v| v.as_f64())
73 .unwrap_or(1.0) as f32;
74
75 let consecutive_fire_limit_raw = props
77 .and_then(|p| p.get("consecutive_fire_limit"))
78 .and_then(|v| v.as_i64())
79 .unwrap_or(0) as u16;
80 let consecutive_fire_limit = if consecutive_fire_limit_raw == 0 {
81 u16::MAX } else {
83 consecutive_fire_limit_raw
84 };
85
86 let snooze_length = props
87 .and_then(|p| p.get("snooze_length"))
88 .and_then(|v| v.as_i64())
89 .unwrap_or(0) as u16;
90
91 let mp_charge_accumulation = props
92 .and_then(|p| p.get("mp_charge_accumulation"))
93 .and_then(|v| v.as_bool())
94 .unwrap_or(false);
95
96 let firing_threshold_limit_raw = props
98 .and_then(|p| p.get("firing_threshold_limit"))
99 .and_then(|v| v.as_f64())
100 .unwrap_or(0.0) as f32;
101 let firing_threshold_limit = if firing_threshold_limit_raw == 0.0 {
102 f32::MAX } else {
104 firing_threshold_limit_raw
105 };
106
107 let neuron_id = manager
109 .add_neuron(
110 &cortical_id_typed,
111 params.coordinates.0,
112 params.coordinates.1,
113 params.coordinates.2,
114 firing_threshold,
115 firing_threshold_limit,
116 leak_coefficient,
117 resting_potential,
118 if is_inhibitory { 1 } else { 0 },
119 refractory_period,
120 excitability,
121 consecutive_fire_limit,
122 snooze_length,
123 mp_charge_accumulation,
124 )
125 .map_err(ServiceError::from)?;
126
127 let cortical_idx = manager
128 .get_cortical_idx(&cortical_id_typed)
129 .ok_or_else(|| ServiceError::NotFound {
130 resource: "CorticalArea".to_string(),
131 id: params.cortical_id.clone(),
132 })?;
133
134 Ok(NeuronInfo {
135 id: neuron_id,
136 cortical_id: params.cortical_id.clone(),
137 cortical_idx,
138 coordinates: params.coordinates,
139 properties: params.properties.unwrap_or_default(),
140 })
141 }
142
143 async fn delete_neuron(&self, neuron_id: u64) -> ServiceResult<()> {
144 debug!(target: "feagi-services","Deleting neuron {}", neuron_id);
145
146 let mut manager = self.connectome.write();
147 let deleted = manager
148 .delete_neuron(neuron_id)
149 .map_err(ServiceError::from)?;
150
151 if !deleted {
152 return Err(ServiceError::NotFound {
153 resource: "Neuron".to_string(),
154 id: neuron_id.to_string(),
155 });
156 }
157
158 Ok(())
159 }
160
161 async fn get_neuron(&self, neuron_id: u64) -> ServiceResult<NeuronInfo> {
162 debug!(target: "feagi-services","Getting neuron {}", neuron_id);
163
164 let manager = self.connectome.read();
165
166 if !manager.has_neuron(neuron_id) {
168 return Err(ServiceError::NotFound {
169 resource: "Neuron".to_string(),
170 id: neuron_id.to_string(),
171 });
172 }
173
174 let coordinates = manager.get_neuron_coordinates(neuron_id);
175 let cortical_idx = manager.get_neuron_cortical_idx(neuron_id);
176 let cortical_id = manager
177 .get_neuron_cortical_id(neuron_id)
178 .map(|id| id.as_base_64())
179 .unwrap_or_else(|| "unknown".to_string());
180
181 Ok(NeuronInfo {
182 id: neuron_id,
183 cortical_id,
184 cortical_idx,
185 coordinates,
186 properties: std::collections::HashMap::new(),
187 })
188 }
189
190 async fn get_neuron_at_coordinates(
191 &self,
192 cortical_id: &str,
193 coordinates: (u32, u32, u32),
194 ) -> ServiceResult<Option<NeuronInfo>> {
195 debug!(target: "feagi-services","Looking up neuron in area {} at {:?}", cortical_id, coordinates);
196
197 let manager = self.connectome.read();
198
199 if !manager.has_cortical_area(
201 &CorticalID::try_from_base_64(cortical_id)
202 .map_err(|e| ServiceError::InvalidInput(format!("Invalid cortical ID: {}", e)))?,
203 ) {
204 return Err(ServiceError::NotFound {
205 resource: "CorticalArea".to_string(),
206 id: cortical_id.to_string(),
207 });
208 }
209
210 let neurons = manager.get_neurons_in_area(
212 &CorticalID::try_from_base_64(cortical_id)
213 .map_err(|e| ServiceError::InvalidInput(format!("Invalid cortical ID: {}", e)))?,
214 );
215
216 for neuron_id in neurons {
217 let neuron_coords = manager.get_neuron_coordinates(neuron_id);
218 if neuron_coords == coordinates {
219 let cortical_idx = manager.get_neuron_cortical_idx(neuron_id);
220 return Ok(Some(NeuronInfo {
221 id: neuron_id,
222 cortical_id: cortical_id.to_string(),
223 cortical_idx,
224 coordinates,
225 properties: std::collections::HashMap::new(),
226 }));
227 }
228 }
229
230 Ok(None)
232 }
233
234 async fn list_neurons_in_area(
235 &self,
236 cortical_id: &str,
237 limit: Option<usize>,
238 ) -> ServiceResult<Vec<NeuronInfo>> {
239 debug!(target: "feagi-services","Listing neurons in area: {}", cortical_id);
240
241 let manager = self.connectome.read();
243 let neuron_ids = manager.get_neurons_in_area(
244 &CorticalID::try_from_base_64(cortical_id)
245 .map_err(|e| ServiceError::InvalidInput(format!("Invalid cortical ID: {}", e)))?,
246 );
247
248 let neurons: Vec<NeuronInfo> = neuron_ids
249 .iter()
250 .take(limit.unwrap_or(usize::MAX))
251 .map(|&id| {
252 let coordinates = manager.get_neuron_coordinates(id);
254 let cortical_idx = manager.get_neuron_cortical_idx(id);
255
256 NeuronInfo {
259 id,
260 cortical_id: cortical_id.to_string(),
261 cortical_idx,
262 coordinates,
263 properties: std::collections::HashMap::new(), }
265 })
266 .collect();
267
268 Ok(neurons)
269 }
270
271 async fn get_neuron_count(&self, cortical_id: &str) -> ServiceResult<usize> {
272 debug!(target: "feagi-services","Getting neuron count for area: {}", cortical_id);
273
274 let cortical_id_typed = CorticalID::try_from_base_64(cortical_id)
276 .map_err(|e| ServiceError::InvalidInput(format!("Invalid cortical ID: {}", e)))?;
277
278 let count = self
279 .connectome
280 .read()
281 .get_neuron_count_in_area(&cortical_id_typed);
282
283 Ok(count)
284 }
285
286 async fn neuron_exists(&self, neuron_id: u64) -> ServiceResult<bool> {
287 debug!(target: "feagi-services","Checking if neuron exists: {}", neuron_id);
288
289 let exists = self.connectome.read().has_neuron(neuron_id);
290
291 Ok(exists)
292 }
293}