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.
- truthound_dashboard/api/alerts.py +75 -86
- truthound_dashboard/api/anomaly.py +7 -13
- truthound_dashboard/api/cross_alerts.py +38 -52
- truthound_dashboard/api/drift.py +49 -59
- truthound_dashboard/api/drift_monitor.py +234 -79
- truthound_dashboard/api/enterprise_sampling.py +498 -0
- truthound_dashboard/api/history.py +57 -5
- truthound_dashboard/api/lineage.py +3 -48
- truthound_dashboard/api/maintenance.py +104 -49
- truthound_dashboard/api/mask.py +1 -2
- truthound_dashboard/api/middleware.py +2 -1
- truthound_dashboard/api/model_monitoring.py +435 -311
- truthound_dashboard/api/notifications.py +227 -191
- truthound_dashboard/api/notifications_advanced.py +21 -20
- truthound_dashboard/api/observability.py +586 -0
- truthound_dashboard/api/plugins.py +2 -433
- truthound_dashboard/api/profile.py +199 -37
- truthound_dashboard/api/quality_reporter.py +701 -0
- truthound_dashboard/api/reports.py +7 -16
- truthound_dashboard/api/router.py +66 -0
- truthound_dashboard/api/rule_suggestions.py +5 -5
- truthound_dashboard/api/scan.py +17 -19
- truthound_dashboard/api/schedules.py +85 -50
- truthound_dashboard/api/schema_evolution.py +6 -6
- truthound_dashboard/api/schema_watcher.py +667 -0
- truthound_dashboard/api/sources.py +98 -27
- truthound_dashboard/api/tiering.py +1323 -0
- truthound_dashboard/api/triggers.py +14 -11
- truthound_dashboard/api/validations.py +12 -11
- truthound_dashboard/api/versioning.py +1 -6
- truthound_dashboard/core/__init__.py +129 -3
- truthound_dashboard/core/actions/__init__.py +62 -0
- truthound_dashboard/core/actions/custom.py +426 -0
- truthound_dashboard/core/actions/notifications.py +910 -0
- truthound_dashboard/core/actions/storage.py +472 -0
- truthound_dashboard/core/actions/webhook.py +281 -0
- truthound_dashboard/core/anomaly.py +262 -67
- truthound_dashboard/core/anomaly_explainer.py +4 -3
- truthound_dashboard/core/backends/__init__.py +67 -0
- truthound_dashboard/core/backends/base.py +299 -0
- truthound_dashboard/core/backends/errors.py +191 -0
- truthound_dashboard/core/backends/factory.py +423 -0
- truthound_dashboard/core/backends/mock_backend.py +451 -0
- truthound_dashboard/core/backends/truthound_backend.py +718 -0
- truthound_dashboard/core/checkpoint/__init__.py +87 -0
- truthound_dashboard/core/checkpoint/adapters.py +814 -0
- truthound_dashboard/core/checkpoint/checkpoint.py +491 -0
- truthound_dashboard/core/checkpoint/runner.py +270 -0
- truthound_dashboard/core/connections.py +437 -10
- truthound_dashboard/core/converters/__init__.py +14 -0
- truthound_dashboard/core/converters/truthound.py +620 -0
- truthound_dashboard/core/cross_alerts.py +540 -320
- truthound_dashboard/core/datasource_factory.py +1672 -0
- truthound_dashboard/core/drift_monitor.py +216 -20
- truthound_dashboard/core/enterprise_sampling.py +1291 -0
- truthound_dashboard/core/interfaces/__init__.py +225 -0
- truthound_dashboard/core/interfaces/actions.py +652 -0
- truthound_dashboard/core/interfaces/base.py +247 -0
- truthound_dashboard/core/interfaces/checkpoint.py +676 -0
- truthound_dashboard/core/interfaces/protocols.py +664 -0
- truthound_dashboard/core/interfaces/reporters.py +650 -0
- truthound_dashboard/core/interfaces/routing.py +646 -0
- truthound_dashboard/core/interfaces/triggers.py +619 -0
- truthound_dashboard/core/lineage.py +407 -71
- truthound_dashboard/core/model_monitoring.py +431 -3
- truthound_dashboard/core/notifications/base.py +4 -0
- truthound_dashboard/core/notifications/channels.py +501 -1203
- truthound_dashboard/core/notifications/deduplication/__init__.py +81 -115
- truthound_dashboard/core/notifications/deduplication/service.py +131 -348
- truthound_dashboard/core/notifications/dispatcher.py +202 -11
- truthound_dashboard/core/notifications/escalation/__init__.py +119 -106
- truthound_dashboard/core/notifications/escalation/engine.py +168 -358
- truthound_dashboard/core/notifications/routing/__init__.py +88 -128
- truthound_dashboard/core/notifications/routing/engine.py +90 -317
- truthound_dashboard/core/notifications/stats_aggregator.py +246 -1
- truthound_dashboard/core/notifications/throttling/__init__.py +67 -50
- truthound_dashboard/core/notifications/throttling/builder.py +117 -255
- truthound_dashboard/core/notifications/truthound_adapter.py +842 -0
- truthound_dashboard/core/phase5/collaboration.py +1 -1
- truthound_dashboard/core/plugins/lifecycle/__init__.py +0 -13
- truthound_dashboard/core/quality_reporter.py +1359 -0
- truthound_dashboard/core/report_history.py +0 -6
- truthound_dashboard/core/reporters/__init__.py +175 -14
- truthound_dashboard/core/reporters/adapters.py +943 -0
- truthound_dashboard/core/reporters/base.py +0 -3
- truthound_dashboard/core/reporters/builtin/__init__.py +18 -0
- truthound_dashboard/core/reporters/builtin/csv_reporter.py +111 -0
- truthound_dashboard/core/reporters/builtin/html_reporter.py +270 -0
- truthound_dashboard/core/reporters/builtin/json_reporter.py +127 -0
- truthound_dashboard/core/reporters/compat.py +266 -0
- truthound_dashboard/core/reporters/csv_reporter.py +2 -35
- truthound_dashboard/core/reporters/factory.py +526 -0
- truthound_dashboard/core/reporters/interfaces.py +745 -0
- truthound_dashboard/core/reporters/registry.py +1 -10
- truthound_dashboard/core/scheduler.py +165 -0
- truthound_dashboard/core/schema_evolution.py +3 -3
- truthound_dashboard/core/schema_watcher.py +1528 -0
- truthound_dashboard/core/services.py +595 -76
- truthound_dashboard/core/store_manager.py +810 -0
- truthound_dashboard/core/streaming_anomaly.py +169 -4
- truthound_dashboard/core/tiering.py +1309 -0
- truthound_dashboard/core/triggers/evaluators.py +178 -8
- truthound_dashboard/core/truthound_adapter.py +2620 -197
- truthound_dashboard/core/unified_alerts.py +23 -20
- truthound_dashboard/db/__init__.py +8 -0
- truthound_dashboard/db/database.py +8 -2
- truthound_dashboard/db/models.py +944 -25
- truthound_dashboard/db/repository.py +2 -0
- truthound_dashboard/main.py +11 -0
- truthound_dashboard/schemas/__init__.py +177 -16
- truthound_dashboard/schemas/base.py +44 -23
- truthound_dashboard/schemas/collaboration.py +19 -6
- truthound_dashboard/schemas/cross_alerts.py +19 -3
- truthound_dashboard/schemas/drift.py +61 -55
- truthound_dashboard/schemas/drift_monitor.py +67 -23
- truthound_dashboard/schemas/enterprise_sampling.py +653 -0
- truthound_dashboard/schemas/lineage.py +0 -33
- truthound_dashboard/schemas/mask.py +10 -8
- truthound_dashboard/schemas/model_monitoring.py +89 -10
- truthound_dashboard/schemas/notifications_advanced.py +13 -0
- truthound_dashboard/schemas/observability.py +453 -0
- truthound_dashboard/schemas/plugins.py +0 -280
- truthound_dashboard/schemas/profile.py +154 -247
- truthound_dashboard/schemas/quality_reporter.py +403 -0
- truthound_dashboard/schemas/reports.py +2 -2
- truthound_dashboard/schemas/rule_suggestion.py +8 -1
- truthound_dashboard/schemas/scan.py +4 -24
- truthound_dashboard/schemas/schedule.py +11 -3
- truthound_dashboard/schemas/schema_watcher.py +727 -0
- truthound_dashboard/schemas/source.py +17 -2
- truthound_dashboard/schemas/tiering.py +822 -0
- truthound_dashboard/schemas/triggers.py +16 -0
- truthound_dashboard/schemas/unified_alerts.py +7 -0
- truthound_dashboard/schemas/validation.py +0 -13
- truthound_dashboard/schemas/validators/base.py +41 -21
- truthound_dashboard/schemas/validators/business_rule_validators.py +244 -0
- truthound_dashboard/schemas/validators/localization_validators.py +273 -0
- truthound_dashboard/schemas/validators/ml_feature_validators.py +308 -0
- truthound_dashboard/schemas/validators/profiling_validators.py +275 -0
- truthound_dashboard/schemas/validators/referential_validators.py +312 -0
- truthound_dashboard/schemas/validators/registry.py +93 -8
- truthound_dashboard/schemas/validators/timeseries_validators.py +389 -0
- truthound_dashboard/schemas/versioning.py +1 -6
- truthound_dashboard/static/index.html +2 -2
- truthound_dashboard-1.5.0.dist-info/METADATA +309 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/RECORD +149 -148
- truthound_dashboard/core/plugins/hooks/__init__.py +0 -63
- truthound_dashboard/core/plugins/hooks/decorators.py +0 -367
- truthound_dashboard/core/plugins/hooks/manager.py +0 -403
- truthound_dashboard/core/plugins/hooks/protocols.py +0 -265
- truthound_dashboard/core/plugins/lifecycle/hot_reload.py +0 -584
- truthound_dashboard/core/reporters/junit_reporter.py +0 -233
- truthound_dashboard/core/reporters/markdown_reporter.py +0 -207
- truthound_dashboard/core/reporters/pdf_reporter.py +0 -209
- truthound_dashboard/static/assets/_baseUniq-BcrSP13d.js +0 -1
- truthound_dashboard/static/assets/arc-DlYjKwIL.js +0 -1
- truthound_dashboard/static/assets/architectureDiagram-VXUJARFQ-Bb2drbQM.js +0 -36
- truthound_dashboard/static/assets/blockDiagram-VD42YOAC-BlsPG1CH.js +0 -122
- truthound_dashboard/static/assets/c4Diagram-YG6GDRKO-B9JdUoaC.js +0 -10
- truthound_dashboard/static/assets/channel-Q6mHF1Hd.js +0 -1
- truthound_dashboard/static/assets/chunk-4BX2VUAB-DmyoPVuJ.js +0 -1
- truthound_dashboard/static/assets/chunk-55IACEB6-Bcz6Siv8.js +0 -1
- truthound_dashboard/static/assets/chunk-B4BG7PRW-Br3G5Rum.js +0 -165
- truthound_dashboard/static/assets/chunk-DI55MBZ5-DuM9c23u.js +0 -220
- truthound_dashboard/static/assets/chunk-FMBD7UC4-DNU-5mvT.js +0 -15
- truthound_dashboard/static/assets/chunk-QN33PNHL-Im2yNcmS.js +0 -1
- truthound_dashboard/static/assets/chunk-QZHKN3VN-kZr8XFm1.js +0 -1
- truthound_dashboard/static/assets/chunk-TZMSLE5B-Q__360q_.js +0 -1
- truthound_dashboard/static/assets/classDiagram-2ON5EDUG-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/classDiagram-v2-WZHVMYZB-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/clone-BOt2LwD0.js +0 -1
- truthound_dashboard/static/assets/cose-bilkent-S5V4N54A-CBDw6iac.js +0 -1
- truthound_dashboard/static/assets/dagre-6UL2VRFP-XdKqmmY9.js +0 -4
- truthound_dashboard/static/assets/diagram-PSM6KHXK-DAZ8nx9V.js +0 -24
- truthound_dashboard/static/assets/diagram-QEK2KX5R-BRvDTbGD.js +0 -43
- truthound_dashboard/static/assets/diagram-S2PKOQOG-bQcczUkl.js +0 -24
- truthound_dashboard/static/assets/erDiagram-Q2GNP2WA-DPje7VMN.js +0 -60
- truthound_dashboard/static/assets/flowDiagram-NV44I4VS-B7BVtFVS.js +0 -162
- truthound_dashboard/static/assets/ganttDiagram-JELNMOA3-D6WKSS7U.js +0 -267
- truthound_dashboard/static/assets/gitGraphDiagram-NY62KEGX-D3vtVd3y.js +0 -65
- truthound_dashboard/static/assets/graph-BKgNKZVp.js +0 -1
- truthound_dashboard/static/assets/index-C6JSrkHo.css +0 -1
- truthound_dashboard/static/assets/index-DkU82VsU.js +0 -1800
- truthound_dashboard/static/assets/infoDiagram-WHAUD3N6-DnNCT429.js +0 -2
- truthound_dashboard/static/assets/journeyDiagram-XKPGCS4Q-DGiMozqS.js +0 -139
- truthound_dashboard/static/assets/kanban-definition-3W4ZIXB7-BV2gUgli.js +0 -89
- truthound_dashboard/static/assets/katex-Cu_Erd72.js +0 -261
- truthound_dashboard/static/assets/layout-DI2MfQ5G.js +0 -1
- truthound_dashboard/static/assets/min-DYdgXVcT.js +0 -1
- truthound_dashboard/static/assets/mindmap-definition-VGOIOE7T-C7x4ruxz.js +0 -68
- truthound_dashboard/static/assets/pieDiagram-ADFJNKIX-CAJaAB9f.js +0 -30
- truthound_dashboard/static/assets/quadrantDiagram-AYHSOK5B-DeqwDI46.js +0 -7
- truthound_dashboard/static/assets/requirementDiagram-UZGBJVZJ-e3XDpZIM.js +0 -64
- truthound_dashboard/static/assets/sankeyDiagram-TZEHDZUN-CNnAv5Ux.js +0 -10
- truthound_dashboard/static/assets/sequenceDiagram-WL72ISMW-Dsne-Of3.js +0 -145
- truthound_dashboard/static/assets/stateDiagram-FKZM4ZOC-Ee0sQXyb.js +0 -1
- truthound_dashboard/static/assets/stateDiagram-v2-4FDKWEC3-B26KqW_W.js +0 -1
- truthound_dashboard/static/assets/timeline-definition-IT6M3QCI-DZYi2yl3.js +0 -61
- truthound_dashboard/static/assets/treemap-KMMF4GRG-CY3f8In2.js +0 -128
- truthound_dashboard/static/assets/unmerged_dictionaries-Dd7xcPWG.js +0 -1
- truthound_dashboard/static/assets/xychartDiagram-PRI3JC2R-CS7fydZZ.js +0 -7
- truthound_dashboard-1.4.4.dist-info/METADATA +0 -507
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,311 +1,173 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
This module provides
|
|
4
|
-
|
|
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 .
|
|
24
|
-
|
|
25
|
-
BaseThrottler,
|
|
26
|
-
CompositeThrottler,
|
|
27
|
-
FixedWindowThrottler,
|
|
11
|
+
from truthound.checkpoint.throttling import (
|
|
12
|
+
ThrottlerBuilder,
|
|
28
13
|
NotificationThrottler,
|
|
29
|
-
|
|
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
|
|
37
|
-
"""
|
|
23
|
+
class DashboardThrottlerBuilder:
|
|
24
|
+
"""Dashboard-specific throttler builder.
|
|
38
25
|
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
Wraps truthound's ThrottlerBuilder and provides integration
|
|
27
|
+
with the Dashboard's database configuration.
|
|
41
28
|
|
|
42
29
|
Example:
|
|
43
|
-
|
|
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
|
-
|
|
32
|
+
builder
|
|
53
33
|
.with_per_minute_limit(10)
|
|
54
34
|
.with_per_hour_limit(100)
|
|
55
|
-
.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
122
|
-
|
|
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
|
|
128
|
-
"""Set
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
134
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
103
|
+
def with_severity_limit(
|
|
143
104
|
self,
|
|
144
|
-
|
|
145
|
-
|
|
105
|
+
severity: str,
|
|
106
|
+
*,
|
|
107
|
+
per_minute: int | None = None,
|
|
108
|
+
per_hour: int | None = None,
|
|
146
109
|
) -> "Self":
|
|
147
|
-
"""
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
161
|
-
|
|
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
|
-
|
|
164
|
-
|
|
128
|
+
Args:
|
|
129
|
+
db_config: Configuration dictionary from database.
|
|
165
130
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return self
|
|
131
|
+
Returns:
|
|
132
|
+
NotificationThrottler instance.
|
|
133
|
+
"""
|
|
134
|
+
builder = DashboardThrottlerBuilder()
|
|
171
135
|
|
|
172
|
-
|
|
173
|
-
""
|
|
136
|
+
if db_config.get("per_minute_limit"):
|
|
137
|
+
builder.with_per_minute_limit(db_config["per_minute_limit"])
|
|
174
138
|
|
|
175
|
-
|
|
176
|
-
|
|
139
|
+
if db_config.get("per_hour_limit"):
|
|
140
|
+
builder.with_per_hour_limit(db_config["per_hour_limit"])
|
|
177
141
|
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
185
|
-
""
|
|
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
|
-
|
|
148
|
+
if db_config.get("algorithm"):
|
|
149
|
+
builder.with_algorithm(db_config["algorithm"])
|
|
290
150
|
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
298
|
-
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
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.
|
|
173
|
+
return builder.build()
|