feat: correx-managed model lifecycle slice 5 — Go TUI model swap + telemetry
Go TUI decodes model.changed / model.list / resource.status and renders a status-bar VRAM/GPU%/RAM gauge plus a models overlay (m key / palette 'models'): lists configured models with the resident one marked, enter swaps (SwapModel), c clears the pin (ClearModelPin). All three new server messages are non-event-bearing control/gauge frames, applied immediately like provider.status_changed. Server addition required for the picker: ServerMessage.ModelList (model.list, NonEventMessage) sent once in the initial snapshot from ManagedInferenceRouter.availableModelIds()/currentModelId() — the TUI otherwise cannot enumerate swap targets. Completes the 5-slice model-lifecycle feature. ./gradlew check green; go build/vet clean. Plan: docs/plans/2026-05-31-model-lifecycle-management.md (slice 5 of 5).
This commit is contained in:
@@ -81,6 +81,9 @@ func (m Model) renderStatus() string {
|
||||
left := strings.Join(parts, sep)
|
||||
|
||||
var right []string
|
||||
if g := m.gaugeText(); g != "" {
|
||||
right = append(right, span(g, t.P.Faint))
|
||||
}
|
||||
if s := m.session(m.selectedID); s != nil && s.Active {
|
||||
right = append(right, lipgloss.NewStyle().Foreground(t.P.Accent).Background(bg).Render(m.spinner())+span(" active", t.P.Accent2))
|
||||
}
|
||||
@@ -93,6 +96,21 @@ func (m Model) renderStatus() string {
|
||||
return m.justify(left, strings.Join(right, span(" · ", t.P.Faint)), m.width, bg)
|
||||
}
|
||||
|
||||
// gaugeText renders the live VRAM/GPU/RAM gauge, omitting any unavailable field.
|
||||
func (m Model) gaugeText() string {
|
||||
var parts []string
|
||||
if m.gpuUsedMB != nil && m.gpuTotalMB != nil {
|
||||
parts = append(parts, "VRAM "+itoa(int(*m.gpuUsedMB))+"/"+itoa(int(*m.gpuTotalMB))+"M")
|
||||
}
|
||||
if m.gpuUtil != nil {
|
||||
parts = append(parts, "GPU "+itoa(*m.gpuUtil)+"%")
|
||||
}
|
||||
if m.ramMB != nil {
|
||||
parts = append(parts, "RAM "+itoa(int(*m.ramMB))+"M")
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
var spinnerFrames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
|
||||
|
||||
func (m Model) spinner() string { return spinnerFrames[m.frame%len(spinnerFrames)] }
|
||||
@@ -121,7 +139,7 @@ func (m Model) renderFooter() string {
|
||||
case m.displayState() == StateIdle:
|
||||
hints = []string{hint("i", "name"), hint("/", "filter"), hint("enter", "open"), hint("jk", "move"), hint("w", "workflows"), hint("p", "cmds"), hint("q", "quit")}
|
||||
default: // StateInSession
|
||||
hints = []string{hint("i", "message"), hint("e", "events"), hint("t", "tools"), hint("s", "mode"), hint("l", "back"), hint("p", "cmds"), hint("q", "quit")}
|
||||
hints = []string{hint("i", "message"), hint("e", "events"), hint("t", "tools"), hint("m", "model"), hint("s", "mode"), hint("l", "back"), hint("p", "cmds"), hint("q", "quit")}
|
||||
}
|
||||
left := strings.Join(hints, gap)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user