Skip to main content

casial_core/
perception.rs

1//! # Perception Module
2//!
3//! Handles different ways of seeing reality that can coexist without forcing consensus.
4//! This is core to the consciousness-computation substrate.
5
6use crate::{CasialError, PerceptionId};
7use ahash::AHashMap;
8use anyhow::Result;
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use std::collections::HashSet;
12
13/// The confidence level of a perception
14#[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/// Different types of perceptions in the system
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub enum PerceptionType {
48    /// Human intuition and insight
49    Human,
50    /// AI analysis and reasoning
51    Artificial,
52    /// Hybrid human-AI collaboration
53    Hybrid,
54    /// System-generated perception from data patterns
55    Systemic,
56    /// External API or service perspective
57    External,
58}
59
60/// A specific viewpoint or way of understanding reality
61#[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    /// Related perceptions that support this view
74    pub supporting_perceptions: Vec<PerceptionId>,
75    /// Perceptions that conflict with this view
76    pub conflicting_perceptions: Vec<PerceptionId>,
77    /// Evidence supporting this perception
78    pub evidence: Vec<PerceptionEvidence>,
79}
80
81/// Evidence supporting or refuting a perception
82#[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, // How much this evidence affects confidence
88    pub timestamp: DateTime<Utc>,
89    pub data: serde_json::Value,
90}
91
92/// Source of perception evidence
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub enum EvidenceSource {
95    /// Direct observation or measurement
96    Observation,
97    /// Logical reasoning or inference
98    Reasoning,
99    /// Historical data or patterns
100    Historical,
101    /// External validation or verification
102    External,
103    /// Collaborative consensus
104    Consensus,
105    /// Expert judgment
106    Expert,
107}
108
109/// Manager for perception states and interactions
110pub struct PerceptionManager {
111    perceptions: AHashMap<PerceptionId, PerceptionView>,
112    perception_relationships: AHashMap<PerceptionId, HashSet<PerceptionId>>,
113    active_locks: AHashMap<PerceptionId, PerceptionLock>,
114}
115
116/// A lock on a perception to coordinate access
117#[derive(Debug, Clone)]
118pub struct PerceptionLock {
119    pub perception_id: PerceptionId,
120    pub locked_by: uuid::Uuid, // Session ID that holds the lock
121    pub locked_at: DateTime<Utc>,
122    pub expires_at: DateTime<Utc>,
123    pub lock_type: PerceptionLockType,
124}
125
126/// Types of perception locks
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub enum PerceptionLockType {
129    /// Exclusive read/write access
130    Exclusive,
131    /// Shared read access, blocks writes
132    Shared,
133    /// Advisory lock, doesn't block but signals intent
134    Advisory,
135}
136
137impl PerceptionManager {
138    /// Create a new perception manager
139    pub fn new() -> Self {
140        Self {
141            perceptions: AHashMap::new(),
142            perception_relationships: AHashMap::new(),
143            active_locks: AHashMap::new(),
144        }
145    }
146
147    /// Register a new perception
148    pub fn register_perception(&mut self, perception: PerceptionView) -> Result<()> {
149        let perception_id = perception.id;
150
151        // Initialize relationships
152        self.perception_relationships
153            .insert(perception_id, HashSet::new());
154
155        // Add relationships from this perception
156        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    /// Get a perception by ID
177    pub fn get_perception(&self, perception_id: PerceptionId) -> Option<&PerceptionView> {
178        self.perceptions.get(&perception_id)
179    }
180
181    /// Update perception confidence based on new evidence
182    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            // Simple confidence update algorithm
191            // In practice, this would be more sophisticated
192            let evidence_impact = evidence.weight * 0.1; // Scale evidence impact
193            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) // Reasoning has less direct impact
199                }
200                EvidenceSource::Historical => {
201                    old_confidence + (evidence_impact * 0.5) // Historical data has moderate impact
202                }
203                EvidenceSource::Consensus => {
204                    old_confidence + (evidence_impact * 0.8) // Consensus has high impact
205                }
206                EvidenceSource::Expert => {
207                    old_confidence + (evidence_impact * 0.9) // Expert judgment has very high impact
208                }
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    /// Attempt to acquire a perception lock
225    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        // Check if already locked
233        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                        // Allow shared locks to coexist
242                    }
243                    (PerceptionLockType::Advisory, _) | (_, PerceptionLockType::Advisory) => {
244                        // Advisory locks don't block
245                    }
246                }
247            }
248        }
249
250        // Acquire the lock
251        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    /// Release a perception lock
264    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    /// Clean up expired locks
290    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    /// Add a relationship between two perceptions
308    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        // Add reverse relationship if symmetric
320        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                // Asymmetric relationship - don't add reverse
335            }
336            PerceptionRelationType::Enhances => {
337                // Can be asymmetric
338            }
339        }
340
341        Ok(())
342    }
343
344    /// Find perceptions that conflict with a given perception
345    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    /// Find perceptions that support a given perception
354    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    /// Get all perceptions with confidence above a threshold
363    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    /// Get statistics about perception states
372    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/// Types of relationships between perceptions
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub enum PerceptionRelationType {
407    /// One perception supports another
408    Supports,
409    /// Perceptions are in conflict
410    Conflicts,
411    /// One perception depends on another
412    DependsOn,
413    /// One perception enhances another
414    Enhances,
415}
416
417/// Statistics for perception manager monitoring
418#[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}