Centralize model-change TUI notification in the main loop#2044
Centralize model-change TUI notification in the main loop#2044dgageot wants to merge 1 commit intodocker:mainfrom
Conversation
Add a change-detection mechanism (lastEmittedModelID + emitModelInfo closure) in RunStream that automatically emits AgentInfo only when the effective model actually changes. This is checked before and after each LLM call, covering per-tool overrides, fallback, model picker, cooldowns, and any future model-switching feature — without each one having to remember to notify the TUI. - loop.go: replace 3 scattered manual AgentInfo emissions with emitModelInfo calls driven by the closure - model_picker.go: remove AgentInfo emission from the tool handler; rename setModelAndEmitInfo to setCurrentAgentModel (no longer emits) - agent_delegation.go: use getEffectiveModelID instead of getAgentModelID so agent-switch events reflect active fallback cooldowns Assisted-By: docker-agent
There was a problem hiding this comment.
Review Summary
Assessment: 🟡 NEEDS ATTENTION
The PR successfully centralizes model-change TUI notification, removing scattered AgentInfo emissions and introducing a clean change-detection mechanism. However, there is a medium-severity issue with TUI updates when agents are idle.
Key Finding
1 Medium Severity Issue Found: The TUI notification system assumes the main loop is actively running. When the change_model tool is called and the agent becomes idle (e.g., conversation ends or agent is waiting), the TUI won't be notified of the model change until the next user message triggers a new loop iteration.
Recommendation
Consider emitting an AgentInfo event immediately after SetAgentModel succeeds in model_picker.go, or ensure the main loop processes the model change before the agent enters an idle state.
| } | ||
|
|
||
| return r.setModelAndEmitInfo(ctx, params.Model, events) | ||
| return r.setCurrentAgentModel(ctx, params.Model) |
There was a problem hiding this comment.
MEDIUM SEVERITY: TUI not notified of model change when agent is idle
The removal of the AgentInfo emission relies on the main loop's emitModelInfo closure to detect and notify the TUI of model changes. However, emitModelInfo is only called at specific points:
- At loop initialization (line 114 in loop.go)
- Before each model call (line 264 in loop.go)
- After each model execution (line 347 in loop.go)
If the agent is idle after change_model completes (e.g., the conversation ended or the agent is waiting for the next user message), the main loop won't iterate through these checkpoints. The TUI will continue showing the old model until the next user message triggers a new loop iteration.
Impact: Users won't see the model change reflected in the sidebar immediately after calling change_model, potentially causing confusion about which model is active.
Suggestion: Consider either:
- Keeping a single
AgentInfoemission here after successfulSetAgentModel - Ensuring the main loop processes at least one iteration after tool calls complete
- Adding a post-tool-processing phase that explicitly checks for model changes
Problem
Every feature that switches models (routing, alloy, fallback, model picker, per-tool overrides…) had to manually emit an
AgentInfoevent to update the TUI sidebar. This was scattered across 6+ call sites and easy to forget when adding new features.Solution
Add a change-detection mechanism in
RunStream: alastEmittedModelIDvariable andemitModelInfoclosure that automatically emitsAgentInfoonly when the effective model actually changes. Called before and after each LLM call, it covers all model-switching features uniformly.Changes
loop.go: Replace 3 scattered manualAgentInfoemissions withemitModelInfocallsmodel_picker.go: RemoveAgentInfoemission from tool handler; renamesetModelAndEmitInfo→setCurrentAgentModelagent_delegation.go: UsegetEffectiveModelIDinstead ofgetAgentModelIDso agent-switch events reflect active fallback cooldownsAny future model-switching feature no longer needs to worry about TUI notification — the loop handles it automatically.