truthound-dashboard 1.3.0__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.0.dist-info → truthound_dashboard-1.4.0.dist-info}/METADATA +142 -18
  162. truthound_dashboard-1.4.0.dist-info/RECORD +239 -0
  163. truthound_dashboard/static/assets/index-BCA8H1hO.js +0 -574
  164. truthound_dashboard/static/assets/index-BNsSQ2fN.css +0 -1
  165. truthound_dashboard/static/assets/unmerged_dictionaries-CsJWCRx9.js +0 -1
  166. truthound_dashboard-1.3.0.dist-info/RECORD +0 -110
  167. {truthound_dashboard-1.3.0.dist-info → truthound_dashboard-1.4.0.dist-info}/WHEEL +0 -0
  168. {truthound_dashboard-1.3.0.dist-info → truthound_dashboard-1.4.0.dist-info}/entry_points.txt +0 -0
  169. {truthound_dashboard-1.3.0.dist-info → truthound_dashboard-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,258 @@
1
+ """API endpoints for Unified Alerts.
2
+
3
+ Provides REST API for:
4
+ - Listing all alerts from all sources
5
+ - Alert summary and statistics
6
+ - Acknowledging and resolving alerts
7
+ - Alert correlation queries
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from fastapi import APIRouter, Depends, HTTPException, Query
13
+ from sqlalchemy.ext.asyncio import AsyncSession
14
+
15
+ from ..core.unified_alerts import UnifiedAlertsService
16
+ from ..db import get_session
17
+ from ..schemas.base import DataResponse
18
+ from ..schemas.unified_alerts import (
19
+ AcknowledgeAlertRequest,
20
+ AlertCorrelation,
21
+ AlertCorrelationResponse,
22
+ AlertSeverity,
23
+ AlertSource,
24
+ AlertStatus,
25
+ AlertSummary,
26
+ BulkAlertActionRequest,
27
+ BulkAlertActionResponse,
28
+ ResolveAlertRequest,
29
+ UnifiedAlertListResponse,
30
+ UnifiedAlertResponse,
31
+ )
32
+
33
+ router = APIRouter(prefix="/alerts", tags=["alerts"])
34
+
35
+
36
+ def get_service(session: AsyncSession = Depends(get_session)) -> UnifiedAlertsService:
37
+ """Get unified alerts service instance."""
38
+ return UnifiedAlertsService(session)
39
+
40
+
41
+ # =============================================================================
42
+ # Alert List Endpoints
43
+ # =============================================================================
44
+
45
+
46
+ @router.get("", response_model=DataResponse[UnifiedAlertListResponse])
47
+ async def list_alerts(
48
+ source: AlertSource | None = None,
49
+ severity: AlertSeverity | None = None,
50
+ status: AlertStatus | None = None,
51
+ source_name: str | None = Query(None, description="Filter by source name (partial match)"),
52
+ time_range_hours: int | None = Query(24, ge=1, le=720, description="Time range in hours"),
53
+ offset: int = Query(0, ge=0),
54
+ limit: int = Query(50, ge=1, le=200),
55
+ service: UnifiedAlertsService = Depends(get_service),
56
+ ):
57
+ """List all unified alerts from all sources.
58
+
59
+ Aggregates alerts from:
60
+ - Model monitoring
61
+ - Drift monitoring
62
+ - Anomaly detection
63
+ - Validation failures
64
+ """
65
+ alerts, total = await service.get_all_alerts(
66
+ source=source,
67
+ severity=severity,
68
+ status=status,
69
+ source_name=source_name,
70
+ time_range_hours=time_range_hours,
71
+ offset=offset,
72
+ limit=limit,
73
+ )
74
+
75
+ return DataResponse(
76
+ data=UnifiedAlertListResponse(
77
+ items=alerts,
78
+ total=total,
79
+ offset=offset,
80
+ limit=limit,
81
+ )
82
+ )
83
+
84
+
85
+ @router.get("/summary", response_model=DataResponse[AlertSummary])
86
+ async def get_alert_summary(
87
+ time_range_hours: int = Query(24, ge=1, le=720, description="Time range for summary"),
88
+ service: UnifiedAlertsService = Depends(get_service),
89
+ ):
90
+ """Get alert summary statistics.
91
+
92
+ Returns counts by severity, source, status, and trend data.
93
+ """
94
+ summary = await service.get_alert_summary(time_range_hours=time_range_hours)
95
+ return DataResponse(data=summary)
96
+
97
+
98
+ @router.get("/count")
99
+ async def get_alert_count(
100
+ status: AlertStatus | None = Query(None, description="Filter by status"),
101
+ service: UnifiedAlertsService = Depends(get_service),
102
+ ):
103
+ """Get quick alert count (for badges).
104
+
105
+ Returns just the count of alerts matching the criteria.
106
+ """
107
+ _, total = await service.get_all_alerts(
108
+ status=status,
109
+ time_range_hours=24,
110
+ limit=1, # We only need the count
111
+ )
112
+
113
+ return DataResponse(
114
+ data={
115
+ "count": total,
116
+ "status_filter": status.value if status else "all",
117
+ }
118
+ )
119
+
120
+
121
+ @router.get("/{alert_id}", response_model=DataResponse[UnifiedAlertResponse])
122
+ async def get_alert(
123
+ alert_id: str,
124
+ service: UnifiedAlertsService = Depends(get_service),
125
+ ):
126
+ """Get a specific alert by unified ID."""
127
+ alert = await service.get_alert_by_id(alert_id)
128
+ if not alert:
129
+ raise HTTPException(status_code=404, detail="Alert not found")
130
+
131
+ return DataResponse(data=alert)
132
+
133
+
134
+ # =============================================================================
135
+ # Alert Action Endpoints
136
+ # =============================================================================
137
+
138
+
139
+ @router.post("/{alert_id}/acknowledge", response_model=DataResponse[UnifiedAlertResponse])
140
+ async def acknowledge_alert(
141
+ alert_id: str,
142
+ request: AcknowledgeAlertRequest,
143
+ service: UnifiedAlertsService = Depends(get_service),
144
+ ):
145
+ """Acknowledge an alert.
146
+
147
+ Note: Not all alert types support acknowledgement.
148
+ Validation and anomaly alerts are derived from their source data.
149
+ """
150
+ alert = await service.acknowledge_alert(alert_id, request.actor, request.message)
151
+ if not alert:
152
+ raise HTTPException(
153
+ status_code=404,
154
+ detail="Alert not found or not eligible for acknowledgement",
155
+ )
156
+
157
+ return DataResponse(data=alert)
158
+
159
+
160
+ @router.post("/{alert_id}/resolve", response_model=DataResponse[UnifiedAlertResponse])
161
+ async def resolve_alert(
162
+ alert_id: str,
163
+ request: ResolveAlertRequest,
164
+ service: UnifiedAlertsService = Depends(get_service),
165
+ ):
166
+ """Resolve an alert.
167
+
168
+ Note: Not all alert types support resolution.
169
+ Validation and anomaly alerts are derived from their source data.
170
+ """
171
+ alert = await service.resolve_alert(alert_id, request.actor, request.message)
172
+ if not alert:
173
+ raise HTTPException(
174
+ status_code=404,
175
+ detail="Alert not found or not eligible for resolution",
176
+ )
177
+
178
+ return DataResponse(data=alert)
179
+
180
+
181
+ # =============================================================================
182
+ # Bulk Action Endpoints
183
+ # =============================================================================
184
+
185
+
186
+ @router.post("/bulk/acknowledge", response_model=DataResponse[BulkAlertActionResponse])
187
+ async def bulk_acknowledge_alerts(
188
+ request: BulkAlertActionRequest,
189
+ service: UnifiedAlertsService = Depends(get_service),
190
+ ):
191
+ """Bulk acknowledge multiple alerts."""
192
+ success, failed, failed_ids = await service.bulk_acknowledge(
193
+ request.alert_ids,
194
+ request.actor,
195
+ request.message,
196
+ )
197
+
198
+ return DataResponse(
199
+ data=BulkAlertActionResponse(
200
+ success_count=success,
201
+ failed_count=failed,
202
+ failed_ids=failed_ids,
203
+ )
204
+ )
205
+
206
+
207
+ @router.post("/bulk/resolve", response_model=DataResponse[BulkAlertActionResponse])
208
+ async def bulk_resolve_alerts(
209
+ request: BulkAlertActionRequest,
210
+ service: UnifiedAlertsService = Depends(get_service),
211
+ ):
212
+ """Bulk resolve multiple alerts."""
213
+ success, failed, failed_ids = await service.bulk_resolve(
214
+ request.alert_ids,
215
+ request.actor,
216
+ request.message,
217
+ )
218
+
219
+ return DataResponse(
220
+ data=BulkAlertActionResponse(
221
+ success_count=success,
222
+ failed_count=failed,
223
+ failed_ids=failed_ids,
224
+ )
225
+ )
226
+
227
+
228
+ # =============================================================================
229
+ # Correlation Endpoints
230
+ # =============================================================================
231
+
232
+
233
+ @router.get("/{alert_id}/correlations", response_model=DataResponse[AlertCorrelationResponse])
234
+ async def get_alert_correlations(
235
+ alert_id: str,
236
+ time_window_hours: int = Query(1, ge=1, le=24, description="Correlation time window"),
237
+ service: UnifiedAlertsService = Depends(get_service),
238
+ ):
239
+ """Get correlated alerts for a given alert.
240
+
241
+ Finds alerts that are related by:
242
+ - Same source (data source, model)
243
+ - Similar time frame
244
+ - Similar severity
245
+ """
246
+ correlations = await service.get_alert_correlations(
247
+ alert_id,
248
+ time_window_hours=time_window_hours,
249
+ )
250
+
251
+ total_correlated = sum(len(c.related_alerts) for c in correlations)
252
+
253
+ return DataResponse(
254
+ data=AlertCorrelationResponse(
255
+ correlations=correlations,
256
+ total_correlated=total_correlated,
257
+ )
258
+ )