detectkit 0.41.0__tar.gz → 0.42.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.41.0/detectkit.egg-info → detectkit-0.42.0}/PKG-INFO +1 -1
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/__init__.py +1 -1
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/__init__.py +0 -2
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/CLAUDE.section.md +6 -4
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/rules/autotune.md +35 -47
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/rules/cli.md +5 -2
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md +32 -33
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +2 -1
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/commands/autotune.py +24 -141
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/commands/init.py +3 -2
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/main.py +6 -28
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/reporting/html_report.py +6 -5
- {detectkit-0.41.0 → detectkit-0.42.0/detectkit.egg-info}/PKG-INFO +1 -1
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit.egg-info/SOURCES.txt +0 -2
- detectkit-0.41.0/detectkit/autotune/html_labeler.py +0 -969
- detectkit-0.41.0/detectkit/autotune/label_server.py +0 -177
- {detectkit-0.41.0 → detectkit-0.42.0}/LICENSE +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/MANIFEST.in +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/README.md +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/channels/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/channels/base.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/channels/branding.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/channels/email.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/channels/factory.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/channels/mattermost.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/channels/slack.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/channels/telegram.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/channels/webhook.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/orchestrator/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/orchestrator/_base.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/orchestrator/_decision.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/orchestrator/_recovery.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/orchestrator/_replay.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/orchestrator/_types.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/_base.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/_types.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/autotuner.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/config_emitter.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/crossval.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/detector_select.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/distribution.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/grid_search.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/labels.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/result.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/scoring.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/seasonality_search.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/settings.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/autotune/window_select.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/_output.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/rules/alerting.md +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/rules/detectors.md +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/rules/overview.md +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/rules/project.md +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/commands/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/commands/clean.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/commands/init_claude.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/commands/run.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/commands/test_alert.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/commands/tune.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/commands/unlock.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/config/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/config/metric_config.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/config/profile.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/config/project_config.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/config/validator.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/core/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/core/interval.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/core/models.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/_sql_manager.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/clickhouse_manager.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/_alert_states.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/_autotune_runs.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/_base.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/_datapoints.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/_detections.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/_maintenance.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/_metrics.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/_schema.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/_tasks.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/internal_tables/manager.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/manager.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/mysql_manager.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/postgres_manager.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/database/tables.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/detectors/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/detectors/base.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/detectors/factory.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/detectors/seasonality.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/detectors/statistical/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/detectors/statistical/_windowed.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/detectors/statistical/iqr.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/detectors/statistical/mad.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/detectors/statistical/manual_bounds.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/detectors/statistical/zscore.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/loaders/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/loaders/metric_loader.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/loaders/query_template.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/orchestration/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/orchestration/error_dispatch.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/orchestration/task_manager/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/orchestration/task_manager/_base.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/orchestration/task_manager/_load_step.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/orchestration/task_manager/_types.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/orchestration/task_manager/manager.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/reporting/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/reporting/assets/report.js +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/reporting/builder.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/tuning/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/tuning/assets/tune.js +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/tuning/config_writer.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/tuning/html.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/tuning/payload.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/tuning/server.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/utils/__init__.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/utils/datetime_utils.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/utils/env_interpolation.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/utils/json_utils.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit/utils/stats.py +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit.egg-info/dependency_links.txt +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit.egg-info/entry_points.txt +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit.egg-info/requires.txt +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/detectkit.egg-info/top_level.txt +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/pyproject.toml +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/requirements.txt +0 -0
- {detectkit-0.41.0 → detectkit-0.42.0}/setup.cfg +0 -0
- {detectkit-0.41.0 → detectkit-0.42.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.42.0"
|
|
8
8
|
|
|
9
9
|
from detectkit.core.interval import Interval
|
|
10
10
|
from detectkit.core.models import ColumnDefinition, TableModel
|
|
@@ -15,7 +15,6 @@ from detectkit.autotune._base import AutoTuneError, _AutoTuneBase
|
|
|
15
15
|
from detectkit.autotune._types import ScoringMetric, TuneMode
|
|
16
16
|
from detectkit.autotune.autotuner import AutoTuner, run_autotune_engine
|
|
17
17
|
from detectkit.autotune.config_emitter import compute_run_id, emit_tuned_config
|
|
18
|
-
from detectkit.autotune.html_labeler import render_labeler_html
|
|
19
18
|
from detectkit.autotune.labels import (
|
|
20
19
|
GroundTruth,
|
|
21
20
|
IncidentLabels,
|
|
@@ -39,6 +38,5 @@ __all__ = [
|
|
|
39
38
|
"emit_tuned_config",
|
|
40
39
|
"parse_incident_labels",
|
|
41
40
|
"parse_labels_file",
|
|
42
|
-
"render_labeler_html",
|
|
43
41
|
"run_autotune_engine",
|
|
44
42
|
]
|
|
@@ -17,10 +17,12 @@ run. But you assist far better with **read access to the same database** (e.g. a
|
|
|
17
17
|
database MCP for the project's ClickHouse / PostgreSQL / MySQL): you can inspect
|
|
18
18
|
a metric's series, find real incidents to label for `dtk autotune`, sanity-check
|
|
19
19
|
a metric query before running it, and confirm detections — instead of asking the
|
|
20
|
-
user to run every query by hand. Without it, fall back to
|
|
21
|
-
`dtk
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
user to run every query by hand. Without it, fall back to marking incidents
|
|
21
|
+
visually in `dtk tune --select <metric>` (**Label** mode to drag spans / Threshold
|
|
22
|
+
capture / Lasso the anomaly cloud, or **Review** mode to confirm fired alerts as
|
|
23
|
+
incidents), then **Save incidents** — which `dtk autotune` auto-discovers in
|
|
24
|
+
`incidents/<metric>/` — and to asking the user. This access is optional and
|
|
25
|
+
separate from the `profiles.yml` connection detectkit uses for the pipeline.
|
|
24
26
|
|
|
25
27
|
### Where to look (read the matching file before answering)
|
|
26
28
|
|
|
@@ -64,28 +64,24 @@ ratios to choose.
|
|
|
64
64
|
## Command
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
-
dtk autotune --select <sel> [--incidents FILE] [--
|
|
67
|
+
dtk autotune --select <sel> [--incidents FILE] [--scoring METRIC] \
|
|
68
68
|
[--from DATE] [--to DATE] [--profile NAME] [--force] [--dry-run] [--report]
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
- `--incidents FILE|DIR` — a labels file (below) → **supervised** tuning. May be a
|
|
72
72
|
**directory** (e.g. `incidents/<name>/`): interactive runs prompt to pick a
|
|
73
|
-
version (default newest), non-interactive use the newest.
|
|
73
|
+
version (default newest), non-interactive use the newest. **Omit it and `dtk
|
|
74
|
+
autotune` auto-discovers the newest labels in `incidents/<metric>/`** (the store
|
|
75
|
+
`dtk tune`'s **Save incidents** writes) — so after labeling in `dtk tune` you run
|
|
76
|
+
`dtk autotune --select <metric>` with no flag. With no labels anywhere, an
|
|
74
77
|
interactive terminal prompts to enter incidents inline; declining (or running
|
|
75
78
|
non-interactively) tunes **unsupervised**.
|
|
76
|
-
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
editing in place. Beyond click-drag marking it offers **Threshold capture**
|
|
83
|
-
(grab every span above/below a horizontal line in one gesture), **Lasso capture**
|
|
84
|
-
(loop around a cloud of points; each grid-adjacent run, gaps bridged, becomes one
|
|
85
|
-
incident span), **on-chart delete** (each band's ✕, or select + Delete key), and
|
|
86
|
-
**Import file…** (load a labels file you pick). `--no-serve` writes a static
|
|
87
|
-
`metrics/<name>__labeler.html` (Export downloads the file) and exits; `--no-open`
|
|
88
|
-
prints the URL instead of launching a browser.
|
|
79
|
+
- **To label incidents**, use `dtk tune --select <metric>`: switch to **Label**
|
|
80
|
+
mode (drag spans, **Threshold capture** every span past a horizontal line,
|
|
81
|
+
**Lasso** the anomaly cloud) or **Review** mode (confirm fired alerts as
|
|
82
|
+
incidents), then **Save incidents**. That writes versioned files into
|
|
83
|
+
`incidents/<metric>/` — the **same store this command reads** — so the labels feed
|
|
84
|
+
the next supervised tune with no `--incidents` flag (see `cli.md`).
|
|
89
85
|
- `--scoring` — `mcc` (default), `f1`, `f_beta`, `balanced_accuracy`, `roc_auc`,
|
|
90
86
|
`pr_auc`. MCC uses the whole confusion matrix and suits rare anomalies.
|
|
91
87
|
- `--dry-run` — run the search but persist nothing and write no config.
|
|
@@ -115,45 +111,35 @@ incidents:
|
|
|
115
111
|
- {at: "2026-05-11 09:05:00"} # point
|
|
116
112
|
```
|
|
117
113
|
|
|
118
|
-
### Getting labels —
|
|
114
|
+
### Getting labels — label in `dtk tune` first
|
|
119
115
|
|
|
120
|
-
When labels would help, **offer
|
|
116
|
+
When labels would help, **offer to mark incidents in `dtk tune` before asking the
|
|
121
117
|
user to recall timestamps** — it is the easiest, most reliable path:
|
|
122
118
|
|
|
123
|
-
1. Run `dtk
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
3. That writes `incidents/<metric>/<metric>[-<set>]-<UTC>.yml` automatically
|
|
139
|
-
(named after the metric, optional set name as a suffix; versioned —
|
|
140
|
-
re-labeling never overwrites) and the **same command continues into the tuning
|
|
141
|
-
run** on it. No manual file moving.
|
|
142
|
-
4. **Editing over time:** re-running `--label` seeds the page from the metric's
|
|
143
|
-
newest saved set, so labeling can grow across sessions. To re-tune later on
|
|
144
|
-
saved sets, point `--incidents` at the folder (`incidents/<name>/`) —
|
|
145
|
-
interactive runs let the user pick a version, and `--label --incidents <file>`
|
|
146
|
-
opens that exact set for editing. The static `--no-serve` path still exists
|
|
147
|
-
(Export downloads a file you move into `incidents/<name>/`; its **Import
|
|
148
|
-
file…** button loads one back in).
|
|
119
|
+
1. Run `dtk tune --select <name>`. It opens a localhost browser view of the real
|
|
120
|
+
series (use `--no-open` on a remote box and share the printed 127.0.0.1 URL).
|
|
121
|
+
2. Mark the real incidents one of two ways:
|
|
122
|
+
- **Label** mode — drag a span over each incident, or **Threshold capture**
|
|
123
|
+
every span past a horizontal line in one gesture, or **Lasso anomalies** to
|
|
124
|
+
loop a cloud of anomaly dots into per-streak incident spans.
|
|
125
|
+
- **Review** mode — the fired alerts lead; click each alert marker to confirm it
|
|
126
|
+
**valid** (which marks it as a ground-truth incident) or **false alarm**.
|
|
127
|
+
**Confirm all unreviewed valid** does the lot — a clean metric whose alerts
|
|
128
|
+
are all good is validated in a few clicks without hand-drawing spans.
|
|
129
|
+
3. Click **Save incidents**. It writes a versioned
|
|
130
|
+
`incidents/<metric>/<…>.yml` automatically.
|
|
131
|
+
4. Run `dtk autotune --select <name>` with **no `--incidents` flag** — it
|
|
132
|
+
auto-discovers the newest file in `incidents/<metric>/` and tunes supervised on
|
|
133
|
+
it. (You can still pass `--incidents <file-or-dir>` to pick a specific set.)
|
|
149
134
|
|
|
150
135
|
Prefer this whenever the user can *recognise* incidents on a chart but doesn't
|
|
151
136
|
have exact times. If they already know the times (or you found them via a DB
|
|
152
137
|
MCP), write the labels file / inline `incidents:` directly instead. If there are
|
|
153
138
|
no known incidents, run unsupervised — the baseline below is good on its own.
|
|
154
139
|
|
|
155
|
-
With no labels (no `--incidents`, no config `labels_file`,
|
|
156
|
-
entry), tuning falls back to an
|
|
140
|
+
With no labels (no `--incidents`, no config `labels_file`, none auto-discovered
|
|
141
|
+
in `incidents/<metric>/`, no interactive entry), tuning falls back to an
|
|
142
|
+
**unsupervised** objective that blends three
|
|
157
143
|
band-fit / flag-budget terms — `0.4·budget + 0.3·sharpness + 0.3·separation`: a
|
|
158
144
|
smooth one-sided flag-rate budget (no hard cliff), sharpness (a tight,
|
|
159
145
|
well-calibrated confidence interval), and separation (flagged points sitting
|
|
@@ -192,8 +178,10 @@ differs from `seasonality_candidates`, which only narrows the *set of columns*
|
|
|
192
178
|
the search may consider but still runs the search and may choose none.
|
|
193
179
|
|
|
194
180
|
Label precedence (highest first): `--incidents` flag → `labels_file` → inline
|
|
195
|
-
`incidents` →
|
|
196
|
-
|
|
181
|
+
`incidents` → auto-discovered `incidents/<metric>/` (the newest file, e.g. from
|
|
182
|
+
`dtk tune`'s **Save incidents**) → interactive prompt → none (unsupervised).
|
|
183
|
+
`labels_file` and `incidents` are mutually exclusive. `--scoring` likewise
|
|
184
|
+
overrides `scoring_metric`.
|
|
197
185
|
|
|
198
186
|
## The annotated config
|
|
199
187
|
|
|
@@ -67,8 +67,11 @@ Automatically chooses a metric's seasonality, detector type, hyperparameters and
|
|
|
67
67
|
history window, then writes an annotated `metrics/<name>__tuned_<id>.yml`. Reads
|
|
68
68
|
the metric's loaded datapoints (run `dtk run --steps load` first if empty), never
|
|
69
69
|
edits the original, never alerts. `--incidents FILE` enables supervised tuning
|
|
70
|
-
against labeled incidents;
|
|
71
|
-
|
|
70
|
+
against labeled incidents; omit it and autotune **auto-discovers** the newest
|
|
71
|
+
labels in `incidents/<metric>/` (the store `dtk tune`'s **Save incidents** writes
|
|
72
|
+
— label there in Label/Review mode, then just run `dtk autotune`); with no labels
|
|
73
|
+
anywhere, an unsupervised objective is used. `--dry-run` searches without writing.
|
|
74
|
+
`--report [PATH]` writes the same
|
|
72
75
|
self-contained HTML report as `dtk run` for the tuned winner (default
|
|
73
76
|
`reports/<metric>__tuned_<id>.html`; `<dir>` or a `.html` file also accepted).
|
|
74
77
|
Full reference: `autotune.md`.
|
{detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md
RENAMED
|
@@ -79,44 +79,39 @@ generalize is rejected.
|
|
|
79
79
|
Labels turn tuning from a good unsupervised default into a config optimised
|
|
80
80
|
against *your* real incidents (it then optimises MCC and also tunes the alert
|
|
81
81
|
window `consecutive_anomalies`). **The easiest, most reliable way to produce them
|
|
82
|
-
is
|
|
82
|
+
is to mark incidents in `dtk tune` — offer this first**, before asking the user to
|
|
83
83
|
recall timestamps:
|
|
84
84
|
|
|
85
85
|
```bash
|
|
86
|
-
dtk
|
|
86
|
+
dtk tune --select <name>
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
user through it:
|
|
89
|
+
This opens a localhost browser view of the metric's **real** series; on a remote
|
|
90
|
+
machine add `--no-open` and share the printed URL. Walk the user through it:
|
|
92
91
|
|
|
93
92
|
1. Navigate a long/dense series: **scroll to zoom**, double-click to reset, drag
|
|
94
|
-
the **navigator strip** to move the view
|
|
95
|
-
2.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
Export downloads a versioned file you then move into `incidents/<name>/` and pass
|
|
117
|
-
via `--incidents`; its **Import file…** button loads a saved set back in.) To
|
|
118
|
-
re-tune later on a saved set, point `--incidents` at `incidents/<name>/`
|
|
119
|
-
(interactive runs let the user pick a version).
|
|
93
|
+
the **navigator strip** to move the view.
|
|
94
|
+
2. Mark the real incidents in one of two modes:
|
|
95
|
+
- **Label** mode — **drag a span** over each incident (edges to resize, middle
|
|
96
|
+
to move, ✕/Delete to remove). When many outliers are obvious, **Threshold
|
|
97
|
+
capture** grabs every span past a horizontal line in one gesture (set the
|
|
98
|
+
line, pick the side, optionally bridge gaps); **Lasso anomalies** loops a
|
|
99
|
+
freeform cloud of anomaly dots into one incident span per grid-adjacent run.
|
|
100
|
+
- **Review** mode — the fired alerts lead; **click each alert marker** to cycle
|
|
101
|
+
its verdict un-reviewed → **valid** (green) → **false alarm** (slate).
|
|
102
|
+
**Confirming an alert valid marks it as a ground-truth incident**, and
|
|
103
|
+
**Confirm all unreviewed valid** does the lot — so a clean metric whose alerts
|
|
104
|
+
are all good is validated in a few clicks without hand-drawing spans.
|
|
105
|
+
3. Click **Save incidents**. It writes `incidents/<metric>/<…>.yml` automatically
|
|
106
|
+
(versioned — saving never overwrites). Saving doesn't end the session, so you
|
|
107
|
+
can keep tuning; you can also click **Apply** to write the detector config back.
|
|
108
|
+
|
|
109
|
+
**Then run autotune with no `--incidents` flag** — `dtk autotune --select <name>`
|
|
110
|
+
**auto-discovers the newest file in `incidents/<metric>/`** (the same store
|
|
111
|
+
`dtk tune` saves to) and tunes supervised on it. To re-tune on a specific set,
|
|
112
|
+
pass `--incidents <file-or-dir>` (point it at `incidents/<name>/` and interactive
|
|
113
|
+
runs let the user pick a version). Labeling grows across sessions — reopen
|
|
114
|
+
`dtk tune`, mark a few more, **Save incidents** again, re-run autotune.
|
|
120
115
|
|
|
121
116
|
Prefer this whenever the user can *recognise* incidents on a chart but doesn't
|
|
122
117
|
have exact timestamps — it is far easier than dictating times, and they label
|
|
@@ -158,10 +153,14 @@ is good on its own; it just can't learn *which* spikes you specifically care abo
|
|
|
158
153
|
## Step 3 — Run autotune
|
|
159
154
|
|
|
160
155
|
```bash
|
|
161
|
-
# Supervised
|
|
156
|
+
# Supervised — labeled in `dtk tune`: just run it, autotune auto-discovers
|
|
157
|
+
# the newest file in incidents/<name>/ (no --incidents flag needed):
|
|
158
|
+
dtk autotune --select <name>
|
|
159
|
+
|
|
160
|
+
# Supervised — point at a specific labels file or set explicitly:
|
|
162
161
|
dtk autotune --select <name> --incidents incidents/<name>.yml
|
|
163
162
|
|
|
164
|
-
# Unsupervised (no labels — tunes on data statistics):
|
|
163
|
+
# Unsupervised (no labels anywhere — tunes on data statistics):
|
|
165
164
|
dtk autotune --select <name>
|
|
166
165
|
```
|
|
167
166
|
|
{detectkit-0.41.0 → detectkit-0.42.0}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md
RENAMED
|
@@ -45,7 +45,8 @@ side by side, ask which one to set up.
|
|
|
45
45
|
> a database MCP): you can inspect metric series, find incidents to label for
|
|
46
46
|
> `dtk autotune`, sanity-check queries, and confirm detections yourself. Suggest
|
|
47
47
|
> the user connect one if they haven't; if they can't, you can still do
|
|
48
|
-
> everything by asking them to run queries and by
|
|
48
|
+
> everything by asking them to run queries and by marking incidents visually in
|
|
49
|
+
> `dtk tune` (Label/Review modes), which `dtk autotune` auto-discovers.
|
|
49
50
|
|
|
50
51
|
## Step 1 — Pick the database backend
|
|
51
52
|
|
|
@@ -24,16 +24,11 @@ from detectkit.autotune import (
|
|
|
24
24
|
compute_run_id,
|
|
25
25
|
emit_tuned_config,
|
|
26
26
|
parse_labels_file,
|
|
27
|
-
render_labeler_html,
|
|
28
27
|
run_autotune_engine,
|
|
29
28
|
)
|
|
30
|
-
from detectkit.autotune.label_server import serve_labeler
|
|
31
29
|
from detectkit.autotune.labels import (
|
|
32
30
|
GroundTruth,
|
|
33
31
|
IncidentLabels,
|
|
34
|
-
incidents_to_display,
|
|
35
|
-
load_capture_windows,
|
|
36
|
-
load_incidents_for_display,
|
|
37
32
|
parse_incident_labels,
|
|
38
33
|
)
|
|
39
34
|
from detectkit.cli._output import echo_done, echo_error, echo_noop
|
|
@@ -107,9 +102,10 @@ _LABELS_GLOBS = ("*.yml", "*.yaml", "*.json")
|
|
|
107
102
|
def _labels_files(directory: Path) -> list[Path]:
|
|
108
103
|
"""All labels files in *directory*, oldest→newest.
|
|
109
104
|
|
|
110
|
-
|
|
111
|
-
which sort chronologically, so name order is
|
|
112
|
-
This lets ``incidents/<metric>/`` keep
|
|
105
|
+
``dtk tune`` (Label mode) saves versioned, ISO-stamped names
|
|
106
|
+
(``<metric>[-<set>]-<UTC>.yml``) which sort chronologically, so name order is
|
|
107
|
+
chronological (tie-broken by mtime). This lets ``incidents/<metric>/`` keep
|
|
108
|
+
every labeling round on disk.
|
|
113
109
|
"""
|
|
114
110
|
files: list[Path] = []
|
|
115
111
|
for pattern in _LABELS_GLOBS:
|
|
@@ -117,64 +113,6 @@ def _labels_files(directory: Path) -> list[Path]:
|
|
|
117
113
|
return sorted(files, key=lambda p: (p.name, p.stat().st_mtime))
|
|
118
114
|
|
|
119
115
|
|
|
120
|
-
def _resolve_preload_incidents(
|
|
121
|
-
*,
|
|
122
|
-
metric: str,
|
|
123
|
-
interval_seconds: int,
|
|
124
|
-
incidents_path: str | None,
|
|
125
|
-
autotune_cfg: AutoTuneConfig,
|
|
126
|
-
project_root: Path,
|
|
127
|
-
) -> tuple[list[dict[str, str]], Path | None]:
|
|
128
|
-
"""Existing incidents to seed the labeler with (edit-in-place flow).
|
|
129
|
-
|
|
130
|
-
Tries, in order: the ``--incidents`` flag, the config ``labels_file``, then the
|
|
131
|
-
default versioned dir ``incidents/<metric>/`` (where ``--label`` saves). A
|
|
132
|
-
directory resolves to its newest file. Best-effort — a missing/invalid file
|
|
133
|
-
just yields an empty seed (and a warning), never aborting the labeling flow.
|
|
134
|
-
"""
|
|
135
|
-
candidates: list[Path] = []
|
|
136
|
-
explicit = incidents_path or autotune_cfg.labels_file
|
|
137
|
-
if explicit:
|
|
138
|
-
p = Path(explicit)
|
|
139
|
-
candidates.append(p if p.is_absolute() else project_root / p)
|
|
140
|
-
candidates.append(project_root / "incidents" / metric)
|
|
141
|
-
for cand in candidates:
|
|
142
|
-
try:
|
|
143
|
-
if cand.is_dir():
|
|
144
|
-
files = _labels_files(cand)
|
|
145
|
-
if not files:
|
|
146
|
-
continue
|
|
147
|
-
target = files[-1]
|
|
148
|
-
elif cand.exists():
|
|
149
|
-
target = cand
|
|
150
|
-
else:
|
|
151
|
-
continue
|
|
152
|
-
disp = load_incidents_for_display(
|
|
153
|
-
target, interval_seconds=interval_seconds, metric_name=metric
|
|
154
|
-
)
|
|
155
|
-
return disp, target
|
|
156
|
-
except Exception as exc: # noqa: BLE001 — preload is a best-effort convenience
|
|
157
|
-
click.echo(click.style(f" Could not preload {cand}: {exc}", fg="yellow"))
|
|
158
|
-
return [], None
|
|
159
|
-
|
|
160
|
-
# No file on disk — fall back to inline config incidents (mirrors the
|
|
161
|
-
# precedence in _resolve_labels) so an inline-labeled metric also seeds.
|
|
162
|
-
if autotune_cfg.incidents:
|
|
163
|
-
try:
|
|
164
|
-
labels = parse_incident_labels(
|
|
165
|
-
{
|
|
166
|
-
"incidents": autotune_cfg.incidents,
|
|
167
|
-
"timezone": autotune_cfg.incidents_timezone,
|
|
168
|
-
},
|
|
169
|
-
interval_seconds=interval_seconds,
|
|
170
|
-
metric_name=metric,
|
|
171
|
-
)
|
|
172
|
-
return incidents_to_display(labels), None
|
|
173
|
-
except Exception as exc: # noqa: BLE001 — best-effort convenience
|
|
174
|
-
click.echo(click.style(f" Could not preload inline incidents: {exc}", fg="yellow"))
|
|
175
|
-
return [], None
|
|
176
|
-
|
|
177
|
-
|
|
178
116
|
def _pick_labels_file(files: list[Path]) -> tuple[Path, str]:
|
|
179
117
|
"""Choose one labels file from a versioned set; prompt when interactive."""
|
|
180
118
|
newest = files[-1]
|
|
@@ -198,8 +136,9 @@ def _resolve_labels(
|
|
|
198
136
|
"""Resolve labels by precedence.
|
|
199
137
|
|
|
200
138
|
``--incidents`` flag > config ``labels_file`` > config inline ``incidents`` >
|
|
201
|
-
|
|
202
|
-
|
|
139
|
+
auto-discovered ``incidents/<metric>/`` (where ``dtk tune`` Label mode saves) >
|
|
140
|
+
interactive prompt > none (unsupervised). A directory may be given for the flag
|
|
141
|
+
or ``labels_file``; its newest versioned file is used.
|
|
203
142
|
"""
|
|
204
143
|
path = incidents_path or autotune_cfg.labels_file
|
|
205
144
|
if path:
|
|
@@ -231,6 +170,23 @@ def _resolve_labels(
|
|
|
231
170
|
n = len(autotune_cfg.incidents)
|
|
232
171
|
return labels, f"inline config ({n} incident{'s' if n != 1 else ''})"
|
|
233
172
|
|
|
173
|
+
# Auto-discover the metric's saved labels — `dtk tune` (Label mode) writes
|
|
174
|
+
# versioned files into incidents/<metric>/, so a labeling round there feeds the
|
|
175
|
+
# next autotune with no extra flags (newest used; interactive runs may pick).
|
|
176
|
+
default_dir = project_root / "incidents" / metric_name
|
|
177
|
+
if default_dir.is_dir():
|
|
178
|
+
files = _labels_files(default_dir)
|
|
179
|
+
if files:
|
|
180
|
+
chosen, how = _pick_labels_file(files)
|
|
181
|
+
labels = parse_labels_file(
|
|
182
|
+
chosen, interval_seconds=interval_seconds, metric_name=metric_name
|
|
183
|
+
)
|
|
184
|
+
try:
|
|
185
|
+
where: object = default_dir.relative_to(project_root)
|
|
186
|
+
except ValueError:
|
|
187
|
+
where = default_dir
|
|
188
|
+
return labels, f"file {chosen} (auto-discovered, {how} in {where}/)"
|
|
189
|
+
|
|
234
190
|
if sys.stdin.isatty():
|
|
235
191
|
return _prompt_labels(interval_seconds=interval_seconds), "interactive"
|
|
236
192
|
|
|
@@ -324,9 +280,6 @@ def run_autotune(
|
|
|
324
280
|
*,
|
|
325
281
|
select: str,
|
|
326
282
|
incidents_path: str | None,
|
|
327
|
-
label: bool,
|
|
328
|
-
no_serve: bool = False,
|
|
329
|
-
no_open: bool = False,
|
|
330
283
|
scoring_override: str | None,
|
|
331
284
|
from_date: str | None,
|
|
332
285
|
to_date: str | None,
|
|
@@ -363,9 +316,6 @@ def run_autotune(
|
|
|
363
316
|
project_root=project_root,
|
|
364
317
|
internal_manager=internal_manager,
|
|
365
318
|
incidents_path=incidents_path,
|
|
366
|
-
label=label,
|
|
367
|
-
no_serve=no_serve,
|
|
368
|
-
no_open=no_open,
|
|
369
319
|
scoring_override=scoring_override,
|
|
370
320
|
from_dt=from_dt,
|
|
371
321
|
to_dt=to_dt,
|
|
@@ -387,9 +337,6 @@ def _tune_one(
|
|
|
387
337
|
project_root: Path,
|
|
388
338
|
internal_manager: InternalTablesManager,
|
|
389
339
|
incidents_path: str | None,
|
|
390
|
-
label: bool,
|
|
391
|
-
no_serve: bool = False,
|
|
392
|
-
no_open: bool = False,
|
|
393
340
|
scoring_override: str | None,
|
|
394
341
|
from_dt: datetime | None,
|
|
395
342
|
to_dt: datetime | None,
|
|
@@ -413,70 +360,6 @@ def _tune_one(
|
|
|
413
360
|
echo_noop(name, "no datapoints — run `dtk run --select " + name + " --steps load` first")
|
|
414
361
|
return False
|
|
415
362
|
|
|
416
|
-
# --label: open the interactive labeler. Default — a local server that saves the
|
|
417
|
-
# marked incidents straight into incidents/<name>/ and then falls through to
|
|
418
|
-
# tuning on them. --no-serve instead writes a static HTML file and exits (you
|
|
419
|
-
# move the downloaded export in yourself); --no-open skips launching a browser.
|
|
420
|
-
if label:
|
|
421
|
-
click.echo(click.style(f"Processing metric: {name}", fg="cyan", bold=True))
|
|
422
|
-
preload, preload_src = _resolve_preload_incidents(
|
|
423
|
-
metric=name,
|
|
424
|
-
interval_seconds=interval_seconds,
|
|
425
|
-
incidents_path=incidents_path,
|
|
426
|
-
autotune_cfg=autotune_cfg,
|
|
427
|
-
project_root=project_root,
|
|
428
|
-
)
|
|
429
|
-
if preload:
|
|
430
|
-
if preload_src is None:
|
|
431
|
-
where: object = "inline config"
|
|
432
|
-
else:
|
|
433
|
-
try:
|
|
434
|
-
where = preload_src.relative_to(project_root)
|
|
435
|
-
except ValueError: # an absolute path outside the project tree
|
|
436
|
-
where = preload_src
|
|
437
|
-
click.echo(f" Editing {len(preload)} existing incident(s) from {where}")
|
|
438
|
-
# Restore the painted threshold-capture window from the same seed file
|
|
439
|
-
# (best-effort — a missing/old file just yields no window).
|
|
440
|
-
capture_windows: list[dict[str, str]] = []
|
|
441
|
-
if preload_src is not None:
|
|
442
|
-
try:
|
|
443
|
-
capture_windows = load_capture_windows(
|
|
444
|
-
preload_src, interval_seconds=interval_seconds, metric_name=name
|
|
445
|
-
)
|
|
446
|
-
except Exception: # noqa: BLE001 — restoring the scope is a convenience
|
|
447
|
-
capture_windows = []
|
|
448
|
-
if no_serve:
|
|
449
|
-
html = render_labeler_html(
|
|
450
|
-
name,
|
|
451
|
-
data,
|
|
452
|
-
interval_seconds=interval_seconds,
|
|
453
|
-
incidents=preload,
|
|
454
|
-
capture_windows=capture_windows,
|
|
455
|
-
)
|
|
456
|
-
out = project_root / "metrics" / f"{metric_path.stem}__labeler.html"
|
|
457
|
-
out.write_text(html, encoding="utf-8")
|
|
458
|
-
click.echo(f" Wrote labeler: {out.relative_to(project_root)}")
|
|
459
|
-
click.echo(
|
|
460
|
-
f" Open it, mark incidents, Export, save into incidents/{name}/, "
|
|
461
|
-
f"then re-run with --incidents incidents/{name}/"
|
|
462
|
-
)
|
|
463
|
-
return True
|
|
464
|
-
saved = serve_labeler(
|
|
465
|
-
metric_name=name,
|
|
466
|
-
data=data,
|
|
467
|
-
incidents_dir=project_root / "incidents" / name,
|
|
468
|
-
interval_seconds=interval_seconds,
|
|
469
|
-
open_browser=not no_open,
|
|
470
|
-
echo=click.echo,
|
|
471
|
-
preload=preload,
|
|
472
|
-
capture_windows=capture_windows,
|
|
473
|
-
)
|
|
474
|
-
if saved is None:
|
|
475
|
-
echo_noop(name, "labeling cancelled — no labels saved")
|
|
476
|
-
return False
|
|
477
|
-
click.echo(f" Saved labels: {saved.relative_to(project_root)}")
|
|
478
|
-
incidents_path = str(saved) # continue into supervised tuning on this set
|
|
479
|
-
|
|
480
363
|
click.echo(click.style(f"Tuning metric: {name}", fg="cyan", bold=True))
|
|
481
364
|
click.echo(f" Config file: {metric_path.relative_to(project_root)}")
|
|
482
365
|
|
|
@@ -152,8 +152,9 @@ _EXAMPLE_INCIDENTS = """# Example incidents (labels) file for supervised `dtk au
|
|
|
152
152
|
# - `label` is optional free text (documentation only).
|
|
153
153
|
#
|
|
154
154
|
# Tip: can't list incidents from memory? Run
|
|
155
|
-
# dtk
|
|
156
|
-
#
|
|
155
|
+
# dtk tune --select example_cpu_usage
|
|
156
|
+
# and use Label mode to mark incidents in a browser; Save incidents writes them
|
|
157
|
+
# into incidents/example_cpu_usage/, which `dtk autotune` auto-discovers.
|
|
157
158
|
|
|
158
159
|
# Optional — must match the metric `name:` it labels (autotune refuses a mismatch).
|
|
159
160
|
metric: example_cpu_usage
|
|
@@ -214,22 +214,7 @@ def run(
|
|
|
214
214
|
"--incidents",
|
|
215
215
|
"incidents_path",
|
|
216
216
|
type=click.Path(),
|
|
217
|
-
help="Canonical labels file marking real incidents
|
|
218
|
-
)
|
|
219
|
-
@click.option(
|
|
220
|
-
"--label",
|
|
221
|
-
is_flag=True,
|
|
222
|
-
help="Open the interactive labeler (local server) to mark incidents, then tune on them",
|
|
223
|
-
)
|
|
224
|
-
@click.option(
|
|
225
|
-
"--no-serve",
|
|
226
|
-
is_flag=True,
|
|
227
|
-
help="With --label, write a static HTML labeler file and exit instead of serving",
|
|
228
|
-
)
|
|
229
|
-
@click.option(
|
|
230
|
-
"--no-open",
|
|
231
|
-
is_flag=True,
|
|
232
|
-
help="With --label, don't auto-open the browser (just print the local URL)",
|
|
217
|
+
help="Canonical labels file (or incidents/<metric>/ dir) marking real incidents",
|
|
233
218
|
)
|
|
234
219
|
@click.option(
|
|
235
220
|
"--scoring",
|
|
@@ -275,9 +260,6 @@ def run(
|
|
|
275
260
|
def autotune(
|
|
276
261
|
select: str,
|
|
277
262
|
incidents_path: str,
|
|
278
|
-
label: bool,
|
|
279
|
-
no_serve: bool,
|
|
280
|
-
no_open: bool,
|
|
281
263
|
scoring_override: str,
|
|
282
264
|
from_date: str,
|
|
283
265
|
to_date: str,
|
|
@@ -295,19 +277,18 @@ def autotune(
|
|
|
295
277
|
tuned config named <metric>__tuned_<id>. Each run is recorded in
|
|
296
278
|
_dtk_autotune_runs.
|
|
297
279
|
|
|
298
|
-
Mark real incidents for supervised tuning via --incidents (a labels file
|
|
299
|
-
without it, tuning falls back to an unsupervised
|
|
280
|
+
Mark real incidents for supervised tuning via --incidents (a labels file or
|
|
281
|
+
the incidents/<metric>/ dir); without it, tuning falls back to an unsupervised
|
|
282
|
+
objective. Incidents marked in `dtk tune` (Label mode) are auto-discovered from
|
|
283
|
+
incidents/<metric>/, so no flag is needed after labeling there.
|
|
300
284
|
|
|
301
285
|
Examples:
|
|
302
286
|
# Supervised tuning against labeled incidents
|
|
303
287
|
dtk autotune --select checkout_errors --incidents incidents/checkout.yml
|
|
304
288
|
|
|
305
|
-
# Unsupervised (no labels)
|
|
289
|
+
# Unsupervised (no labels), or auto-pick incidents/checkout_errors/ if present
|
|
306
290
|
dtk autotune --select checkout_errors
|
|
307
291
|
|
|
308
|
-
# Mark incidents in a browser, save into incidents/<m>/, and tune on them
|
|
309
|
-
dtk autotune --select checkout_errors --label
|
|
310
|
-
|
|
311
292
|
# Search only, change nothing
|
|
312
293
|
dtk autotune --select checkout_errors --dry-run
|
|
313
294
|
"""
|
|
@@ -316,9 +297,6 @@ def autotune(
|
|
|
316
297
|
run_autotune(
|
|
317
298
|
select=select,
|
|
318
299
|
incidents_path=incidents_path,
|
|
319
|
-
label=label,
|
|
320
|
-
no_serve=no_serve,
|
|
321
|
-
no_open=no_open,
|
|
322
300
|
scoring_override=scoring_override,
|
|
323
301
|
from_date=from_date,
|
|
324
302
|
to_date=to_date,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""Render a report payload into a single self-contained HTML file.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
renderer JS inlined (the pre-built
|
|
5
|
-
shared with the website landing demo)
|
|
6
|
-
CDN, no network, nothing leaves the
|
|
7
|
-
``str.replace`` (NOT ``.format``) so
|
|
3
|
+
The self-contained inline-bundle delivery model (shared with the ``dtk tune``
|
|
4
|
+
page): one HTML document with the renderer JS inlined (the pre-built
|
|
5
|
+
``assets/report.js`` bundle — one source shared with the website landing demo)
|
|
6
|
+
and the data baked in as a JS literal. No CDN, no network, nothing leaves the
|
|
7
|
+
browser. Placeholders are substituted with ``str.replace`` (NOT ``.format``) so
|
|
8
|
+
literal ``{}`` in the JS/CSS survive.
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
from __future__ import annotations
|