feat(ai): add recent metrics events
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
97b1854857
commit
3c0d9f853c
3 changed files with 84 additions and 4 deletions
|
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -152,11 +153,21 @@ func Summary(events []Event) map[string]any {
|
|||
}
|
||||
}
|
||||
|
||||
recent := make([]Event, len(events))
|
||||
copy(recent, events)
|
||||
sort.SliceStable(recent, func(i, j int) bool {
|
||||
return recent[i].Timestamp.After(recent[j].Timestamp)
|
||||
})
|
||||
if len(recent) > 10 {
|
||||
recent = recent[:10]
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"total": len(events),
|
||||
"by_type": sortedMap(byType),
|
||||
"by_repo": sortedMap(byRepo),
|
||||
"by_agent": sortedMap(byAgent),
|
||||
"events": briefEvents(recent),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,3 +192,22 @@ func sortedMap(m map[string]int) []map[string]any {
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// briefEvents converts events into the compact shape used by metrics_query.
|
||||
func briefEvents(events []Event) []map[string]any {
|
||||
result := make([]map[string]any, len(events))
|
||||
for i, ev := range events {
|
||||
item := map[string]any{
|
||||
"type": ev.Type,
|
||||
"timestamp": ev.Timestamp,
|
||||
}
|
||||
if ev.AgentID != "" {
|
||||
item["agent_id"] = ev.AgentID
|
||||
}
|
||||
if ev.Repo != "" {
|
||||
item["repo"] = ev.Repo
|
||||
}
|
||||
result[i] = item
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -228,9 +228,9 @@ func TestSummary_Good_Empty(t *testing.T) {
|
|||
|
||||
func TestSummary_Good(t *testing.T) {
|
||||
events := []Event{
|
||||
{Type: "build", Repo: "core-php", AgentID: "agent-1"},
|
||||
{Type: "build", Repo: "core-php", AgentID: "agent-2"},
|
||||
{Type: "test", Repo: "core-api", AgentID: "agent-1"},
|
||||
{Type: "build", Repo: "core-php", AgentID: "agent-1", Timestamp: time.Date(2026, 3, 15, 9, 0, 0, 0, time.UTC)},
|
||||
{Type: "build", Repo: "core-php", AgentID: "agent-2", Timestamp: time.Date(2026, 3, 15, 10, 0, 0, 0, time.UTC)},
|
||||
{Type: "test", Repo: "core-api", AgentID: "agent-1", Timestamp: time.Date(2026, 3, 15, 11, 0, 0, 0, time.UTC)},
|
||||
}
|
||||
|
||||
s := Summary(events)
|
||||
|
|
@ -248,6 +248,39 @@ func TestSummary_Good(t *testing.T) {
|
|||
if byType[0]["key"] != "build" || byType[0]["count"] != 2 {
|
||||
t.Errorf("expected build:2 first, got %v:%v", byType[0]["key"], byType[0]["count"])
|
||||
}
|
||||
|
||||
recent, _ := s["events"].([]map[string]any)
|
||||
if len(recent) != 3 {
|
||||
t.Fatalf("expected 3 recent events, got %d", len(recent))
|
||||
}
|
||||
if recent[0]["type"] != "test" {
|
||||
t.Errorf("expected newest event first, got %v", recent[0]["type"])
|
||||
}
|
||||
if _, ok := recent[0]["timestamp"].(time.Time); !ok {
|
||||
t.Errorf("expected timestamp to be time.Time, got %T", recent[0]["timestamp"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummary_Good_RecentEventsLimit(t *testing.T) {
|
||||
events := make([]Event, 0, 12)
|
||||
for i := 0; i < 12; i++ {
|
||||
events = append(events, Event{
|
||||
Type: "type",
|
||||
Timestamp: time.Date(2026, 3, 15, 12, i, 0, 0, time.UTC),
|
||||
})
|
||||
}
|
||||
|
||||
s := Summary(events)
|
||||
recent, _ := s["events"].([]map[string]any)
|
||||
if len(recent) != 10 {
|
||||
t.Fatalf("expected 10 recent events, got %d", len(recent))
|
||||
}
|
||||
if recent[0]["timestamp"].(time.Time).Minute() != 11 {
|
||||
t.Errorf("expected newest event first, got minute %d", recent[0]["timestamp"].(time.Time).Minute())
|
||||
}
|
||||
if recent[9]["timestamp"].(time.Time).Minute() != 2 {
|
||||
t.Errorf("expected tenth newest event last, got minute %d", recent[9]["timestamp"].(time.Time).Minute())
|
||||
}
|
||||
}
|
||||
|
||||
// --- sortedMap ---
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
"dappco.re/go/core/ai/ai"
|
||||
"dappco.re/go/core/i18n"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -94,6 +94,23 @@ func runMetrics() error {
|
|||
cli.Blank()
|
||||
}
|
||||
|
||||
// Recent events
|
||||
if recent, ok := summary["events"].([]map[string]any); ok && len(recent) > 0 {
|
||||
cli.Print("%s\n", cli.DimStyle.Render("Recent events:"))
|
||||
for _, entry := range recent {
|
||||
ts, _ := entry["timestamp"].(time.Time)
|
||||
agent, _ := entry["agent_id"].(string)
|
||||
repo, _ := entry["repo"].(string)
|
||||
cli.Print(" %-20s %-24s %-20s %-20s\n",
|
||||
ts.Format(time.RFC3339),
|
||||
entry["type"],
|
||||
agent,
|
||||
repo,
|
||||
)
|
||||
}
|
||||
cli.Blank()
|
||||
}
|
||||
|
||||
if len(events) == 0 {
|
||||
cli.Text(i18n.T("cmd.ai.metrics.none_found"))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue