detectkit 0.24.1__tar.gz → 0.24.2__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.
Files changed (126) hide show
  1. {detectkit-0.24.1/detectkit.egg-info → detectkit-0.24.2}/PKG-INFO +1 -1
  2. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/__init__.py +1 -1
  3. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/config_emitter.py +11 -3
  4. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/rules/autotune.md +2 -1
  5. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/rules/detectors.md +1 -1
  6. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/_datapoints.py +15 -0
  7. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/orchestration/task_manager/_detect_step.py +24 -0
  8. {detectkit-0.24.1 → detectkit-0.24.2/detectkit.egg-info}/PKG-INFO +1 -1
  9. {detectkit-0.24.1 → detectkit-0.24.2}/LICENSE +0 -0
  10. {detectkit-0.24.1 → detectkit-0.24.2}/MANIFEST.in +0 -0
  11. {detectkit-0.24.1 → detectkit-0.24.2}/README.md +0 -0
  12. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/__init__.py +0 -0
  13. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/channels/__init__.py +0 -0
  14. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/channels/base.py +0 -0
  15. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/channels/branding.py +0 -0
  16. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/channels/email.py +0 -0
  17. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/channels/factory.py +0 -0
  18. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/channels/mattermost.py +0 -0
  19. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/channels/slack.py +0 -0
  20. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/channels/telegram.py +0 -0
  21. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/channels/webhook.py +0 -0
  22. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/orchestrator/__init__.py +0 -0
  23. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/orchestrator/_base.py +0 -0
  24. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
  25. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/orchestrator/_decision.py +0 -0
  26. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
  27. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/orchestrator/_recovery.py +0 -0
  28. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/orchestrator/_types.py +0 -0
  29. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
  30. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/__init__.py +0 -0
  31. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/_base.py +0 -0
  32. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/_types.py +0 -0
  33. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/autotuner.py +0 -0
  34. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/crossval.py +0 -0
  35. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/detector_select.py +0 -0
  36. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/distribution.py +0 -0
  37. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/grid_search.py +0 -0
  38. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/html_labeler.py +0 -0
  39. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/label_server.py +0 -0
  40. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/labels.py +0 -0
  41. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/result.py +0 -0
  42. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/scoring.py +0 -0
  43. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/seasonality_search.py +0 -0
  44. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/settings.py +0 -0
  45. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/autotune/window_select.py +0 -0
  46. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/__init__.py +0 -0
  47. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/_output.py +0 -0
  48. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/CLAUDE.section.md +0 -0
  49. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/rules/alerting.md +0 -0
  50. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/rules/cli.md +0 -0
  51. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
  52. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/rules/overview.md +0 -0
  53. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/rules/project.md +0 -0
  54. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md +0 -0
  55. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
  56. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
  57. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
  58. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/commands/__init__.py +0 -0
  59. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/commands/autotune.py +0 -0
  60. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/commands/clean.py +0 -0
  61. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/commands/init.py +0 -0
  62. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/commands/init_claude.py +0 -0
  63. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/commands/run.py +0 -0
  64. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/commands/test_alert.py +0 -0
  65. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/commands/unlock.py +0 -0
  66. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/cli/main.py +0 -0
  67. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/config/__init__.py +0 -0
  68. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/config/metric_config.py +0 -0
  69. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/config/profile.py +0 -0
  70. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/config/project_config.py +0 -0
  71. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/config/validator.py +0 -0
  72. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/core/__init__.py +0 -0
  73. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/core/interval.py +0 -0
  74. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/core/models.py +0 -0
  75. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/__init__.py +0 -0
  76. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/_sql_manager.py +0 -0
  77. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/clickhouse_manager.py +0 -0
  78. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/__init__.py +0 -0
  79. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/_alert_states.py +0 -0
  80. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/_autotune_runs.py +0 -0
  81. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/_base.py +0 -0
  82. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/_detections.py +0 -0
  83. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/_maintenance.py +0 -0
  84. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/_metrics.py +0 -0
  85. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/_schema.py +0 -0
  86. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/_tasks.py +0 -0
  87. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/internal_tables/manager.py +0 -0
  88. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/manager.py +0 -0
  89. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/mysql_manager.py +0 -0
  90. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/postgres_manager.py +0 -0
  91. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/database/tables.py +0 -0
  92. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/detectors/__init__.py +0 -0
  93. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/detectors/base.py +0 -0
  94. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/detectors/factory.py +0 -0
  95. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/detectors/seasonality.py +0 -0
  96. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/detectors/statistical/__init__.py +0 -0
  97. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/detectors/statistical/_windowed.py +0 -0
  98. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/detectors/statistical/iqr.py +0 -0
  99. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/detectors/statistical/mad.py +0 -0
  100. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/detectors/statistical/manual_bounds.py +0 -0
  101. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/detectors/statistical/zscore.py +0 -0
  102. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/loaders/__init__.py +0 -0
  103. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/loaders/metric_loader.py +0 -0
  104. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/loaders/query_template.py +0 -0
  105. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/orchestration/__init__.py +0 -0
  106. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/orchestration/error_dispatch.py +0 -0
  107. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/orchestration/task_manager/__init__.py +0 -0
  108. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
  109. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/orchestration/task_manager/_base.py +0 -0
  110. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/orchestration/task_manager/_load_step.py +0 -0
  111. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/orchestration/task_manager/_types.py +0 -0
  112. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/orchestration/task_manager/manager.py +0 -0
  113. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/utils/__init__.py +0 -0
  114. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/utils/datetime_utils.py +0 -0
  115. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/utils/env_interpolation.py +0 -0
  116. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/utils/json_utils.py +0 -0
  117. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit/utils/stats.py +0 -0
  118. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit.egg-info/SOURCES.txt +0 -0
  119. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit.egg-info/dependency_links.txt +0 -0
  120. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit.egg-info/entry_points.txt +0 -0
  121. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit.egg-info/requires.txt +0 -0
  122. {detectkit-0.24.1 → detectkit-0.24.2}/detectkit.egg-info/top_level.txt +0 -0
  123. {detectkit-0.24.1 → detectkit-0.24.2}/pyproject.toml +0 -0
  124. {detectkit-0.24.1 → detectkit-0.24.2}/requirements.txt +0 -0
  125. {detectkit-0.24.1 → detectkit-0.24.2}/setup.cfg +0 -0
  126. {detectkit-0.24.1 → detectkit-0.24.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detectkit
3
- Version: 0.24.1
3
+ Version: 0.24.2
4
4
  Summary: Metric monitoring with automatic anomaly detection
5
5
  Author: detectkit team
6
6
  License: MIT
@@ -4,7 +4,7 @@ detectk - Anomaly Detection for Time-Series Metrics
4
4
  A Python library for data analysts and engineers to monitor metrics with automatic anomaly detection.
5
5
  """
6
6
 
7
- __version__ = "0.24.1"
7
+ __version__ = "0.24.2"
8
8
 
9
9
  from detectkit.core.interval import Interval
10
10
  from detectkit.core.models import ColumnDefinition, TableModel
@@ -103,9 +103,17 @@ def _build_body(original: MetricConfig, result: AutoTuneResult, new_name: str) -
103
103
  elif original.seasonality_columns:
104
104
  body["seasonality_columns"] = original.seasonality_columns
105
105
 
106
- body["detectors"] = [
107
- {"type": result.chosen_detector_type, "params": result.chosen_detector_params}
108
- ]
106
+ # Pin the detector's detection start to the load start so the first
107
+ # `dtk run` on the generated config detects across all loaded history. A
108
+ # fresh detector that omits `start_time` has no lower bound (no prior
109
+ # detections, no --from); DETECT falls back to loading_start_time, but
110
+ # emitting it keeps the generated config explicit and self-sufficient on
111
+ # older detectkit. `start_time` is execution-level and excluded from the
112
+ # detector_id hash, so it never changes detector identity.
113
+ detector_params = dict(result.chosen_detector_params)
114
+ if "start_time" not in detector_params and body.get("loading_start_time"):
115
+ detector_params["start_time"] = body["loading_start_time"]
116
+ body["detectors"] = [{"type": result.chosen_detector_type, "params": detector_params}]
109
117
  alerting = _build_alerting(original, result)
110
118
  if alerting is not None:
111
119
  body["alerting"] = alerting
@@ -32,7 +32,8 @@ same windowed detectors and `detector_id` identity). The fastest path is the
32
32
  4. **History window** — on near-ties uses a trend-gated tie-break: a stationary
33
33
  series prefers the **larger** `window_size` ("more history is better"), a
34
34
  trending / regime-shifting one the **smaller**; sets `loading_start_time` to
35
- cover the lead-in.
35
+ cover the lead-in (and pins the detector's `start_time` to it, so the first
36
+ `dtk run` detects across all loaded history).
36
37
  5. **Alert window** (supervised only) — sweeps `consecutive_anomalies` on the
37
38
  labeled incidents.
38
39
 
@@ -67,7 +67,7 @@ expected interval for the current point.
67
67
  half_life: null # exponential half-life: int points or "3d"/"12h"
68
68
  detrend: null # null | linear
69
69
  # --- execution (NOT hashed) ---
70
- start_time: "2024-01-01 00:00:00" # when detection begins
70
+ start_time: "2024-01-01 00:00:00" # optional; when detection begins (default: loading_start_time)
71
71
  batch_size: 500
72
72
  ```
73
73
 
@@ -40,6 +40,21 @@ class _DatapointsMixin(_InternalTablesBase):
40
40
  last_ts = self._manager.get_last_timestamp(full_table_name, metric_name)
41
41
  return self._normalize_max_timestamp(last_ts)
42
42
 
43
+ def get_first_datapoint_timestamp(self, metric_name: str) -> datetime | None:
44
+ """Return the earliest timestamp stored for *metric_name*, if any."""
45
+ full_table_name = self._manager.get_full_table_name(TABLE_DATAPOINTS, use_internal=True)
46
+ query = f"""
47
+ SELECT min(timestamp) AS first_ts
48
+ FROM {full_table_name}
49
+ WHERE metric_name = %(metric_name)s
50
+ """
51
+ result = self._manager.execute_query(query, {"metric_name": metric_name})
52
+ if not result:
53
+ return None
54
+ # ClickHouse min() over an empty selection yields the epoch sentinel
55
+ # rather than NULL; _normalize_max_timestamp maps that back to None.
56
+ return self._normalize_max_timestamp(result[0].get("first_ts"))
57
+
43
58
  def get_value_at(self, metric_name: str, timestamp: datetime) -> float | None:
44
59
  """Return the stored ``value`` for an exact timestamp.
45
60
 
@@ -78,6 +78,16 @@ class _DetectStepMixin(_TaskManagerBase):
78
78
  actual_from = max(actual_from, start_time) if actual_from else start_time
79
79
 
80
80
  actual_from = to_naive_utc(actual_from)
81
+
82
+ # No lower bound from --from, the resume point, or the detector's
83
+ # `start_time`: a first-ever detect for a detector that omits
84
+ # `start_time` (every `dtk autotune` config does). Fall back to the
85
+ # metric's load start so the run detects across all loaded history,
86
+ # instead of mistaking "no lower bound" for "nothing to do" and
87
+ # silently reporting "already up to date" without ever detecting.
88
+ if actual_from is None:
89
+ actual_from = self._detect_fallback_start(config)
90
+
81
91
  if not actual_from or actual_from >= actual_to:
82
92
  click.echo(" │ Nothing to detect (already up to date)")
83
93
  continue
@@ -200,3 +210,17 @@ class _DetectStepMixin(_TaskManagerBase):
200
210
  )
201
211
  )
202
212
  return {"anomalies_count": anomalies_count}
213
+
214
+ def _detect_fallback_start(self, config: MetricConfig) -> datetime | None:
215
+ """Lower bound for a first-ever detect when nothing else pins it.
216
+
217
+ Mirrors the LOAD step: prefer the configured ``loading_start_time``,
218
+ else the metric's earliest stored datapoint. Returns ``None`` only when
219
+ the metric has no configured start and no data at all (genuinely nothing
220
+ to detect).
221
+ """
222
+ if config.loading_start_time:
223
+ return to_naive_utc(
224
+ datetime.fromisoformat(config.loading_start_time.replace("Z", "+00:00"))
225
+ )
226
+ return to_naive_utc(self.internal.get_first_datapoint_timestamp(config.name))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detectkit
3
- Version: 0.24.1
3
+ Version: 0.24.2
4
4
  Summary: Metric monitoring with automatic anomaly detection
5
5
  Author: detectkit team
6
6
  License: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes