File size: 5,890 Bytes
77bcbf1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
"""
Cascade Core - Event and CausationLink primitives.

These are the fundamental data structures that represent causation.
"""

from dataclasses import dataclass, field
from typing import Dict, List, Any, Optional
from datetime import datetime
import time
import uuid


def _generate_event_id() -> str:
    """Generate a unique event ID with timestamp prefix for ordering."""
    timestamp = int(time.time() * 1000000)
    unique = uuid.uuid4().hex[:8]
    return f"evt_{timestamp}_{unique}"


@dataclass
class Event:
    """
    A discrete event in the causation graph.
    
    Events are the nodes in your causation graph. Each event represents
    something that happened in your system at a point in time.
    
    Attributes:
        event_id: Unique identifier (auto-generated if not provided)
        timestamp: Unix timestamp when event occurred
        component: Which system component generated this event
        event_type: Category of event (e.g., 'training', 'inference', 'error')
        data: Arbitrary key-value data associated with the event
        source_signal: The original signal that created this event (for debugging)
        
    Example:
        >>> event = Event(
        ...     timestamp=time.time(),
        ...     component="neural_network",
        ...     event_type="gradient_explosion",
        ...     data={"layer": "fc3", "magnitude": 1e12}
        ... )
    """
    timestamp: float
    component: str
    event_type: str
    data: Dict[str, Any] = field(default_factory=dict)
    event_id: str = field(default_factory=_generate_event_id)
    source_signal: Optional[Any] = field(default=None, repr=False)
    
    def __post_init__(self):
        """Ensure timestamp is float."""
        if isinstance(self.timestamp, datetime):
            self.timestamp = self.timestamp.timestamp()
    
    def to_dict(self) -> Dict[str, Any]:
        """Serialize event to dictionary."""
        return {
            "event_id": self.event_id,
            "timestamp": self.timestamp,
            "component": self.component,
            "event_type": self.event_type,
            "data": self.data,
        }
    
    @classmethod
    def from_dict(cls, d: Dict[str, Any]) -> "Event":
        """Deserialize event from dictionary."""
        return cls(
            event_id=d.get("event_id", _generate_event_id()),
            timestamp=d["timestamp"],
            component=d["component"],
            event_type=d["event_type"],
            data=d.get("data", {}),
        )
    
    def __hash__(self):
        return hash(self.event_id)
    
    def __eq__(self, other):
        if isinstance(other, Event):
            return self.event_id == other.event_id
        return False


@dataclass  
class CausationLink:
    """
    A causal relationship between two events.
    
    Links are the edges in your causation graph. Each link represents
    a cause-effect relationship: event A caused event B.
    
    Attributes:
        from_event: ID of the causing event
        to_event: ID of the caused event
        causation_type: How the causation was detected
            - 'temporal': A happened shortly before B
            - 'correlation': A and B metrics moved together
            - 'threshold': A crossed a threshold triggering B
            - 'direct': Explicit causation declared in code
        strength: Confidence in the causal relationship (0.0 to 1.0)
        explanation: Human-readable explanation of the link
        metrics_involved: Which metrics connect these events
        
    Example:
        >>> link = CausationLink(
        ...     from_event="evt_123",
        ...     to_event="evt_456",
        ...     causation_type="threshold",
        ...     strength=0.95,
        ...     explanation="Loss exceeded 10.0, triggering gradient clipping"
        ... )
    """
    from_event: str
    to_event: str
    causation_type: str  # 'temporal', 'correlation', 'threshold', 'direct'
    strength: float = 1.0
    explanation: str = ""
    metrics_involved: List[str] = field(default_factory=list)
    
    def __post_init__(self):
        """Validate strength is in range."""
        self.strength = max(0.0, min(1.0, self.strength))
    
    def to_dict(self) -> Dict[str, Any]:
        """Serialize link to dictionary."""
        return {
            "from_event": self.from_event,
            "to_event": self.to_event,
            "causation_type": self.causation_type,
            "strength": self.strength,
            "explanation": self.explanation,
            "metrics_involved": self.metrics_involved,
        }
    
    @classmethod
    def from_dict(cls, d: Dict[str, Any]) -> "CausationLink":
        """Deserialize link from dictionary."""
        return cls(
            from_event=d["from_event"],
            to_event=d["to_event"],
            causation_type=d["causation_type"],
            strength=d.get("strength", 1.0),
            explanation=d.get("explanation", ""),
            metrics_involved=d.get("metrics_involved", []),
        )


@dataclass
class CausationChain:
    """
    A chain of causal events from origin to destination.
    
    Represents a full causal path through the graph.
    
    Attributes:
        events: List of events in causal order
        links: List of links connecting the events
        total_strength: Combined strength of all links
        depth: Number of hops in the chain
        narrative: Human-readable story of what happened
    """
    events: List[Event]
    links: List[CausationLink]
    total_strength: float = 1.0
    depth: int = 0
    narrative: str = ""
    
    def __post_init__(self):
        self.depth = len(self.links)
        if not self.total_strength and self.links:
            # Calculate combined strength
            self.total_strength = 1.0
            for link in self.links:
                self.total_strength *= link.strength