truthound-dashboard 1.3.1__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. truthound_dashboard/api/alerts.py +258 -0
  2. truthound_dashboard/api/anomaly.py +1302 -0
  3. truthound_dashboard/api/cross_alerts.py +352 -0
  4. truthound_dashboard/api/deps.py +143 -0
  5. truthound_dashboard/api/drift_monitor.py +540 -0
  6. truthound_dashboard/api/lineage.py +1151 -0
  7. truthound_dashboard/api/maintenance.py +363 -0
  8. truthound_dashboard/api/middleware.py +373 -1
  9. truthound_dashboard/api/model_monitoring.py +805 -0
  10. truthound_dashboard/api/notifications_advanced.py +2452 -0
  11. truthound_dashboard/api/plugins.py +2096 -0
  12. truthound_dashboard/api/profile.py +211 -14
  13. truthound_dashboard/api/reports.py +853 -0
  14. truthound_dashboard/api/router.py +147 -0
  15. truthound_dashboard/api/rule_suggestions.py +310 -0
  16. truthound_dashboard/api/schema_evolution.py +231 -0
  17. truthound_dashboard/api/sources.py +47 -3
  18. truthound_dashboard/api/triggers.py +190 -0
  19. truthound_dashboard/api/validations.py +13 -0
  20. truthound_dashboard/api/validators.py +333 -4
  21. truthound_dashboard/api/versioning.py +309 -0
  22. truthound_dashboard/api/websocket.py +301 -0
  23. truthound_dashboard/core/__init__.py +27 -0
  24. truthound_dashboard/core/anomaly.py +1395 -0
  25. truthound_dashboard/core/anomaly_explainer.py +633 -0
  26. truthound_dashboard/core/cache.py +206 -0
  27. truthound_dashboard/core/cached_services.py +422 -0
  28. truthound_dashboard/core/charts.py +352 -0
  29. truthound_dashboard/core/connections.py +1069 -42
  30. truthound_dashboard/core/cross_alerts.py +837 -0
  31. truthound_dashboard/core/drift_monitor.py +1477 -0
  32. truthound_dashboard/core/drift_sampling.py +669 -0
  33. truthound_dashboard/core/i18n/__init__.py +42 -0
  34. truthound_dashboard/core/i18n/detector.py +173 -0
  35. truthound_dashboard/core/i18n/messages.py +564 -0
  36. truthound_dashboard/core/lineage.py +971 -0
  37. truthound_dashboard/core/maintenance.py +443 -5
  38. truthound_dashboard/core/model_monitoring.py +1043 -0
  39. truthound_dashboard/core/notifications/channels.py +1020 -1
  40. truthound_dashboard/core/notifications/deduplication/__init__.py +143 -0
  41. truthound_dashboard/core/notifications/deduplication/policies.py +274 -0
  42. truthound_dashboard/core/notifications/deduplication/service.py +400 -0
  43. truthound_dashboard/core/notifications/deduplication/stores.py +2365 -0
  44. truthound_dashboard/core/notifications/deduplication/strategies.py +422 -0
  45. truthound_dashboard/core/notifications/dispatcher.py +43 -0
  46. truthound_dashboard/core/notifications/escalation/__init__.py +149 -0
  47. truthound_dashboard/core/notifications/escalation/backends.py +1384 -0
  48. truthound_dashboard/core/notifications/escalation/engine.py +429 -0
  49. truthound_dashboard/core/notifications/escalation/models.py +336 -0
  50. truthound_dashboard/core/notifications/escalation/scheduler.py +1187 -0
  51. truthound_dashboard/core/notifications/escalation/state_machine.py +330 -0
  52. truthound_dashboard/core/notifications/escalation/stores.py +2896 -0
  53. truthound_dashboard/core/notifications/events.py +49 -0
  54. truthound_dashboard/core/notifications/metrics/__init__.py +115 -0
  55. truthound_dashboard/core/notifications/metrics/base.py +528 -0
  56. truthound_dashboard/core/notifications/metrics/collectors.py +583 -0
  57. truthound_dashboard/core/notifications/routing/__init__.py +169 -0
  58. truthound_dashboard/core/notifications/routing/combinators.py +184 -0
  59. truthound_dashboard/core/notifications/routing/config.py +375 -0
  60. truthound_dashboard/core/notifications/routing/config_parser.py +867 -0
  61. truthound_dashboard/core/notifications/routing/engine.py +382 -0
  62. truthound_dashboard/core/notifications/routing/expression_engine.py +1269 -0
  63. truthound_dashboard/core/notifications/routing/jinja2_engine.py +774 -0
  64. truthound_dashboard/core/notifications/routing/rules.py +625 -0
  65. truthound_dashboard/core/notifications/routing/validator.py +678 -0
  66. truthound_dashboard/core/notifications/service.py +2 -0
  67. truthound_dashboard/core/notifications/stats_aggregator.py +850 -0
  68. truthound_dashboard/core/notifications/throttling/__init__.py +83 -0
  69. truthound_dashboard/core/notifications/throttling/builder.py +311 -0
  70. truthound_dashboard/core/notifications/throttling/stores.py +1859 -0
  71. truthound_dashboard/core/notifications/throttling/throttlers.py +633 -0
  72. truthound_dashboard/core/openlineage.py +1028 -0
  73. truthound_dashboard/core/plugins/__init__.py +39 -0
  74. truthound_dashboard/core/plugins/docs/__init__.py +39 -0
  75. truthound_dashboard/core/plugins/docs/extractor.py +703 -0
  76. truthound_dashboard/core/plugins/docs/renderers.py +804 -0
  77. truthound_dashboard/core/plugins/hooks/__init__.py +63 -0
  78. truthound_dashboard/core/plugins/hooks/decorators.py +367 -0
  79. truthound_dashboard/core/plugins/hooks/manager.py +403 -0
  80. truthound_dashboard/core/plugins/hooks/protocols.py +265 -0
  81. truthound_dashboard/core/plugins/lifecycle/__init__.py +41 -0
  82. truthound_dashboard/core/plugins/lifecycle/hot_reload.py +584 -0
  83. truthound_dashboard/core/plugins/lifecycle/machine.py +419 -0
  84. truthound_dashboard/core/plugins/lifecycle/states.py +266 -0
  85. truthound_dashboard/core/plugins/loader.py +504 -0
  86. truthound_dashboard/core/plugins/registry.py +810 -0
  87. truthound_dashboard/core/plugins/reporter_executor.py +588 -0
  88. truthound_dashboard/core/plugins/sandbox/__init__.py +59 -0
  89. truthound_dashboard/core/plugins/sandbox/code_validator.py +243 -0
  90. truthound_dashboard/core/plugins/sandbox/engines.py +770 -0
  91. truthound_dashboard/core/plugins/sandbox/protocols.py +194 -0
  92. truthound_dashboard/core/plugins/sandbox.py +617 -0
  93. truthound_dashboard/core/plugins/security/__init__.py +68 -0
  94. truthound_dashboard/core/plugins/security/analyzer.py +535 -0
  95. truthound_dashboard/core/plugins/security/policies.py +311 -0
  96. truthound_dashboard/core/plugins/security/protocols.py +296 -0
  97. truthound_dashboard/core/plugins/security/signing.py +842 -0
  98. truthound_dashboard/core/plugins/security.py +446 -0
  99. truthound_dashboard/core/plugins/validator_executor.py +401 -0
  100. truthound_dashboard/core/plugins/versioning/__init__.py +51 -0
  101. truthound_dashboard/core/plugins/versioning/constraints.py +377 -0
  102. truthound_dashboard/core/plugins/versioning/dependencies.py +541 -0
  103. truthound_dashboard/core/plugins/versioning/semver.py +266 -0
  104. truthound_dashboard/core/profile_comparison.py +601 -0
  105. truthound_dashboard/core/report_history.py +570 -0
  106. truthound_dashboard/core/reporters/__init__.py +57 -0
  107. truthound_dashboard/core/reporters/base.py +296 -0
  108. truthound_dashboard/core/reporters/csv_reporter.py +155 -0
  109. truthound_dashboard/core/reporters/html_reporter.py +598 -0
  110. truthound_dashboard/core/reporters/i18n/__init__.py +65 -0
  111. truthound_dashboard/core/reporters/i18n/base.py +494 -0
  112. truthound_dashboard/core/reporters/i18n/catalogs.py +930 -0
  113. truthound_dashboard/core/reporters/json_reporter.py +160 -0
  114. truthound_dashboard/core/reporters/junit_reporter.py +233 -0
  115. truthound_dashboard/core/reporters/markdown_reporter.py +207 -0
  116. truthound_dashboard/core/reporters/pdf_reporter.py +209 -0
  117. truthound_dashboard/core/reporters/registry.py +272 -0
  118. truthound_dashboard/core/rule_generator.py +2088 -0
  119. truthound_dashboard/core/scheduler.py +822 -12
  120. truthound_dashboard/core/schema_evolution.py +858 -0
  121. truthound_dashboard/core/services.py +152 -9
  122. truthound_dashboard/core/statistics.py +718 -0
  123. truthound_dashboard/core/streaming_anomaly.py +883 -0
  124. truthound_dashboard/core/triggers/__init__.py +45 -0
  125. truthound_dashboard/core/triggers/base.py +226 -0
  126. truthound_dashboard/core/triggers/evaluators.py +609 -0
  127. truthound_dashboard/core/triggers/factory.py +363 -0
  128. truthound_dashboard/core/unified_alerts.py +870 -0
  129. truthound_dashboard/core/validation_limits.py +509 -0
  130. truthound_dashboard/core/versioning.py +709 -0
  131. truthound_dashboard/core/websocket/__init__.py +59 -0
  132. truthound_dashboard/core/websocket/manager.py +512 -0
  133. truthound_dashboard/core/websocket/messages.py +130 -0
  134. truthound_dashboard/db/__init__.py +30 -0
  135. truthound_dashboard/db/models.py +3375 -3
  136. truthound_dashboard/main.py +22 -0
  137. truthound_dashboard/schemas/__init__.py +396 -1
  138. truthound_dashboard/schemas/anomaly.py +1258 -0
  139. truthound_dashboard/schemas/base.py +4 -0
  140. truthound_dashboard/schemas/cross_alerts.py +334 -0
  141. truthound_dashboard/schemas/drift_monitor.py +890 -0
  142. truthound_dashboard/schemas/lineage.py +428 -0
  143. truthound_dashboard/schemas/maintenance.py +154 -0
  144. truthound_dashboard/schemas/model_monitoring.py +374 -0
  145. truthound_dashboard/schemas/notifications_advanced.py +1363 -0
  146. truthound_dashboard/schemas/openlineage.py +704 -0
  147. truthound_dashboard/schemas/plugins.py +1293 -0
  148. truthound_dashboard/schemas/profile.py +420 -34
  149. truthound_dashboard/schemas/profile_comparison.py +242 -0
  150. truthound_dashboard/schemas/reports.py +285 -0
  151. truthound_dashboard/schemas/rule_suggestion.py +434 -0
  152. truthound_dashboard/schemas/schema_evolution.py +164 -0
  153. truthound_dashboard/schemas/source.py +117 -2
  154. truthound_dashboard/schemas/triggers.py +511 -0
  155. truthound_dashboard/schemas/unified_alerts.py +223 -0
  156. truthound_dashboard/schemas/validation.py +25 -1
  157. truthound_dashboard/schemas/validators/__init__.py +11 -0
  158. truthound_dashboard/schemas/validators/base.py +151 -0
  159. truthound_dashboard/schemas/versioning.py +152 -0
  160. truthound_dashboard/static/index.html +2 -2
  161. {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/METADATA +142 -22
  162. truthound_dashboard-1.4.0.dist-info/RECORD +239 -0
  163. truthound_dashboard/static/assets/index-BZG20KuF.js +0 -586
  164. truthound_dashboard/static/assets/index-D_HyZ3pb.css +0 -1
  165. truthound_dashboard/static/assets/unmerged_dictionaries-CtpqQBm0.js +0 -1
  166. truthound_dashboard-1.3.1.dist-info/RECORD +0 -110
  167. {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/WHEEL +0 -0
  168. {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/entry_points.txt +0 -0
  169. {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,330 @@
1
+ """State machine for escalation incident lifecycle.
2
+
3
+ This module implements the state machine that controls valid
4
+ state transitions for escalation incidents.
5
+
6
+ State Transitions:
7
+ PENDING -> TRIGGERED (trigger)
8
+ TRIGGERED -> ACKNOWLEDGED (acknowledge)
9
+ TRIGGERED -> ESCALATED (escalate)
10
+ TRIGGERED -> RESOLVED (resolve)
11
+ ACKNOWLEDGED -> ESCALATED (escalate)
12
+ ACKNOWLEDGED -> RESOLVED (resolve)
13
+ ESCALATED -> ACKNOWLEDGED (acknowledge)
14
+ ESCALATED -> ESCALATED (escalate to next level)
15
+ ESCALATED -> RESOLVED (resolve)
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from datetime import datetime
21
+ from typing import Callable
22
+
23
+ from .models import (
24
+ EscalationEvent,
25
+ EscalationIncident,
26
+ EscalationState,
27
+ StateTransition,
28
+ )
29
+
30
+
31
+ class EscalationStateMachine:
32
+ """State machine for escalation incident lifecycle.
33
+
34
+ Validates and executes state transitions for incidents.
35
+ Maintains transition history via events.
36
+
37
+ Example:
38
+ machine = EscalationStateMachine()
39
+
40
+ # Trigger incident
41
+ incident = machine.trigger(incident)
42
+
43
+ # Acknowledge
44
+ incident = machine.acknowledge(incident, actor="user@example.com")
45
+
46
+ # Resolve
47
+ incident = machine.resolve(incident, actor="user@example.com")
48
+ """
49
+
50
+ # Define valid state transitions
51
+ VALID_TRANSITIONS: list[StateTransition] = [
52
+ # PENDING -> TRIGGERED
53
+ StateTransition(
54
+ from_states=[EscalationState.PENDING],
55
+ to_state=EscalationState.TRIGGERED,
56
+ ),
57
+ # TRIGGERED -> ACKNOWLEDGED
58
+ StateTransition(
59
+ from_states=[EscalationState.TRIGGERED],
60
+ to_state=EscalationState.ACKNOWLEDGED,
61
+ requires_actor=True,
62
+ ),
63
+ # TRIGGERED -> ESCALATED
64
+ StateTransition(
65
+ from_states=[EscalationState.TRIGGERED],
66
+ to_state=EscalationState.ESCALATED,
67
+ ),
68
+ # TRIGGERED -> RESOLVED
69
+ StateTransition(
70
+ from_states=[EscalationState.TRIGGERED],
71
+ to_state=EscalationState.RESOLVED,
72
+ ),
73
+ # ACKNOWLEDGED -> ESCALATED
74
+ StateTransition(
75
+ from_states=[EscalationState.ACKNOWLEDGED],
76
+ to_state=EscalationState.ESCALATED,
77
+ ),
78
+ # ACKNOWLEDGED -> RESOLVED
79
+ StateTransition(
80
+ from_states=[EscalationState.ACKNOWLEDGED],
81
+ to_state=EscalationState.RESOLVED,
82
+ ),
83
+ # ESCALATED -> ACKNOWLEDGED
84
+ StateTransition(
85
+ from_states=[EscalationState.ESCALATED],
86
+ to_state=EscalationState.ACKNOWLEDGED,
87
+ requires_actor=True,
88
+ ),
89
+ # ESCALATED -> ESCALATED (next level)
90
+ StateTransition(
91
+ from_states=[EscalationState.ESCALATED],
92
+ to_state=EscalationState.ESCALATED,
93
+ ),
94
+ # ESCALATED -> RESOLVED
95
+ StateTransition(
96
+ from_states=[EscalationState.ESCALATED],
97
+ to_state=EscalationState.RESOLVED,
98
+ ),
99
+ ]
100
+
101
+ def __init__(
102
+ self,
103
+ on_transition: Callable[[EscalationIncident, EscalationEvent], None] | None = None,
104
+ ) -> None:
105
+ """Initialize state machine.
106
+
107
+ Args:
108
+ on_transition: Optional callback for transitions.
109
+ """
110
+ self.on_transition = on_transition
111
+
112
+ def can_transition(
113
+ self,
114
+ incident: EscalationIncident,
115
+ to_state: EscalationState,
116
+ ) -> bool:
117
+ """Check if transition to state is valid.
118
+
119
+ Args:
120
+ incident: The incident.
121
+ to_state: Target state.
122
+
123
+ Returns:
124
+ True if transition is valid.
125
+ """
126
+ for transition in self.VALID_TRANSITIONS:
127
+ if incident.state in transition.from_states and transition.to_state == to_state:
128
+ return True
129
+ return False
130
+
131
+ def transition(
132
+ self,
133
+ incident: EscalationIncident,
134
+ to_state: EscalationState,
135
+ actor: str | None = None,
136
+ message: str = "",
137
+ metadata: dict | None = None,
138
+ ) -> EscalationIncident:
139
+ """Execute a state transition.
140
+
141
+ Args:
142
+ incident: The incident to transition.
143
+ to_state: Target state.
144
+ actor: Who/what triggered the transition.
145
+ message: Optional transition message.
146
+ metadata: Optional additional data.
147
+
148
+ Returns:
149
+ Updated incident.
150
+
151
+ Raises:
152
+ ValueError: If transition is invalid.
153
+ """
154
+ # Find valid transition
155
+ valid_transition = None
156
+ for transition in self.VALID_TRANSITIONS:
157
+ if incident.state in transition.from_states and transition.to_state == to_state:
158
+ valid_transition = transition
159
+ break
160
+
161
+ if valid_transition is None:
162
+ raise ValueError(
163
+ f"Invalid transition from {incident.state.value} to {to_state.value}"
164
+ )
165
+
166
+ # Check actor requirement
167
+ if valid_transition.requires_actor and not actor:
168
+ raise ValueError(f"Transition to {to_state.value} requires an actor")
169
+
170
+ # Create event
171
+ event = EscalationEvent(
172
+ from_state=incident.state,
173
+ to_state=to_state,
174
+ level=incident.current_level,
175
+ actor=actor,
176
+ message=message,
177
+ metadata=metadata or {},
178
+ )
179
+
180
+ # Update incident
181
+ incident.state = to_state
182
+ incident.updated_at = datetime.utcnow()
183
+ incident.events.append(event)
184
+
185
+ # Call callback
186
+ if self.on_transition:
187
+ self.on_transition(incident, event)
188
+
189
+ return incident
190
+
191
+ def trigger(
192
+ self,
193
+ incident: EscalationIncident,
194
+ message: str = "Escalation triggered",
195
+ ) -> EscalationIncident:
196
+ """Trigger an escalation.
197
+
198
+ Transitions from PENDING to TRIGGERED.
199
+
200
+ Args:
201
+ incident: The incident.
202
+ message: Optional message.
203
+
204
+ Returns:
205
+ Updated incident.
206
+ """
207
+ incident.current_level = 1
208
+ return self.transition(
209
+ incident,
210
+ EscalationState.TRIGGERED,
211
+ actor="system",
212
+ message=message,
213
+ )
214
+
215
+ def acknowledge(
216
+ self,
217
+ incident: EscalationIncident,
218
+ actor: str,
219
+ message: str = "",
220
+ ) -> EscalationIncident:
221
+ """Acknowledge an incident.
222
+
223
+ Transitions to ACKNOWLEDGED state.
224
+
225
+ Args:
226
+ incident: The incident.
227
+ actor: Who acknowledged.
228
+ message: Optional message.
229
+
230
+ Returns:
231
+ Updated incident.
232
+ """
233
+ incident.acknowledged_by = actor
234
+ incident.acknowledged_at = datetime.utcnow()
235
+ return self.transition(
236
+ incident,
237
+ EscalationState.ACKNOWLEDGED,
238
+ actor=actor,
239
+ message=message or f"Acknowledged by {actor}",
240
+ )
241
+
242
+ def escalate(
243
+ self,
244
+ incident: EscalationIncident,
245
+ to_level: int,
246
+ message: str = "",
247
+ ) -> EscalationIncident:
248
+ """Escalate to the next level.
249
+
250
+ Transitions to ESCALATED state.
251
+
252
+ Args:
253
+ incident: The incident.
254
+ to_level: Target escalation level.
255
+ message: Optional message.
256
+
257
+ Returns:
258
+ Updated incident.
259
+ """
260
+ incident.current_level = to_level
261
+ incident.escalation_count += 1
262
+ return self.transition(
263
+ incident,
264
+ EscalationState.ESCALATED,
265
+ actor="system",
266
+ message=message or f"Escalated to level {to_level}",
267
+ metadata={"level": to_level},
268
+ )
269
+
270
+ def resolve(
271
+ self,
272
+ incident: EscalationIncident,
273
+ actor: str | None = None,
274
+ message: str = "",
275
+ auto: bool = False,
276
+ ) -> EscalationIncident:
277
+ """Resolve an incident.
278
+
279
+ Transitions to RESOLVED state.
280
+
281
+ Args:
282
+ incident: The incident.
283
+ actor: Who resolved (None for auto-resolve).
284
+ message: Optional message.
285
+ auto: Whether this is auto-resolution.
286
+
287
+ Returns:
288
+ Updated incident.
289
+ """
290
+ incident.resolved_by = actor or ("system" if auto else None)
291
+ incident.resolved_at = datetime.utcnow()
292
+ incident.next_escalation_at = None
293
+
294
+ return self.transition(
295
+ incident,
296
+ EscalationState.RESOLVED,
297
+ actor=actor or "system",
298
+ message=message or ("Auto-resolved" if auto else f"Resolved by {actor}"),
299
+ metadata={"auto_resolved": auto},
300
+ )
301
+
302
+ def get_valid_transitions(
303
+ self,
304
+ incident: EscalationIncident,
305
+ ) -> list[EscalationState]:
306
+ """Get valid target states from current state.
307
+
308
+ Args:
309
+ incident: The incident.
310
+
311
+ Returns:
312
+ List of valid target states.
313
+ """
314
+ valid = []
315
+ for transition in self.VALID_TRANSITIONS:
316
+ if incident.state in transition.from_states:
317
+ if transition.to_state not in valid:
318
+ valid.append(transition.to_state)
319
+ return valid
320
+
321
+ def is_terminal(self, incident: EscalationIncident) -> bool:
322
+ """Check if incident is in a terminal state.
323
+
324
+ Args:
325
+ incident: The incident.
326
+
327
+ Returns:
328
+ True if no more transitions are possible.
329
+ """
330
+ return incident.state == EscalationState.RESOLVED