feat(run): per-frame detection log at logs/detections/YYYY-MM-DD.jsonl
Writes one JSONL line per detector.step() with ts, rgb, match_name, distance, confidence, dot_found, window_found, accepted, color. Captures UNKNOWN classifications and no-dot frames that today's audit log skips, so the user can verify post-session what colors the program actually saw. Reuses AuditLog for daily rotation + buffering. Separate subdir keeps audit.jsonl uncluttered. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -447,6 +447,7 @@ def run_live(cfg, duration_s=None, capture_stub: bool = False) -> None:
|
|||||||
fsm = StateMachine(lockout_s=cfg.lockout_s)
|
fsm = StateMachine(lockout_s=cfg.lockout_s)
|
||||||
canary = Canary(cfg, pause_flag_path=Path("logs/pause.flag"))
|
canary = Canary(cfg, pause_flag_path=Path("logs/pause.flag"))
|
||||||
audit = AuditLog(Path("logs"))
|
audit = AuditLog(Path("logs"))
|
||||||
|
detection_log = AuditLog(Path("logs/detections"))
|
||||||
backends = [
|
backends = [
|
||||||
DiscordNotifier(cfg.discord.webhook_url),
|
DiscordNotifier(cfg.discord.webhook_url),
|
||||||
TelegramNotifier(cfg.telegram.bot_token, cfg.telegram.chat_id),
|
TelegramNotifier(cfg.telegram.bot_token, cfg.telegram.chat_id),
|
||||||
@@ -503,6 +504,18 @@ def run_live(cfg, duration_s=None, capture_stub: bool = False) -> None:
|
|||||||
continue
|
continue
|
||||||
# detection
|
# detection
|
||||||
res = detector.step(now)
|
res = detector.step(now)
|
||||||
|
detection_log.log({
|
||||||
|
"ts": now,
|
||||||
|
"event": "frame",
|
||||||
|
"window_found": res.window_found,
|
||||||
|
"dot_found": res.dot_found,
|
||||||
|
"rgb": list(res.rgb) if res.rgb is not None else None,
|
||||||
|
"match_name": res.match.name if res.match is not None else None,
|
||||||
|
"distance": round(res.match.distance, 2) if res.match is not None else None,
|
||||||
|
"confidence": round(res.match.confidence, 3) if res.match is not None else None,
|
||||||
|
"accepted": res.accepted,
|
||||||
|
"color": res.color,
|
||||||
|
})
|
||||||
if res.accepted and res.color:
|
if res.accepted and res.color:
|
||||||
is_first = first_accepted
|
is_first = first_accepted
|
||||||
first_accepted = False
|
first_accepted = False
|
||||||
@@ -569,6 +582,7 @@ def run_live(cfg, duration_s=None, capture_stub: bool = False) -> None:
|
|||||||
pass
|
pass
|
||||||
notifier.stop()
|
notifier.stop()
|
||||||
audit.close()
|
audit.close()
|
||||||
|
detection_log.close()
|
||||||
|
|
||||||
|
|
||||||
def _build_capture(cfg, capture_stub: bool = False):
|
def _build_capture(cfg, capture_stub: bool = False):
|
||||||
|
|||||||
Reference in New Issue
Block a user