mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-08 16:35:08 +00:00
fix: suppress flow console panels in TUI mode; clear header agent on method change
Two review follow-ups: 1) Method panels break Textual TUI (Cursor): forcing suppress_flow_events off so the STEPS panel receives events also un-gated the EventListener's Rich flow/method panels (ConsoleFormatter.print_panel prints is_flow=True panels regardless of verbose), which interleave with Textual and corrupt the TUI. print_panel now skips is_flow panels when is_tui_mode() is set (the same context the TUI worker already establishes and the tracing listeners already honor). Non-TUI/headless flow runs are unaffected. Test: test_console_formatter_tui_mode. 2) Flow header showed a stale agent (CodeRabbit): _current_agent persisted across methods. It's now cleared when a method starts and when the active method changes, so the header never shows the previous method's agent until a new agent event arrives. Test: test_flow_method_transitions_clear_current_agent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
This commit is contained in:
@@ -737,6 +737,9 @@ FooterKey .footer-key--key {
|
||||
self._current_method = next(
|
||||
(s["name"] for s in self._flow_steps if s["status"] == "active"), None
|
||||
)
|
||||
# The active method changed; drop its agent so the header doesn't show a
|
||||
# stale agent until the next method's agent event arrives.
|
||||
self._current_agent = ""
|
||||
|
||||
def _on_crew_done(self, output: str | None) -> None:
|
||||
with self._lock:
|
||||
@@ -2036,6 +2039,9 @@ FooterKey .footer-key--key {
|
||||
with self._lock:
|
||||
name = event.method_name
|
||||
self._current_method = name
|
||||
# Agent is per-method; clear it so the header doesn't show the
|
||||
# previous method's agent until a new agent event arrives.
|
||||
self._current_agent = ""
|
||||
for step in self._flow_steps:
|
||||
if step["name"] == name:
|
||||
step["status"] = "active"
|
||||
|
||||
@@ -1586,6 +1586,34 @@ def test_current_method_clears_and_falls_back_across_overlap() -> None:
|
||||
app._unsubscribe()
|
||||
|
||||
|
||||
def test_flow_method_transitions_clear_current_agent() -> None:
|
||||
app = CrewRunApp(crew_name="Demo")
|
||||
app._flow = SimpleNamespace()
|
||||
app._subscribe()
|
||||
try:
|
||||
_emit_event(
|
||||
MethodExecutionStartedEvent(flow_name="Demo", method_name="a", state={})
|
||||
)
|
||||
app._current_agent = "Researcher" # an agent ran during method 'a'
|
||||
|
||||
# Starting a new method clears the previous method's agent.
|
||||
_emit_event(
|
||||
MethodExecutionStartedEvent(flow_name="Demo", method_name="b", state={})
|
||||
)
|
||||
assert app._current_agent == ""
|
||||
|
||||
app._current_agent = "Writer"
|
||||
# 'b' ending switches the active method ('a' still active) → agent clears.
|
||||
_emit_event(
|
||||
MethodExecutionFinishedEvent(
|
||||
flow_name="Demo", method_name="b", result=None, state={}
|
||||
)
|
||||
)
|
||||
assert app._current_agent == ""
|
||||
finally:
|
||||
app._unsubscribe()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_declarative_flow_runs_on_tui() -> None:
|
||||
"""End-to-end: on_mount dispatches _run_flow_worker → flow.kickoff →
|
||||
|
||||
@@ -211,6 +211,13 @@ To enable tracing, do any one of these:
|
||||
"""Print a panel with consistent formatting if verbose is enabled."""
|
||||
panel = self.create_panel(content, title, style)
|
||||
if is_flow:
|
||||
# A TUI (e.g. the CLI's CrewRunApp) owns the screen and renders flow
|
||||
# progress in its own STEPS panel; emitting Rich panels here would
|
||||
# interleave with and corrupt the TUI, so suppress them in TUI mode.
|
||||
from crewai.events.listeners.tracing.utils import is_tui_mode
|
||||
|
||||
if is_tui_mode():
|
||||
return
|
||||
self.print(panel)
|
||||
self.print()
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"""Flow panels must be suppressed while a TUI owns the screen."""
|
||||
|
||||
from rich.text import Text
|
||||
|
||||
from crewai.events.listeners.tracing.utils import set_tui_mode
|
||||
from crewai.events.utils.console_formatter import ConsoleFormatter
|
||||
|
||||
|
||||
def _make_formatter(monkeypatch):
|
||||
fmt = ConsoleFormatter(verbose=True)
|
||||
calls: list[object] = []
|
||||
monkeypatch.setattr(fmt, "print", lambda *a, **k: calls.append(a))
|
||||
return fmt, calls
|
||||
|
||||
|
||||
def test_flow_panel_suppressed_in_tui_mode(monkeypatch):
|
||||
fmt, calls = _make_formatter(monkeypatch)
|
||||
set_tui_mode(True)
|
||||
try:
|
||||
fmt.print_panel(Text("x"), "🌊 Flow Started", "blue", is_flow=True)
|
||||
finally:
|
||||
set_tui_mode(False)
|
||||
|
||||
assert calls == []
|
||||
|
||||
|
||||
def test_flow_panel_prints_when_not_tui_mode(monkeypatch):
|
||||
fmt, calls = _make_formatter(monkeypatch)
|
||||
set_tui_mode(False)
|
||||
|
||||
fmt.print_panel(Text("x"), "🌊 Flow Started", "blue", is_flow=True)
|
||||
|
||||
# Panel + trailing blank line.
|
||||
assert len(calls) == 2
|
||||
|
||||
|
||||
def test_non_flow_panel_unaffected_by_tui_mode(monkeypatch):
|
||||
# tui_mode only gates flow panels; regular panels still follow verbose.
|
||||
fmt, calls = _make_formatter(monkeypatch)
|
||||
set_tui_mode(True)
|
||||
try:
|
||||
fmt.print_panel(Text("x"), "Task", "blue", is_flow=False)
|
||||
finally:
|
||||
set_tui_mode(False)
|
||||
|
||||
assert len(calls) == 2
|
||||
Reference in New Issue
Block a user