From d65116f11cc6be3be50424ecba0188ff50b4b989 Mon Sep 17 00:00:00 2001 From: DBT Date: Sun, 1 Mar 2026 05:44:20 +0000 Subject: [PATCH] ekg stats: add 6h/24h/7d window filter and webui selector for time-windowed insights --- pkg/nodes/registry_server.go | 24 ++++++++++++++++++++++++ webui/src/pages/TaskAudit.tsx | 15 ++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/pkg/nodes/registry_server.go b/pkg/nodes/registry_server.go index b4b5336..83f25b4 100644 --- a/pkg/nodes/registry_server.go +++ b/pkg/nodes/registry_server.go @@ -1639,6 +1639,21 @@ func (s *RegistryServer) handleWebUIEKGStats(w http.ResponseWriter, r *http.Requ } workspace := strings.TrimSpace(s.workspacePath) ekgPath := filepath.Join(workspace, "memory", "ekg-events.jsonl") + window := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("window"))) + windowDur := 24 * time.Hour + switch window { + case "6h": + windowDur = 6 * time.Hour + case "24h", "": + windowDur = 24 * time.Hour + case "7d": + windowDur = 7 * 24 * time.Hour + } + selectedWindow := window + if selectedWindow == "" { + selectedWindow = "24h" + } + cutoff := time.Now().UTC().Add(-windowDur) b, _ := os.ReadFile(ekgPath) lines := strings.Split(string(b), "\n") if len(lines) > 0 && lines[len(lines)-1] == "" { @@ -1667,6 +1682,14 @@ func (s *RegistryServer) handleWebUIEKGStats(w http.ResponseWriter, r *http.Requ if json.Unmarshal([]byte(ln), &row) != nil { continue } + ts := strings.TrimSpace(fmt.Sprintf("%v", row["time"])) + if ts != "" { + if tm, err := time.Parse(time.RFC3339, ts); err == nil { + if tm.Before(cutoff) { + continue + } + } + } provider := strings.TrimSpace(fmt.Sprintf("%v", row["provider"])) status := strings.ToLower(strings.TrimSpace(fmt.Sprintf("%v", row["status"]))) errSig := strings.TrimSpace(fmt.Sprintf("%v", row["errsig"])) @@ -1731,6 +1754,7 @@ func (s *RegistryServer) handleWebUIEKGStats(w http.ResponseWriter, r *http.Requ } _ = json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, + "window": selectedWindow, "provider_top": toTopScore(providerScore, 5), "provider_top_workload": toTopScore(providerScoreWorkload, 5), "errsig_top": toTopCount(errSigCount, 5), diff --git a/webui/src/pages/TaskAudit.tsx b/webui/src/pages/TaskAudit.tsx index 1711cc1..cc17007 100644 --- a/webui/src/pages/TaskAudit.tsx +++ b/webui/src/pages/TaskAudit.tsx @@ -47,6 +47,7 @@ const TaskAudit: React.FC = () => { const [ekgSourceStats, setEkgSourceStats] = useState>({}); const [ekgChannelStats, setEkgChannelStats] = useState>({}); const [ekgEscalationCount, setEkgEscalationCount] = useState(0); + const [ekgWindow, setEkgWindow] = useState<'6h' | '24h' | '7d'>('24h'); const fetchData = async () => { setLoading(true); @@ -67,7 +68,8 @@ const TaskAudit: React.FC = () => { } else { setDailyReport(''); } - const ekgUrl = `/webui/api/ekg_stats${q || ''}`; + const ekgJoin = q ? `${q}&window=${encodeURIComponent(ekgWindow)}` : `?window=${encodeURIComponent(ekgWindow)}`; + const ekgUrl = `/webui/api/ekg_stats${ekgJoin}`; const er = await fetch(ekgUrl); if (er.ok) { const ej = await er.json(); @@ -89,7 +91,7 @@ const TaskAudit: React.FC = () => { } }; - useEffect(() => { fetchData(); }, [q, reportDate]); + useEffect(() => { fetchData(); }, [q, reportDate, ekgWindow]); @@ -149,7 +151,14 @@ const TaskAudit: React.FC = () => {
-
EKG Insights
+
+
EKG Insights
+ +
Escalations