-
-
Notifications
You must be signed in to change notification settings - Fork 0
Improve terminal input status layout #152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,10 +63,14 @@ | |
| AppConfig *api.Config // persistent user preferences | ||
| History []api.Message | ||
| Usage api.TokenUsage | ||
| Logger *sysutil.Logger | ||
| Ollama *OllamaClient // optional self-hosted LLM | ||
| Mode OllamaMode // off, chat, or full | ||
| Cerebras *CerebrasClient // optional Cerebras backend; when set, owns the whole turn | ||
| // LastContextTokens is the input-token count reported by the most recent | ||
|
Check notice on line 66 in internal/agent/agent.go
|
||
| // model request. Unlike Usage.InputTokens, it is not cumulative and can be | ||
| // used to estimate the currently occupied context window in the TUI. | ||
| LastContextTokens int | ||
| Logger *sysutil.Logger | ||
| Ollama *OllamaClient // optional self-hosted LLM | ||
| Mode OllamaMode // off, chat, or full | ||
| Cerebras *CerebrasClient // optional Cerebras backend; when set, owns the whole turn | ||
|
|
||
| tools []api.ToolDef | ||
| client *http.Client | ||
|
|
@@ -126,6 +130,7 @@ | |
| // ClearHistory resets conversation history. | ||
| func (a *Agent) ClearHistory() { | ||
| a.History = []api.Message{} | ||
| a.LastContextTokens = 0 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Low-confidence finding — expand to read
More Info
Example: Suggested fix: // In ClearHistory doc comment: // Resets the full conversation state. Per-turn context resets are handled by Run/runStreamingLoop.Prompt To Fix With AI |
||
| } | ||
|
|
||
| // CancelCurrent cancels the current streaming request if one is in progress. | ||
|
|
@@ -141,6 +146,9 @@ | |
| // Run executes a prompt through the agent loop and returns the final text response. | ||
| // Used for non-interactive (one-shot) mode. | ||
| func (a *Agent) Run(prompt string) (string, error) { | ||
| // This is a new top-level turn; do not show a prior request's context size | ||
| // if this request fails before the provider reports usage. | ||
| a.LastContextTokens = 0 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Example: Suggested fix: // At the early return: // LastContextTokens intentionally remains 0 — this turn produced no usage.Why this wasn't caught: No test exercises the More Info
Prompt To Fix With AI |
||
| a.History = append(a.History, api.Message{ | ||
| Role: "user", | ||
| Content: prompt, | ||
|
|
@@ -327,6 +335,11 @@ | |
| } | ||
|
|
||
| func (a *Agent) runStreamingLoop(term *tui.Terminal) (string, error) { | ||
| // All backends share this top-level turn boundary, including Ollama. Ollama's | ||
| // streaming protocol does not expose usage, so leaving this at zero hides the | ||
| // context-fill metric instead of carrying a stale value from another backend. | ||
| a.LastContextTokens = 0 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
More Info
Prompt To Fix With AI |
||
|
|
||
| // Cerebras backend owns the entire turn via its own native function-calling | ||
| // loop (full tool set). No Claude fallback — the user has no Anthropic key. | ||
| if a.Cerebras != nil { | ||
|
|
@@ -654,6 +667,7 @@ | |
| var ev sseMessageStart | ||
| if err := json.Unmarshal([]byte(rawData), &ev); err == nil { | ||
| a.Usage.InputTokens += ev.Message.Usage.InputTokens | ||
| a.LastContextTokens = ev.Message.Usage.InputTokens | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The CC backend ( Example: Suggested fix: a.LastContextTokens = 0Why this wasn't caught: No test exercises the LastContextTokens value after a failed API call. More Info
Prompt To Fix With AI |
||
| a.Usage.Requests++ | ||
| if a.Cfg.Verbose { | ||
| fmt.Printf("[SSE] message_start: input_tokens=%d\n", ev.Message.Usage.InputTokens) | ||
|
|
@@ -813,6 +827,7 @@ | |
|
|
||
| // Track usage | ||
| a.Usage.InputTokens += apiResp.Usage.InputTokens | ||
| a.LastContextTokens = apiResp.Usage.InputTokens | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The CC backend resets its per-turn usage counters at the top of Example: Suggested fix: a.LastContextTokens = 0Why this wasn't caught: No test exercises the LastContextTokens value after a failed direct API call. More Info
Prompt To Fix With AI |
||
| a.Usage.OutputTokens += apiResp.Usage.OutputTokens | ||
| a.Usage.Requests++ | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,6 +68,7 @@ func (a *Agent) RunCerebrasAgent(term *tui.Terminal) (string, bool) { | |
|
|
||
| choice := resp.Choices[0] | ||
| a.Usage.InputTokens += resp.Usage.PromptTokens | ||
| a.LastContextTokens = resp.Usage.PromptTokens | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The CC backend resets its per-turn usage counters at the top of Example: Suggested fix: a.LastContextTokens = 0Why this wasn't caught: No test exercises the LastContextTokens value after a failed Cerebras call. More Info
Prompt To Fix With AI |
||
| a.Usage.OutputTokens += resp.Usage.CompletionTokens | ||
| a.Usage.Requests++ | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -162,6 +162,42 @@ | |
|
|
||
| var inputHistory []string | ||
| var lastCtrlC time.Time | ||
| sessionStarted := time.Now() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The More Info
Prompt To Fix With AI |
||
| var lastTask string | ||
| var lastTurnDur time.Duration | ||
| var lastContextTokens int | ||
|
|
||
| inputStatus := func() *tui.StatusInfo { | ||
| backend := "api" | ||
| if ag.Cfg.Context != nil && ag.Cfg.Context.Backend != "" { | ||
| backend = ag.Cfg.Context.Backend | ||
| } | ||
| model := ag.Cfg.Model | ||
| effort, permissionMode := "", "" | ||
| if ag.AppConfig != nil { | ||
| if ag.AppConfig.ModelOverride != "" { | ||
| model = ag.AppConfig.ModelOverride | ||
| } | ||
| effort = ag.AppConfig.Effort | ||
| if backend != "api" { | ||
| permissionMode = ag.AppConfig.OrchPermissionMode | ||
| } | ||
| } | ||
| return &tui.StatusInfo{ | ||
| Backend: backend, | ||
| Model: model, | ||
| Effort: effort, | ||
| PermissionMode: permissionMode, | ||
| OutputVerbose: ag.Cfg.OutputVerbose, | ||
| Task: lastTask, | ||
| TokensIn: ag.Usage.InputTokens, | ||
| TokensOut: ag.Usage.OutputTokens, | ||
| ContextUsed: lastContextTokens, | ||
| ContextWindow: api.ContextWindow(model), | ||
| LastTurnDur: lastTurnDur, | ||
| SessionStarted: sessionStarted, | ||
| } | ||
| } | ||
|
|
||
| // Prompt consent and open cloud session at startup (idempotent — safe to call again later). | ||
| startCloudSession() | ||
|
|
@@ -184,7 +220,7 @@ | |
| fmt.Println() | ||
| inputHistory = append(inputHistory, input) | ||
| } else { | ||
| result := tui.ReadInput(term.Prompt(), inputHistory, ag.Cfg.OutputVerbose) | ||
| result := tui.ReadInput(term.Prompt(), inputHistory, ag.Cfg.OutputVerbose, inputStatus()) | ||
|
|
||
| if result.OutputToggle { | ||
| toggleOutputVerbose(ag, cliAgent, term) | ||
|
|
@@ -222,6 +258,7 @@ | |
| continue | ||
| case input == "/clear": | ||
| ag.ClearHistory() | ||
| lastContextTokens = 0 | ||
| if oc, ok := cliAgent.(*agent.OpenCodeAgent); ok { | ||
| oc.ClearSession() | ||
| } | ||
|
|
@@ -1058,6 +1095,11 @@ | |
| // Normal mode: use direct Anthropic API. | ||
| var llmResult string | ||
| var err error | ||
| // Clear the UI's per-turn estimate before starting any backend. This also | ||
| // covers CLI backends, whose usage is reported outside Agent.RunStreaming. | ||
| lastContextTokens = 0 | ||
|
Comment on lines
+1098
to
+1100
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
More Info
Prompt To Fix With AIThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The REPL's main turn loop resets Example: Suggested fix: // After the CLI error path, before lastTurnDur:
// lastContextTokens intentionally remains 0 — this turn produced no usage.Why this wasn't caught: No test exercises a failed CLI-backend turn and asserts the context-token fields remain zero. More Info
Prompt To Fix With AI |
||
| ag.LastContextTokens = 0 | ||
| turnStarted := time.Now() | ||
|
|
||
| // In CC/Codex mode, start a pre-connect goroutine that watches for a | ||
| // live_browser_url in the side-channel file every second. When one | ||
|
|
@@ -1070,15 +1112,15 @@ | |
| stream *vnc.VNCStream // nil on dial failure | ||
| } | ||
| preConnChan := make(chan preConnResult, 1) | ||
| // watchCtx is only used to stop the polling ticker (abort a pending | ||
| // drainLiveURLFromChild loop). It is intentionally NOT passed to | ||
| // DialVNC — the stream needs a context that outlives the goroutine. | ||
| watchCtx, watchCancel := context.WithCancel(context.Background()) | ||
| defer watchCancel() // ensures cancel on any return/continue path | ||
| if cliAgent != nil { | ||
| go func() { | ||
| // Do NOT defer watchCancel here — it is called by the main | ||
| // goroutine after the agent turn ends. If we cancelled it here | ||
|
Check warning on line 1123 in internal/repl/repl.go
|
||
| // on exit, DialVNC (which uses context.Background()) is fine, | ||
| // but stopping the ticker via Done() would still work. | ||
| ticker := time.NewTicker(1 * time.Second) | ||
|
|
@@ -1123,6 +1165,15 @@ | |
| llmResult, err = cliAgent.Run(cleanInput, term) | ||
| term.StopThinking() | ||
| if err == nil { | ||
| if stats, ok := cliAgent.(agent.TurnStatsProvider); ok { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The More Info
Prompt To Fix With AI
Comment on lines
1165
to
+1168
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Low-confidence finding — expand to readWhen a CLI agent (CC/Codex) implements More Info
Prompt To Fix With AI |
||
| if inputTokens, outputTokens, reported := stats.LastTurnStats(); reported { | ||
| ag.Usage.InputTokens += inputTokens | ||
| ag.Usage.OutputTokens += outputTokens | ||
| ag.Usage.Requests++ | ||
| lastContextTokens = inputTokens | ||
| ag.LastContextTokens = inputTokens | ||
| } | ||
| } | ||
| // Mirror the turn into ag.History so autoSave records it. | ||
| // agent.CCAgent/agent.CodexAgent manage their own subprocess state; qmax's | ||
| // history would otherwise stay empty and autoSave would no-op. | ||
|
|
@@ -1144,6 +1195,13 @@ | |
| } else { | ||
| llmResult, err = ag.RunStreaming(input, term) | ||
| } | ||
| lastTurnDur = time.Since(turnStarted) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
After a failed turn (err != nil), lastContextTokens is not updated, so the status panel continues to show the context usage from the previous successful turn. This is misleading — the user sees a context-fill percentage that does not reflect the current state. Fix: set lastContextTokens = 0 in the error path, matching the reset done at the start of each turn. More Info
Prompt To Fix With AI |
||
| if err == nil { | ||
| lastTask = cleanInput | ||
| if cliAgent == nil { | ||
| lastContextTokens = ag.LastContextTokens | ||
| } | ||
| } | ||
|
|
||
| // Stop queue reader and wait for the goroutine to exit before we | ||
| // touch stdin again (either via the queue loop or tui.ReadInput). | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LastContextTokensafter a turnThe diff establishes a convention: every backend that reports per-turn input tokens sets
Agent.LastContextTokensso the TUI can estimate context-window fill. The Anthropic streaming path (line 662), the non-streaming API path (line 822), and the Cerebras path (cerebras_agent.goline 71) all seta.LastContextTokens = <per-turn input tokens>. The Ollama path inagent.godoes not, so after an Ollama turnLastContextTokensstays stale (zero or the previous backend's value), and the status bar shows a misleading context-fill percentage. Adda.LastContextTokens = <ollama per-turn input tokens>in the Ollama response-handling branch.Example:
Suggested fix:
Why this wasn't caught: No test covers
LastContextTokensbeing set after an Ollama turn; the existingTestContextWindowonly tests the window-size helper.More Info
agent.goline 662 (a.LastContextTokens = ev.Message.Usage.InputTokens), line 822 (a.LastContextTokens = apiResp.Usage.InputTokens),cerebras_agent.goline 71 (a.LastContextTokens = resp.Usage.PromptTokens). Violation site: the Ollama response-handling code inagent.go(thecallOllamaor equivalent function) — noLastContextTokensassignment.LastContextTokensis only set in the three paths listed above; the Ollama path is a fourth code path that handles its own response and never touches the field.a.LastContextTokens = <that count>.Prompt To Fix With AI