detectkit 0.3.14__tar.gz → 0.3.16__tar.gz
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.
- {detectkit-0.3.14/detectkit.egg-info → detectkit-0.3.16}/PKG-INFO +2 -1
- {detectkit-0.3.14 → detectkit-0.3.16}/README.md +1 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/alerting/channels/base.py +6 -3
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/alerting/orchestrator.py +23 -5
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/config/metric_config.py +5 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/orchestration/task_manager.py +7 -0
- {detectkit-0.3.14 → detectkit-0.3.16/detectkit.egg-info}/PKG-INFO +2 -1
- {detectkit-0.3.14 → detectkit-0.3.16}/pyproject.toml +1 -1
- {detectkit-0.3.14 → detectkit-0.3.16}/LICENSE +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/MANIFEST.in +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/alerting/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/alerting/channels/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/alerting/channels/email.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/alerting/channels/factory.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/alerting/channels/mattermost.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/alerting/channels/slack.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/alerting/channels/telegram.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/alerting/channels/webhook.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/cli/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/cli/commands/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/cli/commands/init.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/cli/commands/run.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/cli/commands/test_alert.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/cli/main.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/config/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/config/profile.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/config/project_config.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/config/validator.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/core/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/core/interval.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/core/models.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/database/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/database/clickhouse_manager.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/database/internal_tables.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/database/manager.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/database/tables.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/detectors/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/detectors/base.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/detectors/factory.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/detectors/statistical/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/detectors/statistical/iqr.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/detectors/statistical/mad.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/detectors/statistical/manual_bounds.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/detectors/statistical/zscore.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/loaders/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/loaders/metric_loader.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/loaders/query_template.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/orchestration/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/utils/__init__.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/utils/datetime_utils.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit/utils/stats.py +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit.egg-info/SOURCES.txt +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit.egg-info/dependency_links.txt +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit.egg-info/entry_points.txt +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit.egg-info/requires.txt +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/detectkit.egg-info/top_level.txt +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/requirements.txt +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/setup.cfg +0 -0
- {detectkit-0.3.14 → detectkit-0.3.16}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: detectkit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.16
|
|
4
4
|
Summary: Metric monitoring with automatic anomaly detection
|
|
5
5
|
Author: detectkit team
|
|
6
6
|
License: MIT
|
|
@@ -138,6 +138,7 @@ alerting:
|
|
|
138
138
|
mentions: [oncall_engineer, here]
|
|
139
139
|
alert_cooldown: "30min"
|
|
140
140
|
notify_on_recovery: true
|
|
141
|
+
suppress_until: "2026-04-11 18:00:00" # Suppress alerts until this UTC time
|
|
141
142
|
```
|
|
142
143
|
|
|
143
144
|
### Python API
|
|
@@ -144,10 +144,13 @@ class BaseAlertChannel(ABC):
|
|
|
144
144
|
if isinstance(ts, np.datetime64):
|
|
145
145
|
ts = ts.astype(datetime)
|
|
146
146
|
|
|
147
|
-
#
|
|
148
|
-
ts_str = ts.strftime("%Y-%m-%d %H:%M:%S")
|
|
147
|
+
# Convert naive UTC timestamp to target timezone if specified
|
|
149
148
|
if alert_data.timezone:
|
|
150
|
-
|
|
149
|
+
from zoneinfo import ZoneInfo
|
|
150
|
+
ts = ts.replace(tzinfo=ZoneInfo("UTC")).astimezone(ZoneInfo(alert_data.timezone))
|
|
151
|
+
ts_str = f"{ts.strftime('%Y-%m-%d %H:%M:%S')} ({alert_data.timezone})"
|
|
152
|
+
else:
|
|
153
|
+
ts_str = ts.strftime("%Y-%m-%d %H:%M:%S")
|
|
151
154
|
|
|
152
155
|
# Format confidence interval
|
|
153
156
|
if alert_data.confidence_lower is not None and alert_data.confidence_upper is not None:
|
|
@@ -683,19 +683,37 @@ class AlertOrchestrator:
|
|
|
683
683
|
if not detections:
|
|
684
684
|
return None
|
|
685
685
|
|
|
686
|
-
# Use the latest (newest) detection point for recovery
|
|
686
|
+
# Use the latest (newest) detection point for recovery timestamp/value.
|
|
687
687
|
# detections are sorted oldest→newest by _load_recent_detections.
|
|
688
688
|
latest = detections[-1]
|
|
689
689
|
|
|
690
|
+
# Use detector info and CI from the last anomalous detection
|
|
691
|
+
# (the one that triggered the alert), since normal points have
|
|
692
|
+
# detector_name="unknown" and confidence=None.
|
|
693
|
+
last_anomalous = next(
|
|
694
|
+
(d for d in reversed(detections) if d.is_anomaly),
|
|
695
|
+
None,
|
|
696
|
+
)
|
|
697
|
+
if last_anomalous:
|
|
698
|
+
recovery_detector_name = last_anomalous.detector_name
|
|
699
|
+
recovery_detector_params = last_anomalous.detector_params
|
|
700
|
+
recovery_ci_lower = last_anomalous.confidence_lower
|
|
701
|
+
recovery_ci_upper = last_anomalous.confidence_upper
|
|
702
|
+
else:
|
|
703
|
+
recovery_detector_name = latest.detector_name
|
|
704
|
+
recovery_detector_params = latest.detector_params
|
|
705
|
+
recovery_ci_lower = latest.confidence_lower
|
|
706
|
+
recovery_ci_upper = latest.confidence_upper
|
|
707
|
+
|
|
690
708
|
return AlertData(
|
|
691
709
|
metric_name=self.metric_name,
|
|
692
710
|
timestamp=latest.timestamp,
|
|
693
711
|
timezone=self.timezone_display,
|
|
694
712
|
value=latest.value,
|
|
695
|
-
confidence_lower=
|
|
696
|
-
confidence_upper=
|
|
697
|
-
detector_name=
|
|
698
|
-
detector_params=
|
|
713
|
+
confidence_lower=recovery_ci_lower,
|
|
714
|
+
confidence_upper=recovery_ci_upper,
|
|
715
|
+
detector_name=recovery_detector_name,
|
|
716
|
+
detector_params=recovery_detector_params,
|
|
699
717
|
direction="none",
|
|
700
718
|
severity=0.0,
|
|
701
719
|
detection_metadata={},
|
|
@@ -146,6 +146,11 @@ class AlertConfig(BaseModel):
|
|
|
146
146
|
"""
|
|
147
147
|
|
|
148
148
|
enabled: bool = Field(default=True, description="Enable alerting")
|
|
149
|
+
suppress_until: Optional[str] = Field(
|
|
150
|
+
default=None,
|
|
151
|
+
description="Suppress alerts until this UTC datetime (e.g., '2026-04-11 18:00:00'). "
|
|
152
|
+
"Load and detect steps still run. Alerts auto-resume after this time."
|
|
153
|
+
)
|
|
149
154
|
timezone: Optional[str] = Field(
|
|
150
155
|
default=None, description="Timezone for displaying timestamps (e.g., 'Europe/Moscow')"
|
|
151
156
|
)
|
|
@@ -606,6 +606,13 @@ class TaskManager:
|
|
|
606
606
|
if multi:
|
|
607
607
|
click.echo(f" │ [config {i + 1}/{len(active_configs)}] channels: {alerting_config.channels}")
|
|
608
608
|
|
|
609
|
+
# Check suppress_until
|
|
610
|
+
if alerting_config.suppress_until:
|
|
611
|
+
suppress_dt = datetime.strptime(alerting_config.suppress_until, "%Y-%m-%d %H:%M:%S")
|
|
612
|
+
if now_utc_naive() < suppress_dt:
|
|
613
|
+
click.echo(f" │ Alerts suppressed until {alerting_config.suppress_until} UTC")
|
|
614
|
+
continue
|
|
615
|
+
|
|
609
616
|
click.echo(f" │ Checking alert conditions...")
|
|
610
617
|
|
|
611
618
|
alert_config_id = _make_alert_config_id(alerting_config)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: detectkit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.16
|
|
4
4
|
Summary: Metric monitoring with automatic anomaly detection
|
|
5
5
|
Author: detectkit team
|
|
6
6
|
License: MIT
|
|
@@ -138,6 +138,7 @@ alerting:
|
|
|
138
138
|
mentions: [oncall_engineer, here]
|
|
139
139
|
alert_cooldown: "30min"
|
|
140
140
|
notify_on_recovery: true
|
|
141
|
+
suppress_until: "2026-04-11 18:00:00" # Suppress alerts until this UTC time
|
|
141
142
|
```
|
|
142
143
|
|
|
143
144
|
### Python API
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|