llm_analytics_hub/models/
correlation.rs

1//! Correlation Schemas
2//!
3//! Cross-module event correlation patterns, anomaly correlation, causality tracking,
4//! and graph-based relationship models for understanding system behavior.
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use uuid::Uuid;
10
11use crate::schemas::events::{EventType, Severity, SourceModule};
12
13/// Correlation identifier linking related events
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
15pub struct CorrelationId(pub Uuid);
16
17impl CorrelationId {
18    pub fn new() -> Self {
19        Self(Uuid::new_v4())
20    }
21}
22
23impl Default for CorrelationId {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29/// Event correlation representing relationships between events
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct EventCorrelation {
32    /// Unique correlation identifier
33    pub correlation_id: CorrelationId,
34
35    /// Type of correlation
36    pub correlation_type: CorrelationType,
37
38    /// Events involved in this correlation
39    pub events: Vec<CorrelatedEvent>,
40
41    /// Correlation strength (0.0 to 1.0)
42    pub strength: f64,
43
44    /// Confidence level of the correlation (0.0 to 1.0)
45    pub confidence: f64,
46
47    /// Time window of the correlation
48    pub time_window: TimeWindow,
49
50    /// Correlation pattern matched
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub pattern: Option<CorrelationPattern>,
53
54    /// Timestamp when correlation was identified
55    pub detected_at: DateTime<Utc>,
56
57    /// Additional metadata
58    #[serde(default)]
59    pub metadata: HashMap<String, String>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct TimeWindow {
64    pub start: DateTime<Utc>,
65    pub end: DateTime<Utc>,
66}
67
68impl TimeWindow {
69    pub fn duration_seconds(&self) -> i64 {
70        (self.end - self.start).num_seconds()
71    }
72}
73
74/// Types of correlations between events
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
76#[serde(rename_all = "snake_case")]
77pub enum CorrelationType {
78    /// Events caused by the same root cause
79    CausalChain,
80
81    /// Events occurring simultaneously
82    Temporal,
83
84    /// Similar patterns across different modules
85    PatternMatch,
86
87    /// Anomalous behavior correlation
88    Anomaly,
89
90    /// Cost impact correlation
91    CostImpact,
92
93    /// Security incident correlation
94    SecurityIncident,
95
96    /// Performance degradation chain
97    PerformanceDegradation,
98
99    /// Compliance violation cascade
100    ComplianceCascade,
101}
102
103/// Individual event in a correlation
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct CorrelatedEvent {
106    /// Event identifier
107    pub event_id: Uuid,
108
109    /// Source module
110    pub source_module: SourceModule,
111
112    /// Event type
113    pub event_type: EventType,
114
115    /// Event severity
116    pub severity: Severity,
117
118    /// Event timestamp
119    pub timestamp: DateTime<Utc>,
120
121    /// Role in the correlation
122    pub role: EventRole,
123
124    /// Event summary/description
125    pub summary: String,
126
127    /// Key metrics from the event
128    #[serde(default)]
129    pub metrics: HashMap<String, f64>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
133#[serde(rename_all = "snake_case")]
134pub enum EventRole {
135    /// Root cause or trigger event
136    RootCause,
137
138    /// Contributing factor
139    Contributor,
140
141    /// Symptom or effect
142    Effect,
143
144    /// Related but not causal
145    Related,
146}
147
148/// Predefined correlation patterns
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct CorrelationPattern {
151    /// Pattern identifier
152    pub pattern_id: String,
153
154    /// Pattern name
155    pub name: String,
156
157    /// Pattern description
158    pub description: String,
159
160    /// Expected modules involved
161    pub modules: Vec<SourceModule>,
162
163    /// Expected event sequence
164    pub sequence: Vec<PatternStep>,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct PatternStep {
169    pub step_number: u32,
170    pub module: SourceModule,
171    pub event_type: EventType,
172    pub time_offset_ms: Option<i64>, // Relative to previous step
173    pub conditions: HashMap<String, String>,
174}
175
176/// Anomaly correlation for detecting related anomalies
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct AnomalyCorrelation {
179    /// Correlation identifier
180    pub correlation_id: CorrelationId,
181
182    /// Detected anomalies
183    pub anomalies: Vec<AnomalyEvent>,
184
185    /// Correlation strength
186    pub strength: f64,
187
188    /// Root cause analysis result
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub root_cause: Option<RootCauseAnalysis>,
191
192    /// Impact assessment
193    pub impact: ImpactAssessment,
194
195    /// Detection timestamp
196    pub detected_at: DateTime<Utc>,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct AnomalyEvent {
201    /// Event identifier
202    pub event_id: Uuid,
203
204    /// Source module
205    pub source_module: SourceModule,
206
207    /// Anomaly type
208    pub anomaly_type: AnomalyType,
209
210    /// Anomaly score (0.0 to 1.0, higher = more anomalous)
211    pub anomaly_score: f64,
212
213    /// Baseline value
214    pub baseline: f64,
215
216    /// Observed value
217    pub observed: f64,
218
219    /// Deviation from baseline
220    pub deviation: f64,
221
222    /// Timestamp
223    pub timestamp: DateTime<Utc>,
224
225    /// Affected metric
226    pub metric: String,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
230#[serde(rename_all = "snake_case")]
231pub enum AnomalyType {
232    /// Value exceeds expected range
233    Spike,
234
235    /// Value below expected range
236    Drop,
237
238    /// Unusual pattern or trend
239    PatternDeviation,
240
241    /// Unexpected frequency
242    FrequencyAnomaly,
243
244    /// Distribution shift
245    DistributionShift,
246}
247
248/// Root cause analysis result
249#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct RootCauseAnalysis {
251    /// Identified root cause event
252    pub root_event_id: Uuid,
253
254    /// Confidence in root cause identification (0.0 to 1.0)
255    pub confidence: f64,
256
257    /// Causal chain from root to effects
258    pub causal_chain: Vec<CausalLink>,
259
260    /// Contributing factors
261    pub contributing_factors: Vec<String>,
262
263    /// Recommended actions
264    pub recommendations: Vec<String>,
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct CausalLink {
269    pub from_event_id: Uuid,
270    pub to_event_id: Uuid,
271    pub relationship: CausalRelationship,
272    pub strength: f64,
273    pub time_delta_ms: i64,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
277#[serde(rename_all = "snake_case")]
278pub enum CausalRelationship {
279    DirectCause,
280    IndirectCause,
281    Correlation,
282    Amplification,
283}
284
285/// Impact assessment of correlated events
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct ImpactAssessment {
288    /// Overall impact severity
289    pub severity: ImpactSeverity,
290
291    /// Affected modules
292    pub affected_modules: Vec<SourceModule>,
293
294    /// Performance impact
295    #[serde(skip_serializing_if = "Option::is_none")]
296    pub performance_impact: Option<PerformanceImpact>,
297
298    /// Cost impact
299    #[serde(skip_serializing_if = "Option::is_none")]
300    pub cost_impact: Option<CostImpact>,
301
302    /// Security impact
303    #[serde(skip_serializing_if = "Option::is_none")]
304    pub security_impact: Option<SecurityImpact>,
305
306    /// Business impact
307    #[serde(skip_serializing_if = "Option::is_none")]
308    pub business_impact: Option<BusinessImpact>,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
312#[serde(rename_all = "lowercase")]
313pub enum ImpactSeverity {
314    Negligible,
315    Low,
316    Medium,
317    High,
318    Critical,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct PerformanceImpact {
323    pub latency_increase_percent: f64,
324    pub throughput_decrease_percent: f64,
325    pub error_rate_increase_percent: f64,
326    pub affected_requests: u64,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
330pub struct CostImpact {
331    pub additional_cost_usd: f64,
332    pub cost_increase_percent: f64,
333    pub wasted_resources_usd: f64,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct SecurityImpact {
338    pub threats_detected: u64,
339    pub vulnerabilities_exposed: u64,
340    pub data_at_risk: bool,
341    pub compliance_violations: u64,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct BusinessImpact {
346    pub users_affected: u64,
347    pub sla_violations: u64,
348    pub revenue_impact_usd: Option<f64>,
349    pub reputation_risk: ReputationRisk,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
353#[serde(rename_all = "lowercase")]
354pub enum ReputationRisk {
355    None,
356    Low,
357    Medium,
358    High,
359    Severe,
360}
361
362/// Graph-based event relationship model
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct EventGraph {
365    /// Graph identifier
366    pub graph_id: String,
367
368    /// Graph time range
369    pub time_range: TimeWindow,
370
371    /// Nodes (events)
372    pub nodes: Vec<EventNode>,
373
374    /// Edges (relationships)
375    pub edges: Vec<EventEdge>,
376
377    /// Graph metadata
378    pub metadata: GraphMetadata,
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct EventNode {
383    pub node_id: String,
384    pub event_id: Uuid,
385    pub source_module: SourceModule,
386    pub event_type: EventType,
387    pub timestamp: DateTime<Utc>,
388    pub attributes: HashMap<String, String>,
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize)]
392pub struct EventEdge {
393    pub edge_id: String,
394    pub from_node: String,
395    pub to_node: String,
396    pub relationship_type: EdgeRelationship,
397    pub weight: f64,
398    pub properties: HashMap<String, String>,
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
402#[serde(rename_all = "snake_case")]
403pub enum EdgeRelationship {
404    Causes,
405    TriggeredBy,
406    RelatedTo,
407    Precedes,
408    Follows,
409    CorrelatesWith,
410    Amplifies,
411    Mitigates,
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct GraphMetadata {
416    pub node_count: usize,
417    pub edge_count: usize,
418    pub connected_components: usize,
419    pub avg_degree: f64,
420    pub density: f64,
421}
422
423/// Correlation query for finding related events
424#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct CorrelationQuery {
426    /// Seed event to find correlations for
427    pub seed_event_id: Uuid,
428
429    /// Time window to search within
430    pub time_window_minutes: i64,
431
432    /// Minimum correlation strength
433    #[serde(default = "default_min_strength")]
434    pub min_strength: f64,
435
436    /// Correlation types to include
437    #[serde(default)]
438    pub correlation_types: Vec<CorrelationType>,
439
440    /// Modules to include in search
441    #[serde(default)]
442    pub include_modules: Vec<SourceModule>,
443
444    /// Maximum depth for causal chain
445    #[serde(default = "default_max_depth")]
446    pub max_depth: u32,
447}
448
449fn default_min_strength() -> f64 {
450    0.7
451}
452
453fn default_max_depth() -> u32 {
454    5
455}
456
457/// Cross-module correlation configuration
458#[derive(Debug, Clone, Serialize, Deserialize)]
459pub struct CorrelationConfig {
460    /// Enable automatic correlation detection
461    pub auto_detect: bool,
462
463    /// Correlation patterns to monitor
464    pub patterns: Vec<CorrelationPattern>,
465
466    /// Time window for correlation (minutes)
467    pub correlation_window_minutes: i64,
468
469    /// Minimum events required for correlation
470    pub min_events: usize,
471
472    /// Alert thresholds
473    pub alert_thresholds: AlertThresholds,
474}
475
476#[derive(Debug, Clone, Serialize, Deserialize)]
477pub struct AlertThresholds {
478    pub min_correlation_strength: f64,
479    pub min_anomaly_score: f64,
480    pub critical_impact_threshold: f64,
481}
482
483impl Default for CorrelationConfig {
484    fn default() -> Self {
485        Self {
486            auto_detect: true,
487            patterns: Vec::new(),
488            correlation_window_minutes: 60,
489            min_events: 2,
490            alert_thresholds: AlertThresholds {
491                min_correlation_strength: 0.8,
492                min_anomaly_score: 0.7,
493                critical_impact_threshold: 0.9,
494            },
495        }
496    }
497}
498
499#[cfg(test)]
500mod tests {
501    use super::*;
502
503    #[test]
504    fn test_correlation_id_creation() {
505        let id1 = CorrelationId::new();
506        let id2 = CorrelationId::new();
507        assert_ne!(id1, id2);
508    }
509
510    #[test]
511    fn test_time_window_duration() {
512        let start = Utc::now();
513        let end = start + chrono::Duration::minutes(30);
514        let window = TimeWindow { start, end };
515        assert_eq!(window.duration_seconds(), 1800);
516    }
517
518    #[test]
519    fn test_event_correlation_serialization() {
520        let correlation = EventCorrelation {
521            correlation_id: CorrelationId::new(),
522            correlation_type: CorrelationType::CausalChain,
523            events: vec![],
524            strength: 0.85,
525            confidence: 0.9,
526            time_window: TimeWindow {
527                start: Utc::now(),
528                end: Utc::now() + chrono::Duration::minutes(10),
529            },
530            pattern: None,
531            detected_at: Utc::now(),
532            metadata: HashMap::new(),
533        };
534
535        let json = serde_json::to_string_pretty(&correlation).unwrap();
536        assert!(json.contains("causal_chain"));
537        assert!(json.contains("0.85"));
538    }
539}