Skip to content

Commit fcf5e8c

Browse files
committed
perf: unify info panel and context window
1 parent d466761 commit fcf5e8c

13 files changed

Lines changed: 241 additions & 153 deletions

File tree

internal/agent/session_runtime.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/voocel/agentcore"
13+
agentctx "github.com/voocel/agentcore/context"
1314
"github.com/voocel/codebot/internal/config"
1415
"github.com/voocel/codebot/internal/provider"
1516
"github.com/voocel/codebot/internal/storage"
@@ -620,22 +621,26 @@ func (s *Session) updateContextFromRegistry(providerKey, modelID string) {
620621
if err == nil {
621622
entry, _, err := s.registry.Resolve(provType + "/" + modelID)
622623
if err == nil && entry.ContextWindow > 0 {
623-
s.agent.SetContextWindow(entry.ContextWindow)
624-
s.mu.Lock()
625-
s.settings.ContextWindow = entry.ContextWindow
626-
s.mu.Unlock()
624+
s.applyContextWindow(entry.ContextWindow)
627625
return
628626
}
629627
}
630628
entry, _, err := s.registry.Resolve(modelID)
631629
if err != nil || entry.ContextWindow <= 0 {
632-
// Fallback to bare model ID for custom providers.
633630
return
634631
}
635-
s.agent.SetContextWindow(entry.ContextWindow)
632+
s.applyContextWindow(entry.ContextWindow)
633+
}
634+
635+
func (s *Session) applyContextWindow(window int) {
636+
s.agent.SetContextWindow(window)
636637
s.mu.Lock()
637-
s.settings.ContextWindow = entry.ContextWindow
638+
s.settings.ContextWindow = window
639+
cm := s.contextManager
638640
s.mu.Unlock()
641+
if engine, ok := cm.(*agentctx.ContextEngine); ok {
642+
engine.SetContextWindow(window)
643+
}
639644
}
640645

641646
func (s *Session) SetThinkingLevel(level agentcore.ThinkingLevel) {

internal/bootstrap/assemble_session.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,10 @@ func resolveActiveModel(input *resolvedInput) (config.Resolved, string, agentcor
8989
}
9090

9191
settings := input.settings
92-
if settings.ContextWindow <= 0 {
93-
if entry, _, err := input.registry.Resolve(activeModel); err == nil && entry.ContextWindow > 0 {
94-
settings.ContextWindow = entry.ContextWindow
95-
} else {
96-
settings.ContextWindow = 128000
97-
}
92+
if entry, _, err := input.registry.Resolve(activeModel); err == nil && entry.ContextWindow > 0 {
93+
settings.ContextWindow = entry.ContextWindow
94+
} else if settings.ContextWindow <= 0 {
95+
settings.ContextWindow = 128000
9896
}
9997
settings.Provider = activeProvider
10098
settings.Model = activeModel

internal/config/settings.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ type Settings struct {
8989
SmallModel *string `json:"small_model,omitempty"` // sub-agent model; defaults to Model if empty
9090
Providers map[string]*ProviderConfig `json:"providers,omitempty"`
9191

92-
ContextWindow *int `json:"context_window,omitempty"`
93-
9492
ThinkingLevel *string `json:"thinking_level,omitempty"`
9593

9694
MaxTurns *int `json:"max_turns,omitempty"`
@@ -206,9 +204,6 @@ func (s Settings) Resolve() Resolved {
206204
if s.SmallModel != nil {
207205
r.SmallModel = *s.SmallModel
208206
}
209-
if s.ContextWindow != nil {
210-
r.ContextWindow = *s.ContextWindow
211-
}
212207
for k, v := range s.Providers {
213208
if v != nil {
214209
r.Providers[k] = *v
@@ -357,9 +352,6 @@ func mergeSettings(base, override Settings) Settings {
357352
if override.SmallModel != nil {
358353
base.SmallModel = override.SmallModel
359354
}
360-
if override.ContextWindow != nil {
361-
base.ContextWindow = override.ContextWindow
362-
}
363355
if len(override.Providers) > 0 {
364356
if base.Providers == nil {
365357
base.Providers = make(map[string]*ProviderConfig)

internal/ui/cmd_model.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,10 @@ func (c *ModelCommand) HandleKey(msg tea.KeyMsg) (bool, tea.Cmd) {
172172
}
173173
return true, func() tea.Msg {
174174
return tui.CommandResultMsg{
175-
Text: tui.SystemMsgStyle.Render("Switched to model: " + display),
176-
NewProvider: entry.provider,
177-
NewModel: entry.model,
175+
Text: tui.SystemMsgStyle.Render("Switched to model: " + display),
176+
NewProvider: entry.provider,
177+
NewModel: entry.model,
178+
NewContextWindow: c.app.Session.Settings().ContextWindow,
178179
}
179180
}
180181

internal/ui/command.go

Lines changed: 66 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,10 @@ func (a *App) cmdModel(args []string) tea.Cmd {
333333

334334
return func() tea.Msg {
335335
return tui.CommandResultMsg{
336-
Text: tui.SystemMsgStyle.Render(fmt.Sprintf("Switched to model: %s", resolved)),
337-
NewProvider: a.Session.Provider(),
338-
NewModel: a.Session.ModelName(),
336+
Text: tui.SystemMsgStyle.Render(fmt.Sprintf("Switched to model: %s", resolved)),
337+
NewProvider: a.Session.Provider(),
338+
NewModel: a.Session.ModelName(),
339+
NewContextWindow: a.Session.Settings().ContextWindow,
339340
}
340341
}
341342
}
@@ -423,38 +424,19 @@ func (a *App) cmdSettings() tea.Cmd {
423424
if thinking == "" {
424425
thinking = "(unset)"
425426
}
426-
apiKey := a.Session.APIKey()
427-
masked := maskKey(apiKey)
428427

429-
labelStyle := lipgloss.NewStyle().Foreground(tui.ColorMuted)
430-
valueStyle := lipgloss.NewStyle().Foreground(tui.ColorSoftText)
431-
metaStyle := lipgloss.NewStyle().Foreground(tui.ColorToken)
432-
433-
var sb strings.Builder
434-
renderRow := func(label, value string) {
435-
sb.WriteString(labelStyle.Render(fmt.Sprintf("%-16s", label)))
436-
sb.WriteString(" ")
437-
sb.WriteString(valueStyle.Render(value))
438-
sb.WriteString("\n")
439-
}
440-
renderMetaRow := func(label, value string) {
441-
sb.WriteString(labelStyle.Render(fmt.Sprintf("%-16s", label)))
442-
sb.WriteString(" ")
443-
sb.WriteString(metaStyle.Render(value))
444-
sb.WriteString("\n")
445-
}
446-
447-
renderRow("Provider", s.Provider)
448-
renderRow("Model", a.Session.ModelName())
449-
renderRow("API key", masked)
450-
renderRow("Base URL", baseURL)
451-
sb.WriteString("\n")
452-
renderRow("Thinking", thinking)
453-
renderRow("Context", tui.FormatTokens(s.ContextWindow))
454-
renderRow("Max turns", fmt.Sprintf("%d", s.MaxTurns))
455-
renderMetaRow("Config", config.SettingsPath(a.Cwd))
428+
p := tui.NewInfoPanel("Settings")
429+
p.Row("Provider", s.Provider)
430+
p.Row("Model", a.Session.ModelName())
431+
p.Row("API key", maskKey(a.Session.APIKey()))
432+
p.Row("Base URL", baseURL)
433+
p.Section("Runtime")
434+
p.Row("Thinking", thinking)
435+
p.Row("Context", tui.FormatTokens(s.ContextWindow))
436+
p.Row("Max turns", fmt.Sprintf("%d", s.MaxTurns))
437+
p.Hint("Config", config.SettingsPath(a.Cwd))
456438

457-
return tui.SendCommandResult(strings.TrimRight(sb.String(), "\n"))
439+
return tui.SendCommandResult(p.Render())
458440
}
459441

460442
func maskKey(key string) string {
@@ -1355,133 +1337,106 @@ func formatContextSnapshot(
13551337
lastCompaction agent.CompactionSnapshot,
13561338
hasCompaction bool,
13571339
) string {
1358-
labelStyle := lipgloss.NewStyle().Foreground(tui.ColorMuted)
1359-
valueStyle := lipgloss.NewStyle().Foreground(tui.ColorSoftText)
1360-
metaStyle := lipgloss.NewStyle().Foreground(tui.ColorToken)
1361-
warnStyle := lipgloss.NewStyle().Foreground(tui.ColorAccent)
1362-
1363-
var sb strings.Builder
1364-
renderSection := func(title, meta string) {
1365-
if sb.Len() > 0 {
1366-
sb.WriteString("\n")
1367-
}
1368-
sb.WriteString(title)
1369-
sb.WriteString("\n")
1370-
if meta != "" {
1371-
sb.WriteString(metaStyle.Render(meta))
1372-
sb.WriteString("\n")
1373-
}
1374-
}
1375-
renderRow := func(label, value string) {
1376-
sb.WriteString(labelStyle.Render(fmt.Sprintf("%-20s", label)))
1377-
sb.WriteString(" ")
1378-
sb.WriteString(valueStyle.Render(value))
1379-
sb.WriteString("\n")
1380-
}
1381-
renderMetaRow := func(label, value string) {
1382-
sb.WriteString(labelStyle.Render(fmt.Sprintf("%-20s", label)))
1383-
sb.WriteString(" ")
1384-
sb.WriteString(metaStyle.Render(value))
1385-
sb.WriteString("\n")
1386-
}
1340+
p := tui.NewInfoPanel("Context")
13871341

1388-
sb.WriteString("Context Snapshot\n")
1389-
renderSection("Live usage", "")
1342+
// Live usage
1343+
p.Section("Usage")
13901344
usage := contextUsage
13911345
if ok && snapshot != nil && snapshot.Usage != nil {
13921346
usage = snapshot.Usage
13931347
}
13941348
if usage != nil {
1395-
renderRow("Context used", fmt.Sprintf("%s (%.1f%%)", tui.FormatTokens(usage.Tokens), usage.Percent))
1396-
renderRow("Context window", tui.FormatTokens(usage.ContextWindow))
1397-
renderMetaRow("Usage detail", fmt.Sprintf("usage=%s, trailing=%s", tui.FormatTokens(usage.UsageTokens), tui.FormatTokens(usage.TrailingTokens)))
1349+
p.Row("Used", fmt.Sprintf("%s (%.1f%%)", tui.FormatTokens(usage.Tokens), usage.Percent))
1350+
p.Row("Window", tui.FormatTokens(usage.ContextWindow))
1351+
p.Hint("Detail", fmt.Sprintf("usage=%s, trailing=%s", tui.FormatTokens(usage.UsageTokens), tui.FormatTokens(usage.TrailingTokens)))
13981352
} else {
1399-
renderMetaRow("Context used", "(unavailable)")
1353+
p.Hint("Used", "(unavailable)")
14001354
}
14011355

1402-
// Token breakdown by category
1356+
// Token breakdown
14031357
if breakdown.Total > 0 {
1404-
renderSection("Token breakdown", "Estimated token distribution by category.")
14051358
window := breakdown.ContextWindow
14061359
fmtPct := func(tokens int) string {
14071360
if window <= 0 {
14081361
return tui.FormatTokens(tokens)
14091362
}
14101363
return fmt.Sprintf("%s (%.0f%%)", tui.FormatTokens(tokens), float64(tokens)/float64(window)*100)
14111364
}
1412-
renderRow("User text", fmtPct(breakdown.UserText))
1413-
renderRow("Assistant text", fmtPct(breakdown.AssistantText))
1414-
renderRow("Tool calls", fmtPct(breakdown.ToolCalls))
1415-
renderRow("Tool results", fmtPct(breakdown.ToolResults))
1365+
p.Section("Breakdown")
1366+
p.Row("User text", fmtPct(breakdown.UserText))
1367+
p.Row("Assistant text", fmtPct(breakdown.AssistantText))
1368+
p.Row("Tool calls", fmtPct(breakdown.ToolCalls))
1369+
p.Row("Tool results", fmtPct(breakdown.ToolResults))
14161370
if breakdown.Summaries > 0 {
1417-
renderRow("Summaries", fmtPct(breakdown.Summaries))
1371+
p.Row("Summaries", fmtPct(breakdown.Summaries))
14181372
}
14191373
if breakdown.Images > 0 {
1420-
renderRow("Images", fmtPct(breakdown.Images))
1374+
p.Row("Images", fmtPct(breakdown.Images))
14211375
}
14221376

14231377
if len(breakdown.TopTools) > 0 {
1424-
renderSection("Top tools", "Heaviest tools by token consumption.")
1378+
p.Section("Top tools")
14251379
for _, t := range breakdown.TopTools {
1426-
detail := fmt.Sprintf("calls=%s, results=%s", tui.FormatTokens(t.CallTokens), tui.FormatTokens(t.ResultTokens))
1427-
renderRow(t.Name, fmt.Sprintf("%s %s", fmtPct(t.Total), metaStyle.Render(detail)))
1380+
p.Row(t.Name, fmt.Sprintf("%s calls=%s results=%s", fmtPct(t.Total), tui.FormatTokens(t.CallTokens), tui.FormatTokens(t.ResultTokens)))
14281381
}
14291382
}
14301383
}
14311384

1385+
// Composition
14321386
if ok && snapshot != nil {
1433-
renderSection("Composition", "")
1434-
renderRow("Active scope", formatContextScope(snapshot.Scope))
1387+
p.Section("Composition")
1388+
p.Row("Scope", formatContextScope(snapshot.Scope))
14351389
if snapshot.TranscriptMessages != snapshot.ActiveMessages {
1436-
renderRow("Messages", fmt.Sprintf("%d active / %d transcript", snapshot.ActiveMessages, snapshot.TranscriptMessages))
1390+
p.Row("Messages", fmt.Sprintf("%d active / %d transcript", snapshot.ActiveMessages, snapshot.TranscriptMessages))
14371391
} else {
1438-
renderRow("Messages", fmt.Sprintf("%d", snapshot.ActiveMessages))
1392+
p.Row("Messages", fmt.Sprintf("%d", snapshot.ActiveMessages))
14391393
}
1440-
renderRow("Summary checkpoints", fmt.Sprintf("%d", snapshot.SummaryMessages))
1441-
renderRow("Tool result msgs", fmt.Sprintf("%d", snapshot.ToolMessages))
1442-
renderRow("Cleared results", fmt.Sprintf("%d", snapshot.ClearedToolResults))
1443-
renderRow("Trimmed blocks", fmt.Sprintf("%d", snapshot.TrimmedTextBlocks))
1394+
p.Row("Summaries", fmt.Sprintf("%d", snapshot.SummaryMessages))
1395+
p.Row("Cleared results", fmt.Sprintf("%d", snapshot.ClearedToolResults))
1396+
p.Row("Trimmed blocks", fmt.Sprintf("%d", snapshot.TrimmedTextBlocks))
14441397

1445-
renderSection("Last rewrite", "")
1398+
p.Section("Last rewrite")
14461399
strategy := prettyCompactionStrategy(snapshot.LastStrategy)
14471400
if strategy == "" {
14481401
strategy = "(none)"
14491402
}
1450-
renderMetaRow("Strategy", strategy)
1451-
renderRow("Changed", formatBool(snapshot.LastChanged))
1452-
renderMetaRow("Details", formatContextRewriteDetails(snapshot))
1403+
p.Hint("Strategy", strategy)
1404+
p.Row("Changed", formatBool(snapshot.LastChanged))
1405+
p.Hint("Details", formatContextRewriteDetails(snapshot))
14531406
} else {
1454-
renderSection("Composition", "")
1455-
renderMetaRow("Snapshot", "(unavailable)")
1407+
p.Section("Composition")
1408+
p.Hint("Snapshot", "(unavailable)")
14561409
}
14571410

1458-
renderSection("Compaction totals", "")
1459-
renderRow("Compactions total", fmt.Sprintf("%d", metrics.CompactionTotal))
1460-
renderRow("Compactions changed", fmt.Sprintf("%d", metrics.CompactionChanged))
1461-
renderRow("Compaction saved", tui.FormatTokens(metrics.CompactionSaved))
1462-
renderMetaRow("By kind", formatCompactionCounts(metrics.CompactionByKind))
1463-
renderMetaRow("Last compaction", formatLastCompaction(lastCompaction, hasCompaction))
1411+
// Compaction totals
1412+
p.Section("Compaction")
1413+
p.Row("Total", fmt.Sprintf("%d", metrics.CompactionTotal))
1414+
p.Row("Changed", fmt.Sprintf("%d", metrics.CompactionChanged))
1415+
p.Row("Saved", tui.FormatTokens(metrics.CompactionSaved))
1416+
p.Hint("By kind", formatCompactionCounts(metrics.CompactionByKind))
1417+
p.Hint("Last", formatLastCompaction(lastCompaction, hasCompaction))
14641418

14651419
// Suggestions
14661420
if len(suggestions) > 0 {
1467-
renderSection("Suggestions", "")
1421+
p.Section("Suggestions")
14681422
for _, s := range suggestions {
1469-
prefix := "i"
1470-
style := metaStyle
14711423
if s.Severity == "warning" {
1472-
prefix = "!"
1473-
style = warnStyle
1474-
}
1475-
line := fmt.Sprintf("[%s] %s", prefix, s.Message)
1476-
if s.Savings > 0 {
1477-
line += fmt.Sprintf(" (~%s saveable)", tui.FormatTokens(s.Savings))
1424+
msg := s.Message
1425+
if s.Savings > 0 {
1426+
msg += fmt.Sprintf(" (~%s saveable)", tui.FormatTokens(s.Savings))
1427+
}
1428+
p.Warn("!", msg)
1429+
} else {
1430+
msg := s.Message
1431+
if s.Savings > 0 {
1432+
msg += fmt.Sprintf(" (~%s saveable)", tui.FormatTokens(s.Savings))
1433+
}
1434+
p.Hint("i", msg)
14781435
}
1479-
sb.WriteString(style.Render(line))
1480-
sb.WriteString("\n")
14811436
}
14821437
}
14831438

1484-
return strings.TrimRight(sb.String(), "\n")
1439+
return p.Render()
14851440
}
14861441

14871442
func formatRunSummary(summary agentcore.RunSummary, ok bool) string {

internal/ui/command_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -866,18 +866,18 @@ func TestFormatContextSnapshot(t *testing.T) {
866866
)
867867

868868
for _, want := range []string{
869-
"Context Snapshot",
869+
"Context",
870870
"64.0k (50.0%)",
871871
"usage=32.0k, trailing=4.0k",
872-
"Token breakdown",
872+
"Breakdown",
873873
"User text",
874874
"Tool results",
875875
"Top tools",
876876
"read",
877877
"bash",
878878
"projected view",
879879
"12 active / 18 transcript",
880-
"Summary checkpoints",
880+
"Summaries",
881881
"Cleared results",
882882
"full summary",
883883
"compacted=9, kept=3, split-turn",

0 commit comments

Comments
 (0)