truthound-dashboard 1.4.4__py3-none-any.whl → 1.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +645 -23
- 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 +15 -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.1.dist-info/METADATA +312 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.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.1.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.1.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
|
|
1
|
+
"""Notification throttling using truthound.checkpoint.throttling.
|
|
2
2
|
|
|
3
|
-
This module provides rate limiting for notifications
|
|
4
|
-
|
|
3
|
+
This module provides rate limiting for notifications using truthound's
|
|
4
|
+
throttling infrastructure.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
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
|
|
22
|
+
from truthound.checkpoint.throttling import (
|
|
14
23
|
ThrottlerBuilder,
|
|
15
|
-
|
|
24
|
+
RateLimitScope,
|
|
16
25
|
)
|
|
17
26
|
|
|
18
|
-
#
|
|
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.
|
|
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}
|
|
44
|
+
print(f"Retry after {result.retry_after:.1f}s")
|
|
33
45
|
"""
|
|
34
46
|
|
|
35
|
-
from .
|
|
36
|
-
from .
|
|
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
|
-
|
|
50
|
+
ThrottlingConfig,
|
|
51
|
+
ThrottlerBuilder,
|
|
52
|
+
ThrottlingMiddleware,
|
|
53
|
+
throttled,
|
|
54
|
+
RateLimit,
|
|
55
|
+
RateLimitScope,
|
|
56
|
+
TimeUnit,
|
|
57
|
+
ThrottleStatus,
|
|
54
58
|
ThrottleResult,
|
|
55
|
-
|
|
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
|
-
#
|
|
61
|
-
"
|
|
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
|
-
|
|
68
|
-
"ThrottleResult",
|
|
69
|
-
# Service
|
|
70
|
-
"NotificationThrottler",
|
|
71
|
-
# Builder
|
|
72
|
-
"ThrottlerBuilder",
|
|
73
|
-
# Stores
|
|
74
|
-
"BaseThrottlingStore",
|
|
96
|
+
# Storage
|
|
75
97
|
"InMemoryThrottlingStore",
|
|
76
|
-
|
|
77
|
-
"
|
|
78
|
-
"ThrottlingEntry",
|
|
79
|
-
"ThrottlingMetrics",
|
|
80
|
-
"ThrottlingStoreType",
|
|
81
|
-
"create_throttling_store",
|
|
82
|
-
"REDIS_AVAILABLE",
|
|
98
|
+
# Dashboard adapters
|
|
99
|
+
"DashboardThrottlerBuilder",
|
|
83
100
|
]
|