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.
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 +437 -10
  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 +11 -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.0.dist-info/METADATA +309 -0
  146. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.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.0.dist-info}/WHEEL +0 -0
  204. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt +0 -0
  205. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -4,6 +4,9 @@ This module provides efficient stats aggregation using SQLAlchemy
4
4
  aggregate queries instead of fetching all records. Includes caching
5
5
  layer for frequently accessed statistics.
6
6
 
7
+ Now integrates with truthound library to provide runtime statistics
8
+ from truthound's deduplication, throttling, and escalation engines.
9
+
7
10
  The StatsAggregator follows the Repository pattern with caching
8
11
  to optimize database queries for stats endpoints.
9
12
 
@@ -18,6 +21,9 @@ Example:
18
21
 
19
22
  # Get deduplication stats with caching (default 30s TTL)
20
23
  stats = await aggregator.get_deduplication_stats(use_cache=True)
24
+
25
+ # Get truthound runtime stats
26
+ truthound_stats = await aggregator.get_truthound_stats()
21
27
  """
22
28
 
23
29
  from __future__ import annotations
@@ -28,7 +34,7 @@ import logging
28
34
  from dataclasses import dataclass, field
29
35
  from datetime import datetime, timedelta
30
36
  from enum import Enum
31
- from typing import Any, Generic, TypeVar
37
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar
32
38
 
33
39
  from sqlalchemy import func, select
34
40
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -41,6 +47,9 @@ from ...db.models import (
41
47
  ThrottlingConfig,
42
48
  )
43
49
 
50
+ if TYPE_CHECKING:
51
+ from .truthound_adapter import TruthoundNotificationAdapter, TruthoundStats
52
+
44
53
  logger = logging.getLogger(__name__)
45
54
 
46
55
  T = TypeVar("T")
@@ -358,6 +367,51 @@ class ThrottlingStatsResult:
358
367
  cached_at: datetime | None = None
359
368
 
360
369
 
370
+ @dataclass
371
+ class TruthoundRuntimeStatsResult:
372
+ """Truthound runtime statistics result.
373
+
374
+ Contains live statistics from truthound library's deduplication,
375
+ throttling, and escalation engines.
376
+
377
+ Attributes:
378
+ deduplication: Deduplication runtime stats.
379
+ throttling: Throttling runtime stats.
380
+ escalation: Escalation runtime stats.
381
+ routing: Routing runtime stats.
382
+ cached: Whether result was served from cache.
383
+ cached_at: When result was cached (if cached).
384
+ """
385
+
386
+ deduplication: dict[str, Any]
387
+ throttling: dict[str, Any]
388
+ escalation: dict[str, Any]
389
+ routing: dict[str, Any]
390
+ cached: bool = False
391
+ cached_at: datetime | None = None
392
+
393
+
394
+ @dataclass
395
+ class CombinedStatsResult:
396
+ """Combined dashboard and truthound statistics.
397
+
398
+ Provides a unified view of both:
399
+ - Dashboard config stats (from DB)
400
+ - Truthound runtime stats (from library)
401
+
402
+ Attributes:
403
+ dashboard: Dashboard config statistics.
404
+ truthound: Truthound runtime statistics.
405
+ cached: Whether result was served from cache.
406
+ cached_at: When result was cached (if cached).
407
+ """
408
+
409
+ dashboard: dict[str, Any]
410
+ truthound: TruthoundRuntimeStatsResult | None
411
+ cached: bool = False
412
+ cached_at: datetime | None = None
413
+
414
+
361
415
  class StatsAggregator:
362
416
  """Efficient stats aggregation service with caching.
363
417
 
@@ -848,3 +902,194 @@ class StatsAggregator:
848
902
  Dictionary with cache statistics.
849
903
  """
850
904
  return await self._cache.get_stats()
905
+
906
+ # =========================================================================
907
+ # Truthound Runtime Stats
908
+ # =========================================================================
909
+
910
+ async def get_truthound_stats(
911
+ self,
912
+ use_cache: bool = True,
913
+ cache_ttl_seconds: int | None = None,
914
+ ) -> TruthoundRuntimeStatsResult:
915
+ """Get truthound library runtime statistics.
916
+
917
+ Returns live statistics from truthound's deduplication, throttling,
918
+ and escalation engines. These are different from the DB config stats -
919
+ they reflect actual runtime behavior.
920
+
921
+ Args:
922
+ use_cache: Whether to use caching.
923
+ cache_ttl_seconds: Cache TTL override.
924
+
925
+ Returns:
926
+ TruthoundRuntimeStatsResult with live statistics.
927
+ """
928
+ cache_key = "truthound_runtime_stats"
929
+ ttl = cache_ttl_seconds if cache_ttl_seconds is not None else self._cache_ttl
930
+
931
+ # Try cache first
932
+ if use_cache:
933
+ cached = await self._cache.get(cache_key)
934
+ if cached is not None:
935
+ cached.cached = True
936
+ return cached
937
+
938
+ # Get stats from truthound adapter
939
+ try:
940
+ from .truthound_adapter import TruthoundNotificationAdapter
941
+
942
+ adapter = TruthoundNotificationAdapter(self._session)
943
+ await adapter.initialize()
944
+
945
+ stats = adapter.get_stats()
946
+
947
+ result = TruthoundRuntimeStatsResult(
948
+ deduplication={
949
+ "total_evaluated": stats.dedup_total_evaluated,
950
+ "suppressed": stats.dedup_suppressed,
951
+ "suppression_ratio": (
952
+ stats.dedup_suppressed / stats.dedup_total_evaluated
953
+ if stats.dedup_total_evaluated > 0
954
+ else 0.0
955
+ ),
956
+ "active_fingerprints": stats.dedup_active_fingerprints,
957
+ },
958
+ throttling={
959
+ "total_checked": stats.throttle_total_checked,
960
+ "total_allowed": stats.throttle_total_allowed,
961
+ "total_throttled": stats.throttle_total_throttled,
962
+ "throttle_rate": (
963
+ stats.throttle_total_throttled / stats.throttle_total_checked
964
+ if stats.throttle_total_checked > 0
965
+ else 0.0
966
+ ),
967
+ },
968
+ escalation={
969
+ "total_escalations": stats.escalation_total,
970
+ "active_escalations": stats.escalation_active,
971
+ "acknowledged": stats.escalation_acknowledged,
972
+ "acknowledgment_rate": (
973
+ stats.escalation_acknowledged / stats.escalation_total
974
+ if stats.escalation_total > 0
975
+ else 0.0
976
+ ),
977
+ },
978
+ routing={
979
+ "total_routes": stats.routing_total_routes,
980
+ "total_matched": stats.routing_total_matched,
981
+ "match_rate": (
982
+ stats.routing_total_matched / stats.routing_total_routes
983
+ if stats.routing_total_routes > 0
984
+ else 0.0
985
+ ),
986
+ },
987
+ cached=False,
988
+ cached_at=None,
989
+ )
990
+ except Exception as e:
991
+ logger.warning(f"Failed to get truthound stats: {e}")
992
+ # Return empty stats on error
993
+ result = TruthoundRuntimeStatsResult(
994
+ deduplication={
995
+ "total_evaluated": 0,
996
+ "suppressed": 0,
997
+ "suppression_ratio": 0.0,
998
+ "active_fingerprints": 0,
999
+ "error": str(e),
1000
+ },
1001
+ throttling={
1002
+ "total_checked": 0,
1003
+ "total_allowed": 0,
1004
+ "total_throttled": 0,
1005
+ "throttle_rate": 0.0,
1006
+ "error": str(e),
1007
+ },
1008
+ escalation={
1009
+ "total_escalations": 0,
1010
+ "active_escalations": 0,
1011
+ "acknowledged": 0,
1012
+ "acknowledgment_rate": 0.0,
1013
+ "error": str(e),
1014
+ },
1015
+ routing={
1016
+ "total_routes": 0,
1017
+ "total_matched": 0,
1018
+ "match_rate": 0.0,
1019
+ "error": str(e),
1020
+ },
1021
+ cached=False,
1022
+ cached_at=None,
1023
+ )
1024
+
1025
+ # Cache result
1026
+ if use_cache:
1027
+ result.cached_at = datetime.utcnow()
1028
+ await self._cache.set(cache_key, result, ttl)
1029
+
1030
+ return result
1031
+
1032
+ async def get_combined_stats(
1033
+ self,
1034
+ time_range: TimeRange | None = None,
1035
+ use_cache: bool = True,
1036
+ include_truthound: bool = True,
1037
+ ) -> CombinedStatsResult:
1038
+ """Get combined dashboard and truthound statistics.
1039
+
1040
+ Provides a unified view of both configuration-based stats (from DB)
1041
+ and runtime stats (from truthound library).
1042
+
1043
+ Args:
1044
+ time_range: Optional time range filter for dashboard stats.
1045
+ use_cache: Whether to use caching.
1046
+ include_truthound: Whether to include truthound runtime stats.
1047
+
1048
+ Returns:
1049
+ CombinedStatsResult with both dashboard and truthound stats.
1050
+ """
1051
+ cache_key = self._generate_cache_key(
1052
+ "combined_stats",
1053
+ time_range,
1054
+ include_truthound=include_truthound,
1055
+ )
1056
+ ttl = self._cache_ttl
1057
+
1058
+ # Try cache first
1059
+ if use_cache:
1060
+ cached = await self._cache.get(cache_key)
1061
+ if cached is not None:
1062
+ cached.cached = True
1063
+ return cached
1064
+
1065
+ # Get dashboard stats
1066
+ dashboard_stats = await self.get_all_stats(time_range, use_cache=False)
1067
+
1068
+ # Get truthound stats if requested
1069
+ truthound_stats = None
1070
+ if include_truthound:
1071
+ truthound_stats = await self.get_truthound_stats(use_cache=False)
1072
+
1073
+ result = CombinedStatsResult(
1074
+ dashboard=dashboard_stats,
1075
+ truthound=truthound_stats,
1076
+ cached=False,
1077
+ cached_at=None,
1078
+ )
1079
+
1080
+ # Cache result
1081
+ if use_cache:
1082
+ result.cached_at = datetime.utcnow()
1083
+ await self._cache.set(cache_key, result, ttl)
1084
+
1085
+ return result
1086
+
1087
+ async def invalidate_truthound_cache(self) -> int:
1088
+ """Invalidate truthound stats cache entries.
1089
+
1090
+ Returns:
1091
+ Number of entries invalidated.
1092
+ """
1093
+ count = await self._cache.invalidate_pattern("truthound")
1094
+ count += await self._cache.invalidate_pattern("combined_stats")
1095
+ return count
@@ -1,83 +1,100 @@
1
- """Notification throttling system.
1
+ """Notification throttling using truthound.checkpoint.throttling.
2
2
 
3
- This module provides rate limiting for notifications to prevent
4
- overwhelming recipients during incidents or high-frequency events.
3
+ This module provides rate limiting for notifications using truthound's
4
+ throttling infrastructure.
5
5
 
6
- Features:
7
- - 5 throttler types (TokenBucket, FixedWindow, SlidingWindow, Composite, NoOp)
8
- - Fluent builder API for configuration
9
- - Per-channel and global throttling
10
- - Burst allowance support
6
+ Key Components from truthound.checkpoint.throttling:
7
+ - NotificationThrottler: High-level throttling service
8
+ - ThrottlingConfig: Configuration for throttling
9
+ - ThrottlerBuilder: Fluent builder API
10
+ - RateLimit: Rate limit configuration
11
+ - RateLimitScope: Scope of rate limit application
12
+ - ThrottleStatus: Throttle result status
13
+
14
+ Throttler Types from truthound (5 types):
15
+ - TokenBucketThrottler: Token bucket algorithm (allows bursts)
16
+ - SlidingWindowThrottler: Sliding window algorithm (more accurate)
17
+ - FixedWindowThrottler: Fixed window algorithm (simple)
18
+ - CompositeThrottler: Multi-level rate limits
19
+ - NoOpThrottler: Pass-through (for testing)
11
20
 
12
21
  Example:
13
- from truthound_dashboard.core.notifications.throttling import (
22
+ from truthound.checkpoint.throttling import (
14
23
  ThrottlerBuilder,
15
- TokenBucketThrottler,
24
+ RateLimitScope,
16
25
  )
17
26
 
18
- # Using builder
27
+ # Build throttler with fluent API
19
28
  throttler = (
20
29
  ThrottlerBuilder()
21
30
  .with_per_minute_limit(10)
22
31
  .with_per_hour_limit(100)
32
+ .with_per_day_limit(500)
23
33
  .with_burst_allowance(1.5)
34
+ .with_scope(RateLimitScope.PER_ACTION)
35
+ .with_priority_bypass("critical")
24
36
  .build()
25
37
  )
26
38
 
27
39
  # Check if allowed
28
- result = throttler.allow("slack-channel")
40
+ result = throttler.acquire(action_type="slack", checkpoint_name="my_check")
29
41
  if result.allowed:
30
42
  send_notification()
31
43
  else:
32
- print(f"Retry after {result.retry_after} seconds")
44
+ print(f"Retry after {result.retry_after:.1f}s")
33
45
  """
34
46
 
35
- from .builder import ThrottlerBuilder
36
- from .stores import (
37
- REDIS_AVAILABLE,
38
- BaseThrottlingStore,
39
- InMemoryThrottlingStore,
40
- RedisThrottlingStore,
41
- SQLiteThrottlingStore,
42
- ThrottlingEntry,
43
- ThrottlingMetrics,
44
- ThrottlingStoreType,
45
- create_throttling_store,
46
- )
47
- from .throttlers import (
48
- BaseThrottler,
49
- CompositeThrottler,
50
- FixedWindowThrottler,
51
- NoOpThrottler,
47
+ # Re-export from truthound.checkpoint.throttling
48
+ from truthound.checkpoint.throttling import (
52
49
  NotificationThrottler,
53
- SlidingWindowThrottler,
50
+ ThrottlingConfig,
51
+ ThrottlerBuilder,
52
+ ThrottlingMiddleware,
53
+ throttled,
54
+ RateLimit,
55
+ RateLimitScope,
56
+ TimeUnit,
57
+ ThrottleStatus,
54
58
  ThrottleResult,
55
- ThrottlerRegistry,
59
+ ThrottlingKey,
60
+ )
61
+
62
+ # Throttler implementations
63
+ from truthound.checkpoint.throttling import (
56
64
  TokenBucketThrottler,
65
+ SlidingWindowThrottler,
66
+ FixedWindowThrottler,
67
+ CompositeThrottler,
68
+ NoOpThrottler,
57
69
  )
58
70
 
71
+ # Storage
72
+ from truthound.checkpoint.throttling import InMemoryThrottlingStore
73
+
74
+ # Dashboard-specific adapters
75
+ from .builder import DashboardThrottlerBuilder
76
+
59
77
  __all__ = [
60
- # Throttlers
61
- "BaseThrottler",
78
+ # truthound core
79
+ "NotificationThrottler",
80
+ "ThrottlingConfig",
81
+ "ThrottlerBuilder",
82
+ "ThrottlingMiddleware",
83
+ "throttled",
84
+ "RateLimit",
85
+ "RateLimitScope",
86
+ "TimeUnit",
87
+ "ThrottleStatus",
88
+ "ThrottleResult",
89
+ "ThrottlingKey",
90
+ # Throttler implementations
62
91
  "TokenBucketThrottler",
63
- "FixedWindowThrottler",
64
92
  "SlidingWindowThrottler",
93
+ "FixedWindowThrottler",
65
94
  "CompositeThrottler",
66
95
  "NoOpThrottler",
67
- "ThrottlerRegistry",
68
- "ThrottleResult",
69
- # Service
70
- "NotificationThrottler",
71
- # Builder
72
- "ThrottlerBuilder",
73
- # Stores
74
- "BaseThrottlingStore",
96
+ # Storage
75
97
  "InMemoryThrottlingStore",
76
- "SQLiteThrottlingStore",
77
- "RedisThrottlingStore",
78
- "ThrottlingEntry",
79
- "ThrottlingMetrics",
80
- "ThrottlingStoreType",
81
- "create_throttling_store",
82
- "REDIS_AVAILABLE",
98
+ # Dashboard adapters
99
+ "DashboardThrottlerBuilder",
83
100
  ]