diff options
| author | Paul Buetow <paul@buetow.org> | 2025-09-08 09:50:38 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-09-08 09:50:38 +0300 |
| commit | cead3ebde8f3aee0ef8677158d37f4d04c6629dc (patch) | |
| tree | eadf4928c13e4f1fd782e8e0955116a24cef1d27 /internal/lsp | |
| parent | 29b0da31acf02816ee9e8f1d5a1b9a0ad5993593 (diff) | |
tmux: colored LLM status with provider + stats; add start heartbeat for LSP/CLI/TUI; theme support via HEXAI_TMUX_STATUS_THEME and HEXAI_TMUX_STATUS_FG/BG; docs: update tmux options and add Helix+tmux quickstart
Diffstat (limited to 'internal/lsp')
| -rw-r--r-- | internal/lsp/handlers_init.go | 5 | ||||
| -rw-r--r-- | internal/lsp/handlers_utils.go | 50 |
2 files changed, 44 insertions, 11 deletions
diff --git a/internal/lsp/handlers_init.go b/internal/lsp/handlers_init.go index ac1d566..ba00333 100644 --- a/internal/lsp/handlers_init.go +++ b/internal/lsp/handlers_init.go @@ -6,6 +6,7 @@ import ( "codeberg.org/snonux/hexai/internal" "codeberg.org/snonux/hexai/internal/logging" + tmx "codeberg.org/snonux/hexai/internal/tmux" ) func (s *Server) handleInitialize(req Request) { @@ -29,6 +30,10 @@ func (s *Server) handleInitialize(req Request) { func (s *Server) handleInitialized() { logging.Logf("lsp ", "client initialized") + // Emit an initial tmux heartbeat with provider/model + if s.llmClient != nil { + _ = tmx.SetStatus(tmx.FormatLLMStartStatus(s.llmClient.Name(), s.llmClient.DefaultModel())) + } } func (s *Server) handleShutdown(req Request) { diff --git a/internal/lsp/handlers_utils.go b/internal/lsp/handlers_utils.go index 7f116cd..15f0174 100644 --- a/internal/lsp/handlers_utils.go +++ b/internal/lsp/handlers_utils.go @@ -2,13 +2,14 @@ package lsp import ( - "strings" - "time" + "context" + "strings" + "time" - "codeberg.org/snonux/hexai/internal/llm" - "codeberg.org/snonux/hexai/internal/logging" - "codeberg.org/snonux/hexai/internal/textutil" - tmx "codeberg.org/snonux/hexai/internal/tmux" + "codeberg.org/snonux/hexai/internal/llm" + "codeberg.org/snonux/hexai/internal/logging" + "codeberg.org/snonux/hexai/internal/textutil" + tmx "codeberg.org/snonux/hexai/internal/tmux" ) // Configurable inline trigger characters (default to '>') used by free helpers below. @@ -61,11 +62,14 @@ func (s *Server) logLLMStats() { rpm := 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 - if s.llmClient != nil { - _ = tmx.SetStatus("LLM:" + s.llmClient.DefaultModel()) - } + 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() + provider := s.llmClient.Name() + status := tmx.FormatLLMStatsStatusColored(provider, model, reqs, rpm, sentTot, recvTot) + _ = tmx.SetStatus(status) + } } // Completion prompt builders and filters @@ -127,6 +131,30 @@ func isIdentChar(ch byte) bool { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' } +// chatWithStats wraps llmClient.Chat to increment counters and emit a tmux heartbeat. +func (s *Server) chatWithStats(ctx context.Context, msgs []llm.Message, opts ...llm.RequestOption) (string, error) { + // Count bytes sent + sent := 0 + for _, m := range msgs { + sent += len(m.Content) + } + s.incSentCounters(sent) + // Debounce/throttle if configured (reuse completion gates) + s.waitForDebounce(ctx) + if !s.waitForThrottle(ctx) { + return "", context.Canceled + } + // Perform request + txt, err := s.llmClient.Chat(ctx, msgs, opts...) + if err != nil { + s.logLLMStats() + return "", err + } + s.incRecvCounters(len(txt)) + s.logLLMStats() + return txt, nil +} + // Inline prompt utilities func lineHasInlinePrompt(line string) bool { if _, _, _, ok := findStrictInlineTag(line); ok { |
