summaryrefslogtreecommitdiff
path: root/internal/lsp
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-09-17 21:33:45 +0300
committerPaul Buetow <paul@buetow.org>2025-09-17 21:33:45 +0300
commit88103657fb230bb41217a06aa5602ae23e7acb8b (patch)
tree524c437e4e40ee5d6713b6ea5414ad975654cc52 /internal/lsp
parent2b6232704ecc90630196b9f829f966533e5cdccd (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/lsp')
-rw-r--r--internal/lsp/handlers_completion.go5
-rw-r--r--internal/lsp/handlers_utils.go31
2 files changed, 30 insertions, 6 deletions
diff --git a/internal/lsp/handlers_completion.go b/internal/lsp/handlers_completion.go
index 14c5f3e..9ef62f1 100644
--- a/internal/lsp/handlers_completion.go
+++ b/internal/lsp/handlers_completion.go
@@ -10,6 +10,7 @@ import (
"codeberg.org/snonux/hexai/internal/llm"
"codeberg.org/snonux/hexai/internal/logging"
+ "codeberg.org/snonux/hexai/internal/stats"
)
func (s *Server) handleCompletion(req Request) {
@@ -257,6 +258,10 @@ func (s *Server) tryProviderNativeCompletion(current string, p CompletionParams,
// Update counters and heartbeat
s.incSentCounters(sentBytes)
s.incRecvCounters(len(suggestions[0]))
+ // Contribute to global stats (provider-native path)
+ if s.llmClient != nil {
+ _ = stats.Update(ctx2, s.llmClient.Name(), s.llmClient.DefaultModel(), sentBytes, len(suggestions[0]))
+ }
s.logLLMStats()
cleaned := strings.TrimSpace(suggestions[0])
if cleaned != "" {
diff --git a/internal/lsp/handlers_utils.go b/internal/lsp/handlers_utils.go
index 15f0174..43bfdc8 100644
--- a/internal/lsp/handlers_utils.go
+++ b/internal/lsp/handlers_utils.go
@@ -8,6 +8,7 @@ import (
"codeberg.org/snonux/hexai/internal/llm"
"codeberg.org/snonux/hexai/internal/logging"
+ "codeberg.org/snonux/hexai/internal/stats"
"codeberg.org/snonux/hexai/internal/textutil"
tmx "codeberg.org/snonux/hexai/internal/tmux"
)
@@ -59,15 +60,29 @@ func (s *Server) logLLMStats() {
if mins <= 0 {
mins = 0.001
}
- rpm := float64(reqs) / mins
+ rpmLocal := float64(reqs) / mins
sentPerMin := float64(sentTot) / mins
recvPerMin := float64(recvTot) / mins
- logging.Logf("lsp ", "llm stats reqs=%d avg_sent=%d avg_recv=%d sent_total=%d recv_total=%d rpm=%.2f sent_per_min=%.0f recv_per_min=%.0f", reqs, avgSent, avgRecv, sentTot, recvTot, rpm, sentPerMin, recvPerMin)
- // Best-effort tmux status update with a compact stats heartbeat
- if s.llmClient != nil {
- model := s.llmClient.DefaultModel()
+ // Log local process counters
+ logging.Logf("lsp ", "llm stats (local) reqs=%d avg_sent=%d avg_recv=%d sent_total=%d recv_total=%d rpm=%.2f sent_per_min=%.0f recv_per_min=%.0f", reqs, avgSent, avgRecv, sentTot, recvTot, rpmLocal, sentPerMin, recvPerMin)
+ // Global snapshot for tmux status
+ snap, err := stats.TakeSnapshot()
+ if err == nil && s.llmClient != nil {
provider := s.llmClient.Name()
- status := tmx.FormatLLMStatsStatusColored(provider, model, reqs, rpm, sentTot, recvTot)
+ model := s.llmClient.DefaultModel()
+ // Per-scope rpm estimated from window
+ scopeReqs := int64(0)
+ if pe, ok := snap.Providers[provider]; ok {
+ if mc, ok2 := pe.Models[model]; ok2 {
+ scopeReqs = mc.Reqs
+ }
+ }
+ minsWin := snap.Window.Minutes()
+ if minsWin <= 0 {
+ minsWin = 0.001
+ }
+ scopeRPM := float64(scopeReqs) / minsWin
+ status := tmx.FormatGlobalStatusColored(snap.Global.Reqs, snap.RPM, snap.Global.Sent, snap.Global.Recv, provider, model, scopeRPM, scopeReqs, snap.Window)
_ = tmx.SetStatus(status)
}
}
@@ -151,6 +166,10 @@ func (s *Server) chatWithStats(ctx context.Context, msgs []llm.Message, opts ...
return "", err
}
s.incRecvCounters(len(txt))
+ // Update global stats cache
+ if s.llmClient != nil {
+ _ = stats.Update(ctx, s.llmClient.Name(), s.llmClient.DefaultModel(), sent, len(txt))
+ }
s.logLLMStats()
return txt, nil
}