detectkit 0.23.0__tar.gz → 0.23.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.23.0/detectkit.egg-info → detectkit-0.23.2}/PKG-INFO +1 -1
  2. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/__init__.py +1 -1
  3. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/html_labeler.py +58 -5
  4. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/label_server.py +4 -1
  5. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/commands/autotune.py +1 -1
  6. {detectkit-0.23.0 → detectkit-0.23.2/detectkit.egg-info}/PKG-INFO +1 -1
  7. {detectkit-0.23.0 → detectkit-0.23.2}/LICENSE +0 -0
  8. {detectkit-0.23.0 → detectkit-0.23.2}/MANIFEST.in +0 -0
  9. {detectkit-0.23.0 → detectkit-0.23.2}/README.md +0 -0
  10. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/__init__.py +0 -0
  11. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/channels/__init__.py +0 -0
  12. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/channels/base.py +0 -0
  13. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/channels/branding.py +0 -0
  14. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/channels/email.py +0 -0
  15. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/channels/factory.py +0 -0
  16. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/channels/mattermost.py +0 -0
  17. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/channels/slack.py +0 -0
  18. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/channels/telegram.py +0 -0
  19. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/channels/webhook.py +0 -0
  20. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/orchestrator/__init__.py +0 -0
  21. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/orchestrator/_base.py +0 -0
  22. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
  23. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/orchestrator/_decision.py +0 -0
  24. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
  25. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/orchestrator/_recovery.py +0 -0
  26. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/orchestrator/_types.py +0 -0
  27. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
  28. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/__init__.py +0 -0
  29. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/_base.py +0 -0
  30. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/_types.py +0 -0
  31. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/autotuner.py +0 -0
  32. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/config_emitter.py +0 -0
  33. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/crossval.py +0 -0
  34. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/detector_select.py +0 -0
  35. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/distribution.py +0 -0
  36. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/grid_search.py +0 -0
  37. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/labels.py +0 -0
  38. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/result.py +0 -0
  39. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/scoring.py +0 -0
  40. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/seasonality_search.py +0 -0
  41. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/settings.py +0 -0
  42. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/autotune/window_select.py +0 -0
  43. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/__init__.py +0 -0
  44. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/_output.py +0 -0
  45. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/CLAUDE.section.md +0 -0
  46. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/rules/alerting.md +0 -0
  47. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/rules/autotune.md +0 -0
  48. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/rules/cli.md +0 -0
  49. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/rules/detectors.md +0 -0
  50. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
  51. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/rules/overview.md +0 -0
  52. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/rules/project.md +0 -0
  53. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md +0 -0
  54. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
  55. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
  56. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
  57. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/commands/__init__.py +0 -0
  58. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/commands/clean.py +0 -0
  59. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/commands/init.py +0 -0
  60. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/commands/init_claude.py +0 -0
  61. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/commands/run.py +0 -0
  62. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/commands/test_alert.py +0 -0
  63. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/commands/unlock.py +0 -0
  64. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/cli/main.py +0 -0
  65. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/config/__init__.py +0 -0
  66. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/config/metric_config.py +0 -0
  67. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/config/profile.py +0 -0
  68. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/config/project_config.py +0 -0
  69. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/config/validator.py +0 -0
  70. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/core/__init__.py +0 -0
  71. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/core/interval.py +0 -0
  72. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/core/models.py +0 -0
  73. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/__init__.py +0 -0
  74. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/_sql_manager.py +0 -0
  75. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/clickhouse_manager.py +0 -0
  76. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/__init__.py +0 -0
  77. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/_alert_states.py +0 -0
  78. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/_autotune_runs.py +0 -0
  79. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/_base.py +0 -0
  80. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/_datapoints.py +0 -0
  81. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/_detections.py +0 -0
  82. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/_maintenance.py +0 -0
  83. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/_metrics.py +0 -0
  84. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/_schema.py +0 -0
  85. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/_tasks.py +0 -0
  86. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/internal_tables/manager.py +0 -0
  87. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/manager.py +0 -0
  88. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/mysql_manager.py +0 -0
  89. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/postgres_manager.py +0 -0
  90. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/database/tables.py +0 -0
  91. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/detectors/__init__.py +0 -0
  92. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/detectors/base.py +0 -0
  93. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/detectors/factory.py +0 -0
  94. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/detectors/seasonality.py +0 -0
  95. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/detectors/statistical/__init__.py +0 -0
  96. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/detectors/statistical/_windowed.py +0 -0
  97. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/detectors/statistical/iqr.py +0 -0
  98. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/detectors/statistical/mad.py +0 -0
  99. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/detectors/statistical/manual_bounds.py +0 -0
  100. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/detectors/statistical/zscore.py +0 -0
  101. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/loaders/__init__.py +0 -0
  102. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/loaders/metric_loader.py +0 -0
  103. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/loaders/query_template.py +0 -0
  104. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/orchestration/__init__.py +0 -0
  105. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/orchestration/error_dispatch.py +0 -0
  106. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/orchestration/task_manager/__init__.py +0 -0
  107. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
  108. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/orchestration/task_manager/_base.py +0 -0
  109. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
  110. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/orchestration/task_manager/_load_step.py +0 -0
  111. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/orchestration/task_manager/_types.py +0 -0
  112. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/orchestration/task_manager/manager.py +0 -0
  113. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/utils/__init__.py +0 -0
  114. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/utils/datetime_utils.py +0 -0
  115. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/utils/env_interpolation.py +0 -0
  116. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/utils/json_utils.py +0 -0
  117. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit/utils/stats.py +0 -0
  118. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit.egg-info/SOURCES.txt +0 -0
  119. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit.egg-info/dependency_links.txt +0 -0
  120. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit.egg-info/entry_points.txt +0 -0
  121. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit.egg-info/requires.txt +0 -0
  122. {detectkit-0.23.0 → detectkit-0.23.2}/detectkit.egg-info/top_level.txt +0 -0
  123. {detectkit-0.23.0 → detectkit-0.23.2}/pyproject.toml +0 -0
  124. {detectkit-0.23.0 → detectkit-0.23.2}/requirements.txt +0 -0
  125. {detectkit-0.23.0 → detectkit-0.23.2}/setup.cfg +0 -0
  126. {detectkit-0.23.0 → detectkit-0.23.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detectkit
3
- Version: 0.23.0
3
+ Version: 0.23.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.23.0"
7
+ __version__ = "0.23.2"
8
8
 
9
9
  from detectkit.core.interval import Interval
10
10
  from detectkit.core.models import ColumnDefinition, TableModel
@@ -58,6 +58,11 @@ _TEMPLATE = """<!doctype html>
58
58
  .brand span { color: var(--faint); font-size: 12px; }
59
59
  h1 { font-size: 18px; line-height: 1.3; margin: 0 0 6px; color: var(--paper); font-weight: 600; }
60
60
  h1 code { color: var(--clay); font-family: var(--mono); font-size: .82em; }
61
+ .ichip { display:inline-flex; align-items:center; gap:6px; vertical-align: middle; margin-left: 8px;
62
+ font-family: var(--mono); font-size: 12px; font-weight: 500; color: var(--paper);
63
+ background: rgba(209,91,54,0.16); border: 1px solid var(--clay); border-radius: 999px; padding: 3px 10px; }
64
+ .ichip .d { width:6px; height:6px; border-radius:50%; background: var(--clay); }
65
+ .ichip b { color: var(--clay); font-weight: 700; }
61
66
  .hint { color: var(--faint); font-size: 13px; margin: 0 0 18px; line-height: 1.55; }
62
67
  .hint code, code.k { color: var(--term-text); font-family: var(--mono); font-size: 12px;
63
68
  background: var(--term-surface); border: 1px solid var(--term-border); border-radius: 5px; padding: 1px 6px; }
@@ -106,7 +111,8 @@ _TEMPLATE = """<!doctype html>
106
111
  <svg viewBox="0 0 100 100" aria-hidden="true"><rect x="3" y="3" width="94" height="94" rx="26" fill="#D15B36"/><polyline points="14,62 36,62 50,22 64,62 86,62" fill="none" stroke="#FBF9F3" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/><circle cx="50" cy="22" r="6.5" fill="#FBF9F3"/></svg>
107
112
  <b>detectkit</b><span>· incident labeler</span>
108
113
  </div>
109
- <h1>Label incidents — <code>__METRIC__</code></h1>
114
+ <h1>Label incidents — <code>__METRIC__</code><span id="intervalchip" class="ichip"
115
+ title="The metric's sampling interval — the spacing between points, taken straight from the metric."></span></h1>
110
116
  <p class="hint">Click-drag across the chart to mark each real incident, add a short description, then
111
117
  <b>Export</b>. Save the file into <code class="k">incidents/__METRIC__/</code> and run
112
118
  <code class="k">dtk autotune --select __METRIC__ --incidents incidents/__METRIC__/</code></p>
@@ -138,6 +144,9 @@ const DATA = __PAYLOAD__;
138
144
  // and Export POSTs straight into incidents/<metric>/. As a static file it is null,
139
145
  // and Export falls back to a browser download.
140
146
  const SAVE_URL = __SAVE_URL__;
147
+ // The metric's sampling interval (seconds). Passed straight from the metric when
148
+ // known; otherwise inferred from the median spacing of points.
149
+ const INTERVAL_S = __INTERVAL__;
141
150
  const pts = DATA.points.map(p => ({ts: Date.parse(p.t.replace(' ','T')+'Z'), v: p.v}));
142
151
  const N = pts.length;
143
152
  const vraw = pts.filter(p => p.v !== null).map(p => p.v);
@@ -242,7 +251,35 @@ function draw() {
242
251
  ctx.fillStyle='rgba(240,173,78,0.28)'; ctx.fillRect(Math.min(x0,x1), M.t*dpr, Math.abs(x1-x0), plotH()); }
243
252
  drawSeries(ctx, px, py, viewMin, viewMax, M.l*dpr, plotW(), '#d15b36', 1.5);
244
253
  ctx.restore();
245
- if (hover && !dragging && !ovAct) drawHover();
254
+ if (dragging && !ovAct) drawDragLabel();
255
+ else if (hover && !ovAct) drawHover();
256
+ }
257
+
258
+ // Live time readout while marking/resizing/moving an incident, so you can place
259
+ // an edge precisely (an edge shows old → new; move/new show the resulting span).
260
+ function drawDragLabel() {
261
+ let text, atTs;
262
+ if (dragging.mode==='new') {
263
+ const a=Math.min(dragging.a,dragging.b), b=Math.max(dragging.a,dragging.b);
264
+ text = fmtTs(a)+' → '+fmtTs(b); atTs = dragging.b;
265
+ } else { const iv=incidents[dragging.i]; if (!iv) return;
266
+ if (dragging.mode==='edge') {
267
+ const old = dragging.edge==='a' ? dragging.a0 : dragging.b0;
268
+ const cur = dragging.edge==='a' ? iv.a : iv.b;
269
+ text = (dragging.edge==='a' ? 'start: ' : 'end: ') + fmtTs(old) + ' → ' + fmtTs(cur);
270
+ atTs = cur;
271
+ } else { text = fmtTs(iv.a) + ' → ' + fmtTs(iv.b); atTs = (iv.a+iv.b)/2; }
272
+ }
273
+ const X = clamp(px(atTs), M.l*dpr, c.width-M.r*dpr);
274
+ ctx.font=(11*dpr)+'px ui-monospace, monospace';
275
+ const tw=ctx.measureText(text).width, bw=tw+14*dpr, bh=22*dpr, by=M.t*dpr+2;
276
+ let bx = X - bw/2;
277
+ if (bx < M.l*dpr) bx = M.l*dpr;
278
+ if (bx+bw > c.width-M.r*dpr) bx = c.width-M.r*dpr-bw;
279
+ ctx.fillStyle='rgba(27,25,22,0.96)'; ctx.strokeStyle='#d15b36'; ctx.lineWidth=1*dpr;
280
+ ctx.fillRect(bx, by, bw, bh); ctx.strokeRect(bx, by, bw, bh);
281
+ ctx.fillStyle='#f5f1e8'; ctx.textAlign='left'; ctx.textBaseline='middle';
282
+ ctx.fillText(text, bx+7*dpr, by+bh/2);
246
283
  }
247
284
 
248
285
  function drawHover() {
@@ -309,7 +346,8 @@ c.addEventListener('mousedown', e => {
309
346
  const hit = hitIncident(e.clientX), t = tsAt(e.clientX);
310
347
  if (hit && hit.edge==='move') { const iv=incidents[hit.i];
311
348
  dragging={mode:'move', i:hit.i, grab:t, a0:iv.a, b0:iv.b, sx:e.clientX, cx:e.clientX}; }
312
- else if (hit) dragging={mode:'edge', i:hit.i, edge:hit.edge, sx:e.clientX, cx:e.clientX};
349
+ else if (hit) { const iv=incidents[hit.i];
350
+ dragging={mode:'edge', i:hit.i, edge:hit.edge, a0:iv.a, b0:iv.b, sx:e.clientX, cx:e.clientX}; }
313
351
  else dragging={mode:'new', a:t, b:t, sx:e.clientX, cx:e.clientX};
314
352
  });
315
353
  c.addEventListener('mousemove', e => { if (ovAct) return;
@@ -426,19 +464,33 @@ function drawAll() { draw(); drawOverview();
426
464
  function fit() { dpr = window.devicePixelRatio || 1;
427
465
  c.width=c.clientWidth*dpr; c.height=c.clientHeight*dpr;
428
466
  ov.width=ov.clientWidth*dpr; ov.height=ov.clientHeight*dpr; drawAll(); }
467
+ function fmtInterval(s) { if (s<=0) return '?';
468
+ if (s%86400===0) return (s/86400)+'d'; if (s%3600===0) return (s/3600)+'h';
469
+ if (s%60===0) return (s/60)+'min'; return s+'s'; }
470
+ function medianIntervalSec() { if (N<2) return 0;
471
+ const d=[]; for (let i=1;i<N;i++) d.push(pts[i].ts-pts[i-1].ts);
472
+ d.sort((a,b)=>a-b); return Math.round(d[Math.floor(d.length/2)]/1000); }
473
+ const intervalSec = (typeof INTERVAL_S==='number' && INTERVAL_S>0) ? INTERVAL_S : medianIntervalSec();
474
+ document.getElementById('intervalchip').innerHTML =
475
+ '<span class="d"></span>interval <b>'+fmtInterval(intervalSec)+'</b>';
429
476
  window.addEventListener('resize', fit); fit(); render();
430
477
  </script>
431
478
  """
432
479
 
433
480
 
434
481
  def render_labeler_html(
435
- metric_name: str, data: dict[str, np.ndarray], *, save_url: str | None = None
482
+ metric_name: str,
483
+ data: dict[str, np.ndarray],
484
+ *,
485
+ save_url: str | None = None,
486
+ interval_seconds: int | None = None,
436
487
  ) -> str:
437
488
  """Return a self-contained HTML labeler page for *metric_name*'s series.
438
489
 
439
490
  With ``save_url`` (set by ``dtk autotune --label``'s local server) the page's
440
491
  Export button POSTs the labels straight to that endpoint; without it (a static
441
- file) Export falls back to a browser download.
492
+ file) Export falls back to a browser download. ``interval_seconds`` is the
493
+ metric's sampling interval shown as a chip (inferred from the data if omitted).
442
494
  """
443
495
  import json
444
496
 
@@ -452,5 +504,6 @@ def render_labeler_html(
452
504
  return (
453
505
  _TEMPLATE.replace("__PAYLOAD__", payload)
454
506
  .replace("__SAVE_URL__", json.dumps(save_url))
507
+ .replace("__INTERVAL__", json.dumps(interval_seconds))
455
508
  .replace("__METRIC__", metric_name)
456
509
  )
@@ -126,7 +126,10 @@ def build_label_server(
126
126
  server.incidents_dir = incidents_dir
127
127
  server.interval_seconds = interval_seconds
128
128
  server.html = render_labeler_html(
129
- metric_name, data, save_url=f"http://127.0.0.1:{port}/save?token={token}"
129
+ metric_name,
130
+ data,
131
+ save_url=f"http://127.0.0.1:{port}/save?token={token}",
132
+ interval_seconds=interval_seconds,
130
133
  )
131
134
  return server, f"http://127.0.0.1:{port}/?token={token}"
132
135
 
@@ -348,7 +348,7 @@ def _tune_one(
348
348
  if label:
349
349
  click.echo(click.style(f"Processing metric: {name}", fg="cyan", bold=True))
350
350
  if no_serve:
351
- html = render_labeler_html(name, data)
351
+ html = render_labeler_html(name, data, interval_seconds=interval_seconds)
352
352
  out = project_root / "metrics" / f"{metric_path.stem}__labeler.html"
353
353
  out.write_text(html, encoding="utf-8")
354
354
  click.echo(f" Wrote labeler: {out.relative_to(project_root)}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detectkit
3
- Version: 0.23.0
3
+ Version: 0.23.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