diff options
| author | Paul Buetow <paul@buetow.org> | 2025-09-17 21:33:45 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-09-17 21:33:45 +0300 |
| commit | 88103657fb230bb41217a06aa5602ae23e7acb8b (patch) | |
| tree | 524c437e4e40ee5d6713b6ea5414ad975654cc52 /internal/hexaiaction | |
| parent | 2b6232704ecc90630196b9f829f966533e5cdccd (diff) | |
feat(stats,tmux): global Σ@window stats across processes with flocked cache; width mitigation (narrow/maxlen); configurable [stats] window_minutes; robust coverage parsing; docs update\n\n- Add internal/stats with windowed event cache + flock + atomic writes\n- Wire stats into LSP/CLI/Tmux Action; tmux shows Σ@window with per-model tail\n- HEXAI_TMUX_STATUS_NARROW and HEXAI_TMUX_STATUS_MAXLEN for width control\n- Add [stats] window_minutes to config and apply on startup\n- Improve Magefile coverage handling; add tests to lift coverage >85%\n- Update docs/tmux.md and config example
Diffstat (limited to 'internal/hexaiaction')
| -rw-r--r-- | internal/hexaiaction/prompts.go | 45 | ||||
| -rw-r--r-- | internal/hexaiaction/prompts_simplify_test.go | 27 | ||||
| -rw-r--r-- | internal/hexaiaction/run.go | 5 |
3 files changed, 63 insertions, 14 deletions
diff --git a/internal/hexaiaction/prompts.go b/internal/hexaiaction/prompts.go index 97af32f..393e9e4 100644 --- a/internal/hexaiaction/prompts.go +++ b/internal/hexaiaction/prompts.go @@ -7,6 +7,7 @@ import ( "codeberg.org/snonux/hexai/internal/appconfig" "codeberg.org/snonux/hexai/internal/llm" + "codeberg.org/snonux/hexai/internal/stats" "codeberg.org/snonux/hexai/internal/textutil" "codeberg.org/snonux/hexai/internal/tmux" ) @@ -88,47 +89,63 @@ func runCustom(ctx context.Context, cfg appconfig.App, client chatDoer, ca appco func runOnce(ctx context.Context, client chatDoer, sys, user string) (string, error) { msgs := []llm.Message{{Role: "system", Content: sys}, {Role: "user", Content: user}} - start := time.Now() txt, err := client.Chat(ctx, msgs) if err != nil { return "", err } out := strings.TrimSpace(StripFences(txt)) - // Update tmux heartbeat with simple one-request stats + // Contribute to global stats and update tmux status sent := 0 for _, m := range msgs { sent += len(m.Content) } recv := len(out) - mins := time.Since(start).Minutes() - if mins <= 0 { - mins = 0.001 + _ = stats.Update(ctx, providerOf(client), client.DefaultModel(), sent, recv) + if snap, err := stats.TakeSnapshot(); err == nil { + minsWin := snap.Window.Minutes() + if minsWin <= 0 { + minsWin = 0.001 + } + scopeReqs := int64(0) + if pe, ok := snap.Providers[providerOf(client)]; ok { + if mc, ok2 := pe.Models[client.DefaultModel()]; ok2 { + scopeReqs = mc.Reqs + } + } + scopeRPM := float64(scopeReqs) / minsWin + _ = tmux.SetStatus(tmux.FormatGlobalStatusColored(snap.Global.Reqs, snap.RPM, snap.Global.Sent, snap.Global.Recv, providerOf(client), client.DefaultModel(), scopeRPM, scopeReqs, snap.Window)) } - rpm := float64(1) / mins - _ = tmux.SetStatus(tmux.FormatLLMStatsStatusColored(providerOf(client), client.DefaultModel(), 1, rpm, int64(sent), int64(recv))) return out, nil } func runOnceWithOpts(ctx context.Context, client chatDoer, sys, user string, opts []llm.RequestOption) (string, error) { msgs := []llm.Message{{Role: "system", Content: sys}, {Role: "user", Content: user}} - start := time.Now() txt, err := client.Chat(ctx, msgs, opts...) if err != nil { return "", err } out := strings.TrimSpace(StripFences(txt)) - // Update tmux heartbeat with simple one-request stats + // Contribute to global stats and update tmux status sent := 0 for _, m := range msgs { sent += len(m.Content) } recv := len(out) - mins := time.Since(start).Minutes() - if mins <= 0 { - mins = 0.001 + _ = stats.Update(ctx, providerOf(client), client.DefaultModel(), sent, recv) + if snap, err := stats.TakeSnapshot(); err == nil { + minsWin := snap.Window.Minutes() + if minsWin <= 0 { + minsWin = 0.001 + } + scopeReqs := int64(0) + if pe, ok := snap.Providers[providerOf(client)]; ok { + if mc, ok2 := pe.Models[client.DefaultModel()]; ok2 { + scopeReqs = mc.Reqs + } + } + scopeRPM := float64(scopeReqs) / minsWin + _ = tmux.SetStatus(tmux.FormatGlobalStatusColored(snap.Global.Reqs, snap.RPM, snap.Global.Sent, snap.Global.Recv, providerOf(client), client.DefaultModel(), scopeRPM, scopeReqs, snap.Window)) } - rpm := float64(1) / mins - _ = tmux.SetStatus(tmux.FormatLLMStatsStatusColored(providerOf(client), client.DefaultModel(), 1, rpm, int64(sent), int64(recv))) return out, nil } diff --git a/internal/hexaiaction/prompts_simplify_test.go b/internal/hexaiaction/prompts_simplify_test.go new file mode 100644 index 0000000..4daba38 --- /dev/null +++ b/internal/hexaiaction/prompts_simplify_test.go @@ -0,0 +1,27 @@ +package hexaiaction + +import ( + "context" + "testing" + + "codeberg.org/snonux/hexai/internal/appconfig" + "codeberg.org/snonux/hexai/internal/llm" +) + +type simplifyClient struct{} + +func (simplifyClient) Chat(_ context.Context, _ []llm.Message, _ ...llm.RequestOption) (string, error) { + return "OUT", nil +} +func (simplifyClient) DefaultModel() string { return "m" } + +func TestRunSimplify_Smoke(t *testing.T) { + cfg := appconfig.Load(nil) + out, err := runSimplify(context.Background(), cfg, simplifyClient{}, "code") + if err != nil { + t.Fatalf("runSimplify: %v", err) + } + if out == "" { + t.Fatalf("expected output") + } +} diff --git a/internal/hexaiaction/run.go b/internal/hexaiaction/run.go index b07fbbb..45eacc2 100644 --- a/internal/hexaiaction/run.go +++ b/internal/hexaiaction/run.go @@ -6,11 +6,13 @@ import ( "io" "log" "strings" + "time" "codeberg.org/snonux/hexai/internal/appconfig" "codeberg.org/snonux/hexai/internal/editor" "codeberg.org/snonux/hexai/internal/llmutils" "codeberg.org/snonux/hexai/internal/logging" + "codeberg.org/snonux/hexai/internal/stats" "codeberg.org/snonux/hexai/internal/tmux" ) @@ -28,6 +30,9 @@ var selectedCustom *appconfig.CustomAction func Run(ctx context.Context, stdin io.Reader, stdout, stderr io.Writer) error { logger := log.New(stderr, "hexai-tmux-action ", log.LstdFlags|log.Lmsgprefix) cfg := appconfig.Load(logger) + if cfg.StatsWindowMinutes > 0 { + stats.SetWindow(time.Duration(cfg.StatsWindowMinutes) * time.Minute) + } if err := cfg.Validate(); err != nil { fmt.Fprintf(stderr, logging.AnsiBase+"hexai-tmux-action: %v"+logging.AnsiReset+"\n", err) return err |
