detectkit 0.62.1__tar.gz → 0.63.0__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.62.1/detectkit.egg-info → detectkit-0.63.0}/PKG-INFO +1 -1
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/__init__.py +1 -1
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/rules/metrics.md +8 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/loaders/metric_loader.py +83 -0
- {detectkit-0.62.1 → detectkit-0.63.0/detectkit.egg-info}/PKG-INFO +1 -1
- {detectkit-0.62.1 → detectkit-0.63.0}/LICENSE +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/MANIFEST.in +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/README.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/base.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/branding.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/discord.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/email.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/factory.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/googlechat.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/mattermost.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/ntfy.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/slack.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/teams.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/telegram.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/channels/webhook.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/orchestrator/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/orchestrator/_base.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/orchestrator/_decision.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/orchestrator/_recovery.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/orchestrator/_replay.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/orchestrator/_types.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/_base.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/_types.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/autotuner.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/axis_spec.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/config_emitter.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/crossval.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/detector_select.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/distribution.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/grid_search.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/labels.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/result.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/runner.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/scoring.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/seasonality_search.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/settings.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/autotune/window_select.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/_output.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/CLAUDE.section.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/rules/alerting.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/rules/autotune.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/rules/cli.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/rules/detectors.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/rules/overview.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/rules/project.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/skills/dtk-tune/SKILL.md +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/autotune.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/clean.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/init.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/init_claude.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/mcp.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/osi.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/run.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/test_alert.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/tune.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/ui.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/commands/unlock.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/main.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/config/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/config/metric_config.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/config/metric_io.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/config/profile.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/config/project_config.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/config/validator.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/core/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/core/interval.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/core/models.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/_sql_manager.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/clickhouse_manager.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/duckdb_manager.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/_alert_states.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/_autotune_runs.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/_base.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/_datapoints.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/_detections.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/_maintenance.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/_metrics.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/_schema.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/_tasks.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/internal_tables/manager.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/manager.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/mysql_manager.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/postgres_manager.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/database/tables.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/base.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/factory.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/seasonality.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/statistical/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/statistical/_windowed.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/statistical/autoreg.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/statistical/iqr.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/statistical/mad.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/statistical/manual_bounds.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/detectors/statistical/zscore.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/loaders/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/loaders/errors.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/loaders/query_template.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/mcp/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/mcp/context.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/mcp/errors.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/mcp/serialize.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/mcp/server.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/mcp/tools.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/orchestration/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/orchestration/error_dispatch.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/orchestration/task_manager/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/orchestration/task_manager/_base.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/orchestration/task_manager/_load_step.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/orchestration/task_manager/_types.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/orchestration/task_manager/manager.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/reporting/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/reporting/assets/report.js +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/reporting/builder.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/reporting/html_report.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/semantic/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/semantic/errors.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/semantic/exporter.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/semantic/importer.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/semantic/osi_model.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/semantic/query_gen.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/tuning/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/tuning/assets/tune.js +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/tuning/config_writer.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/tuning/html.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/tuning/payload.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/tuning/server.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/ui/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/ui/assets/ui.js +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/ui/html.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/ui/jobs.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/ui/metric_files.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/ui/overview.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/ui/server.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/utils/__init__.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/utils/datetime_utils.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/utils/env_interpolation.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/utils/json_utils.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit/utils/stats.py +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit.egg-info/SOURCES.txt +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit.egg-info/dependency_links.txt +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit.egg-info/entry_points.txt +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit.egg-info/requires.txt +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/detectkit.egg-info/top_level.txt +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/pyproject.toml +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/requirements.txt +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/setup.cfg +0 -0
- {detectkit-0.62.1 → detectkit-0.63.0}/setup.py +0 -0
|
@@ -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.
|
|
7
|
+
__version__ = "0.63.0"
|
|
8
8
|
|
|
9
9
|
from detectkit.core.interval import Interval
|
|
10
10
|
from detectkit.core.models import ColumnDefinition, TableModel
|
|
@@ -119,6 +119,14 @@ The query must return a **timestamp** column and a numeric **value** column
|
|
|
119
119
|
(default names `timestamp` / `value`; remap via `query_columns`). It may also
|
|
120
120
|
return seasonality columns (declare them in `query_columns.seasonality`).
|
|
121
121
|
|
|
122
|
+
Returned timestamps must already be **bucketed to the interval grid**
|
|
123
|
+
(`toStartOfInterval` / `time_bucket` / `date_trunc`) — gap filling maps rows
|
|
124
|
+
onto the grid by exact match, and the grid's phase comes from
|
|
125
|
+
`loading_start_time`. A batch that returns rows on a different phase (e.g.
|
|
126
|
+
`:28` past the hour vs a grid phased at `:00`) can land 100% off-grid and
|
|
127
|
+
load as silently all-`NULL`; detectkit logs a one-time warning naming both
|
|
128
|
+
phases when that happens.
|
|
129
|
+
|
|
122
130
|
## Seasonality features
|
|
123
131
|
|
|
124
132
|
Two ways to provide seasonality keys that detectors group by:
|
|
@@ -9,6 +9,7 @@ Loads time-series data from databases with:
|
|
|
9
9
|
- Integration with InternalTablesManager
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
+
import logging
|
|
12
13
|
from datetime import datetime, timedelta
|
|
13
14
|
|
|
14
15
|
import numpy as np
|
|
@@ -21,6 +22,30 @@ from detectkit.loaders.query_template import QueryTemplate
|
|
|
21
22
|
from detectkit.utils.datetime_utils import now_utc_naive, to_naive_utc
|
|
22
23
|
from detectkit.utils.json_utils import json_dumps_sorted
|
|
23
24
|
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _format_offset_seconds(seconds: int) -> str:
|
|
29
|
+
"""Render an offset-within-interval (seconds) as a human clock offset.
|
|
30
|
+
|
|
31
|
+
Used to describe a grid/source "phase" without assuming a particular
|
|
32
|
+
interval unit: ``0`` -> ``":00"``, ``1680`` -> ``":28"`` (28 minutes),
|
|
33
|
+
``90`` -> ``":01:30"``, ``3661`` -> ``"1:01:01"``.
|
|
34
|
+
"""
|
|
35
|
+
hours, remainder = divmod(int(seconds), 3600)
|
|
36
|
+
minutes, secs = divmod(remainder, 60)
|
|
37
|
+
if hours:
|
|
38
|
+
return f"{hours}:{minutes:02d}:{secs:02d}"
|
|
39
|
+
if secs:
|
|
40
|
+
return f":{minutes:02d}:{secs:02d}"
|
|
41
|
+
return f":{minutes:02d}"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _format_datetime64(ts: np.datetime64) -> str:
|
|
45
|
+
"""Render a datetime64 as ``YYYY-MM-DD HH:MM:SS`` (naive UTC convention)."""
|
|
46
|
+
dt: datetime = ts.astype("datetime64[s]").astype(datetime)
|
|
47
|
+
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
48
|
+
|
|
24
49
|
|
|
25
50
|
class MetricLoader:
|
|
26
51
|
"""
|
|
@@ -76,6 +101,9 @@ class MetricLoader:
|
|
|
76
101
|
self.internal_manager = internal_manager
|
|
77
102
|
self.source_profile_name = source_profile_name
|
|
78
103
|
self.query_template = QueryTemplate()
|
|
104
|
+
# Emitted at most once per loader instance (one instance per metric
|
|
105
|
+
# per run) - see _check_grid_alignment.
|
|
106
|
+
self._warned_grid_misalignment = False
|
|
79
107
|
|
|
80
108
|
def load(
|
|
81
109
|
self,
|
|
@@ -246,6 +274,7 @@ class MetricLoader:
|
|
|
246
274
|
timestamp_array, value_array = self._fill_gaps(
|
|
247
275
|
timestamp_array, value_array, from_date, to_date, interval_seconds
|
|
248
276
|
)
|
|
277
|
+
self._check_grid_alignment(original_timestamps, timestamp_array, interval_seconds)
|
|
249
278
|
|
|
250
279
|
# Realign query-provided seasonality to the gap-filled grid.
|
|
251
280
|
# Gap rows can appear ANYWHERE in the range and _fill_gaps also
|
|
@@ -423,6 +452,60 @@ class MetricLoader:
|
|
|
423
452
|
|
|
424
453
|
return full_timestamps, filled_values
|
|
425
454
|
|
|
455
|
+
def _check_grid_alignment(
|
|
456
|
+
self,
|
|
457
|
+
original_timestamps: np.ndarray,
|
|
458
|
+
grid_timestamps: np.ndarray,
|
|
459
|
+
interval_seconds: int,
|
|
460
|
+
) -> None:
|
|
461
|
+
"""Warn once if a non-empty query result landed entirely off the grid.
|
|
462
|
+
|
|
463
|
+
``_fill_gaps`` maps source rows onto the gap-filled grid by exact
|
|
464
|
+
timestamp match. If the query returns rows bucketed to a different
|
|
465
|
+
grid "phase" than ``loading_start_time`` establishes (e.g. rows at
|
|
466
|
+
:28 past the hour against a 10min grid phased at :00), NONE of them
|
|
467
|
+
match and the batch loads as silently 100% NULL - no error, no
|
|
468
|
+
partial data, just an empty-looking series. Partial alignment is not
|
|
469
|
+
the trap this guards against (some rows landing is normal, e.g. a
|
|
470
|
+
source with occasional off-grid duplicates), so this only fires when
|
|
471
|
+
the overlap is exactly zero. Observability only - does not change
|
|
472
|
+
what gets loaded.
|
|
473
|
+
"""
|
|
474
|
+
if self._warned_grid_misalignment:
|
|
475
|
+
return
|
|
476
|
+
if len(original_timestamps) == 0 or len(grid_timestamps) == 0:
|
|
477
|
+
return
|
|
478
|
+
if np.any(np.isin(original_timestamps, grid_timestamps)):
|
|
479
|
+
return
|
|
480
|
+
|
|
481
|
+
self._warned_grid_misalignment = True
|
|
482
|
+
|
|
483
|
+
grid_phase_seconds = int(
|
|
484
|
+
grid_timestamps[0].astype("datetime64[s]").astype(np.int64) % interval_seconds
|
|
485
|
+
)
|
|
486
|
+
source_offsets = (
|
|
487
|
+
original_timestamps.astype("datetime64[s]").astype(np.int64) % interval_seconds
|
|
488
|
+
)
|
|
489
|
+
offsets, counts = np.unique(source_offsets, return_counts=True)
|
|
490
|
+
source_phase_seconds = int(offsets[int(np.argmax(counts))])
|
|
491
|
+
|
|
492
|
+
sample_ts = original_timestamps[0]
|
|
493
|
+
nearest_ts = grid_timestamps[int(np.argmin(np.abs(grid_timestamps - sample_ts)))]
|
|
494
|
+
|
|
495
|
+
logger.warning(
|
|
496
|
+
"%s: query returned %d row(s) but none align with the metric's %s grid "
|
|
497
|
+
"(grid phase %s, source phase %s). e.g. source row %s vs nearest grid "
|
|
498
|
+
"slot %s. Bucket the query's timestamps (toStartOfInterval / time_bucket "
|
|
499
|
+
"/ date_trunc) or set loading_start_time to match the source phase.",
|
|
500
|
+
self.config.name,
|
|
501
|
+
len(original_timestamps),
|
|
502
|
+
self.config.interval,
|
|
503
|
+
_format_offset_seconds(grid_phase_seconds),
|
|
504
|
+
_format_offset_seconds(source_phase_seconds),
|
|
505
|
+
_format_datetime64(sample_ts),
|
|
506
|
+
_format_datetime64(nearest_ts),
|
|
507
|
+
)
|
|
508
|
+
|
|
426
509
|
def _extract_seasonality(
|
|
427
510
|
self,
|
|
428
511
|
timestamps: np.ndarray,
|
|
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
|
{detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md
RENAMED
|
File without changes
|
{detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md
RENAMED
|
File without changes
|
{detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md
RENAMED
|
File without changes
|
{detectkit-0.62.1 → detectkit-0.63.0}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md
RENAMED
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|