diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-16 04:25:26 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-16 04:25:26 +0200 |
| commit | f55a1e88ea5948582d0e5a33efea0c5d806e1f4b (patch) | |
| tree | a180dc8c0266c87b0bc8d05a8e9c18d18748d751 | |
| parent | 7dd1bfa797b462791dc398690f599a2c5e5d1962 (diff) | |
Add Snapshot.ScopeReqs/ScopeRPM and simplify 3 callers
Centralizes the provider+model map traversal and window-minutes guard
that was duplicated in hexaiaction, hexaicli, and lsp.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| -rw-r--r-- | internal/hexaiaction/prompts.go | 18 | ||||
| -rw-r--r-- | internal/hexaicli/run.go | 12 | ||||
| -rw-r--r-- | internal/lsp/handlers_utils.go | 14 | ||||
| -rw-r--r-- | internal/stats/stats.go | 25 | ||||
| -rw-r--r-- | internal/stats/stats_test.go | 47 |
5 files changed, 81 insertions, 35 deletions
diff --git a/internal/hexaiaction/prompts.go b/internal/hexaiaction/prompts.go index 61775d4..8cf8c48 100644 --- a/internal/hexaiaction/prompts.go +++ b/internal/hexaiaction/prompts.go @@ -129,20 +129,12 @@ func runOnce(ctx context.Context, client chatDoer, sys, user string, req request if model == "" { model = client.DefaultModel() } - _ = stats.Update(ctx, providerOf(client), model, sent, recv) + provider := providerOf(client) + _ = stats.Update(ctx, provider, model, 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[model]; 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), model, scopeRPM, scopeReqs, snap.Window)) + scopeReqs := snap.ScopeReqs(provider, model) + scopeRPM := snap.ScopeRPM(provider, model) + _ = tmux.SetStatus(tmux.FormatGlobalStatusColored(snap.Global.Reqs, snap.RPM, snap.Global.Sent, snap.Global.Recv, provider, model, scopeRPM, scopeReqs, snap.Window)) } return out, nil } diff --git a/internal/hexaicli/run.go b/internal/hexaicli/run.go index 1563f5e..806f824 100644 --- a/internal/hexaicli/run.go +++ b/internal/hexaicli/run.go @@ -593,16 +593,8 @@ func summarizeChatRun(ctx context.Context, client llm.Client, model string, msgs if err == nil { summary.snapshot = snap } - minsWin := summary.snapshot.Window.Minutes() - if minsWin <= 0 { - minsWin = 0.001 - } - if pe, ok := summary.snapshot.Providers[client.Name()]; ok { - if mc, ok2 := pe.Models[model]; ok2 { - summary.scopeReq = mc.Reqs - } - } - summary.scopeRPM = float64(summary.scopeReq) / minsWin + summary.scopeReq = summary.snapshot.ScopeReqs(client.Name(), model) + summary.scopeRPM = summary.snapshot.ScopeRPM(client.Name(), model) return summary } diff --git a/internal/lsp/handlers_utils.go b/internal/lsp/handlers_utils.go index 7fb17d2..408fdb1 100644 --- a/internal/lsp/handlers_utils.go +++ b/internal/lsp/handlers_utils.go @@ -170,18 +170,8 @@ func (s *Server) logLLMStats(model string) { if modelName == "" { modelName = client.DefaultModel() } - // Per-scope rpm estimated from window - scopeReqs := int64(0) - if pe, ok := snap.Providers[provider]; ok { - if mc, ok2 := pe.Models[modelName]; ok2 { - scopeReqs = mc.Reqs - } - } - minsWin := snap.Window.Minutes() - if minsWin <= 0 { - minsWin = 0.001 - } - scopeRPM := float64(scopeReqs) / minsWin + scopeReqs := snap.ScopeReqs(provider, modelName) + scopeRPM := snap.ScopeRPM(provider, modelName) s.emitGlobalStatus(snap.Global.Reqs, snap.RPM, snap.Global.Sent, snap.Global.Recv, provider, modelName, scopeRPM, scopeReqs, snap.Window) } } diff --git a/internal/stats/stats.go b/internal/stats/stats.go index 939e1aa..742a5be 100644 --- a/internal/stats/stats.go +++ b/internal/stats/stats.go @@ -75,6 +75,31 @@ type Snapshot struct { Window time.Duration } +// ScopeReqs returns the request count for a specific provider+model pair. +// Returns 0 when the provider or model is not present in the snapshot. +func (s Snapshot) ScopeReqs(provider, model string) int64 { + if pe, ok := s.Providers[provider]; ok { + if mc, ok2 := pe.Models[model]; ok2 { + return mc.Reqs + } + } + return 0 +} + +// ScopeRPM returns the requests-per-minute for a specific provider+model +// pair, derived from ScopeReqs and the snapshot's sliding window. +func (s Snapshot) ScopeRPM(provider, model string) float64 { + reqs := s.ScopeReqs(provider, model) + if reqs == 0 { + return 0 + } + mins := s.Window.Minutes() + if mins <= 0 { + mins = 0.001 + } + return float64(reqs) / mins +} + // Update appends one event and prunes old entries under lock. func Update(ctx context.Context, provider, model string, sentBytes, recvBytes int) error { dir, err := CacheDir() diff --git a/internal/stats/stats_test.go b/internal/stats/stats_test.go index 45f9e2a..a9b3d22 100644 --- a/internal/stats/stats_test.go +++ b/internal/stats/stats_test.go @@ -334,3 +334,50 @@ func TestUpdate_CancelledContext(t *testing.T) { t.Fatal("expected error from cancelled context, got nil") } } + +func TestSnapshot_ScopeReqs(t *testing.T) { + snap := Snapshot{ + Providers: map[string]ProviderEntry{ + "openai": {Models: map[string]Counters{"gpt-5.0": {Reqs: 42}}}, + }, + } + if got := snap.ScopeReqs("openai", "gpt-5.0"); got != 42 { + t.Fatalf("expected 42, got %d", got) + } + if got := snap.ScopeReqs("openai", "gpt-4.1"); got != 0 { + t.Fatalf("expected 0 for missing model, got %d", got) + } + if got := snap.ScopeReqs("anthropic", "gpt-5.0"); got != 0 { + t.Fatalf("expected 0 for missing provider, got %d", got) + } +} + +func TestSnapshot_ScopeRPM(t *testing.T) { + snap := Snapshot{ + Providers: map[string]ProviderEntry{ + "openai": {Models: map[string]Counters{"gpt-5.0": {Reqs: 60}}}, + }, + Window: time.Hour, + } + rpm := snap.ScopeRPM("openai", "gpt-5.0") + if rpm != 1.0 { + t.Fatalf("expected 1.0 rpm, got %v", rpm) + } + // Missing model should return 0 + if rpm := snap.ScopeRPM("openai", "missing"); rpm != 0 { + t.Fatalf("expected 0 rpm for missing, got %v", rpm) + } +} + +func TestSnapshot_ScopeRPM_ZeroWindow(t *testing.T) { + snap := Snapshot{ + Providers: map[string]ProviderEntry{ + "openai": {Models: map[string]Counters{"gpt-5.0": {Reqs: 10}}}, + }, + Window: 0, // edge case + } + rpm := snap.ScopeRPM("openai", "gpt-5.0") + if rpm <= 0 { + t.Fatalf("expected positive rpm even with zero window, got %v", rpm) + } +} |
