1use crate::{CasialError, ParadoxStrategy, PerceptionId};
7use ahash::AHashMap;
8use anyhow::Result;
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Paradox {
16 pub id: Uuid,
17 pub name: String,
18 pub description: String,
19 pub conflicting_elements: Vec<ParadoxElement>,
20 pub severity: ParadoxSeverity,
21 pub resolution_strategy: ParadoxStrategy,
22 pub created_at: DateTime<Utc>,
23 pub resolved_at: Option<DateTime<Utc>>,
24 pub resolution_outcome: Option<ParadoxResolution>,
25 pub metadata: AHashMap<String, serde_json::Value>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ParadoxElement {
31 pub element_type: ParadoxElementType,
32 pub element_id: String,
33 pub confidence: f64,
34 pub evidence: Vec<String>,
35 pub perspective: Option<PerceptionId>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub enum ParadoxElementType {
41 Template,
43 Perception,
45 Environment,
47 Tool,
49 Mission,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
55pub enum ParadoxSeverity {
56 Low,
58 Medium,
60 High,
62 Critical,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct ParadoxResolution {
69 pub strategy_used: ParadoxStrategy,
70 pub resolution_time_ms: f64,
71 pub confidence_impact: f64,
72 pub synthesis_result: Option<String>,
73 pub chosen_elements: Vec<String>,
74 pub metadata: AHashMap<String, serde_json::Value>,
75}
76
77pub struct ParadoxManager {
79 active_paradoxes: AHashMap<Uuid, Paradox>,
80 resolved_paradoxes: AHashMap<Uuid, Paradox>,
81 resolution_history: Vec<ParadoxResolutionEvent>,
82 detection_rules: Vec<ParadoxDetectionRule>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct ParadoxResolutionEvent {
88 pub paradox_id: Uuid,
89 pub event_type: ResolutionEventType,
90 pub timestamp: DateTime<Utc>,
91 pub details: serde_json::Value,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
96pub enum ResolutionEventType {
97 Detected,
98 AnalysisStarted,
99 StrategySelected,
100 ResolutionAttempted,
101 Resolved,
102 Escalated,
103 Timeout,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct ParadoxDetectionRule {
109 pub id: String,
110 pub name: String,
111 pub enabled: bool,
112 pub detection_pattern: DetectionPattern,
113 pub severity_threshold: ParadoxSeverity,
114 pub auto_resolve: bool,
115 pub preferred_strategy: ParadoxStrategy,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub enum DetectionPattern {
121 ConflictingTemplates {
123 similarity_threshold: f64,
124 contradiction_keywords: Vec<String>,
125 },
126 ConflictingPerceptions {
128 confidence_threshold: f64,
129 overlap_threshold: f64,
130 },
131 EnvironmentalConflict {
133 variable_patterns: Vec<String>,
134 value_conflicts: Vec<(String, String)>,
135 },
136 ToolConflicts {
138 tool_categories: Vec<String>,
139 behavior_patterns: Vec<String>,
140 },
141}
142
143impl ParadoxManager {
144 pub fn new() -> Self {
146 let mut manager = Self {
147 active_paradoxes: AHashMap::new(),
148 resolved_paradoxes: AHashMap::new(),
149 resolution_history: Vec::new(),
150 detection_rules: Vec::new(),
151 };
152
153 manager.add_default_detection_rules();
155 manager
156 }
157
158 fn add_default_detection_rules(&mut self) {
160 let rules = vec![
161 ParadoxDetectionRule {
162 id: "conflicting-templates".to_string(),
163 name: "Conflicting Template Content".to_string(),
164 enabled: true,
165 detection_pattern: DetectionPattern::ConflictingTemplates {
166 similarity_threshold: 0.7,
167 contradiction_keywords: vec![
168 "not".to_string(),
169 "never".to_string(),
170 "don't".to_string(),
171 "avoid".to_string(),
172 "opposite".to_string(),
173 ],
174 },
175 severity_threshold: ParadoxSeverity::Medium,
176 auto_resolve: false,
177 preferred_strategy: ParadoxStrategy::Coexist,
178 },
179 ParadoxDetectionRule {
180 id: "perception-conflicts".to_string(),
181 name: "Conflicting Perceptions".to_string(),
182 enabled: true,
183 detection_pattern: DetectionPattern::ConflictingPerceptions {
184 confidence_threshold: 0.8,
185 overlap_threshold: 0.5,
186 },
187 severity_threshold: ParadoxSeverity::High,
188 auto_resolve: true,
189 preferred_strategy: ParadoxStrategy::Synthesize,
190 },
191 ];
192
193 self.detection_rules.extend(rules);
194 }
195
196 pub fn detect_paradoxes(
198 &mut self,
199 templates: &[crate::CasialTemplate],
200 perceptions: &[crate::Perception],
201 environment: &AHashMap<String, String>,
202 ) -> Result<Vec<Uuid>> {
203 let mut detected_paradoxes = Vec::new();
204
205 for rule in &self.detection_rules {
206 if !rule.enabled {
207 continue;
208 }
209
210 let paradoxes = self.apply_detection_rule(rule, templates, perceptions, environment)?;
211 for paradox in paradoxes {
212 let paradox_id = paradox.id;
213 self.active_paradoxes.insert(paradox_id, paradox);
214 detected_paradoxes.push(paradox_id);
215
216 self.resolution_history.push(ParadoxResolutionEvent {
218 paradox_id,
219 event_type: ResolutionEventType::Detected,
220 timestamp: Utc::now(),
221 details: serde_json::json!({
222 "rule_id": rule.id,
223 "rule_name": rule.name
224 }),
225 });
226 }
227 }
228
229 Ok(detected_paradoxes)
230 }
231
232 fn apply_detection_rule(
234 &self,
235 rule: &ParadoxDetectionRule,
236 templates: &[crate::CasialTemplate],
237 perceptions: &[crate::Perception],
238 environment: &AHashMap<String, String>,
239 ) -> Result<Vec<Paradox>> {
240 let mut paradoxes = Vec::new();
241
242 match &rule.detection_pattern {
243 DetectionPattern::ConflictingTemplates {
244 similarity_threshold,
245 contradiction_keywords,
246 } => {
247 paradoxes.extend(self.detect_template_conflicts(
248 templates,
249 *similarity_threshold,
250 contradiction_keywords,
251 &rule.preferred_strategy,
252 )?);
253 }
254 DetectionPattern::ConflictingPerceptions {
255 confidence_threshold,
256 overlap_threshold,
257 } => {
258 paradoxes.extend(self.detect_perception_conflicts(
259 perceptions,
260 *confidence_threshold,
261 *overlap_threshold,
262 &rule.preferred_strategy,
263 )?);
264 }
265 DetectionPattern::EnvironmentalConflict {
266 variable_patterns,
267 value_conflicts,
268 } => {
269 paradoxes.extend(self.detect_environmental_conflicts(
270 environment,
271 variable_patterns,
272 value_conflicts,
273 &rule.preferred_strategy,
274 )?);
275 }
276 DetectionPattern::ToolConflicts {
277 tool_categories: _,
278 behavior_patterns: _,
279 } => {
280 }
283 }
284
285 Ok(paradoxes)
286 }
287
288 fn detect_template_conflicts(
290 &self,
291 templates: &[crate::CasialTemplate],
292 similarity_threshold: f64,
293 contradiction_keywords: &[String],
294 strategy: &ParadoxStrategy,
295 ) -> Result<Vec<Paradox>> {
296 let mut conflicts = Vec::new();
297
298 for i in 0..templates.len() {
299 for j in (i + 1)..templates.len() {
300 let template_a = &templates[i];
301 let template_b = &templates[j];
302
303 let has_contradiction = contradiction_keywords.iter().any(|keyword| {
305 (template_a.content.contains(keyword) && !template_b.content.contains(keyword))
306 || (!template_a.content.contains(keyword)
307 && template_b.content.contains(keyword))
308 });
309
310 let similarity =
312 self.calculate_content_similarity(&template_a.content, &template_b.content);
313
314 if has_contradiction && similarity > similarity_threshold {
315 let paradox = Paradox {
316 id: Uuid::new_v4(),
317 name: format!("Template Conflict: {} vs {}", template_a.name, template_b.name),
318 description: format!(
319 "Templates '{}' and '{}' contain contradictory guidance with high content similarity",
320 template_a.name, template_b.name
321 ),
322 conflicting_elements: vec![
323 ParadoxElement {
324 element_type: ParadoxElementType::Template,
325 element_id: template_a.id.clone(),
326 confidence: 1.0 - template_a.paradox_resistance,
327 evidence: vec![template_a.content.clone()],
328 perspective: template_a.perception_affinity.first().copied(),
329 },
330 ParadoxElement {
331 element_type: ParadoxElementType::Template,
332 element_id: template_b.id.clone(),
333 confidence: 1.0 - template_b.paradox_resistance,
334 evidence: vec![template_b.content.clone()],
335 perspective: template_b.perception_affinity.first().copied(),
336 },
337 ],
338 severity: if similarity > 0.9 {
339 ParadoxSeverity::High
340 } else {
341 ParadoxSeverity::Medium
342 },
343 resolution_strategy: strategy.clone(),
344 created_at: Utc::now(),
345 resolved_at: None,
346 resolution_outcome: None,
347 metadata: AHashMap::from([
348 ("similarity".to_string(), serde_json::json!(similarity)),
349 ("contradiction_detected".to_string(), serde_json::json!(has_contradiction)),
350 ]),
351 };
352
353 conflicts.push(paradox);
354 }
355 }
356 }
357
358 Ok(conflicts)
359 }
360
361 fn detect_perception_conflicts(
363 &self,
364 perceptions: &[crate::Perception],
365 confidence_threshold: f64,
366 overlap_threshold: f64,
367 strategy: &ParadoxStrategy,
368 ) -> Result<Vec<Paradox>> {
369 let mut conflicts = Vec::new();
370
371 for i in 0..perceptions.len() {
372 for j in (i + 1)..perceptions.len() {
373 let perception_a = &perceptions[i];
374 let perception_b = &perceptions[j];
375
376 if perception_a.confidence < confidence_threshold
377 || perception_b.confidence < confidence_threshold
378 {
379 continue;
380 }
381
382 let overlap = self.calculate_perception_overlap(perception_a, perception_b);
384
385 if overlap > overlap_threshold {
386 let paradox = Paradox {
387 id: Uuid::new_v4(),
388 name: format!("Perception Conflict: {} vs {}", perception_a.name, perception_b.name),
389 description: format!(
390 "High-confidence perceptions '{}' and '{}' have overlapping domains but different conclusions",
391 perception_a.name, perception_b.name
392 ),
393 conflicting_elements: vec![
394 ParadoxElement {
395 element_type: ParadoxElementType::Perception,
396 element_id: perception_a.id.0.to_string(),
397 confidence: perception_a.confidence,
398 evidence: vec![perception_a.description.clone()],
399 perspective: Some(perception_a.id),
400 },
401 ParadoxElement {
402 element_type: ParadoxElementType::Perception,
403 element_id: perception_b.id.0.to_string(),
404 confidence: perception_b.confidence,
405 evidence: vec![perception_b.description.clone()],
406 perspective: Some(perception_b.id),
407 },
408 ],
409 severity: if overlap > 0.8 {
410 ParadoxSeverity::Critical
411 } else {
412 ParadoxSeverity::High
413 },
414 resolution_strategy: strategy.clone(),
415 created_at: Utc::now(),
416 resolved_at: None,
417 resolution_outcome: None,
418 metadata: AHashMap::from([
419 ("overlap_score".to_string(), serde_json::json!(overlap)),
420 ]),
421 };
422
423 conflicts.push(paradox);
424 }
425 }
426 }
427
428 Ok(conflicts)
429 }
430
431 fn detect_environmental_conflicts(
433 &self,
434 environment: &AHashMap<String, String>,
435 variable_patterns: &[String],
436 value_conflicts: &[(String, String)],
437 strategy: &ParadoxStrategy,
438 ) -> Result<Vec<Paradox>> {
439 let mut conflicts = Vec::new();
440
441 for (conflict_a, conflict_b) in value_conflicts {
443 for pattern in variable_patterns {
444 if let Some(value) = environment.get(pattern) {
445 if (value.contains(conflict_a) && value.contains(conflict_b))
446 || (environment.values().any(|v| v.contains(conflict_a))
447 && environment.values().any(|v| v.contains(conflict_b)))
448 {
449 let paradox = Paradox {
450 id: Uuid::new_v4(),
451 name: "Environmental Conflict".to_string(),
452 description: format!(
453 "Environment contains conflicting values: '{}' and '{}'",
454 conflict_a, conflict_b
455 ),
456 conflicting_elements: vec![
457 ParadoxElement {
458 element_type: ParadoxElementType::Environment,
459 element_id: conflict_a.clone(),
460 confidence: 1.0,
461 evidence: vec![format!(
462 "Pattern: {}, Value: {}",
463 pattern, value
464 )],
465 perspective: None,
466 },
467 ParadoxElement {
468 element_type: ParadoxElementType::Environment,
469 element_id: conflict_b.clone(),
470 confidence: 1.0,
471 evidence: vec![format!(
472 "Pattern: {}, Value: {}",
473 pattern, value
474 )],
475 perspective: None,
476 },
477 ],
478 severity: ParadoxSeverity::Medium,
479 resolution_strategy: strategy.clone(),
480 created_at: Utc::now(),
481 resolved_at: None,
482 resolution_outcome: None,
483 metadata: AHashMap::from([
484 ("pattern".to_string(), serde_json::json!(pattern)),
485 ("detected_value".to_string(), serde_json::json!(value)),
486 ]),
487 };
488
489 conflicts.push(paradox);
490 }
491 }
492 }
493 }
494
495 Ok(conflicts)
496 }
497
498 pub fn resolve_paradox(&mut self, paradox_id: Uuid) -> Result<ParadoxResolution> {
500 let (strategy, conflicting_elements, description) = {
502 let paradox = self.active_paradoxes.get(¶dox_id).ok_or_else(|| {
503 CasialError::ParadoxTimeout(format!("Paradox {} not found", paradox_id))
504 })?;
505 (paradox.resolution_strategy.clone(), paradox.conflicting_elements.clone(), paradox.description.clone())
506 };
507
508 let start_time = std::time::Instant::now();
509
510 let resolution = match &strategy {
511 ParadoxStrategy::Ignore => {
512 ParadoxResolution {
514 strategy_used: ParadoxStrategy::Ignore,
515 resolution_time_ms: start_time.elapsed().as_secs_f64() * 1000.0,
516 confidence_impact: 0.0,
517 synthesis_result: None,
518 chosen_elements: vec![],
519 metadata: AHashMap::new(),
520 }
521 }
522 ParadoxStrategy::Coexist => {
523 ParadoxResolution {
525 strategy_used: ParadoxStrategy::Coexist,
526 resolution_time_ms: start_time.elapsed().as_secs_f64() * 1000.0,
527 confidence_impact: -0.1, synthesis_result: Some("Multiple perspectives maintained".to_string()),
529 chosen_elements: conflicting_elements
530 .iter()
531 .map(|e| e.element_id.clone())
532 .collect(),
533 metadata: AHashMap::new(),
534 }
535 }
536 ParadoxStrategy::Synthesize => {
537 let synthesis = self.synthesize_paradox_elements(&conflicting_elements);
539 ParadoxResolution {
540 strategy_used: ParadoxStrategy::Synthesize,
541 resolution_time_ms: start_time.elapsed().as_secs_f64() * 1000.0,
542 confidence_impact: 0.1, synthesis_result: Some(synthesis),
544 chosen_elements: vec!["synthesized".to_string()],
545 metadata: AHashMap::new(),
546 }
547 }
548 ParadoxStrategy::Expose => {
549 ParadoxResolution {
551 strategy_used: ParadoxStrategy::Expose,
552 resolution_time_ms: start_time.elapsed().as_secs_f64() * 1000.0,
553 confidence_impact: 0.0,
554 synthesis_result: Some(format!("PARADOX DETECTED: {}", description)),
555 chosen_elements: conflicting_elements
556 .iter()
557 .map(|e| e.element_id.clone())
558 .collect(),
559 metadata: AHashMap::new(),
560 }
561 }
562 };
563
564 let paradox = self.active_paradoxes.get_mut(¶dox_id).unwrap(); paradox.resolved_at = Some(Utc::now());
569 paradox.resolution_outcome = Some(resolution.clone());
570
571 let resolved_paradox = paradox.clone();
573 self.resolved_paradoxes.insert(paradox_id, resolved_paradox);
574 self.active_paradoxes.remove(¶dox_id);
575
576 self.resolution_history.push(ParadoxResolutionEvent {
578 paradox_id,
579 event_type: ResolutionEventType::Resolved,
580 timestamp: Utc::now(),
581 details: serde_json::json!({
582 "strategy": resolution.strategy_used,
583 "resolution_time_ms": resolution.resolution_time_ms
584 }),
585 });
586
587 Ok(resolution)
588 }
589
590 fn synthesize_paradox_elements(&self, conflicting_elements: &[ParadoxElement]) -> String {
592 match conflicting_elements.len() {
595 2 => {
596 let element_a = &conflicting_elements[0];
597 let element_b = &conflicting_elements[1];
598
599 format!(
600 "SYNTHESIS: Both '{}' and '{}' perspectives have validity. \
601 Consider '{}' in contexts requiring {} confidence, \
602 and '{}' where {} confidence is appropriate. \
603 The apparent contradiction may reflect different operational contexts.",
604 element_a.element_id,
605 element_b.element_id,
606 element_a.element_id,
607 element_a.confidence,
608 element_b.element_id,
609 element_b.confidence
610 )
611 }
612 _ => {
613 format!(
614 "SYNTHESIS: Multiple conflicting perspectives detected ({}). \
615 Consider contextual application based on specific circumstances \
616 and confidence levels of each perspective.",
617 conflicting_elements.len()
618 )
619 }
620 }
621 }
622
623 fn calculate_content_similarity(&self, content_a: &str, content_b: &str) -> f64 {
625 let words_a: std::collections::HashSet<&str> = content_a.split_whitespace().collect();
628 let words_b: std::collections::HashSet<&str> = content_b.split_whitespace().collect();
629
630 let intersection = words_a.intersection(&words_b).count();
631 let union = words_a.union(&words_b).count();
632
633 if union == 0 {
634 0.0
635 } else {
636 intersection as f64 / union as f64
637 }
638 }
639
640 fn calculate_perception_overlap(
642 &self,
643 perception_a: &crate::Perception,
644 perception_b: &crate::Perception,
645 ) -> f64 {
646 self.calculate_content_similarity(&perception_a.description, &perception_b.description)
648 }
649
650 pub fn get_statistics(&self) -> ParadoxManagerStats {
652 let active_count = self.active_paradoxes.len();
653 let resolved_count = self.resolved_paradoxes.len();
654 let total_count = active_count + resolved_count;
655
656 let avg_resolution_time = if resolved_count > 0 {
657 self.resolved_paradoxes
658 .values()
659 .filter_map(|p| p.resolution_outcome.as_ref())
660 .map(|r| r.resolution_time_ms)
661 .sum::<f64>()
662 / resolved_count as f64
663 } else {
664 0.0
665 };
666
667 let strategy_distribution: AHashMap<String, usize> = self
668 .resolved_paradoxes
669 .values()
670 .filter_map(|p| p.resolution_outcome.as_ref())
671 .fold(AHashMap::new(), |mut acc, outcome| {
672 let strategy_name = format!("{:?}", outcome.strategy_used);
673 *acc.entry(strategy_name).or_insert(0) += 1;
674 acc
675 });
676
677 ParadoxManagerStats {
678 active_paradoxes: active_count,
679 resolved_paradoxes: resolved_count,
680 total_paradoxes: total_count,
681 average_resolution_time_ms: avg_resolution_time,
682 strategy_distribution,
683 }
684 }
685}
686
687#[derive(Debug, Clone, Serialize, Deserialize)]
689pub struct ParadoxManagerStats {
690 pub active_paradoxes: usize,
691 pub resolved_paradoxes: usize,
692 pub total_paradoxes: usize,
693 pub average_resolution_time_ms: f64,
694 pub strategy_distribution: AHashMap<String, usize>,
695}
696
697impl Default for ParadoxManager {
698 fn default() -> Self {
699 Self::new()
700 }
701}
702
703#[cfg(test)]
704mod tests {
705 use super::*;
706
707 #[test]
708 fn test_paradox_creation() {
709 let paradox = Paradox {
710 id: Uuid::new_v4(),
711 name: "Test Paradox".to_string(),
712 description: "A test paradox".to_string(),
713 conflicting_elements: vec![],
714 severity: ParadoxSeverity::Low,
715 resolution_strategy: ParadoxStrategy::Ignore,
716 created_at: Utc::now(),
717 resolved_at: None,
718 resolution_outcome: None,
719 metadata: AHashMap::new(),
720 };
721
722 assert_eq!(paradox.severity, ParadoxSeverity::Low);
723 assert!(paradox.resolved_at.is_none());
724 }
725
726 #[test]
727 fn test_paradox_manager() {
728 let manager = ParadoxManager::new();
729 assert_eq!(manager.active_paradoxes.len(), 0);
730 assert_eq!(manager.resolved_paradoxes.len(), 0);
731 assert!(!manager.detection_rules.is_empty()); }
733
734 #[test]
735 fn test_content_similarity() {
736 let manager = ParadoxManager::new();
737 let similarity = manager.calculate_content_similarity("hello world", "hello universe");
738 assert!(similarity > 0.0);
739 assert!(similarity < 1.0);
740 }
741}