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
@@ -0,0 +1,299 @@
1
+ """Abstract base class for data quality backends.
2
+
3
+ This module defines the abstract base class that all data quality
4
+ backends must implement. It provides the contract for validation,
5
+ profiling, schema learning, and other data quality operations.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ from abc import ABC, abstractmethod
12
+ from concurrent.futures import ThreadPoolExecutor
13
+ from functools import partial
14
+ from typing import Any
15
+
16
+ from truthound_dashboard.core.interfaces import DataInput
17
+
18
+ # Import result types from the main adapter (will be moved later)
19
+ from truthound_dashboard.core.truthound_adapter import (
20
+ CheckResult,
21
+ CompareResult,
22
+ GenerateSuiteResult,
23
+ LearnResult,
24
+ MaskResult,
25
+ ProfileResult,
26
+ ScanResult,
27
+ )
28
+
29
+
30
+ class BaseDataQualityBackend(ABC):
31
+ """Abstract base class for data quality backends.
32
+
33
+ This class defines the interface that all data quality backends
34
+ must implement. Concrete implementations (e.g., TruthoundBackend,
35
+ MockBackend) inherit from this class.
36
+
37
+ The backend is responsible for:
38
+ - Running data validations (check)
39
+ - Learning schemas from data (learn)
40
+ - Profiling data (profile)
41
+ - Detecting drift between datasets (compare)
42
+ - Scanning for PII (scan)
43
+ - Masking sensitive data (mask)
44
+ - Generating validation suites (generate_suite)
45
+
46
+ Example:
47
+ class MyBackend(BaseDataQualityBackend):
48
+ def is_available(self) -> bool:
49
+ return True
50
+
51
+ async def check(self, data, **kwargs) -> CheckResult:
52
+ # Custom implementation
53
+ pass
54
+ """
55
+
56
+ def __init__(self, max_workers: int = 4) -> None:
57
+ """Initialize backend.
58
+
59
+ Args:
60
+ max_workers: Maximum worker threads for async operations.
61
+ """
62
+ self._executor = ThreadPoolExecutor(max_workers=max_workers)
63
+ self._max_workers = max_workers
64
+
65
+ @abstractmethod
66
+ def is_available(self) -> bool:
67
+ """Check if the backend is available.
68
+
69
+ This method should verify that all required dependencies
70
+ are installed and the backend can function.
71
+
72
+ Returns:
73
+ True if the backend is ready to use.
74
+ """
75
+ ...
76
+
77
+ def get_version(self) -> str | None:
78
+ """Get the backend library version.
79
+
80
+ Returns:
81
+ Version string or None if not available.
82
+ """
83
+ return None
84
+
85
+ @abstractmethod
86
+ async def check(
87
+ self,
88
+ data: DataInput,
89
+ *,
90
+ validators: list[str] | None = None,
91
+ validator_config: dict[str, dict[str, Any]] | None = None,
92
+ schema: str | None = None,
93
+ auto_schema: bool = False,
94
+ columns: list[str] | None = None,
95
+ min_severity: str | None = None,
96
+ strict: bool = False,
97
+ parallel: bool = False,
98
+ max_workers: int | None = None,
99
+ pushdown: bool | None = None,
100
+ ) -> CheckResult:
101
+ """Run data validation.
102
+
103
+ Args:
104
+ data: File path or DataSource object.
105
+ validators: List of validator names to run.
106
+ validator_config: Per-validator configuration.
107
+ schema: Path to schema YAML file.
108
+ auto_schema: Auto-learn schema for validation.
109
+ columns: Columns to validate.
110
+ min_severity: Minimum severity to report.
111
+ strict: Raise exception on failures.
112
+ parallel: Use parallel execution.
113
+ max_workers: Max threads for parallel.
114
+ pushdown: Enable query pushdown.
115
+
116
+ Returns:
117
+ CheckResult with validation results.
118
+ """
119
+ ...
120
+
121
+ @abstractmethod
122
+ async def learn(
123
+ self,
124
+ source: DataInput,
125
+ *,
126
+ infer_constraints: bool = True,
127
+ categorical_threshold: int | None = None,
128
+ sample_size: int | None = None,
129
+ ) -> LearnResult:
130
+ """Learn schema from data.
131
+
132
+ Args:
133
+ source: File path or DataSource object.
134
+ infer_constraints: Infer constraints from statistics.
135
+ categorical_threshold: Max unique values for categorical.
136
+ sample_size: Number of rows to sample.
137
+
138
+ Returns:
139
+ LearnResult with schema information.
140
+ """
141
+ ...
142
+
143
+ @abstractmethod
144
+ async def profile(
145
+ self,
146
+ source: DataInput,
147
+ *,
148
+ sample_size: int | None = None,
149
+ include_patterns: bool = True,
150
+ include_correlations: bool = False,
151
+ include_distributions: bool = True,
152
+ top_n_values: int = 10,
153
+ pattern_sample_size: int = 1000,
154
+ correlation_threshold: float = 0.7,
155
+ min_pattern_match_ratio: float = 0.8,
156
+ n_jobs: int = 1,
157
+ ) -> ProfileResult:
158
+ """Run data profiling.
159
+
160
+ Args:
161
+ source: File path or DataSource object.
162
+ sample_size: Max rows to sample.
163
+ include_patterns: Enable pattern detection.
164
+ include_correlations: Calculate correlations.
165
+ include_distributions: Include distribution stats.
166
+ top_n_values: Top/bottom values per column.
167
+ pattern_sample_size: Sample size for pattern matching.
168
+ correlation_threshold: Minimum correlation to report.
169
+ min_pattern_match_ratio: Minimum pattern match ratio.
170
+ n_jobs: Number of parallel jobs.
171
+
172
+ Returns:
173
+ ProfileResult with profiling information.
174
+ """
175
+ ...
176
+
177
+ @abstractmethod
178
+ async def compare(
179
+ self,
180
+ baseline: DataInput,
181
+ current: DataInput,
182
+ *,
183
+ columns: list[str] | None = None,
184
+ method: str = "auto",
185
+ threshold: float | None = None,
186
+ sample_size: int | None = None,
187
+ ) -> CompareResult:
188
+ """Compare datasets for drift detection.
189
+
190
+ Args:
191
+ baseline: Reference data.
192
+ current: Current data to compare.
193
+ columns: Columns to compare.
194
+ method: Detection method.
195
+ threshold: Drift threshold.
196
+ sample_size: Sample size for large datasets.
197
+
198
+ Returns:
199
+ CompareResult with drift results.
200
+ """
201
+ ...
202
+
203
+ @abstractmethod
204
+ async def scan(
205
+ self,
206
+ data: DataInput,
207
+ *,
208
+ columns: list[str] | None = None,
209
+ regulations: list[str] | None = None,
210
+ min_confidence: float = 0.8,
211
+ ) -> ScanResult:
212
+ """Scan for PII.
213
+
214
+ Args:
215
+ data: File path or DataSource object.
216
+ columns: Columns to scan.
217
+ regulations: Regulations to check.
218
+ min_confidence: Minimum PII confidence.
219
+
220
+ Returns:
221
+ ScanResult with PII findings.
222
+ """
223
+ ...
224
+
225
+ @abstractmethod
226
+ async def mask(
227
+ self,
228
+ data: DataInput,
229
+ output: str,
230
+ *,
231
+ columns: list[str] | None = None,
232
+ strategy: str = "redact",
233
+ ) -> MaskResult:
234
+ """Mask sensitive data.
235
+
236
+ Args:
237
+ data: File path or DataSource object.
238
+ output: Output file path.
239
+ columns: Columns to mask.
240
+ strategy: Masking strategy.
241
+
242
+ Returns:
243
+ MaskResult with masking details.
244
+ """
245
+ ...
246
+
247
+ async def generate_suite(
248
+ self,
249
+ profile: ProfileResult | dict[str, Any],
250
+ *,
251
+ strictness: str = "medium",
252
+ preset: str = "default",
253
+ include: list[str] | None = None,
254
+ exclude: list[str] | None = None,
255
+ output_format: str = "yaml",
256
+ ) -> GenerateSuiteResult:
257
+ """Generate validation suite from profile.
258
+
259
+ Default implementation raises NotImplementedError.
260
+ Override in backends that support suite generation.
261
+
262
+ Args:
263
+ profile: Profile result or dictionary.
264
+ strictness: Rule strictness level.
265
+ preset: Rule generation preset.
266
+ include: Rule categories to include.
267
+ exclude: Rule categories to exclude.
268
+ output_format: Output format.
269
+
270
+ Returns:
271
+ GenerateSuiteResult with generated rules.
272
+
273
+ Raises:
274
+ NotImplementedError: If backend doesn't support this.
275
+ """
276
+ raise NotImplementedError(
277
+ f"{self.__class__.__name__} does not support generate_suite"
278
+ )
279
+
280
+ async def _run_in_executor(self, func, *args, **kwargs):
281
+ """Run a synchronous function in the thread pool executor.
282
+
283
+ Args:
284
+ func: Function to run.
285
+ *args: Positional arguments.
286
+ **kwargs: Keyword arguments.
287
+
288
+ Returns:
289
+ Function result.
290
+ """
291
+ loop = asyncio.get_event_loop()
292
+ if kwargs:
293
+ func = partial(func, *args, **kwargs)
294
+ return await loop.run_in_executor(self._executor, func)
295
+ return await loop.run_in_executor(self._executor, func, *args)
296
+
297
+ def shutdown(self) -> None:
298
+ """Shutdown the executor."""
299
+ self._executor.shutdown(wait=False)
@@ -0,0 +1,191 @@
1
+ """Backend-specific error types.
2
+
3
+ This module defines errors that can occur during backend operations.
4
+ These errors provide meaningful context for troubleshooting and
5
+ enable graceful degradation when backends are unavailable.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from truthound_dashboard.core.exceptions import (
11
+ ErrorCode,
12
+ TruthoundDashboardError,
13
+ )
14
+
15
+
16
+ class BackendError(TruthoundDashboardError):
17
+ """Base error for backend operations.
18
+
19
+ All backend-specific errors inherit from this class.
20
+ """
21
+
22
+ code = ErrorCode.INTERNAL_ERROR
23
+ http_status = 500
24
+
25
+ def __init__(
26
+ self,
27
+ message: str,
28
+ *,
29
+ backend_name: str | None = None,
30
+ operation: str | None = None,
31
+ **kwargs,
32
+ ) -> None:
33
+ """Initialize backend error.
34
+
35
+ Args:
36
+ message: Error message.
37
+ backend_name: Name of the backend that failed.
38
+ operation: Operation that was being performed.
39
+ **kwargs: Additional context.
40
+ """
41
+ super().__init__(message, **kwargs)
42
+ self.backend_name = backend_name
43
+ self.operation = operation
44
+
45
+
46
+ class BackendUnavailableError(BackendError):
47
+ """Backend library is not installed or unavailable.
48
+
49
+ This error is raised when the required backend library (e.g., truthound)
50
+ is not installed or cannot be imported.
51
+
52
+ Example:
53
+ try:
54
+ import truthound
55
+ except ImportError:
56
+ raise BackendUnavailableError("truthound", "Library not installed")
57
+ """
58
+
59
+ code = ErrorCode.INTERNAL_ERROR
60
+ http_status = 503 # Service Unavailable
61
+
62
+ def __init__(
63
+ self,
64
+ backend_name: str,
65
+ message: str | None = None,
66
+ **kwargs,
67
+ ) -> None:
68
+ """Initialize unavailable error.
69
+
70
+ Args:
71
+ backend_name: Name of the unavailable backend.
72
+ message: Optional additional message.
73
+ **kwargs: Additional context.
74
+ """
75
+ msg = f"Backend '{backend_name}' is not available"
76
+ if message:
77
+ msg = f"{msg}: {message}"
78
+ super().__init__(msg, backend_name=backend_name, **kwargs)
79
+
80
+
81
+ class BackendVersionError(BackendError):
82
+ """Backend version is incompatible.
83
+
84
+ This error is raised when the installed backend library version
85
+ is incompatible with the dashboard's expected API.
86
+
87
+ Example:
88
+ if truthound.__version__ < "2.0":
89
+ raise BackendVersionError("truthound", "2.0", truthound.__version__)
90
+ """
91
+
92
+ code = ErrorCode.INTERNAL_ERROR
93
+ http_status = 500
94
+
95
+ def __init__(
96
+ self,
97
+ backend_name: str,
98
+ required_version: str | None = None,
99
+ current_version: str | None = None,
100
+ message: str | None = None,
101
+ **kwargs,
102
+ ) -> None:
103
+ """Initialize version error.
104
+
105
+ Args:
106
+ backend_name: Name of the backend.
107
+ required_version: Minimum required version.
108
+ current_version: Currently installed version.
109
+ message: Optional additional message.
110
+ **kwargs: Additional context.
111
+ """
112
+ msg = f"Backend '{backend_name}' version incompatibility"
113
+ if required_version and current_version:
114
+ msg = f"{msg}: requires >={required_version}, found {current_version}"
115
+ elif message:
116
+ msg = f"{msg}: {message}"
117
+ super().__init__(msg, backend_name=backend_name, **kwargs)
118
+ self.required_version = required_version
119
+ self.current_version = current_version
120
+
121
+
122
+ class BackendOperationError(BackendError):
123
+ """Backend operation failed.
124
+
125
+ This error wraps exceptions that occur during backend operations,
126
+ providing context about what operation was being performed.
127
+
128
+ Example:
129
+ try:
130
+ result = th.check(data)
131
+ except Exception as e:
132
+ raise BackendOperationError("truthound", "check", str(e))
133
+ """
134
+
135
+ code = ErrorCode.INTERNAL_ERROR
136
+ http_status = 500
137
+
138
+ def __init__(
139
+ self,
140
+ backend_name: str,
141
+ operation: str,
142
+ message: str,
143
+ *,
144
+ original_error: Exception | None = None,
145
+ **kwargs,
146
+ ) -> None:
147
+ """Initialize operation error.
148
+
149
+ Args:
150
+ backend_name: Name of the backend.
151
+ operation: Operation that failed.
152
+ message: Error message.
153
+ original_error: Original exception if available.
154
+ **kwargs: Additional context.
155
+ """
156
+ msg = f"Backend '{backend_name}' operation '{operation}' failed: {message}"
157
+ super().__init__(msg, backend_name=backend_name, operation=operation, **kwargs)
158
+ self.original_error = original_error
159
+
160
+
161
+ class BackendConfigError(BackendError):
162
+ """Backend configuration is invalid.
163
+
164
+ This error is raised when backend configuration is missing or invalid.
165
+ """
166
+
167
+ code = ErrorCode.VALIDATION_ERROR
168
+ http_status = 400
169
+
170
+ def __init__(
171
+ self,
172
+ backend_name: str,
173
+ config_key: str | None = None,
174
+ message: str | None = None,
175
+ **kwargs,
176
+ ) -> None:
177
+ """Initialize config error.
178
+
179
+ Args:
180
+ backend_name: Name of the backend.
181
+ config_key: Configuration key that's invalid.
182
+ message: Optional additional message.
183
+ **kwargs: Additional context.
184
+ """
185
+ msg = f"Backend '{backend_name}' configuration error"
186
+ if config_key:
187
+ msg = f"{msg}: invalid '{config_key}'"
188
+ if message:
189
+ msg = f"{msg}: {message}"
190
+ super().__init__(msg, backend_name=backend_name, **kwargs)
191
+ self.config_key = config_key