[eric] browser: fire the learning self-audit every 25 finished tasks in a daemon thread (zero hot-path latency)

This commit is contained in:
ciregenz
2026-06-07 21:33:38 -07:00
parent 6ef7b33667
commit 2409e2d4cd
2 changed files with 53 additions and 0 deletions
@@ -22,6 +22,7 @@ import json
import logging
import os
import re
import threading
import time
from collections import Counter
@@ -191,4 +192,30 @@ def record_task(session_id, browser_id, task, status, started_at, turns,
f"tools={len(action_log)} tok_in={summary['tokens_in']} tok_out={summary['tokens_out']} "
f"recurring_errs={summary['recurring_errors'][:2]}"
)
_maybe_self_audit()
return summary
_AUDIT_EVERY_N = 25 # refresh the learning self-audit roughly this often
_task_count = 0
def _maybe_self_audit() -> None:
"""Every N finished tasks, refresh the self-audit report in a daemon thread so
it never adds latency to a run (the audit is ~3ms but stays off the hot path).
Proposal-only: it writes a report a human reads, it changes nothing."""
global _task_count
_task_count += 1
if _task_count % _AUDIT_EVERY_N != 0:
return
def _run():
try:
from backend.apps.agents.browser import browser_self_audit
browser_self_audit.run_and_write()
except Exception:
pass
try:
threading.Thread(target=_run, name="browser-self-audit", daemon=True).start()
except Exception:
pass
+26
View File
@@ -60,6 +60,32 @@ def test_clean_history_proposes_nothing():
assert "learning cleanly" in audit.render_report(r)
def test_audit_fires_every_n_finished_tasks(monkeypatch, tmp_path):
# the trigger refreshes the report once every N tasks, off the hot path. Make
# threads synchronous so the test is deterministic, and use a small N.
from backend.apps.agents.browser import browser_metrics as m
monkeypatch.setenv("OPENSWARM_BROWSER_METRICS_DIR", str(tmp_path))
m._metrics_dir_cache = None
m._task_count = 0
monkeypatch.setattr(m, "_AUDIT_EVERY_N", 5)
class _SyncThread:
def __init__(self, target=None, **kw):
self._t = target
def start(self):
self._t()
monkeypatch.setattr(m.threading, "Thread", _SyncThread)
log = [{"tool": "BrowserClickIndex", "elapsed_ms": 5, "result_summary": "ok"}]
report = tmp_path / "self_audit_report.md"
for i in range(4):
m.record_task(f"s{i}", "b1", "t", "completed", 0, 7, log, {})
assert not report.exists(), "audit fired before N tasks"
m.record_task("s5", "b1", "t", "completed", 0, 7, log, {})
assert report.exists(), "audit did not fire at the Nth task"
def test_run_and_write_emits_a_report_file_and_never_raises():
d = tempfile.mkdtemp()
_write(d, "tasks.jsonl", [])