truthound-dashboard 1.3.1__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. truthound_dashboard/api/alerts.py +258 -0
  2. truthound_dashboard/api/anomaly.py +1302 -0
  3. truthound_dashboard/api/cross_alerts.py +352 -0
  4. truthound_dashboard/api/deps.py +143 -0
  5. truthound_dashboard/api/drift_monitor.py +540 -0
  6. truthound_dashboard/api/lineage.py +1151 -0
  7. truthound_dashboard/api/maintenance.py +363 -0
  8. truthound_dashboard/api/middleware.py +373 -1
  9. truthound_dashboard/api/model_monitoring.py +805 -0
  10. truthound_dashboard/api/notifications_advanced.py +2452 -0
  11. truthound_dashboard/api/plugins.py +2096 -0
  12. truthound_dashboard/api/profile.py +211 -14
  13. truthound_dashboard/api/reports.py +853 -0
  14. truthound_dashboard/api/router.py +147 -0
  15. truthound_dashboard/api/rule_suggestions.py +310 -0
  16. truthound_dashboard/api/schema_evolution.py +231 -0
  17. truthound_dashboard/api/sources.py +47 -3
  18. truthound_dashboard/api/triggers.py +190 -0
  19. truthound_dashboard/api/validations.py +13 -0
  20. truthound_dashboard/api/validators.py +333 -4
  21. truthound_dashboard/api/versioning.py +309 -0
  22. truthound_dashboard/api/websocket.py +301 -0
  23. truthound_dashboard/core/__init__.py +27 -0
  24. truthound_dashboard/core/anomaly.py +1395 -0
  25. truthound_dashboard/core/anomaly_explainer.py +633 -0
  26. truthound_dashboard/core/cache.py +206 -0
  27. truthound_dashboard/core/cached_services.py +422 -0
  28. truthound_dashboard/core/charts.py +352 -0
  29. truthound_dashboard/core/connections.py +1069 -42
  30. truthound_dashboard/core/cross_alerts.py +837 -0
  31. truthound_dashboard/core/drift_monitor.py +1477 -0
  32. truthound_dashboard/core/drift_sampling.py +669 -0
  33. truthound_dashboard/core/i18n/__init__.py +42 -0
  34. truthound_dashboard/core/i18n/detector.py +173 -0
  35. truthound_dashboard/core/i18n/messages.py +564 -0
  36. truthound_dashboard/core/lineage.py +971 -0
  37. truthound_dashboard/core/maintenance.py +443 -5
  38. truthound_dashboard/core/model_monitoring.py +1043 -0
  39. truthound_dashboard/core/notifications/channels.py +1020 -1
  40. truthound_dashboard/core/notifications/deduplication/__init__.py +143 -0
  41. truthound_dashboard/core/notifications/deduplication/policies.py +274 -0
  42. truthound_dashboard/core/notifications/deduplication/service.py +400 -0
  43. truthound_dashboard/core/notifications/deduplication/stores.py +2365 -0
  44. truthound_dashboard/core/notifications/deduplication/strategies.py +422 -0
  45. truthound_dashboard/core/notifications/dispatcher.py +43 -0
  46. truthound_dashboard/core/notifications/escalation/__init__.py +149 -0
  47. truthound_dashboard/core/notifications/escalation/backends.py +1384 -0
  48. truthound_dashboard/core/notifications/escalation/engine.py +429 -0
  49. truthound_dashboard/core/notifications/escalation/models.py +336 -0
  50. truthound_dashboard/core/notifications/escalation/scheduler.py +1187 -0
  51. truthound_dashboard/core/notifications/escalation/state_machine.py +330 -0
  52. truthound_dashboard/core/notifications/escalation/stores.py +2896 -0
  53. truthound_dashboard/core/notifications/events.py +49 -0
  54. truthound_dashboard/core/notifications/metrics/__init__.py +115 -0
  55. truthound_dashboard/core/notifications/metrics/base.py +528 -0
  56. truthound_dashboard/core/notifications/metrics/collectors.py +583 -0
  57. truthound_dashboard/core/notifications/routing/__init__.py +169 -0
  58. truthound_dashboard/core/notifications/routing/combinators.py +184 -0
  59. truthound_dashboard/core/notifications/routing/config.py +375 -0
  60. truthound_dashboard/core/notifications/routing/config_parser.py +867 -0
  61. truthound_dashboard/core/notifications/routing/engine.py +382 -0
  62. truthound_dashboard/core/notifications/routing/expression_engine.py +1269 -0
  63. truthound_dashboard/core/notifications/routing/jinja2_engine.py +774 -0
  64. truthound_dashboard/core/notifications/routing/rules.py +625 -0
  65. truthound_dashboard/core/notifications/routing/validator.py +678 -0
  66. truthound_dashboard/core/notifications/service.py +2 -0
  67. truthound_dashboard/core/notifications/stats_aggregator.py +850 -0
  68. truthound_dashboard/core/notifications/throttling/__init__.py +83 -0
  69. truthound_dashboard/core/notifications/throttling/builder.py +311 -0
  70. truthound_dashboard/core/notifications/throttling/stores.py +1859 -0
  71. truthound_dashboard/core/notifications/throttling/throttlers.py +633 -0
  72. truthound_dashboard/core/openlineage.py +1028 -0
  73. truthound_dashboard/core/plugins/__init__.py +39 -0
  74. truthound_dashboard/core/plugins/docs/__init__.py +39 -0
  75. truthound_dashboard/core/plugins/docs/extractor.py +703 -0
  76. truthound_dashboard/core/plugins/docs/renderers.py +804 -0
  77. truthound_dashboard/core/plugins/hooks/__init__.py +63 -0
  78. truthound_dashboard/core/plugins/hooks/decorators.py +367 -0
  79. truthound_dashboard/core/plugins/hooks/manager.py +403 -0
  80. truthound_dashboard/core/plugins/hooks/protocols.py +265 -0
  81. truthound_dashboard/core/plugins/lifecycle/__init__.py +41 -0
  82. truthound_dashboard/core/plugins/lifecycle/hot_reload.py +584 -0
  83. truthound_dashboard/core/plugins/lifecycle/machine.py +419 -0
  84. truthound_dashboard/core/plugins/lifecycle/states.py +266 -0
  85. truthound_dashboard/core/plugins/loader.py +504 -0
  86. truthound_dashboard/core/plugins/registry.py +810 -0
  87. truthound_dashboard/core/plugins/reporter_executor.py +588 -0
  88. truthound_dashboard/core/plugins/sandbox/__init__.py +59 -0
  89. truthound_dashboard/core/plugins/sandbox/code_validator.py +243 -0
  90. truthound_dashboard/core/plugins/sandbox/engines.py +770 -0
  91. truthound_dashboard/core/plugins/sandbox/protocols.py +194 -0
  92. truthound_dashboard/core/plugins/sandbox.py +617 -0
  93. truthound_dashboard/core/plugins/security/__init__.py +68 -0
  94. truthound_dashboard/core/plugins/security/analyzer.py +535 -0
  95. truthound_dashboard/core/plugins/security/policies.py +311 -0
  96. truthound_dashboard/core/plugins/security/protocols.py +296 -0
  97. truthound_dashboard/core/plugins/security/signing.py +842 -0
  98. truthound_dashboard/core/plugins/security.py +446 -0
  99. truthound_dashboard/core/plugins/validator_executor.py +401 -0
  100. truthound_dashboard/core/plugins/versioning/__init__.py +51 -0
  101. truthound_dashboard/core/plugins/versioning/constraints.py +377 -0
  102. truthound_dashboard/core/plugins/versioning/dependencies.py +541 -0
  103. truthound_dashboard/core/plugins/versioning/semver.py +266 -0
  104. truthound_dashboard/core/profile_comparison.py +601 -0
  105. truthound_dashboard/core/report_history.py +570 -0
  106. truthound_dashboard/core/reporters/__init__.py +57 -0
  107. truthound_dashboard/core/reporters/base.py +296 -0
  108. truthound_dashboard/core/reporters/csv_reporter.py +155 -0
  109. truthound_dashboard/core/reporters/html_reporter.py +598 -0
  110. truthound_dashboard/core/reporters/i18n/__init__.py +65 -0
  111. truthound_dashboard/core/reporters/i18n/base.py +494 -0
  112. truthound_dashboard/core/reporters/i18n/catalogs.py +930 -0
  113. truthound_dashboard/core/reporters/json_reporter.py +160 -0
  114. truthound_dashboard/core/reporters/junit_reporter.py +233 -0
  115. truthound_dashboard/core/reporters/markdown_reporter.py +207 -0
  116. truthound_dashboard/core/reporters/pdf_reporter.py +209 -0
  117. truthound_dashboard/core/reporters/registry.py +272 -0
  118. truthound_dashboard/core/rule_generator.py +2088 -0
  119. truthound_dashboard/core/scheduler.py +822 -12
  120. truthound_dashboard/core/schema_evolution.py +858 -0
  121. truthound_dashboard/core/services.py +152 -9
  122. truthound_dashboard/core/statistics.py +718 -0
  123. truthound_dashboard/core/streaming_anomaly.py +883 -0
  124. truthound_dashboard/core/triggers/__init__.py +45 -0
  125. truthound_dashboard/core/triggers/base.py +226 -0
  126. truthound_dashboard/core/triggers/evaluators.py +609 -0
  127. truthound_dashboard/core/triggers/factory.py +363 -0
  128. truthound_dashboard/core/unified_alerts.py +870 -0
  129. truthound_dashboard/core/validation_limits.py +509 -0
  130. truthound_dashboard/core/versioning.py +709 -0
  131. truthound_dashboard/core/websocket/__init__.py +59 -0
  132. truthound_dashboard/core/websocket/manager.py +512 -0
  133. truthound_dashboard/core/websocket/messages.py +130 -0
  134. truthound_dashboard/db/__init__.py +30 -0
  135. truthound_dashboard/db/models.py +3375 -3
  136. truthound_dashboard/main.py +22 -0
  137. truthound_dashboard/schemas/__init__.py +396 -1
  138. truthound_dashboard/schemas/anomaly.py +1258 -0
  139. truthound_dashboard/schemas/base.py +4 -0
  140. truthound_dashboard/schemas/cross_alerts.py +334 -0
  141. truthound_dashboard/schemas/drift_monitor.py +890 -0
  142. truthound_dashboard/schemas/lineage.py +428 -0
  143. truthound_dashboard/schemas/maintenance.py +154 -0
  144. truthound_dashboard/schemas/model_monitoring.py +374 -0
  145. truthound_dashboard/schemas/notifications_advanced.py +1363 -0
  146. truthound_dashboard/schemas/openlineage.py +704 -0
  147. truthound_dashboard/schemas/plugins.py +1293 -0
  148. truthound_dashboard/schemas/profile.py +420 -34
  149. truthound_dashboard/schemas/profile_comparison.py +242 -0
  150. truthound_dashboard/schemas/reports.py +285 -0
  151. truthound_dashboard/schemas/rule_suggestion.py +434 -0
  152. truthound_dashboard/schemas/schema_evolution.py +164 -0
  153. truthound_dashboard/schemas/source.py +117 -2
  154. truthound_dashboard/schemas/triggers.py +511 -0
  155. truthound_dashboard/schemas/unified_alerts.py +223 -0
  156. truthound_dashboard/schemas/validation.py +25 -1
  157. truthound_dashboard/schemas/validators/__init__.py +11 -0
  158. truthound_dashboard/schemas/validators/base.py +151 -0
  159. truthound_dashboard/schemas/versioning.py +152 -0
  160. truthound_dashboard/static/index.html +2 -2
  161. {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/METADATA +142 -22
  162. truthound_dashboard-1.4.0.dist-info/RECORD +239 -0
  163. truthound_dashboard/static/assets/index-BZG20KuF.js +0 -586
  164. truthound_dashboard/static/assets/index-D_HyZ3pb.css +0 -1
  165. truthound_dashboard/static/assets/unmerged_dictionaries-CtpqQBm0.js +0 -1
  166. truthound_dashboard-1.3.1.dist-info/RECORD +0 -110
  167. {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/WHEEL +0 -0
  168. {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/entry_points.txt +0 -0
  169. {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,266 @@
1
+ """Semantic Versioning Implementation.
2
+
3
+ This module provides semantic versioning (semver) parsing
4
+ and comparison following the Semantic Versioning 2.0.0 spec.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import re
10
+ from dataclasses import dataclass, field
11
+ from functools import total_ordering
12
+ from typing import Any
13
+
14
+
15
+ # Semver regex pattern
16
+ SEMVER_PATTERN = re.compile(
17
+ r"^v?" # Optional 'v' prefix
18
+ r"(?P<major>0|[1-9]\d*)"
19
+ r"\.(?P<minor>0|[1-9]\d*)"
20
+ r"\.(?P<patch>0|[1-9]\d*)"
21
+ r"(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)"
22
+ r"(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"
23
+ r"(?:\+(?P<build>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
24
+ )
25
+
26
+
27
+ @total_ordering
28
+ @dataclass
29
+ class Version:
30
+ """Semantic version representation.
31
+
32
+ Attributes:
33
+ major: Major version (breaking changes).
34
+ minor: Minor version (new features).
35
+ patch: Patch version (bug fixes).
36
+ prerelease: Pre-release identifier (e.g., alpha.1).
37
+ build: Build metadata (e.g., build.123).
38
+ """
39
+
40
+ major: int
41
+ minor: int
42
+ patch: int
43
+ prerelease: str | None = None
44
+ build: str | None = None
45
+
46
+ def __str__(self) -> str:
47
+ """Return string representation."""
48
+ version = f"{self.major}.{self.minor}.{self.patch}"
49
+ if self.prerelease:
50
+ version += f"-{self.prerelease}"
51
+ if self.build:
52
+ version += f"+{self.build}"
53
+ return version
54
+
55
+ def __repr__(self) -> str:
56
+ """Return repr string."""
57
+ return f"Version('{self}')"
58
+
59
+ def __hash__(self) -> int:
60
+ """Hash based on version components (excluding build)."""
61
+ return hash((self.major, self.minor, self.patch, self.prerelease))
62
+
63
+ def __eq__(self, other: object) -> bool:
64
+ """Check equality (build metadata is ignored)."""
65
+ if not isinstance(other, Version):
66
+ return NotImplemented
67
+ return (
68
+ self.major == other.major
69
+ and self.minor == other.minor
70
+ and self.patch == other.patch
71
+ and self.prerelease == other.prerelease
72
+ )
73
+
74
+ def __lt__(self, other: object) -> bool:
75
+ """Compare versions."""
76
+ if not isinstance(other, Version):
77
+ return NotImplemented
78
+
79
+ # Compare major.minor.patch
80
+ if (self.major, self.minor, self.patch) != (
81
+ other.major,
82
+ other.minor,
83
+ other.patch,
84
+ ):
85
+ return (self.major, self.minor, self.patch) < (
86
+ other.major,
87
+ other.minor,
88
+ other.patch,
89
+ )
90
+
91
+ # Pre-release versions have lower precedence than release
92
+ if self.prerelease and not other.prerelease:
93
+ return True
94
+ if not self.prerelease and other.prerelease:
95
+ return False
96
+
97
+ # Compare pre-release identifiers
98
+ if self.prerelease and other.prerelease:
99
+ return self._compare_prerelease(self.prerelease, other.prerelease) < 0
100
+
101
+ return False
102
+
103
+ @staticmethod
104
+ def _compare_prerelease(a: str, b: str) -> int:
105
+ """Compare pre-release strings.
106
+
107
+ Returns:
108
+ -1 if a < b, 0 if a == b, 1 if a > b.
109
+ """
110
+ a_parts = a.split(".")
111
+ b_parts = b.split(".")
112
+
113
+ for i in range(max(len(a_parts), len(b_parts))):
114
+ if i >= len(a_parts):
115
+ return -1
116
+ if i >= len(b_parts):
117
+ return 1
118
+
119
+ a_part = a_parts[i]
120
+ b_part = b_parts[i]
121
+
122
+ # Numeric identifiers have lower precedence
123
+ a_is_num = a_part.isdigit()
124
+ b_is_num = b_part.isdigit()
125
+
126
+ if a_is_num and b_is_num:
127
+ a_num = int(a_part)
128
+ b_num = int(b_part)
129
+ if a_num != b_num:
130
+ return -1 if a_num < b_num else 1
131
+ elif a_is_num:
132
+ return -1
133
+ elif b_is_num:
134
+ return 1
135
+ else:
136
+ if a_part != b_part:
137
+ return -1 if a_part < b_part else 1
138
+
139
+ return 0
140
+
141
+ def bump_major(self) -> "Version":
142
+ """Return a new version with major bumped."""
143
+ return Version(self.major + 1, 0, 0)
144
+
145
+ def bump_minor(self) -> "Version":
146
+ """Return a new version with minor bumped."""
147
+ return Version(self.major, self.minor + 1, 0)
148
+
149
+ def bump_patch(self) -> "Version":
150
+ """Return a new version with patch bumped."""
151
+ return Version(self.major, self.minor, self.patch + 1)
152
+
153
+ def to_dict(self) -> dict[str, Any]:
154
+ """Convert to dictionary."""
155
+ return {
156
+ "major": self.major,
157
+ "minor": self.minor,
158
+ "patch": self.patch,
159
+ "prerelease": self.prerelease,
160
+ "build": self.build,
161
+ "string": str(self),
162
+ }
163
+
164
+ @classmethod
165
+ def from_dict(cls, data: dict[str, Any]) -> "Version":
166
+ """Create from dictionary."""
167
+ return cls(
168
+ major=data["major"],
169
+ minor=data["minor"],
170
+ patch=data["patch"],
171
+ prerelease=data.get("prerelease"),
172
+ build=data.get("build"),
173
+ )
174
+
175
+ def is_prerelease(self) -> bool:
176
+ """Check if this is a pre-release version."""
177
+ return self.prerelease is not None
178
+
179
+ def is_stable(self) -> bool:
180
+ """Check if this is a stable release (major > 0, no prerelease)."""
181
+ return self.major > 0 and not self.prerelease
182
+
183
+ def is_compatible_with(self, other: "Version") -> bool:
184
+ """Check if this version is API-compatible with another.
185
+
186
+ For semver, versions are compatible if they have the same major
187
+ version (for major > 0) or same major.minor (for major == 0).
188
+ """
189
+ if self.major == 0 and other.major == 0:
190
+ # 0.x.y versions: minor change is breaking
191
+ return self.minor == other.minor
192
+ return self.major == other.major
193
+
194
+
195
+ def parse_version(version_str: str) -> Version:
196
+ """Parse a version string into a Version object.
197
+
198
+ Args:
199
+ version_str: Version string (e.g., "1.2.3", "v1.2.3-alpha.1+build.123").
200
+
201
+ Returns:
202
+ Version object.
203
+
204
+ Raises:
205
+ ValueError: If version string is invalid.
206
+ """
207
+ if not version_str:
208
+ raise ValueError("Empty version string")
209
+
210
+ match = SEMVER_PATTERN.match(version_str.strip())
211
+ if not match:
212
+ # Try simple parsing for common formats
213
+ parts = version_str.lstrip("v").split(".")
214
+ if len(parts) >= 3:
215
+ try:
216
+ major = int(parts[0])
217
+ minor = int(parts[1])
218
+ patch_str = parts[2].split("-")[0].split("+")[0]
219
+ patch = int(patch_str)
220
+ prerelease = None
221
+ build = None
222
+
223
+ if "-" in version_str:
224
+ pre_build = version_str.split("-", 1)[1]
225
+ if "+" in pre_build:
226
+ prerelease, build = pre_build.split("+", 1)
227
+ else:
228
+ prerelease = pre_build
229
+ elif "+" in version_str:
230
+ build = version_str.split("+", 1)[1]
231
+
232
+ return Version(major, minor, patch, prerelease, build)
233
+ except (ValueError, IndexError):
234
+ pass
235
+
236
+ raise ValueError(f"Invalid version string: {version_str}")
237
+
238
+ return Version(
239
+ major=int(match.group("major")),
240
+ minor=int(match.group("minor")),
241
+ patch=int(match.group("patch")),
242
+ prerelease=match.group("prerelease"),
243
+ build=match.group("build"),
244
+ )
245
+
246
+
247
+ def compare_versions(v1: str | Version, v2: str | Version) -> int:
248
+ """Compare two versions.
249
+
250
+ Args:
251
+ v1: First version.
252
+ v2: Second version.
253
+
254
+ Returns:
255
+ -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2.
256
+ """
257
+ if isinstance(v1, str):
258
+ v1 = parse_version(v1)
259
+ if isinstance(v2, str):
260
+ v2 = parse_version(v2)
261
+
262
+ if v1 < v2:
263
+ return -1
264
+ elif v1 > v2:
265
+ return 1
266
+ return 0