feat(server,tui): session stats pane — metrics over the WS bus + Bubble Tea overlay
Surfaces the observability metrics (observability-spec §3-tier-2) in the Go TUI: a session-summary pane showing duration, token throughput per provider, tool time, approval latency per tier, failure counts, and the session wall-time accounting split. Press `S` (or palette → "session stats"). Mirrors the artifacts/config fetch pattern exactly — a WS request/response, not a new transport. Server-side reuses the already-tested MetricsInspectionService (one MetricsReport definition, two wires: REST `correx stats` + WS). Server: - ClientMessage.GetSessionStats + ServerMessage.SessionStats(@SerialName session.stats), reusing MetricsReport as the nested payload. - StreamQueries.sessionStats replays via MetricsInspectionService (pure read, replay-neutral); routed in GlobalStreamHandler. - ServerMessageSerializationTest golden pins the session.stats wire format. TUI (Go/Bubble Tea): - protocol: TypeSessionStats + StatsDto/nested structs + GetSessionStats encoder + golden decode test (cross-language contract). - OverlayStats pane (statsModal), `S` key + palette entry + footer hint, loading/cache-by-session state, bounded breakdown rows. - demo `stats` preview case for serverless visual verification.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package app
|
||||
|
||||
import "github.com/correx/tui-go/internal/protocol"
|
||||
|
||||
// PreviewFrame renders a single static frame for a named UI state at the given
|
||||
// terminal size. Used by cmd/preview to screenshot the look without a live
|
||||
// server. Not part of the runtime path.
|
||||
@@ -138,10 +140,57 @@ func PreviewFrame(kind string, w, h int) string {
|
||||
m.selectedID = "04a546aa"
|
||||
m.overlay = OverlayPalette
|
||||
m.paletteFilter = "to"
|
||||
|
||||
case "stats":
|
||||
m.connected = true
|
||||
m.currentModel = "llama-cpp:default"
|
||||
m.sessions = sampleSessions()
|
||||
m.selectedID = "04a546aa"
|
||||
m.sessionEntered = true
|
||||
m.overlay = OverlayStats
|
||||
m.statsFor = "04a546aa"
|
||||
m.stats = sampleStats()
|
||||
}
|
||||
return m.View()
|
||||
}
|
||||
|
||||
func sampleStats() *protocol.StatsDto {
|
||||
return &protocol.StatsDto{
|
||||
SessionID: "04a546aa",
|
||||
EventCount: 28,
|
||||
SessionDurationMs: 204_000,
|
||||
InferenceCount: 5,
|
||||
InferenceMs: 38_400,
|
||||
PromptTokens: 4120,
|
||||
CompletionTokens: 1860,
|
||||
TokensPerSecond: 48.4,
|
||||
PerProvider: []protocol.ProviderStatsDto{
|
||||
{Provider: "llama-cpp:qwen2.5-coder-14b", CompletedCount: 4, TotalLatencyMs: 33_200, PromptTokens: 3800, CompletionTokens: 1700, TokensPerSecond: 51.2},
|
||||
{Provider: "llama-cpp:default", CompletedCount: 1, TotalLatencyMs: 5200, PromptTokens: 320, CompletionTokens: 160, TokensPerSecond: 30.8},
|
||||
},
|
||||
ToolCount: 6,
|
||||
ToolMs: 4300,
|
||||
PerTool: []protocol.ToolStatsDto{
|
||||
{ToolName: "file_write", CompletedCount: 2, TotalDurationMs: 2600},
|
||||
{ToolName: "read_file", CompletedCount: 3, TotalDurationMs: 1200},
|
||||
{ToolName: "shell_exec", CompletedCount: 1, TotalDurationMs: 500},
|
||||
},
|
||||
ApprovalsRequested: 2,
|
||||
ApprovalsResolved: 2,
|
||||
ApprovalsPending: 0,
|
||||
ApprovalWaitMs: 45_000,
|
||||
AvgApprovalWaitMs: 22_500,
|
||||
PerTier: []protocol.TierApprovalStatsDto{
|
||||
{Tier: "T2", RequestedCount: 1, ResolvedCount: 1, TotalWaitMs: 15_000, AvgWaitMs: 15_000},
|
||||
{Tier: "T3", RequestedCount: 1, ResolvedCount: 1, TotalWaitMs: 30_000, AvgWaitMs: 30_000},
|
||||
},
|
||||
Failures: protocol.FailureMetricsDto{ToolFailures: 1},
|
||||
InferencePct: 18.8,
|
||||
ToolPct: 2.1,
|
||||
ApprovalWaitPct: 22.1,
|
||||
}
|
||||
}
|
||||
|
||||
func sampleWorkflows() []Workflow {
|
||||
return []Workflow{
|
||||
{ID: "healthcheck", Description: "ping endpoints and report status"},
|
||||
|
||||
Reference in New Issue
Block a user