truthound-dashboard 1.4.3__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.3.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.3.dist-info/METADATA +0 -505
  203. {truthound_dashboard-1.4.3.dist-info → truthound_dashboard-1.5.0.dist-info}/WHEEL +0 -0
  204. {truthound_dashboard-1.4.3.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt +0 -0
  205. {truthound_dashboard-1.4.3.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,311 +1,173 @@
1
- """Fluent builder for throttler configuration.
2
-
3
- This module provides a builder pattern for easily configuring
4
- multi-level rate limiting with a fluent API.
5
-
6
- Example:
7
- throttler = (
8
- ThrottlerBuilder()
9
- .with_per_minute_limit(10)
10
- .with_per_hour_limit(100)
11
- .with_per_day_limit(500)
12
- .with_burst_allowance(1.5)
13
- .build()
14
- )
15
-
16
- result = throttler.allow("channel-1")
1
+ """Dashboard-specific throttler builder using truthound.
2
+
3
+ This module provides adapters that integrate truthound's throttling
4
+ system with the Dashboard's database configuration.
17
5
  """
18
6
 
19
7
  from __future__ import annotations
20
8
 
21
- from typing import TYPE_CHECKING
9
+ from typing import Any, TYPE_CHECKING
22
10
 
23
- from .stores import BaseThrottlingStore, InMemoryThrottlingStore
24
- from .throttlers import (
25
- BaseThrottler,
26
- CompositeThrottler,
27
- FixedWindowThrottler,
11
+ from truthound.checkpoint.throttling import (
12
+ ThrottlerBuilder,
28
13
  NotificationThrottler,
29
- TokenBucketThrottler,
14
+ ThrottlingConfig,
15
+ RateLimitScope,
16
+ RateLimit,
30
17
  )
31
18
 
32
19
  if TYPE_CHECKING:
33
20
  from typing import Self
34
21
 
35
22
 
36
- class ThrottlerBuilder:
37
- """Fluent builder for creating throttlers.
23
+ class DashboardThrottlerBuilder:
24
+ """Dashboard-specific throttler builder.
38
25
 
39
- Provides a convenient API for configuring multi-level
40
- rate limiting with various algorithms.
26
+ Wraps truthound's ThrottlerBuilder and provides integration
27
+ with the Dashboard's database configuration.
41
28
 
42
29
  Example:
43
- # Simple per-minute limit
44
- throttler = (
45
- ThrottlerBuilder()
46
- .with_per_minute_limit(10)
47
- .build()
48
- )
49
-
50
- # Multi-level limits with burst
30
+ builder = DashboardThrottlerBuilder()
51
31
  throttler = (
52
- ThrottlerBuilder()
32
+ builder
53
33
  .with_per_minute_limit(10)
54
34
  .with_per_hour_limit(100)
55
- .with_burst_allowance(1.5)
56
- .build()
57
- )
58
-
59
- # Token bucket for smooth limiting
60
- throttler = (
61
- ThrottlerBuilder()
62
- .with_token_bucket(capacity=10, refill_rate=1)
35
+ .with_action_limit("pagerduty", per_minute=5)
63
36
  .build()
64
37
  )
65
38
  """
66
39
 
67
40
  def __init__(self) -> None:
68
- """Initialize builder."""
69
- self._per_second: int | None = None
70
- self._per_minute: int | None = None
71
- self._per_hour: int | None = None
72
- self._per_day: int | None = None
73
- self._burst_allowance: float = 1.0
74
- self._token_bucket_capacity: float | None = None
75
- self._token_bucket_rate: float | None = None
76
- self._store: BaseThrottlingStore | None = None
77
- self._throttlers: list[BaseThrottler] = []
78
-
79
- def with_per_second_limit(self, limit: int) -> "Self":
80
- """Set per-second limit.
81
-
82
- Args:
83
- limit: Maximum requests per second.
84
-
85
- Returns:
86
- Self for chaining.
87
- """
88
- self._per_second = limit
89
- return self
41
+ """Initialize the builder."""
42
+ self._builder = ThrottlerBuilder()
90
43
 
91
44
  def with_per_minute_limit(self, limit: int) -> "Self":
92
- """Set per-minute limit.
93
-
94
- Args:
95
- limit: Maximum requests per minute.
96
-
97
- Returns:
98
- Self for chaining.
99
- """
100
- self._per_minute = limit
45
+ """Set per-minute rate limit."""
46
+ self._builder.with_per_minute_limit(limit)
101
47
  return self
102
48
 
103
49
  def with_per_hour_limit(self, limit: int) -> "Self":
104
- """Set per-hour limit.
105
-
106
- Args:
107
- limit: Maximum requests per hour.
108
-
109
- Returns:
110
- Self for chaining.
111
- """
112
- self._per_hour = limit
50
+ """Set per-hour rate limit."""
51
+ self._builder.with_per_hour_limit(limit)
113
52
  return self
114
53
 
115
54
  def with_per_day_limit(self, limit: int) -> "Self":
116
- """Set per-day limit.
117
-
118
- Args:
119
- limit: Maximum requests per day.
55
+ """Set per-day rate limit."""
56
+ self._builder.with_per_day_limit(limit)
57
+ return self
120
58
 
121
- Returns:
122
- Self for chaining.
123
- """
124
- self._per_day = limit
59
+ def with_burst_allowance(self, multiplier: float) -> "Self":
60
+ """Set burst allowance multiplier."""
61
+ self._builder.with_burst_allowance(multiplier)
125
62
  return self
126
63
 
127
- def with_burst_allowance(self, factor: float) -> "Self":
128
- """Set burst allowance factor.
64
+ def with_algorithm(self, algorithm: str) -> "Self":
65
+ """Set throttling algorithm (token_bucket, sliding_window, fixed_window)."""
66
+ self._builder.with_algorithm(algorithm)
67
+ return self
129
68
 
130
- Multiplies the limit to allow temporary bursts.
131
- For example, 1.5 allows 150% of normal rate for short periods.
69
+ def with_scope(self, scope: str) -> "Self":
70
+ """Set rate limit scope."""
71
+ scope_map = {
72
+ "global": RateLimitScope.GLOBAL,
73
+ "per_action": RateLimitScope.PER_ACTION,
74
+ "per_checkpoint": RateLimitScope.PER_CHECKPOINT,
75
+ "per_action_checkpoint": RateLimitScope.PER_ACTION_CHECKPOINT,
76
+ "per_severity": RateLimitScope.PER_SEVERITY,
77
+ "per_data_asset": RateLimitScope.PER_DATA_ASSET,
78
+ }
79
+ self._builder.with_scope(scope_map.get(scope.lower(), RateLimitScope.GLOBAL))
80
+ return self
132
81
 
133
- Args:
134
- factor: Burst multiplier (1.0 = no burst allowed).
82
+ def with_priority_bypass(self, threshold: str) -> "Self":
83
+ """Enable priority bypass for notifications above threshold."""
84
+ self._builder.with_priority_bypass(threshold)
85
+ return self
135
86
 
136
- Returns:
137
- Self for chaining.
138
- """
139
- self._burst_allowance = max(1.0, factor)
87
+ def with_action_limit(
88
+ self,
89
+ action_type: str,
90
+ *,
91
+ per_minute: int | None = None,
92
+ per_hour: int | None = None,
93
+ per_day: int | None = None,
94
+ ) -> "Self":
95
+ """Set custom limits for a specific action type."""
96
+ self._builder.with_action_limit(
97
+ action_type,
98
+ per_minute=per_minute,
99
+ per_hour=per_hour,
100
+ )
140
101
  return self
141
102
 
142
- def with_token_bucket(
103
+ def with_severity_limit(
143
104
  self,
144
- capacity: float,
145
- refill_rate: float,
105
+ severity: str,
106
+ *,
107
+ per_minute: int | None = None,
108
+ per_hour: int | None = None,
146
109
  ) -> "Self":
147
- """Configure token bucket algorithm.
110
+ """Set custom limits for a severity level."""
111
+ self._builder.with_severity_limit(
112
+ severity,
113
+ per_minute=per_minute,
114
+ per_hour=per_hour,
115
+ )
116
+ return self
148
117
 
149
- Args:
150
- capacity: Maximum tokens (burst capacity).
151
- refill_rate: Tokens added per second.
118
+ def build(self) -> NotificationThrottler:
119
+ """Build the throttler."""
120
+ return self._builder.build()
152
121
 
153
- Returns:
154
- Self for chaining.
155
- """
156
- self._token_bucket_capacity = capacity
157
- self._token_bucket_rate = refill_rate
158
- return self
159
122
 
160
- def with_store(self, store: BaseThrottlingStore) -> "Self":
161
- """Set storage backend.
123
+ def create_throttler_from_db_config(
124
+ db_config: dict[str, Any],
125
+ ) -> NotificationThrottler:
126
+ """Create a NotificationThrottler from database configuration.
162
127
 
163
- Args:
164
- store: Storage backend to use.
128
+ Args:
129
+ db_config: Configuration dictionary from database.
165
130
 
166
- Returns:
167
- Self for chaining.
168
- """
169
- self._store = store
170
- return self
131
+ Returns:
132
+ NotificationThrottler instance.
133
+ """
134
+ builder = DashboardThrottlerBuilder()
171
135
 
172
- def add_throttler(self, throttler: BaseThrottler) -> "Self":
173
- """Add a custom throttler to the composite.
136
+ if db_config.get("per_minute_limit"):
137
+ builder.with_per_minute_limit(db_config["per_minute_limit"])
174
138
 
175
- Args:
176
- throttler: Custom throttler to add.
139
+ if db_config.get("per_hour_limit"):
140
+ builder.with_per_hour_limit(db_config["per_hour_limit"])
177
141
 
178
- Returns:
179
- Self for chaining.
180
- """
181
- self._throttlers.append(throttler)
182
- return self
142
+ if db_config.get("per_day_limit"):
143
+ builder.with_per_day_limit(db_config["per_day_limit"])
183
144
 
184
- def build(self) -> BaseThrottler:
185
- """Build the throttler.
186
-
187
- Creates a CompositeThrottler if multiple limits are
188
- configured, or a single throttler for simple configs.
189
-
190
- Returns:
191
- Configured throttler.
192
- """
193
- store = self._store or InMemoryThrottlingStore()
194
- throttlers: list[BaseThrottler] = list(self._throttlers)
195
-
196
- # Add token bucket if configured
197
- if self._token_bucket_capacity and self._token_bucket_rate:
198
- throttlers.append(
199
- TokenBucketThrottler(
200
- capacity=self._token_bucket_capacity * self._burst_allowance,
201
- refill_rate=self._token_bucket_rate,
202
- store=store,
203
- )
204
- )
205
-
206
- # Add window-based throttlers
207
- if self._per_second:
208
- throttlers.append(
209
- FixedWindowThrottler(
210
- limit=int(self._per_second * self._burst_allowance),
211
- window_seconds=1,
212
- store=store,
213
- )
214
- )
215
-
216
- if self._per_minute:
217
- throttlers.append(
218
- FixedWindowThrottler(
219
- limit=int(self._per_minute * self._burst_allowance),
220
- window_seconds=60,
221
- store=store,
222
- )
223
- )
224
-
225
- if self._per_hour:
226
- throttlers.append(
227
- FixedWindowThrottler(
228
- limit=int(self._per_hour * self._burst_allowance),
229
- window_seconds=3600,
230
- store=store,
231
- )
232
- )
233
-
234
- if self._per_day:
235
- throttlers.append(
236
- FixedWindowThrottler(
237
- limit=int(self._per_day * self._burst_allowance),
238
- window_seconds=86400,
239
- store=store,
240
- )
241
- )
242
-
243
- # Return appropriate throttler
244
- if not throttlers:
245
- # No limits configured - use simple token bucket default
246
- return TokenBucketThrottler(
247
- capacity=100,
248
- refill_rate=10,
249
- store=store,
250
- )
251
-
252
- if len(throttlers) == 1:
253
- return throttlers[0]
254
-
255
- return CompositeThrottler(throttlers=throttlers)
256
-
257
- def build_notification_throttler(
258
- self,
259
- global_limits: bool = True,
260
- ) -> NotificationThrottler:
261
- """Build a NotificationThrottler service.
262
-
263
- Args:
264
- global_limits: Apply limits globally (vs per-channel).
265
-
266
- Returns:
267
- Configured NotificationThrottler.
268
- """
269
- throttler = self.build()
270
-
271
- if global_limits:
272
- return NotificationThrottler(
273
- global_throttler=throttler,
274
- )
275
- else:
276
- return NotificationThrottler(
277
- default_throttler=throttler,
278
- )
279
-
280
-
281
- def configure_global_throttling(
282
- per_minute: int | None = None,
283
- per_hour: int | None = None,
284
- per_day: int | None = None,
285
- burst_allowance: float = 1.5,
286
- ) -> NotificationThrottler:
287
- """Configure global notification throttling.
145
+ if db_config.get("burst_multiplier"):
146
+ builder.with_burst_allowance(db_config["burst_multiplier"])
288
147
 
289
- Convenience function for common throttling setup.
148
+ if db_config.get("algorithm"):
149
+ builder.with_algorithm(db_config["algorithm"])
290
150
 
291
- Args:
292
- per_minute: Max notifications per minute.
293
- per_hour: Max notifications per hour.
294
- per_day: Max notifications per day.
295
- burst_allowance: Burst factor.
151
+ if db_config.get("scope"):
152
+ builder.with_scope(db_config["scope"])
296
153
 
297
- Returns:
298
- Configured NotificationThrottler.
299
- """
300
- builder = ThrottlerBuilder()
154
+ if db_config.get("priority_bypass") and db_config.get("priority_threshold"):
155
+ builder.with_priority_bypass(db_config["priority_threshold"])
301
156
 
302
- if per_minute:
303
- builder.with_per_minute_limit(per_minute)
304
- if per_hour:
305
- builder.with_per_hour_limit(per_hour)
306
- if per_day:
307
- builder.with_per_day_limit(per_day)
157
+ # Action-specific limits
158
+ for action_type, limits in db_config.get("action_limits", {}).items():
159
+ builder.with_action_limit(
160
+ action_type,
161
+ per_minute=limits.get("per_minute"),
162
+ per_hour=limits.get("per_hour"),
163
+ )
308
164
 
309
- builder.with_burst_allowance(burst_allowance)
165
+ # Severity-specific limits
166
+ for severity, limits in db_config.get("severity_limits", {}).items():
167
+ builder.with_severity_limit(
168
+ severity,
169
+ per_minute=limits.get("per_minute"),
170
+ per_hour=limits.get("per_hour"),
171
+ )
310
172
 
311
- return builder.build_notification_throttler(global_limits=True)
173
+ return builder.build()