truthound-dashboard 1.4.4__py3-none-any.whl → 1.5.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.
- truthound_dashboard/api/alerts.py +75 -86
- truthound_dashboard/api/anomaly.py +7 -13
- truthound_dashboard/api/cross_alerts.py +38 -52
- truthound_dashboard/api/drift.py +49 -59
- truthound_dashboard/api/drift_monitor.py +234 -79
- truthound_dashboard/api/enterprise_sampling.py +498 -0
- truthound_dashboard/api/history.py +57 -5
- truthound_dashboard/api/lineage.py +3 -48
- truthound_dashboard/api/maintenance.py +104 -49
- truthound_dashboard/api/mask.py +1 -2
- truthound_dashboard/api/middleware.py +2 -1
- truthound_dashboard/api/model_monitoring.py +435 -311
- truthound_dashboard/api/notifications.py +227 -191
- truthound_dashboard/api/notifications_advanced.py +21 -20
- truthound_dashboard/api/observability.py +586 -0
- truthound_dashboard/api/plugins.py +2 -433
- truthound_dashboard/api/profile.py +199 -37
- truthound_dashboard/api/quality_reporter.py +701 -0
- truthound_dashboard/api/reports.py +7 -16
- truthound_dashboard/api/router.py +66 -0
- truthound_dashboard/api/rule_suggestions.py +5 -5
- truthound_dashboard/api/scan.py +17 -19
- truthound_dashboard/api/schedules.py +85 -50
- truthound_dashboard/api/schema_evolution.py +6 -6
- truthound_dashboard/api/schema_watcher.py +667 -0
- truthound_dashboard/api/sources.py +98 -27
- truthound_dashboard/api/tiering.py +1323 -0
- truthound_dashboard/api/triggers.py +14 -11
- truthound_dashboard/api/validations.py +12 -11
- truthound_dashboard/api/versioning.py +1 -6
- truthound_dashboard/core/__init__.py +129 -3
- truthound_dashboard/core/actions/__init__.py +62 -0
- truthound_dashboard/core/actions/custom.py +426 -0
- truthound_dashboard/core/actions/notifications.py +910 -0
- truthound_dashboard/core/actions/storage.py +472 -0
- truthound_dashboard/core/actions/webhook.py +281 -0
- truthound_dashboard/core/anomaly.py +262 -67
- truthound_dashboard/core/anomaly_explainer.py +4 -3
- truthound_dashboard/core/backends/__init__.py +67 -0
- truthound_dashboard/core/backends/base.py +299 -0
- truthound_dashboard/core/backends/errors.py +191 -0
- truthound_dashboard/core/backends/factory.py +423 -0
- truthound_dashboard/core/backends/mock_backend.py +451 -0
- truthound_dashboard/core/backends/truthound_backend.py +718 -0
- truthound_dashboard/core/checkpoint/__init__.py +87 -0
- truthound_dashboard/core/checkpoint/adapters.py +814 -0
- truthound_dashboard/core/checkpoint/checkpoint.py +491 -0
- truthound_dashboard/core/checkpoint/runner.py +270 -0
- truthound_dashboard/core/connections.py +437 -10
- truthound_dashboard/core/converters/__init__.py +14 -0
- truthound_dashboard/core/converters/truthound.py +620 -0
- truthound_dashboard/core/cross_alerts.py +540 -320
- truthound_dashboard/core/datasource_factory.py +1672 -0
- truthound_dashboard/core/drift_monitor.py +216 -20
- truthound_dashboard/core/enterprise_sampling.py +1291 -0
- truthound_dashboard/core/interfaces/__init__.py +225 -0
- truthound_dashboard/core/interfaces/actions.py +652 -0
- truthound_dashboard/core/interfaces/base.py +247 -0
- truthound_dashboard/core/interfaces/checkpoint.py +676 -0
- truthound_dashboard/core/interfaces/protocols.py +664 -0
- truthound_dashboard/core/interfaces/reporters.py +650 -0
- truthound_dashboard/core/interfaces/routing.py +646 -0
- truthound_dashboard/core/interfaces/triggers.py +619 -0
- truthound_dashboard/core/lineage.py +407 -71
- truthound_dashboard/core/model_monitoring.py +431 -3
- truthound_dashboard/core/notifications/base.py +4 -0
- truthound_dashboard/core/notifications/channels.py +501 -1203
- truthound_dashboard/core/notifications/deduplication/__init__.py +81 -115
- truthound_dashboard/core/notifications/deduplication/service.py +131 -348
- truthound_dashboard/core/notifications/dispatcher.py +202 -11
- truthound_dashboard/core/notifications/escalation/__init__.py +119 -106
- truthound_dashboard/core/notifications/escalation/engine.py +168 -358
- truthound_dashboard/core/notifications/routing/__init__.py +88 -128
- truthound_dashboard/core/notifications/routing/engine.py +90 -317
- truthound_dashboard/core/notifications/stats_aggregator.py +246 -1
- truthound_dashboard/core/notifications/throttling/__init__.py +67 -50
- truthound_dashboard/core/notifications/throttling/builder.py +117 -255
- truthound_dashboard/core/notifications/truthound_adapter.py +842 -0
- truthound_dashboard/core/phase5/collaboration.py +1 -1
- truthound_dashboard/core/plugins/lifecycle/__init__.py +0 -13
- truthound_dashboard/core/quality_reporter.py +1359 -0
- truthound_dashboard/core/report_history.py +0 -6
- truthound_dashboard/core/reporters/__init__.py +175 -14
- truthound_dashboard/core/reporters/adapters.py +943 -0
- truthound_dashboard/core/reporters/base.py +0 -3
- truthound_dashboard/core/reporters/builtin/__init__.py +18 -0
- truthound_dashboard/core/reporters/builtin/csv_reporter.py +111 -0
- truthound_dashboard/core/reporters/builtin/html_reporter.py +270 -0
- truthound_dashboard/core/reporters/builtin/json_reporter.py +127 -0
- truthound_dashboard/core/reporters/compat.py +266 -0
- truthound_dashboard/core/reporters/csv_reporter.py +2 -35
- truthound_dashboard/core/reporters/factory.py +526 -0
- truthound_dashboard/core/reporters/interfaces.py +745 -0
- truthound_dashboard/core/reporters/registry.py +1 -10
- truthound_dashboard/core/scheduler.py +165 -0
- truthound_dashboard/core/schema_evolution.py +3 -3
- truthound_dashboard/core/schema_watcher.py +1528 -0
- truthound_dashboard/core/services.py +595 -76
- truthound_dashboard/core/store_manager.py +810 -0
- truthound_dashboard/core/streaming_anomaly.py +169 -4
- truthound_dashboard/core/tiering.py +1309 -0
- truthound_dashboard/core/triggers/evaluators.py +178 -8
- truthound_dashboard/core/truthound_adapter.py +2620 -197
- truthound_dashboard/core/unified_alerts.py +23 -20
- truthound_dashboard/db/__init__.py +8 -0
- truthound_dashboard/db/database.py +8 -2
- truthound_dashboard/db/models.py +944 -25
- truthound_dashboard/db/repository.py +2 -0
- truthound_dashboard/main.py +11 -0
- truthound_dashboard/schemas/__init__.py +177 -16
- truthound_dashboard/schemas/base.py +44 -23
- truthound_dashboard/schemas/collaboration.py +19 -6
- truthound_dashboard/schemas/cross_alerts.py +19 -3
- truthound_dashboard/schemas/drift.py +61 -55
- truthound_dashboard/schemas/drift_monitor.py +67 -23
- truthound_dashboard/schemas/enterprise_sampling.py +653 -0
- truthound_dashboard/schemas/lineage.py +0 -33
- truthound_dashboard/schemas/mask.py +10 -8
- truthound_dashboard/schemas/model_monitoring.py +89 -10
- truthound_dashboard/schemas/notifications_advanced.py +13 -0
- truthound_dashboard/schemas/observability.py +453 -0
- truthound_dashboard/schemas/plugins.py +0 -280
- truthound_dashboard/schemas/profile.py +154 -247
- truthound_dashboard/schemas/quality_reporter.py +403 -0
- truthound_dashboard/schemas/reports.py +2 -2
- truthound_dashboard/schemas/rule_suggestion.py +8 -1
- truthound_dashboard/schemas/scan.py +4 -24
- truthound_dashboard/schemas/schedule.py +11 -3
- truthound_dashboard/schemas/schema_watcher.py +727 -0
- truthound_dashboard/schemas/source.py +17 -2
- truthound_dashboard/schemas/tiering.py +822 -0
- truthound_dashboard/schemas/triggers.py +16 -0
- truthound_dashboard/schemas/unified_alerts.py +7 -0
- truthound_dashboard/schemas/validation.py +0 -13
- truthound_dashboard/schemas/validators/base.py +41 -21
- truthound_dashboard/schemas/validators/business_rule_validators.py +244 -0
- truthound_dashboard/schemas/validators/localization_validators.py +273 -0
- truthound_dashboard/schemas/validators/ml_feature_validators.py +308 -0
- truthound_dashboard/schemas/validators/profiling_validators.py +275 -0
- truthound_dashboard/schemas/validators/referential_validators.py +312 -0
- truthound_dashboard/schemas/validators/registry.py +93 -8
- truthound_dashboard/schemas/validators/timeseries_validators.py +389 -0
- truthound_dashboard/schemas/versioning.py +1 -6
- truthound_dashboard/static/index.html +2 -2
- truthound_dashboard-1.5.0.dist-info/METADATA +309 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/RECORD +149 -148
- truthound_dashboard/core/plugins/hooks/__init__.py +0 -63
- truthound_dashboard/core/plugins/hooks/decorators.py +0 -367
- truthound_dashboard/core/plugins/hooks/manager.py +0 -403
- truthound_dashboard/core/plugins/hooks/protocols.py +0 -265
- truthound_dashboard/core/plugins/lifecycle/hot_reload.py +0 -584
- truthound_dashboard/core/reporters/junit_reporter.py +0 -233
- truthound_dashboard/core/reporters/markdown_reporter.py +0 -207
- truthound_dashboard/core/reporters/pdf_reporter.py +0 -209
- truthound_dashboard/static/assets/_baseUniq-BcrSP13d.js +0 -1
- truthound_dashboard/static/assets/arc-DlYjKwIL.js +0 -1
- truthound_dashboard/static/assets/architectureDiagram-VXUJARFQ-Bb2drbQM.js +0 -36
- truthound_dashboard/static/assets/blockDiagram-VD42YOAC-BlsPG1CH.js +0 -122
- truthound_dashboard/static/assets/c4Diagram-YG6GDRKO-B9JdUoaC.js +0 -10
- truthound_dashboard/static/assets/channel-Q6mHF1Hd.js +0 -1
- truthound_dashboard/static/assets/chunk-4BX2VUAB-DmyoPVuJ.js +0 -1
- truthound_dashboard/static/assets/chunk-55IACEB6-Bcz6Siv8.js +0 -1
- truthound_dashboard/static/assets/chunk-B4BG7PRW-Br3G5Rum.js +0 -165
- truthound_dashboard/static/assets/chunk-DI55MBZ5-DuM9c23u.js +0 -220
- truthound_dashboard/static/assets/chunk-FMBD7UC4-DNU-5mvT.js +0 -15
- truthound_dashboard/static/assets/chunk-QN33PNHL-Im2yNcmS.js +0 -1
- truthound_dashboard/static/assets/chunk-QZHKN3VN-kZr8XFm1.js +0 -1
- truthound_dashboard/static/assets/chunk-TZMSLE5B-Q__360q_.js +0 -1
- truthound_dashboard/static/assets/classDiagram-2ON5EDUG-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/classDiagram-v2-WZHVMYZB-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/clone-BOt2LwD0.js +0 -1
- truthound_dashboard/static/assets/cose-bilkent-S5V4N54A-CBDw6iac.js +0 -1
- truthound_dashboard/static/assets/dagre-6UL2VRFP-XdKqmmY9.js +0 -4
- truthound_dashboard/static/assets/diagram-PSM6KHXK-DAZ8nx9V.js +0 -24
- truthound_dashboard/static/assets/diagram-QEK2KX5R-BRvDTbGD.js +0 -43
- truthound_dashboard/static/assets/diagram-S2PKOQOG-bQcczUkl.js +0 -24
- truthound_dashboard/static/assets/erDiagram-Q2GNP2WA-DPje7VMN.js +0 -60
- truthound_dashboard/static/assets/flowDiagram-NV44I4VS-B7BVtFVS.js +0 -162
- truthound_dashboard/static/assets/ganttDiagram-JELNMOA3-D6WKSS7U.js +0 -267
- truthound_dashboard/static/assets/gitGraphDiagram-NY62KEGX-D3vtVd3y.js +0 -65
- truthound_dashboard/static/assets/graph-BKgNKZVp.js +0 -1
- truthound_dashboard/static/assets/index-C6JSrkHo.css +0 -1
- truthound_dashboard/static/assets/index-DkU82VsU.js +0 -1800
- truthound_dashboard/static/assets/infoDiagram-WHAUD3N6-DnNCT429.js +0 -2
- truthound_dashboard/static/assets/journeyDiagram-XKPGCS4Q-DGiMozqS.js +0 -139
- truthound_dashboard/static/assets/kanban-definition-3W4ZIXB7-BV2gUgli.js +0 -89
- truthound_dashboard/static/assets/katex-Cu_Erd72.js +0 -261
- truthound_dashboard/static/assets/layout-DI2MfQ5G.js +0 -1
- truthound_dashboard/static/assets/min-DYdgXVcT.js +0 -1
- truthound_dashboard/static/assets/mindmap-definition-VGOIOE7T-C7x4ruxz.js +0 -68
- truthound_dashboard/static/assets/pieDiagram-ADFJNKIX-CAJaAB9f.js +0 -30
- truthound_dashboard/static/assets/quadrantDiagram-AYHSOK5B-DeqwDI46.js +0 -7
- truthound_dashboard/static/assets/requirementDiagram-UZGBJVZJ-e3XDpZIM.js +0 -64
- truthound_dashboard/static/assets/sankeyDiagram-TZEHDZUN-CNnAv5Ux.js +0 -10
- truthound_dashboard/static/assets/sequenceDiagram-WL72ISMW-Dsne-Of3.js +0 -145
- truthound_dashboard/static/assets/stateDiagram-FKZM4ZOC-Ee0sQXyb.js +0 -1
- truthound_dashboard/static/assets/stateDiagram-v2-4FDKWEC3-B26KqW_W.js +0 -1
- truthound_dashboard/static/assets/timeline-definition-IT6M3QCI-DZYi2yl3.js +0 -61
- truthound_dashboard/static/assets/treemap-KMMF4GRG-CY3f8In2.js +0 -128
- truthound_dashboard/static/assets/unmerged_dictionaries-Dd7xcPWG.js +0 -1
- truthound_dashboard/static/assets/xychartDiagram-PRI3JC2R-CS7fydZZ.js +0 -7
- truthound_dashboard-1.4.4.dist-info/METADATA +0 -507
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -12,7 +12,7 @@ from typing import Literal
|
|
|
12
12
|
|
|
13
13
|
from pydantic import Field
|
|
14
14
|
|
|
15
|
-
from .base import BaseSchema, IDMixin
|
|
15
|
+
from .base import BaseSchema, IDMixin
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class MaskingStrategy(str, Enum):
|
|
@@ -47,7 +47,9 @@ class MaskRequest(BaseSchema):
|
|
|
47
47
|
Attributes:
|
|
48
48
|
columns: Optional list of columns to mask. If None, auto-detects PII.
|
|
49
49
|
strategy: Masking strategy to use. Defaults to "redact".
|
|
50
|
-
|
|
50
|
+
|
|
51
|
+
Note: output_format parameter was removed as truthound's th.mask()
|
|
52
|
+
does not support this parameter. Output format is handled by the dashboard.
|
|
51
53
|
"""
|
|
52
54
|
|
|
53
55
|
columns: list[str] | None = Field(
|
|
@@ -58,10 +60,6 @@ class MaskRequest(BaseSchema):
|
|
|
58
60
|
default="redact",
|
|
59
61
|
description="Masking strategy: 'redact' (asterisks), 'hash' (SHA256), 'fake' (realistic data)",
|
|
60
62
|
)
|
|
61
|
-
output_format: Literal["csv", "parquet", "json"] = Field(
|
|
62
|
-
default="csv",
|
|
63
|
-
description="Output file format",
|
|
64
|
-
)
|
|
65
63
|
|
|
66
64
|
|
|
67
65
|
class MaskSummary(BaseSchema):
|
|
@@ -86,7 +84,7 @@ class MaskSummary(BaseSchema):
|
|
|
86
84
|
duration_ms: int | None = None
|
|
87
85
|
|
|
88
86
|
|
|
89
|
-
class MaskResponse(BaseSchema, IDMixin
|
|
87
|
+
class MaskResponse(BaseSchema, IDMixin):
|
|
90
88
|
"""Response for a masking operation.
|
|
91
89
|
|
|
92
90
|
Attributes:
|
|
@@ -103,6 +101,8 @@ class MaskResponse(BaseSchema, IDMixin, TimestampMixin):
|
|
|
103
101
|
error_message: Error message if operation failed.
|
|
104
102
|
started_at: When the operation started.
|
|
105
103
|
completed_at: When the operation completed.
|
|
104
|
+
created_at: When the operation was created.
|
|
105
|
+
updated_at: When the operation was last updated.
|
|
106
106
|
"""
|
|
107
107
|
|
|
108
108
|
source_id: str
|
|
@@ -117,6 +117,8 @@ class MaskResponse(BaseSchema, IDMixin, TimestampMixin):
|
|
|
117
117
|
error_message: str | None = None
|
|
118
118
|
started_at: datetime | None = None
|
|
119
119
|
completed_at: datetime | None = None
|
|
120
|
+
created_at: datetime = Field(..., description="Creation timestamp")
|
|
121
|
+
updated_at: datetime | None = Field(default=None, description="Last update timestamp")
|
|
120
122
|
|
|
121
123
|
@classmethod
|
|
122
124
|
def from_db(cls, db_mask: object) -> MaskResponse:
|
|
@@ -185,7 +187,7 @@ class MaskListItem(BaseSchema, IDMixin):
|
|
|
185
187
|
return cls(
|
|
186
188
|
id=db_mask.id,
|
|
187
189
|
source_id=db_mask.source_id,
|
|
188
|
-
source_name=source_name
|
|
190
|
+
source_name=source_name if source_name is not None else None,
|
|
189
191
|
status=db_mask.status,
|
|
190
192
|
strategy=db_mask.strategy,
|
|
191
193
|
columns_masked=len(db_mask.columns_masked) if db_mask.columns_masked else 0,
|
|
@@ -54,19 +54,32 @@ class MetricType(str, Enum):
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
class AlertRuleType(str, Enum):
|
|
57
|
-
"""Types of alert rules.
|
|
57
|
+
"""Types of alert rules.
|
|
58
|
+
|
|
59
|
+
Maps to truthound.ml.monitoring.alerting rules:
|
|
60
|
+
- ThresholdRule: Threshold-based alerting
|
|
61
|
+
- AnomalyRule: Anomaly-based alerting (statistical)
|
|
62
|
+
- TrendRule: Trend-based alerting
|
|
63
|
+
"""
|
|
58
64
|
|
|
59
65
|
THRESHOLD = "threshold"
|
|
60
|
-
STATISTICAL = "statistical"
|
|
66
|
+
STATISTICAL = "statistical" # Maps to AnomalyRule
|
|
61
67
|
TREND = "trend"
|
|
62
68
|
|
|
63
69
|
|
|
64
70
|
class AlertHandlerType(str, Enum):
|
|
65
|
-
"""Types of alert handlers.
|
|
71
|
+
"""Types of alert handlers.
|
|
72
|
+
|
|
73
|
+
Maps to truthound.ml.monitoring.alerting handlers:
|
|
74
|
+
- SlackAlertHandler
|
|
75
|
+
- PagerDutyAlertHandler
|
|
76
|
+
- WebhookAlertHandler
|
|
77
|
+
"""
|
|
66
78
|
|
|
67
79
|
SLACK = "slack"
|
|
68
80
|
WEBHOOK = "webhook"
|
|
69
81
|
EMAIL = "email"
|
|
82
|
+
PAGERDUTY = "pagerduty"
|
|
70
83
|
|
|
71
84
|
|
|
72
85
|
# =============================================================================
|
|
@@ -75,14 +88,63 @@ class AlertHandlerType(str, Enum):
|
|
|
75
88
|
|
|
76
89
|
|
|
77
90
|
class ModelConfigBase(BaseSchema):
|
|
78
|
-
"""Configuration for model monitoring.
|
|
91
|
+
"""Configuration for model monitoring.
|
|
92
|
+
|
|
93
|
+
Maps to truthound.ml.monitoring.MonitorConfig parameters.
|
|
94
|
+
See: .truthound_docs/advanced/ml-anomaly.md#model-monitoring
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
# Core monitoring settings (from MonitorConfig)
|
|
98
|
+
batch_size: int = Field(
|
|
99
|
+
default=100,
|
|
100
|
+
description="Batch size for metric collection",
|
|
101
|
+
ge=1,
|
|
102
|
+
le=10000,
|
|
103
|
+
)
|
|
104
|
+
collect_interval_seconds: int = Field(
|
|
105
|
+
default=60,
|
|
106
|
+
description="Interval for collecting metrics (seconds)",
|
|
107
|
+
ge=1,
|
|
108
|
+
le=3600,
|
|
109
|
+
)
|
|
110
|
+
alert_evaluation_interval_seconds: int = Field(
|
|
111
|
+
default=30,
|
|
112
|
+
description="Interval for evaluating alert rules (seconds)",
|
|
113
|
+
ge=1,
|
|
114
|
+
le=3600,
|
|
115
|
+
)
|
|
116
|
+
retention_hours: int = Field(
|
|
117
|
+
default=24,
|
|
118
|
+
description="Hours to retain metrics data",
|
|
119
|
+
ge=1,
|
|
120
|
+
le=720, # 30 days max
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Feature toggles
|
|
124
|
+
enable_drift_detection: bool = Field(
|
|
125
|
+
default=True,
|
|
126
|
+
description="Enable drift detection using th.compare()",
|
|
127
|
+
)
|
|
128
|
+
enable_quality_metrics: bool = Field(
|
|
129
|
+
default=True,
|
|
130
|
+
description="Enable quality metrics (accuracy, precision, recall, F1)",
|
|
131
|
+
)
|
|
132
|
+
enable_performance_metrics: bool = Field(
|
|
133
|
+
default=True,
|
|
134
|
+
description="Enable performance metrics (latency, throughput, error rate)",
|
|
135
|
+
)
|
|
79
136
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
137
|
+
# Drift detection settings
|
|
138
|
+
drift_threshold: float = Field(
|
|
139
|
+
default=0.1,
|
|
140
|
+
description="Threshold for drift detection alerts",
|
|
141
|
+
ge=0.0,
|
|
142
|
+
le=1.0,
|
|
143
|
+
)
|
|
144
|
+
drift_method: str = Field(
|
|
145
|
+
default="auto",
|
|
146
|
+
description="Drift detection method (auto, psi, ks, js, wasserstein, chi2, etc.)",
|
|
147
|
+
)
|
|
86
148
|
|
|
87
149
|
|
|
88
150
|
class RegisteredModelBase(BaseSchema):
|
|
@@ -372,3 +434,20 @@ class MonitoringOverview(BaseModel):
|
|
|
372
434
|
active_alerts: int = Field(default=0, description="Currently active alerts")
|
|
373
435
|
models_with_drift: int = Field(default=0, description="Models with detected drift")
|
|
374
436
|
avg_latency_ms: float | None = Field(None, description="Average latency across all models")
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
class AlertHandlerTestResult(BaseModel):
|
|
440
|
+
"""Result of testing an alert handler."""
|
|
441
|
+
|
|
442
|
+
success: bool = Field(..., description="Whether test was successful")
|
|
443
|
+
message: str = Field(..., description="Test result message")
|
|
444
|
+
handler_id: str = Field(..., description="Handler ID that was tested")
|
|
445
|
+
handler_type: str = Field(..., description="Type of the handler")
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
class RuleEvaluationResult(BaseModel):
|
|
449
|
+
"""Result of rule evaluation for a model."""
|
|
450
|
+
|
|
451
|
+
model_id: str = Field(..., description="Model ID that was evaluated")
|
|
452
|
+
alerts_created: int = Field(..., description="Number of alerts created")
|
|
453
|
+
alert_ids: list[str] = Field(default_factory=list, description="IDs of created alerts")
|
|
@@ -1361,3 +1361,16 @@ class ExpressionValidateResponse(BaseModel):
|
|
|
1361
1361
|
default_factory=list,
|
|
1362
1362
|
description="Non-fatal warnings about the expression",
|
|
1363
1363
|
)
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
# =============================================================================
|
|
1367
|
+
# Cache Invalidation Response
|
|
1368
|
+
# =============================================================================
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
class CacheInvalidateResponse(BaseSchema):
|
|
1372
|
+
"""Response for cache invalidation operations."""
|
|
1373
|
+
|
|
1374
|
+
message: str = Field(..., description="Descriptive message about the operation")
|
|
1375
|
+
target: str = Field(..., description="Cache target that was invalidated")
|
|
1376
|
+
timestamp: str = Field(..., description="ISO timestamp of the operation")
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"""Observability schemas for audit, metrics, and tracing.
|
|
2
|
+
|
|
3
|
+
This module defines schemas for observability API operations
|
|
4
|
+
using truthound's observability module.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from pydantic import Field
|
|
14
|
+
|
|
15
|
+
from .base import BaseSchema, ListResponseWrapper
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AuditEventTypeEnum(str, Enum):
|
|
19
|
+
"""Audit event types from truthound."""
|
|
20
|
+
|
|
21
|
+
CREATE = "create"
|
|
22
|
+
READ = "read"
|
|
23
|
+
UPDATE = "update"
|
|
24
|
+
DELETE = "delete"
|
|
25
|
+
QUERY = "query"
|
|
26
|
+
LIST = "list"
|
|
27
|
+
COUNT = "count"
|
|
28
|
+
INITIALIZE = "initialize"
|
|
29
|
+
CLOSE = "close"
|
|
30
|
+
FLUSH = "flush"
|
|
31
|
+
BATCH_CREATE = "batch_create"
|
|
32
|
+
BATCH_DELETE = "batch_delete"
|
|
33
|
+
REPLICATE = "replicate"
|
|
34
|
+
SYNC = "sync"
|
|
35
|
+
MIGRATE = "migrate"
|
|
36
|
+
ROLLBACK = "rollback"
|
|
37
|
+
ACCESS_DENIED = "access_denied"
|
|
38
|
+
ACCESS_GRANTED = "access_granted"
|
|
39
|
+
ERROR = "error"
|
|
40
|
+
VALIDATION_ERROR = "validation_error"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AuditStatusEnum(str, Enum):
|
|
44
|
+
"""Audit status from truthound."""
|
|
45
|
+
|
|
46
|
+
SUCCESS = "success"
|
|
47
|
+
FAILURE = "failure"
|
|
48
|
+
PARTIAL = "partial"
|
|
49
|
+
DENIED = "denied"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class MetricTypeEnum(str, Enum):
|
|
53
|
+
"""Metric types from truthound."""
|
|
54
|
+
|
|
55
|
+
COUNTER = "counter"
|
|
56
|
+
GAUGE = "gauge"
|
|
57
|
+
HISTOGRAM = "histogram"
|
|
58
|
+
SUMMARY = "summary"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SpanKindEnum(str, Enum):
|
|
62
|
+
"""Span kinds from truthound."""
|
|
63
|
+
|
|
64
|
+
INTERNAL = "internal"
|
|
65
|
+
SERVER = "server"
|
|
66
|
+
CLIENT = "client"
|
|
67
|
+
PRODUCER = "producer"
|
|
68
|
+
CONSUMER = "consumer"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class SpanStatusEnum(str, Enum):
|
|
72
|
+
"""Span status from truthound."""
|
|
73
|
+
|
|
74
|
+
UNSET = "unset"
|
|
75
|
+
OK = "ok"
|
|
76
|
+
ERROR = "error"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# =============================================================================
|
|
80
|
+
# Observability Configuration
|
|
81
|
+
# =============================================================================
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class ObservabilityConfigRequest(BaseSchema):
|
|
85
|
+
"""Request to update observability configuration."""
|
|
86
|
+
|
|
87
|
+
enable_audit: bool = Field(
|
|
88
|
+
default=True,
|
|
89
|
+
description="Enable audit logging",
|
|
90
|
+
)
|
|
91
|
+
enable_metrics: bool = Field(
|
|
92
|
+
default=True,
|
|
93
|
+
description="Enable metrics collection",
|
|
94
|
+
)
|
|
95
|
+
enable_tracing: bool = Field(
|
|
96
|
+
default=False,
|
|
97
|
+
description="Enable distributed tracing",
|
|
98
|
+
)
|
|
99
|
+
audit_log_path: str | None = Field(
|
|
100
|
+
default=None,
|
|
101
|
+
description="Path for audit log files",
|
|
102
|
+
)
|
|
103
|
+
audit_rotate_daily: bool = Field(
|
|
104
|
+
default=True,
|
|
105
|
+
description="Rotate audit logs daily",
|
|
106
|
+
)
|
|
107
|
+
audit_max_events: int = Field(
|
|
108
|
+
default=10000,
|
|
109
|
+
ge=1000,
|
|
110
|
+
le=1000000,
|
|
111
|
+
description="Maximum events to keep in memory",
|
|
112
|
+
)
|
|
113
|
+
redact_fields: list[str] = Field(
|
|
114
|
+
default_factory=lambda: ["password", "api_key", "token", "secret"],
|
|
115
|
+
description="Fields to redact in audit logs",
|
|
116
|
+
)
|
|
117
|
+
metrics_prefix: str = Field(
|
|
118
|
+
default="truthound_dashboard",
|
|
119
|
+
max_length=50,
|
|
120
|
+
description="Prefix for metric names",
|
|
121
|
+
)
|
|
122
|
+
tracing_service_name: str = Field(
|
|
123
|
+
default="truthound-dashboard",
|
|
124
|
+
max_length=100,
|
|
125
|
+
description="Service name for tracing",
|
|
126
|
+
)
|
|
127
|
+
tracing_endpoint: str | None = Field(
|
|
128
|
+
default=None,
|
|
129
|
+
description="OpenTelemetry endpoint (e.g., http://localhost:4317)",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class ObservabilityConfigResponse(ObservabilityConfigRequest):
|
|
134
|
+
"""Response with current observability configuration."""
|
|
135
|
+
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# =============================================================================
|
|
140
|
+
# Audit Events
|
|
141
|
+
# =============================================================================
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class AuditEventResponse(BaseSchema):
|
|
145
|
+
"""Audit event response."""
|
|
146
|
+
|
|
147
|
+
event_id: str = Field(..., description="Unique event ID")
|
|
148
|
+
event_type: AuditEventTypeEnum = Field(..., description="Event type")
|
|
149
|
+
timestamp: datetime = Field(..., description="Event timestamp")
|
|
150
|
+
status: AuditStatusEnum = Field(..., description="Event status")
|
|
151
|
+
store_type: str = Field(..., description="Store backend type")
|
|
152
|
+
store_id: str = Field(..., description="Store identifier")
|
|
153
|
+
item_id: str | None = Field(default=None, description="Related item ID")
|
|
154
|
+
user_id: str | None = Field(default=None, description="User who triggered event")
|
|
155
|
+
session_id: str | None = Field(default=None, description="Session ID")
|
|
156
|
+
duration_ms: float | None = Field(default=None, description="Operation duration")
|
|
157
|
+
metadata: dict[str, Any] | None = Field(
|
|
158
|
+
default=None, description="Additional metadata"
|
|
159
|
+
)
|
|
160
|
+
error_message: str | None = Field(default=None, description="Error message")
|
|
161
|
+
ip_address: str | None = Field(default=None, description="Client IP address")
|
|
162
|
+
user_agent: str | None = Field(default=None, description="Client user agent")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class AuditEventListResponse(ListResponseWrapper):
|
|
166
|
+
"""Response for audit event list."""
|
|
167
|
+
|
|
168
|
+
items: list[AuditEventResponse]
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class AuditQueryRequest(BaseSchema):
|
|
172
|
+
"""Request to query audit events."""
|
|
173
|
+
|
|
174
|
+
event_type: AuditEventTypeEnum | None = Field(
|
|
175
|
+
default=None, description="Filter by event type"
|
|
176
|
+
)
|
|
177
|
+
status: AuditStatusEnum | None = Field(
|
|
178
|
+
default=None, description="Filter by status"
|
|
179
|
+
)
|
|
180
|
+
start_time: datetime | None = Field(
|
|
181
|
+
default=None, description="Filter events after this time"
|
|
182
|
+
)
|
|
183
|
+
end_time: datetime | None = Field(
|
|
184
|
+
default=None, description="Filter events before this time"
|
|
185
|
+
)
|
|
186
|
+
item_id: str | None = Field(
|
|
187
|
+
default=None, description="Filter by item ID"
|
|
188
|
+
)
|
|
189
|
+
user_id: str | None = Field(
|
|
190
|
+
default=None, description="Filter by user ID"
|
|
191
|
+
)
|
|
192
|
+
limit: int = Field(
|
|
193
|
+
default=100, ge=1, le=1000, description="Maximum events to return"
|
|
194
|
+
)
|
|
195
|
+
offset: int = Field(
|
|
196
|
+
default=0, ge=0, description="Offset for pagination"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class AuditStatsResponse(BaseSchema):
|
|
201
|
+
"""Audit statistics response."""
|
|
202
|
+
|
|
203
|
+
total_events: int = Field(default=0, ge=0, description="Total audit events")
|
|
204
|
+
events_today: int = Field(default=0, ge=0, description="Events today")
|
|
205
|
+
events_this_week: int = Field(default=0, ge=0, description="Events this week")
|
|
206
|
+
by_event_type: dict[str, int] = Field(
|
|
207
|
+
default_factory=dict, description="Events by type"
|
|
208
|
+
)
|
|
209
|
+
by_status: dict[str, int] = Field(
|
|
210
|
+
default_factory=dict, description="Events by status"
|
|
211
|
+
)
|
|
212
|
+
error_rate: float = Field(
|
|
213
|
+
default=0.0, ge=0, le=1, description="Error event rate"
|
|
214
|
+
)
|
|
215
|
+
avg_duration_ms: float | None = Field(
|
|
216
|
+
default=None, description="Average operation duration"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# =============================================================================
|
|
221
|
+
# Metrics
|
|
222
|
+
# =============================================================================
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class MetricValue(BaseSchema):
|
|
226
|
+
"""A single metric value."""
|
|
227
|
+
|
|
228
|
+
name: str = Field(..., description="Metric name")
|
|
229
|
+
value: float = Field(..., description="Metric value")
|
|
230
|
+
labels: dict[str, str] = Field(
|
|
231
|
+
default_factory=dict, description="Metric labels"
|
|
232
|
+
)
|
|
233
|
+
timestamp: datetime | None = Field(
|
|
234
|
+
default=None, description="Metric timestamp"
|
|
235
|
+
)
|
|
236
|
+
metric_type: MetricTypeEnum = Field(
|
|
237
|
+
default=MetricTypeEnum.GAUGE, description="Metric type"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class HistogramBucket(BaseSchema):
|
|
242
|
+
"""Histogram bucket."""
|
|
243
|
+
|
|
244
|
+
le: float = Field(..., description="Less than or equal boundary")
|
|
245
|
+
count: int = Field(..., ge=0, description="Count in bucket")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class HistogramValue(BaseSchema):
|
|
249
|
+
"""Histogram metric value."""
|
|
250
|
+
|
|
251
|
+
name: str = Field(..., description="Metric name")
|
|
252
|
+
count: int = Field(..., ge=0, description="Total count")
|
|
253
|
+
sum: float = Field(..., description="Sum of values")
|
|
254
|
+
buckets: list[HistogramBucket] = Field(
|
|
255
|
+
default_factory=list, description="Histogram buckets"
|
|
256
|
+
)
|
|
257
|
+
labels: dict[str, str] = Field(
|
|
258
|
+
default_factory=dict, description="Metric labels"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class SummaryQuantile(BaseSchema):
|
|
263
|
+
"""Summary quantile."""
|
|
264
|
+
|
|
265
|
+
quantile: float = Field(..., ge=0, le=1, description="Quantile (0-1)")
|
|
266
|
+
value: float = Field(..., description="Value at quantile")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class SummaryValue(BaseSchema):
|
|
270
|
+
"""Summary metric value."""
|
|
271
|
+
|
|
272
|
+
name: str = Field(..., description="Metric name")
|
|
273
|
+
count: int = Field(..., ge=0, description="Total count")
|
|
274
|
+
sum: float = Field(..., description="Sum of values")
|
|
275
|
+
quantiles: list[SummaryQuantile] = Field(
|
|
276
|
+
default_factory=list, description="Quantile values"
|
|
277
|
+
)
|
|
278
|
+
labels: dict[str, str] = Field(
|
|
279
|
+
default_factory=dict, description="Metric labels"
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class MetricsResponse(BaseSchema):
|
|
284
|
+
"""Response with all metrics."""
|
|
285
|
+
|
|
286
|
+
counters: list[MetricValue] = Field(
|
|
287
|
+
default_factory=list, description="Counter metrics"
|
|
288
|
+
)
|
|
289
|
+
gauges: list[MetricValue] = Field(
|
|
290
|
+
default_factory=list, description="Gauge metrics"
|
|
291
|
+
)
|
|
292
|
+
histograms: list[HistogramValue] = Field(
|
|
293
|
+
default_factory=list, description="Histogram metrics"
|
|
294
|
+
)
|
|
295
|
+
summaries: list[SummaryValue] = Field(
|
|
296
|
+
default_factory=list, description="Summary metrics"
|
|
297
|
+
)
|
|
298
|
+
timestamp: datetime = Field(..., description="Metrics snapshot timestamp")
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class StoreMetricsResponse(BaseSchema):
|
|
302
|
+
"""Store-specific metrics response."""
|
|
303
|
+
|
|
304
|
+
operations_total: int = Field(
|
|
305
|
+
default=0, ge=0, description="Total operations"
|
|
306
|
+
)
|
|
307
|
+
operations_by_type: dict[str, int] = Field(
|
|
308
|
+
default_factory=dict, description="Operations by type"
|
|
309
|
+
)
|
|
310
|
+
bytes_read_total: int = Field(
|
|
311
|
+
default=0, ge=0, description="Total bytes read"
|
|
312
|
+
)
|
|
313
|
+
bytes_written_total: int = Field(
|
|
314
|
+
default=0, ge=0, description="Total bytes written"
|
|
315
|
+
)
|
|
316
|
+
active_connections: int = Field(
|
|
317
|
+
default=0, ge=0, description="Active connections"
|
|
318
|
+
)
|
|
319
|
+
cache_hits: int = Field(
|
|
320
|
+
default=0, ge=0, description="Cache hits"
|
|
321
|
+
)
|
|
322
|
+
cache_misses: int = Field(
|
|
323
|
+
default=0, ge=0, description="Cache misses"
|
|
324
|
+
)
|
|
325
|
+
cache_hit_rate: float = Field(
|
|
326
|
+
default=0.0, ge=0, le=1, description="Cache hit rate"
|
|
327
|
+
)
|
|
328
|
+
errors_total: int = Field(
|
|
329
|
+
default=0, ge=0, description="Total errors"
|
|
330
|
+
)
|
|
331
|
+
errors_by_type: dict[str, int] = Field(
|
|
332
|
+
default_factory=dict, description="Errors by type"
|
|
333
|
+
)
|
|
334
|
+
avg_operation_duration_ms: float | None = Field(
|
|
335
|
+
default=None, description="Average operation duration"
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
# =============================================================================
|
|
340
|
+
# Tracing
|
|
341
|
+
# =============================================================================
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class SpanContext(BaseSchema):
|
|
345
|
+
"""Span context for distributed tracing."""
|
|
346
|
+
|
|
347
|
+
trace_id: str = Field(..., description="Trace ID")
|
|
348
|
+
span_id: str = Field(..., description="Span ID")
|
|
349
|
+
parent_span_id: str | None = Field(
|
|
350
|
+
default=None, description="Parent span ID"
|
|
351
|
+
)
|
|
352
|
+
trace_flags: int = Field(default=0, description="Trace flags")
|
|
353
|
+
trace_state: dict[str, str] = Field(
|
|
354
|
+
default_factory=dict, description="Trace state"
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class SpanEvent(BaseSchema):
|
|
359
|
+
"""Event within a span."""
|
|
360
|
+
|
|
361
|
+
name: str = Field(..., description="Event name")
|
|
362
|
+
timestamp: datetime = Field(..., description="Event timestamp")
|
|
363
|
+
attributes: dict[str, Any] = Field(
|
|
364
|
+
default_factory=dict, description="Event attributes"
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class SpanResponse(BaseSchema):
|
|
369
|
+
"""Span response for tracing."""
|
|
370
|
+
|
|
371
|
+
name: str = Field(..., description="Span name")
|
|
372
|
+
kind: SpanKindEnum = Field(..., description="Span kind")
|
|
373
|
+
context: SpanContext = Field(..., description="Span context")
|
|
374
|
+
start_time: datetime = Field(..., description="Span start time")
|
|
375
|
+
end_time: datetime | None = Field(default=None, description="Span end time")
|
|
376
|
+
duration_ms: float | None = Field(default=None, description="Duration in ms")
|
|
377
|
+
status: SpanStatusEnum = Field(
|
|
378
|
+
default=SpanStatusEnum.UNSET, description="Span status"
|
|
379
|
+
)
|
|
380
|
+
status_message: str | None = Field(
|
|
381
|
+
default=None, description="Status message if error"
|
|
382
|
+
)
|
|
383
|
+
attributes: dict[str, Any] = Field(
|
|
384
|
+
default_factory=dict, description="Span attributes"
|
|
385
|
+
)
|
|
386
|
+
events: list[SpanEvent] = Field(
|
|
387
|
+
default_factory=list, description="Span events"
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class SpanListResponse(ListResponseWrapper):
|
|
392
|
+
"""Response for span list."""
|
|
393
|
+
|
|
394
|
+
items: list[SpanResponse]
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
class TraceResponse(BaseSchema):
|
|
398
|
+
"""Full trace with all spans."""
|
|
399
|
+
|
|
400
|
+
trace_id: str = Field(..., description="Trace ID")
|
|
401
|
+
spans: list[SpanResponse] = Field(
|
|
402
|
+
default_factory=list, description="All spans in trace"
|
|
403
|
+
)
|
|
404
|
+
duration_ms: float | None = Field(
|
|
405
|
+
default=None, description="Total trace duration"
|
|
406
|
+
)
|
|
407
|
+
service_count: int = Field(
|
|
408
|
+
default=0, ge=0, description="Number of services involved"
|
|
409
|
+
)
|
|
410
|
+
error_count: int = Field(
|
|
411
|
+
default=0, ge=0, description="Number of error spans"
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
class TracingStatsResponse(BaseSchema):
|
|
416
|
+
"""Tracing statistics response."""
|
|
417
|
+
|
|
418
|
+
enabled: bool = Field(..., description="Whether tracing is enabled")
|
|
419
|
+
total_traces: int = Field(default=0, ge=0, description="Total traces")
|
|
420
|
+
total_spans: int = Field(default=0, ge=0, description="Total spans")
|
|
421
|
+
avg_trace_duration_ms: float | None = Field(
|
|
422
|
+
default=None, description="Average trace duration"
|
|
423
|
+
)
|
|
424
|
+
traces_today: int = Field(default=0, ge=0, description="Traces today")
|
|
425
|
+
error_rate: float = Field(
|
|
426
|
+
default=0.0, ge=0, le=1, description="Error span rate"
|
|
427
|
+
)
|
|
428
|
+
by_service: dict[str, int] = Field(
|
|
429
|
+
default_factory=dict, description="Spans by service"
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
# =============================================================================
|
|
434
|
+
# Combined Statistics
|
|
435
|
+
# =============================================================================
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
class ObservabilityStatsResponse(BaseSchema):
|
|
439
|
+
"""Combined observability statistics."""
|
|
440
|
+
|
|
441
|
+
audit: AuditStatsResponse = Field(
|
|
442
|
+
default_factory=AuditStatsResponse,
|
|
443
|
+
description="Audit statistics",
|
|
444
|
+
)
|
|
445
|
+
store_metrics: StoreMetricsResponse = Field(
|
|
446
|
+
default_factory=StoreMetricsResponse,
|
|
447
|
+
description="Store metrics",
|
|
448
|
+
)
|
|
449
|
+
tracing: TracingStatsResponse | None = Field(
|
|
450
|
+
default=None,
|
|
451
|
+
description="Tracing statistics (if enabled)",
|
|
452
|
+
)
|
|
453
|
+
last_updated: datetime = Field(..., description="Last update timestamp")
|