truthound-dashboard 1.4.4__py3-none-any.whl → 1.5.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.
Files changed (205) hide show
  1. truthound_dashboard/api/alerts.py +75 -86
  2. truthound_dashboard/api/anomaly.py +7 -13
  3. truthound_dashboard/api/cross_alerts.py +38 -52
  4. truthound_dashboard/api/drift.py +49 -59
  5. truthound_dashboard/api/drift_monitor.py +234 -79
  6. truthound_dashboard/api/enterprise_sampling.py +498 -0
  7. truthound_dashboard/api/history.py +57 -5
  8. truthound_dashboard/api/lineage.py +3 -48
  9. truthound_dashboard/api/maintenance.py +104 -49
  10. truthound_dashboard/api/mask.py +1 -2
  11. truthound_dashboard/api/middleware.py +2 -1
  12. truthound_dashboard/api/model_monitoring.py +435 -311
  13. truthound_dashboard/api/notifications.py +227 -191
  14. truthound_dashboard/api/notifications_advanced.py +21 -20
  15. truthound_dashboard/api/observability.py +586 -0
  16. truthound_dashboard/api/plugins.py +2 -433
  17. truthound_dashboard/api/profile.py +199 -37
  18. truthound_dashboard/api/quality_reporter.py +701 -0
  19. truthound_dashboard/api/reports.py +7 -16
  20. truthound_dashboard/api/router.py +66 -0
  21. truthound_dashboard/api/rule_suggestions.py +5 -5
  22. truthound_dashboard/api/scan.py +17 -19
  23. truthound_dashboard/api/schedules.py +85 -50
  24. truthound_dashboard/api/schema_evolution.py +6 -6
  25. truthound_dashboard/api/schema_watcher.py +667 -0
  26. truthound_dashboard/api/sources.py +98 -27
  27. truthound_dashboard/api/tiering.py +1323 -0
  28. truthound_dashboard/api/triggers.py +14 -11
  29. truthound_dashboard/api/validations.py +12 -11
  30. truthound_dashboard/api/versioning.py +1 -6
  31. truthound_dashboard/core/__init__.py +129 -3
  32. truthound_dashboard/core/actions/__init__.py +62 -0
  33. truthound_dashboard/core/actions/custom.py +426 -0
  34. truthound_dashboard/core/actions/notifications.py +910 -0
  35. truthound_dashboard/core/actions/storage.py +472 -0
  36. truthound_dashboard/core/actions/webhook.py +281 -0
  37. truthound_dashboard/core/anomaly.py +262 -67
  38. truthound_dashboard/core/anomaly_explainer.py +4 -3
  39. truthound_dashboard/core/backends/__init__.py +67 -0
  40. truthound_dashboard/core/backends/base.py +299 -0
  41. truthound_dashboard/core/backends/errors.py +191 -0
  42. truthound_dashboard/core/backends/factory.py +423 -0
  43. truthound_dashboard/core/backends/mock_backend.py +451 -0
  44. truthound_dashboard/core/backends/truthound_backend.py +718 -0
  45. truthound_dashboard/core/checkpoint/__init__.py +87 -0
  46. truthound_dashboard/core/checkpoint/adapters.py +814 -0
  47. truthound_dashboard/core/checkpoint/checkpoint.py +491 -0
  48. truthound_dashboard/core/checkpoint/runner.py +270 -0
  49. truthound_dashboard/core/connections.py +645 -23
  50. truthound_dashboard/core/converters/__init__.py +14 -0
  51. truthound_dashboard/core/converters/truthound.py +620 -0
  52. truthound_dashboard/core/cross_alerts.py +540 -320
  53. truthound_dashboard/core/datasource_factory.py +1672 -0
  54. truthound_dashboard/core/drift_monitor.py +216 -20
  55. truthound_dashboard/core/enterprise_sampling.py +1291 -0
  56. truthound_dashboard/core/interfaces/__init__.py +225 -0
  57. truthound_dashboard/core/interfaces/actions.py +652 -0
  58. truthound_dashboard/core/interfaces/base.py +247 -0
  59. truthound_dashboard/core/interfaces/checkpoint.py +676 -0
  60. truthound_dashboard/core/interfaces/protocols.py +664 -0
  61. truthound_dashboard/core/interfaces/reporters.py +650 -0
  62. truthound_dashboard/core/interfaces/routing.py +646 -0
  63. truthound_dashboard/core/interfaces/triggers.py +619 -0
  64. truthound_dashboard/core/lineage.py +407 -71
  65. truthound_dashboard/core/model_monitoring.py +431 -3
  66. truthound_dashboard/core/notifications/base.py +4 -0
  67. truthound_dashboard/core/notifications/channels.py +501 -1203
  68. truthound_dashboard/core/notifications/deduplication/__init__.py +81 -115
  69. truthound_dashboard/core/notifications/deduplication/service.py +131 -348
  70. truthound_dashboard/core/notifications/dispatcher.py +202 -11
  71. truthound_dashboard/core/notifications/escalation/__init__.py +119 -106
  72. truthound_dashboard/core/notifications/escalation/engine.py +168 -358
  73. truthound_dashboard/core/notifications/routing/__init__.py +88 -128
  74. truthound_dashboard/core/notifications/routing/engine.py +90 -317
  75. truthound_dashboard/core/notifications/stats_aggregator.py +246 -1
  76. truthound_dashboard/core/notifications/throttling/__init__.py +67 -50
  77. truthound_dashboard/core/notifications/throttling/builder.py +117 -255
  78. truthound_dashboard/core/notifications/truthound_adapter.py +842 -0
  79. truthound_dashboard/core/phase5/collaboration.py +1 -1
  80. truthound_dashboard/core/plugins/lifecycle/__init__.py +0 -13
  81. truthound_dashboard/core/quality_reporter.py +1359 -0
  82. truthound_dashboard/core/report_history.py +0 -6
  83. truthound_dashboard/core/reporters/__init__.py +175 -14
  84. truthound_dashboard/core/reporters/adapters.py +943 -0
  85. truthound_dashboard/core/reporters/base.py +0 -3
  86. truthound_dashboard/core/reporters/builtin/__init__.py +18 -0
  87. truthound_dashboard/core/reporters/builtin/csv_reporter.py +111 -0
  88. truthound_dashboard/core/reporters/builtin/html_reporter.py +270 -0
  89. truthound_dashboard/core/reporters/builtin/json_reporter.py +127 -0
  90. truthound_dashboard/core/reporters/compat.py +266 -0
  91. truthound_dashboard/core/reporters/csv_reporter.py +2 -35
  92. truthound_dashboard/core/reporters/factory.py +526 -0
  93. truthound_dashboard/core/reporters/interfaces.py +745 -0
  94. truthound_dashboard/core/reporters/registry.py +1 -10
  95. truthound_dashboard/core/scheduler.py +165 -0
  96. truthound_dashboard/core/schema_evolution.py +3 -3
  97. truthound_dashboard/core/schema_watcher.py +1528 -0
  98. truthound_dashboard/core/services.py +595 -76
  99. truthound_dashboard/core/store_manager.py +810 -0
  100. truthound_dashboard/core/streaming_anomaly.py +169 -4
  101. truthound_dashboard/core/tiering.py +1309 -0
  102. truthound_dashboard/core/triggers/evaluators.py +178 -8
  103. truthound_dashboard/core/truthound_adapter.py +2620 -197
  104. truthound_dashboard/core/unified_alerts.py +23 -20
  105. truthound_dashboard/db/__init__.py +8 -0
  106. truthound_dashboard/db/database.py +8 -2
  107. truthound_dashboard/db/models.py +944 -25
  108. truthound_dashboard/db/repository.py +2 -0
  109. truthound_dashboard/main.py +15 -0
  110. truthound_dashboard/schemas/__init__.py +177 -16
  111. truthound_dashboard/schemas/base.py +44 -23
  112. truthound_dashboard/schemas/collaboration.py +19 -6
  113. truthound_dashboard/schemas/cross_alerts.py +19 -3
  114. truthound_dashboard/schemas/drift.py +61 -55
  115. truthound_dashboard/schemas/drift_monitor.py +67 -23
  116. truthound_dashboard/schemas/enterprise_sampling.py +653 -0
  117. truthound_dashboard/schemas/lineage.py +0 -33
  118. truthound_dashboard/schemas/mask.py +10 -8
  119. truthound_dashboard/schemas/model_monitoring.py +89 -10
  120. truthound_dashboard/schemas/notifications_advanced.py +13 -0
  121. truthound_dashboard/schemas/observability.py +453 -0
  122. truthound_dashboard/schemas/plugins.py +0 -280
  123. truthound_dashboard/schemas/profile.py +154 -247
  124. truthound_dashboard/schemas/quality_reporter.py +403 -0
  125. truthound_dashboard/schemas/reports.py +2 -2
  126. truthound_dashboard/schemas/rule_suggestion.py +8 -1
  127. truthound_dashboard/schemas/scan.py +4 -24
  128. truthound_dashboard/schemas/schedule.py +11 -3
  129. truthound_dashboard/schemas/schema_watcher.py +727 -0
  130. truthound_dashboard/schemas/source.py +17 -2
  131. truthound_dashboard/schemas/tiering.py +822 -0
  132. truthound_dashboard/schemas/triggers.py +16 -0
  133. truthound_dashboard/schemas/unified_alerts.py +7 -0
  134. truthound_dashboard/schemas/validation.py +0 -13
  135. truthound_dashboard/schemas/validators/base.py +41 -21
  136. truthound_dashboard/schemas/validators/business_rule_validators.py +244 -0
  137. truthound_dashboard/schemas/validators/localization_validators.py +273 -0
  138. truthound_dashboard/schemas/validators/ml_feature_validators.py +308 -0
  139. truthound_dashboard/schemas/validators/profiling_validators.py +275 -0
  140. truthound_dashboard/schemas/validators/referential_validators.py +312 -0
  141. truthound_dashboard/schemas/validators/registry.py +93 -8
  142. truthound_dashboard/schemas/validators/timeseries_validators.py +389 -0
  143. truthound_dashboard/schemas/versioning.py +1 -6
  144. truthound_dashboard/static/index.html +2 -2
  145. truthound_dashboard-1.5.1.dist-info/METADATA +312 -0
  146. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.dist-info}/RECORD +149 -148
  147. truthound_dashboard/core/plugins/hooks/__init__.py +0 -63
  148. truthound_dashboard/core/plugins/hooks/decorators.py +0 -367
  149. truthound_dashboard/core/plugins/hooks/manager.py +0 -403
  150. truthound_dashboard/core/plugins/hooks/protocols.py +0 -265
  151. truthound_dashboard/core/plugins/lifecycle/hot_reload.py +0 -584
  152. truthound_dashboard/core/reporters/junit_reporter.py +0 -233
  153. truthound_dashboard/core/reporters/markdown_reporter.py +0 -207
  154. truthound_dashboard/core/reporters/pdf_reporter.py +0 -209
  155. truthound_dashboard/static/assets/_baseUniq-BcrSP13d.js +0 -1
  156. truthound_dashboard/static/assets/arc-DlYjKwIL.js +0 -1
  157. truthound_dashboard/static/assets/architectureDiagram-VXUJARFQ-Bb2drbQM.js +0 -36
  158. truthound_dashboard/static/assets/blockDiagram-VD42YOAC-BlsPG1CH.js +0 -122
  159. truthound_dashboard/static/assets/c4Diagram-YG6GDRKO-B9JdUoaC.js +0 -10
  160. truthound_dashboard/static/assets/channel-Q6mHF1Hd.js +0 -1
  161. truthound_dashboard/static/assets/chunk-4BX2VUAB-DmyoPVuJ.js +0 -1
  162. truthound_dashboard/static/assets/chunk-55IACEB6-Bcz6Siv8.js +0 -1
  163. truthound_dashboard/static/assets/chunk-B4BG7PRW-Br3G5Rum.js +0 -165
  164. truthound_dashboard/static/assets/chunk-DI55MBZ5-DuM9c23u.js +0 -220
  165. truthound_dashboard/static/assets/chunk-FMBD7UC4-DNU-5mvT.js +0 -15
  166. truthound_dashboard/static/assets/chunk-QN33PNHL-Im2yNcmS.js +0 -1
  167. truthound_dashboard/static/assets/chunk-QZHKN3VN-kZr8XFm1.js +0 -1
  168. truthound_dashboard/static/assets/chunk-TZMSLE5B-Q__360q_.js +0 -1
  169. truthound_dashboard/static/assets/classDiagram-2ON5EDUG-vtixxUyK.js +0 -1
  170. truthound_dashboard/static/assets/classDiagram-v2-WZHVMYZB-vtixxUyK.js +0 -1
  171. truthound_dashboard/static/assets/clone-BOt2LwD0.js +0 -1
  172. truthound_dashboard/static/assets/cose-bilkent-S5V4N54A-CBDw6iac.js +0 -1
  173. truthound_dashboard/static/assets/dagre-6UL2VRFP-XdKqmmY9.js +0 -4
  174. truthound_dashboard/static/assets/diagram-PSM6KHXK-DAZ8nx9V.js +0 -24
  175. truthound_dashboard/static/assets/diagram-QEK2KX5R-BRvDTbGD.js +0 -43
  176. truthound_dashboard/static/assets/diagram-S2PKOQOG-bQcczUkl.js +0 -24
  177. truthound_dashboard/static/assets/erDiagram-Q2GNP2WA-DPje7VMN.js +0 -60
  178. truthound_dashboard/static/assets/flowDiagram-NV44I4VS-B7BVtFVS.js +0 -162
  179. truthound_dashboard/static/assets/ganttDiagram-JELNMOA3-D6WKSS7U.js +0 -267
  180. truthound_dashboard/static/assets/gitGraphDiagram-NY62KEGX-D3vtVd3y.js +0 -65
  181. truthound_dashboard/static/assets/graph-BKgNKZVp.js +0 -1
  182. truthound_dashboard/static/assets/index-C6JSrkHo.css +0 -1
  183. truthound_dashboard/static/assets/index-DkU82VsU.js +0 -1800
  184. truthound_dashboard/static/assets/infoDiagram-WHAUD3N6-DnNCT429.js +0 -2
  185. truthound_dashboard/static/assets/journeyDiagram-XKPGCS4Q-DGiMozqS.js +0 -139
  186. truthound_dashboard/static/assets/kanban-definition-3W4ZIXB7-BV2gUgli.js +0 -89
  187. truthound_dashboard/static/assets/katex-Cu_Erd72.js +0 -261
  188. truthound_dashboard/static/assets/layout-DI2MfQ5G.js +0 -1
  189. truthound_dashboard/static/assets/min-DYdgXVcT.js +0 -1
  190. truthound_dashboard/static/assets/mindmap-definition-VGOIOE7T-C7x4ruxz.js +0 -68
  191. truthound_dashboard/static/assets/pieDiagram-ADFJNKIX-CAJaAB9f.js +0 -30
  192. truthound_dashboard/static/assets/quadrantDiagram-AYHSOK5B-DeqwDI46.js +0 -7
  193. truthound_dashboard/static/assets/requirementDiagram-UZGBJVZJ-e3XDpZIM.js +0 -64
  194. truthound_dashboard/static/assets/sankeyDiagram-TZEHDZUN-CNnAv5Ux.js +0 -10
  195. truthound_dashboard/static/assets/sequenceDiagram-WL72ISMW-Dsne-Of3.js +0 -145
  196. truthound_dashboard/static/assets/stateDiagram-FKZM4ZOC-Ee0sQXyb.js +0 -1
  197. truthound_dashboard/static/assets/stateDiagram-v2-4FDKWEC3-B26KqW_W.js +0 -1
  198. truthound_dashboard/static/assets/timeline-definition-IT6M3QCI-DZYi2yl3.js +0 -61
  199. truthound_dashboard/static/assets/treemap-KMMF4GRG-CY3f8In2.js +0 -128
  200. truthound_dashboard/static/assets/unmerged_dictionaries-Dd7xcPWG.js +0 -1
  201. truthound_dashboard/static/assets/xychartDiagram-PRI3JC2R-CS7fydZZ.js +0 -7
  202. truthound_dashboard-1.4.4.dist-info/METADATA +0 -507
  203. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.dist-info}/WHEEL +0 -0
  204. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.dist-info}/entry_points.txt +0 -0
  205. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.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, TimestampMixin
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
- output_format: Output file format. Defaults to "csv".
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, TimestampMixin):
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 or getattr(db_mask.source, "name", None),
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
- enable_drift_detection: bool = Field(default=True, description="Enable drift detection")
81
- enable_quality_metrics: bool = Field(default=True, description="Enable quality metrics")
82
- enable_performance_metrics: bool = Field(default=True, description="Enable performance metrics")
83
- sample_rate: float = Field(default=1.0, description="Sampling rate for metrics", ge=0.0, le=1.0)
84
- drift_threshold: float = Field(default=0.1, description="Threshold for drift detection", ge=0.0, le=1.0)
85
- drift_window_size: int = Field(default=1000, description="Window size for drift detection", ge=100)
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")