1use crate::{CasialError, PerceptionId};
7use ahash::AHashMap;
8use anyhow::Result;
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use std::collections::HashSet;
12
13#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
15pub struct PerceptionConfidence(pub f64);
16
17impl PerceptionConfidence {
18 pub fn new(confidence: f64) -> Result<Self> {
19 if !(0.0..=1.0).contains(&confidence) {
20 return Err(CasialError::PerceptionLock(
21 "Confidence must be between 0.0 and 1.0".to_string(),
22 )
23 .into());
24 }
25 Ok(Self(confidence))
26 }
27
28 pub fn value(&self) -> f64 {
29 self.0
30 }
31
32 pub fn is_high(&self) -> bool {
33 self.0 >= 0.8
34 }
35
36 pub fn is_low(&self) -> bool {
37 self.0 <= 0.3
38 }
39
40 pub fn is_uncertain(&self) -> bool {
41 (0.4..0.6).contains(&self.0)
42 }
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47pub enum PerceptionType {
48 Human,
50 Artificial,
52 Hybrid,
54 Systemic,
56 External,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct PerceptionView {
63 pub id: PerceptionId,
64 pub name: String,
65 pub description: String,
66 pub perception_type: PerceptionType,
67 pub confidence: PerceptionConfidence,
68 pub created_at: DateTime<Utc>,
69 pub updated_at: DateTime<Utc>,
70 pub tags: Vec<String>,
71 pub metadata: AHashMap<String, serde_json::Value>,
72
73 pub supporting_perceptions: Vec<PerceptionId>,
75 pub conflicting_perceptions: Vec<PerceptionId>,
77 pub evidence: Vec<PerceptionEvidence>,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct PerceptionEvidence {
84 pub id: uuid::Uuid,
85 pub description: String,
86 pub source: EvidenceSource,
87 pub weight: f64, pub timestamp: DateTime<Utc>,
89 pub data: serde_json::Value,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub enum EvidenceSource {
95 Observation,
97 Reasoning,
99 Historical,
101 External,
103 Consensus,
105 Expert,
107}
108
109pub struct PerceptionManager {
111 perceptions: AHashMap<PerceptionId, PerceptionView>,
112 perception_relationships: AHashMap<PerceptionId, HashSet<PerceptionId>>,
113 active_locks: AHashMap<PerceptionId, PerceptionLock>,
114}
115
116#[derive(Debug, Clone)]
118pub struct PerceptionLock {
119 pub perception_id: PerceptionId,
120 pub locked_by: uuid::Uuid, pub locked_at: DateTime<Utc>,
122 pub expires_at: DateTime<Utc>,
123 pub lock_type: PerceptionLockType,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub enum PerceptionLockType {
129 Exclusive,
131 Shared,
133 Advisory,
135}
136
137impl PerceptionManager {
138 pub fn new() -> Self {
140 Self {
141 perceptions: AHashMap::new(),
142 perception_relationships: AHashMap::new(),
143 active_locks: AHashMap::new(),
144 }
145 }
146
147 pub fn register_perception(&mut self, perception: PerceptionView) -> Result<()> {
149 let perception_id = perception.id;
150
151 self.perception_relationships
153 .insert(perception_id, HashSet::new());
154
155 for supporting_id in &perception.supporting_perceptions {
157 self.add_relationship(
158 perception_id,
159 *supporting_id,
160 PerceptionRelationType::Supports,
161 )?;
162 }
163
164 for conflicting_id in &perception.conflicting_perceptions {
165 self.add_relationship(
166 perception_id,
167 *conflicting_id,
168 PerceptionRelationType::Conflicts,
169 )?;
170 }
171
172 self.perceptions.insert(perception_id, perception);
173 Ok(())
174 }
175
176 pub fn get_perception(&self, perception_id: PerceptionId) -> Option<&PerceptionView> {
178 self.perceptions.get(&perception_id)
179 }
180
181 pub fn update_confidence(
183 &mut self,
184 perception_id: PerceptionId,
185 evidence: PerceptionEvidence,
186 ) -> Result<()> {
187 if let Some(perception) = self.perceptions.get_mut(&perception_id) {
188 let old_confidence = perception.confidence.value();
189
190 let evidence_impact = evidence.weight * 0.1; let new_confidence = match evidence.source {
194 EvidenceSource::Observation | EvidenceSource::External => {
195 (old_confidence + evidence_impact).min(1.0)
196 }
197 EvidenceSource::Reasoning => {
198 old_confidence + (evidence_impact * 0.7) }
200 EvidenceSource::Historical => {
201 old_confidence + (evidence_impact * 0.5) }
203 EvidenceSource::Consensus => {
204 old_confidence + (evidence_impact * 0.8) }
206 EvidenceSource::Expert => {
207 old_confidence + (evidence_impact * 0.9) }
209 };
210
211 perception.confidence = PerceptionConfidence::new(new_confidence)?;
212 perception.evidence.push(evidence);
213 perception.updated_at = Utc::now();
214
215 Ok(())
216 } else {
217 Err(
218 CasialError::PerceptionLock(format!("Perception {} not found", perception_id.0))
219 .into(),
220 )
221 }
222 }
223
224 pub fn acquire_lock(
226 &mut self,
227 perception_id: PerceptionId,
228 session_id: uuid::Uuid,
229 lock_type: PerceptionLockType,
230 duration_seconds: u64,
231 ) -> Result<bool> {
232 if let Some(existing_lock) = self.active_locks.get(&perception_id) {
234 if existing_lock.expires_at > Utc::now() {
235 match (&existing_lock.lock_type, &lock_type) {
236 (PerceptionLockType::Exclusive, _) => return Ok(false),
237 (PerceptionLockType::Shared, PerceptionLockType::Exclusive) => {
238 return Ok(false)
239 }
240 (PerceptionLockType::Shared, PerceptionLockType::Shared) => {
241 }
243 (PerceptionLockType::Advisory, _) | (_, PerceptionLockType::Advisory) => {
244 }
246 }
247 }
248 }
249
250 let lock = PerceptionLock {
252 perception_id,
253 locked_by: session_id,
254 locked_at: Utc::now(),
255 expires_at: Utc::now() + chrono::Duration::seconds(duration_seconds as i64),
256 lock_type,
257 };
258
259 self.active_locks.insert(perception_id, lock);
260 Ok(true)
261 }
262
263 pub fn release_lock(
265 &mut self,
266 perception_id: PerceptionId,
267 session_id: uuid::Uuid,
268 ) -> Result<()> {
269 if let Some(lock) = self.active_locks.get(&perception_id) {
270 if lock.locked_by == session_id {
271 self.active_locks.remove(&perception_id);
272 Ok(())
273 } else {
274 Err(CasialError::PerceptionLock(format!(
275 "Lock not owned by session {}",
276 session_id
277 ))
278 .into())
279 }
280 } else {
281 Err(CasialError::PerceptionLock(format!(
282 "No lock found for perception {}",
283 perception_id.0
284 ))
285 .into())
286 }
287 }
288
289 pub fn cleanup_expired_locks(&mut self) -> usize {
291 let now = Utc::now();
292 let expired_locks: Vec<PerceptionId> = self
293 .active_locks
294 .iter()
295 .filter(|(_, lock)| lock.expires_at <= now)
296 .map(|(id, _)| *id)
297 .collect();
298
299 let count = expired_locks.len();
300 for perception_id in expired_locks {
301 self.active_locks.remove(&perception_id);
302 }
303
304 count
305 }
306
307 pub fn add_relationship(
309 &mut self,
310 from_perception: PerceptionId,
311 to_perception: PerceptionId,
312 relationship_type: PerceptionRelationType,
313 ) -> Result<()> {
314 self.perception_relationships
315 .entry(from_perception)
316 .or_default()
317 .insert(to_perception);
318
319 match relationship_type {
321 PerceptionRelationType::Supports => {
322 self.perception_relationships
323 .entry(to_perception)
324 .or_default()
325 .insert(from_perception);
326 }
327 PerceptionRelationType::Conflicts => {
328 self.perception_relationships
329 .entry(to_perception)
330 .or_default()
331 .insert(from_perception);
332 }
333 PerceptionRelationType::DependsOn => {
334 }
336 PerceptionRelationType::Enhances => {
337 }
339 }
340
341 Ok(())
342 }
343
344 pub fn find_conflicts(&self, perception_id: PerceptionId) -> Vec<PerceptionId> {
346 if let Some(perception) = self.perceptions.get(&perception_id) {
347 perception.conflicting_perceptions.clone()
348 } else {
349 Vec::new()
350 }
351 }
352
353 pub fn find_supporters(&self, perception_id: PerceptionId) -> Vec<PerceptionId> {
355 if let Some(perception) = self.perceptions.get(&perception_id) {
356 perception.supporting_perceptions.clone()
357 } else {
358 Vec::new()
359 }
360 }
361
362 pub fn get_high_confidence_perceptions(&self, threshold: f64) -> Vec<PerceptionId> {
364 self.perceptions
365 .iter()
366 .filter(|(_, perception)| perception.confidence.value() >= threshold)
367 .map(|(id, _)| *id)
368 .collect()
369 }
370
371 pub fn get_statistics(&self) -> PerceptionManagerStats {
373 let total_perceptions = self.perceptions.len();
374 let active_locks = self.active_locks.len();
375
376 let avg_confidence = if total_perceptions > 0 {
377 self.perceptions
378 .values()
379 .map(|p| p.confidence.value())
380 .sum::<f64>()
381 / total_perceptions as f64
382 } else {
383 0.0
384 };
385
386 let perception_types: AHashMap<String, usize> =
387 self.perceptions
388 .values()
389 .fold(AHashMap::new(), |mut acc, perception| {
390 let type_name = format!("{:?}", perception.perception_type);
391 *acc.entry(type_name).or_insert(0) += 1;
392 acc
393 });
394
395 PerceptionManagerStats {
396 total_perceptions,
397 active_locks,
398 average_confidence: avg_confidence,
399 perception_types,
400 }
401 }
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
406pub enum PerceptionRelationType {
407 Supports,
409 Conflicts,
411 DependsOn,
413 Enhances,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct PerceptionManagerStats {
420 pub total_perceptions: usize,
421 pub active_locks: usize,
422 pub average_confidence: f64,
423 pub perception_types: AHashMap<String, usize>,
424}
425
426impl Default for PerceptionManager {
427 fn default() -> Self {
428 Self::new()
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435
436 #[test]
437 fn test_perception_confidence() {
438 let confidence = PerceptionConfidence::new(0.8).unwrap();
439 assert!(confidence.is_high());
440 assert!(!confidence.is_low());
441 assert!(!confidence.is_uncertain());
442 }
443
444 #[test]
445 fn test_perception_manager() {
446 let mut manager = PerceptionManager::new();
447
448 let perception = PerceptionView {
449 id: PerceptionId::new(),
450 name: "Test Perception".to_string(),
451 description: "A test perception".to_string(),
452 perception_type: PerceptionType::Human,
453 confidence: PerceptionConfidence::new(0.7).unwrap(),
454 created_at: Utc::now(),
455 updated_at: Utc::now(),
456 tags: vec!["test".to_string()],
457 metadata: AHashMap::new(),
458 supporting_perceptions: vec![],
459 conflicting_perceptions: vec![],
460 evidence: vec![],
461 };
462
463 let perception_id = perception.id;
464 manager.register_perception(perception).unwrap();
465
466 assert!(manager.get_perception(perception_id).is_some());
467 }
468
469 #[test]
470 fn test_perception_locking() {
471 let mut manager = PerceptionManager::new();
472 let perception_id = PerceptionId::new();
473 let session_id = uuid::Uuid::new_v4();
474
475 let lock_acquired = manager
476 .acquire_lock(perception_id, session_id, PerceptionLockType::Exclusive, 60)
477 .unwrap();
478
479 assert!(lock_acquired);
480
481 manager.release_lock(perception_id, session_id).unwrap();
482 }
483}