truthound-dashboard 1.3.1__py3-none-any.whl → 1.4.1__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.
- truthound_dashboard/api/alerts.py +258 -0
- truthound_dashboard/api/anomaly.py +1302 -0
- truthound_dashboard/api/cross_alerts.py +352 -0
- truthound_dashboard/api/deps.py +143 -0
- truthound_dashboard/api/drift_monitor.py +540 -0
- truthound_dashboard/api/lineage.py +1151 -0
- truthound_dashboard/api/maintenance.py +363 -0
- truthound_dashboard/api/middleware.py +373 -1
- truthound_dashboard/api/model_monitoring.py +805 -0
- truthound_dashboard/api/notifications_advanced.py +2452 -0
- truthound_dashboard/api/plugins.py +2096 -0
- truthound_dashboard/api/profile.py +211 -14
- truthound_dashboard/api/reports.py +853 -0
- truthound_dashboard/api/router.py +147 -0
- truthound_dashboard/api/rule_suggestions.py +310 -0
- truthound_dashboard/api/schema_evolution.py +231 -0
- truthound_dashboard/api/sources.py +47 -3
- truthound_dashboard/api/triggers.py +190 -0
- truthound_dashboard/api/validations.py +13 -0
- truthound_dashboard/api/validators.py +333 -4
- truthound_dashboard/api/versioning.py +309 -0
- truthound_dashboard/api/websocket.py +301 -0
- truthound_dashboard/core/__init__.py +27 -0
- truthound_dashboard/core/anomaly.py +1395 -0
- truthound_dashboard/core/anomaly_explainer.py +633 -0
- truthound_dashboard/core/cache.py +206 -0
- truthound_dashboard/core/cached_services.py +422 -0
- truthound_dashboard/core/charts.py +352 -0
- truthound_dashboard/core/connections.py +1069 -42
- truthound_dashboard/core/cross_alerts.py +837 -0
- truthound_dashboard/core/drift_monitor.py +1477 -0
- truthound_dashboard/core/drift_sampling.py +669 -0
- truthound_dashboard/core/i18n/__init__.py +42 -0
- truthound_dashboard/core/i18n/detector.py +173 -0
- truthound_dashboard/core/i18n/messages.py +564 -0
- truthound_dashboard/core/lineage.py +971 -0
- truthound_dashboard/core/maintenance.py +443 -5
- truthound_dashboard/core/model_monitoring.py +1043 -0
- truthound_dashboard/core/notifications/channels.py +1020 -1
- truthound_dashboard/core/notifications/deduplication/__init__.py +143 -0
- truthound_dashboard/core/notifications/deduplication/policies.py +274 -0
- truthound_dashboard/core/notifications/deduplication/service.py +400 -0
- truthound_dashboard/core/notifications/deduplication/stores.py +2365 -0
- truthound_dashboard/core/notifications/deduplication/strategies.py +422 -0
- truthound_dashboard/core/notifications/dispatcher.py +43 -0
- truthound_dashboard/core/notifications/escalation/__init__.py +149 -0
- truthound_dashboard/core/notifications/escalation/backends.py +1384 -0
- truthound_dashboard/core/notifications/escalation/engine.py +429 -0
- truthound_dashboard/core/notifications/escalation/models.py +336 -0
- truthound_dashboard/core/notifications/escalation/scheduler.py +1187 -0
- truthound_dashboard/core/notifications/escalation/state_machine.py +330 -0
- truthound_dashboard/core/notifications/escalation/stores.py +2896 -0
- truthound_dashboard/core/notifications/events.py +49 -0
- truthound_dashboard/core/notifications/metrics/__init__.py +115 -0
- truthound_dashboard/core/notifications/metrics/base.py +528 -0
- truthound_dashboard/core/notifications/metrics/collectors.py +583 -0
- truthound_dashboard/core/notifications/routing/__init__.py +169 -0
- truthound_dashboard/core/notifications/routing/combinators.py +184 -0
- truthound_dashboard/core/notifications/routing/config.py +375 -0
- truthound_dashboard/core/notifications/routing/config_parser.py +867 -0
- truthound_dashboard/core/notifications/routing/engine.py +382 -0
- truthound_dashboard/core/notifications/routing/expression_engine.py +1269 -0
- truthound_dashboard/core/notifications/routing/jinja2_engine.py +774 -0
- truthound_dashboard/core/notifications/routing/rules.py +625 -0
- truthound_dashboard/core/notifications/routing/validator.py +678 -0
- truthound_dashboard/core/notifications/service.py +2 -0
- truthound_dashboard/core/notifications/stats_aggregator.py +850 -0
- truthound_dashboard/core/notifications/throttling/__init__.py +83 -0
- truthound_dashboard/core/notifications/throttling/builder.py +311 -0
- truthound_dashboard/core/notifications/throttling/stores.py +1859 -0
- truthound_dashboard/core/notifications/throttling/throttlers.py +633 -0
- truthound_dashboard/core/openlineage.py +1028 -0
- truthound_dashboard/core/plugins/__init__.py +39 -0
- truthound_dashboard/core/plugins/docs/__init__.py +39 -0
- truthound_dashboard/core/plugins/docs/extractor.py +703 -0
- truthound_dashboard/core/plugins/docs/renderers.py +804 -0
- truthound_dashboard/core/plugins/hooks/__init__.py +63 -0
- truthound_dashboard/core/plugins/hooks/decorators.py +367 -0
- truthound_dashboard/core/plugins/hooks/manager.py +403 -0
- truthound_dashboard/core/plugins/hooks/protocols.py +265 -0
- truthound_dashboard/core/plugins/lifecycle/__init__.py +41 -0
- truthound_dashboard/core/plugins/lifecycle/hot_reload.py +584 -0
- truthound_dashboard/core/plugins/lifecycle/machine.py +419 -0
- truthound_dashboard/core/plugins/lifecycle/states.py +266 -0
- truthound_dashboard/core/plugins/loader.py +504 -0
- truthound_dashboard/core/plugins/registry.py +810 -0
- truthound_dashboard/core/plugins/reporter_executor.py +588 -0
- truthound_dashboard/core/plugins/sandbox/__init__.py +59 -0
- truthound_dashboard/core/plugins/sandbox/code_validator.py +243 -0
- truthound_dashboard/core/plugins/sandbox/engines.py +770 -0
- truthound_dashboard/core/plugins/sandbox/protocols.py +194 -0
- truthound_dashboard/core/plugins/sandbox.py +617 -0
- truthound_dashboard/core/plugins/security/__init__.py +68 -0
- truthound_dashboard/core/plugins/security/analyzer.py +535 -0
- truthound_dashboard/core/plugins/security/policies.py +311 -0
- truthound_dashboard/core/plugins/security/protocols.py +296 -0
- truthound_dashboard/core/plugins/security/signing.py +842 -0
- truthound_dashboard/core/plugins/security.py +446 -0
- truthound_dashboard/core/plugins/validator_executor.py +401 -0
- truthound_dashboard/core/plugins/versioning/__init__.py +51 -0
- truthound_dashboard/core/plugins/versioning/constraints.py +377 -0
- truthound_dashboard/core/plugins/versioning/dependencies.py +541 -0
- truthound_dashboard/core/plugins/versioning/semver.py +266 -0
- truthound_dashboard/core/profile_comparison.py +601 -0
- truthound_dashboard/core/report_history.py +570 -0
- truthound_dashboard/core/reporters/__init__.py +57 -0
- truthound_dashboard/core/reporters/base.py +296 -0
- truthound_dashboard/core/reporters/csv_reporter.py +155 -0
- truthound_dashboard/core/reporters/html_reporter.py +598 -0
- truthound_dashboard/core/reporters/i18n/__init__.py +65 -0
- truthound_dashboard/core/reporters/i18n/base.py +494 -0
- truthound_dashboard/core/reporters/i18n/catalogs.py +930 -0
- truthound_dashboard/core/reporters/json_reporter.py +160 -0
- truthound_dashboard/core/reporters/junit_reporter.py +233 -0
- truthound_dashboard/core/reporters/markdown_reporter.py +207 -0
- truthound_dashboard/core/reporters/pdf_reporter.py +209 -0
- truthound_dashboard/core/reporters/registry.py +272 -0
- truthound_dashboard/core/rule_generator.py +2088 -0
- truthound_dashboard/core/scheduler.py +822 -12
- truthound_dashboard/core/schema_evolution.py +858 -0
- truthound_dashboard/core/services.py +152 -9
- truthound_dashboard/core/statistics.py +718 -0
- truthound_dashboard/core/streaming_anomaly.py +883 -0
- truthound_dashboard/core/triggers/__init__.py +45 -0
- truthound_dashboard/core/triggers/base.py +226 -0
- truthound_dashboard/core/triggers/evaluators.py +609 -0
- truthound_dashboard/core/triggers/factory.py +363 -0
- truthound_dashboard/core/unified_alerts.py +870 -0
- truthound_dashboard/core/validation_limits.py +509 -0
- truthound_dashboard/core/versioning.py +709 -0
- truthound_dashboard/core/websocket/__init__.py +59 -0
- truthound_dashboard/core/websocket/manager.py +512 -0
- truthound_dashboard/core/websocket/messages.py +130 -0
- truthound_dashboard/db/__init__.py +30 -0
- truthound_dashboard/db/models.py +3375 -3
- truthound_dashboard/main.py +22 -0
- truthound_dashboard/schemas/__init__.py +396 -1
- truthound_dashboard/schemas/anomaly.py +1258 -0
- truthound_dashboard/schemas/base.py +4 -0
- truthound_dashboard/schemas/cross_alerts.py +334 -0
- truthound_dashboard/schemas/drift_monitor.py +890 -0
- truthound_dashboard/schemas/lineage.py +428 -0
- truthound_dashboard/schemas/maintenance.py +154 -0
- truthound_dashboard/schemas/model_monitoring.py +374 -0
- truthound_dashboard/schemas/notifications_advanced.py +1363 -0
- truthound_dashboard/schemas/openlineage.py +704 -0
- truthound_dashboard/schemas/plugins.py +1293 -0
- truthound_dashboard/schemas/profile.py +420 -34
- truthound_dashboard/schemas/profile_comparison.py +242 -0
- truthound_dashboard/schemas/reports.py +285 -0
- truthound_dashboard/schemas/rule_suggestion.py +434 -0
- truthound_dashboard/schemas/schema_evolution.py +164 -0
- truthound_dashboard/schemas/source.py +117 -2
- truthound_dashboard/schemas/triggers.py +511 -0
- truthound_dashboard/schemas/unified_alerts.py +223 -0
- truthound_dashboard/schemas/validation.py +25 -1
- truthound_dashboard/schemas/validators/__init__.py +11 -0
- truthound_dashboard/schemas/validators/base.py +151 -0
- truthound_dashboard/schemas/versioning.py +152 -0
- truthound_dashboard/static/index.html +2 -2
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.1.dist-info}/METADATA +147 -23
- truthound_dashboard-1.4.1.dist-info/RECORD +239 -0
- truthound_dashboard/static/assets/index-BZG20KuF.js +0 -586
- truthound_dashboard/static/assets/index-D_HyZ3pb.css +0 -1
- truthound_dashboard/static/assets/unmerged_dictionaries-CtpqQBm0.js +0 -1
- truthound_dashboard-1.3.1.dist-info/RECORD +0 -110
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.1.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.1.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.1.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
|
+
)
|