From 5878627ca6336ea884b09b5f1b9f3dd104f15443 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 20:25:30 +0000 Subject: [PATCH] Fix #6072: Display agent output in feedback prompt regardless of verbose setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When human_input=True but verbose=False, the feedback prompt said 'Provide feedback on the Final Result above' without ever showing the result. The result display was gated on verbose via AgentLogsExecutionEvent, while the feedback prompt fired unconditionally. Now _prompt_input/_prompt_input_async accept and render the agent output in a dedicated panel before the feedback prompt, ensuring the user always sees what they are reviewing. Co-Authored-By: João --- .../src/crewai/core/providers/human_input.py | 44 +++++- .../test_flow_human_input_integration.py | 134 +++++++++++++++++- 2 files changed, 171 insertions(+), 7 deletions(-) diff --git a/lib/crewai/src/crewai/core/providers/human_input.py b/lib/crewai/src/crewai/core/providers/human_input.py index b82e408d9..7f365366e 100644 --- a/lib/crewai/src/crewai/core/providers/human_input.py +++ b/lib/crewai/src/crewai/core/providers/human_input.py @@ -179,7 +179,8 @@ class SyncHumanInputProvider(HumanInputProvider): Returns: The final answer after feedback processing. """ - feedback = self._prompt_input(context.crew) + output_str = self._get_output_string(formatted_answer) + feedback = self._prompt_input(context.crew, output_str) if context._is_training_mode(): return self._handle_training_feedback(formatted_answer, feedback, context) @@ -200,7 +201,8 @@ class SyncHumanInputProvider(HumanInputProvider): Returns: The final answer after feedback processing. """ - feedback = await self._prompt_input_async(context.crew) + output_str = self._get_output_string(formatted_answer) + feedback = await self._prompt_input_async(context.crew, output_str) if context._is_training_mode(): return await self._handle_training_feedback_async( @@ -259,7 +261,9 @@ class SyncHumanInputProvider(HumanInputProvider): else: context.messages.append(context._format_feedback_message(feedback)) answer = context._invoke_loop() - feedback = self._prompt_input(context.crew) + feedback = self._prompt_input( + context.crew, self._get_output_string(answer) + ) return answer @@ -311,16 +315,19 @@ class SyncHumanInputProvider(HumanInputProvider): else: context.messages.append(context._format_feedback_message(feedback)) answer = await context._ainvoke_loop() - feedback = await self._prompt_input_async(context.crew) + feedback = await self._prompt_input_async( + context.crew, self._get_output_string(answer) + ) return answer @staticmethod - def _prompt_input(crew: Crew | None) -> str: + def _prompt_input(crew: Crew | None, agent_output: str | None = None) -> str: """Show rich panel and prompt for input. Args: crew: The crew instance for context. + agent_output: The agent's final output to display for review. Returns: User input string from terminal. @@ -334,6 +341,17 @@ class SyncHumanInputProvider(HumanInputProvider): formatter.pause_live_updates() try: + if agent_output is not None: + result_content = Text() + result_content.append(agent_output, style="bright_green") + result_panel = Panel( + result_content, + title="Agent Final Answer", + border_style="green", + padding=(1, 2), + ) + formatter.console.print(result_panel) + if crew and getattr(crew, "_train", False): prompt_text = ( "TRAINING MODE: Provide feedback to improve the agent's performance.\n\n" @@ -369,11 +387,14 @@ class SyncHumanInputProvider(HumanInputProvider): formatter.resume_live_updates() @staticmethod - async def _prompt_input_async(crew: Crew | None) -> str: + async def _prompt_input_async( + crew: Crew | None, agent_output: str | None = None + ) -> str: """Show rich panel and prompt for input without blocking the event loop. Args: crew: The crew instance for context. + agent_output: The agent's final output to display for review. Returns: User input string from terminal. @@ -387,6 +408,17 @@ class SyncHumanInputProvider(HumanInputProvider): formatter.pause_live_updates() try: + if agent_output is not None: + result_content = Text() + result_content.append(agent_output, style="bright_green") + result_panel = Panel( + result_content, + title="Agent Final Answer", + border_style="green", + padding=(1, 2), + ) + formatter.console.print(result_panel) + if crew and getattr(crew, "_train", False): prompt_text = ( "TRAINING MODE: Provide feedback to improve the agent's performance.\n\n" diff --git a/lib/crewai/tests/test_flow_human_input_integration.py b/lib/crewai/tests/test_flow_human_input_integration.py index 461111e09..148c6a446 100644 --- a/lib/crewai/tests/test_flow_human_input_integration.py +++ b/lib/crewai/tests/test_flow_human_input_integration.py @@ -138,4 +138,136 @@ class TestFlowHumanInputIntegration: for call in call_args if call[0] ) - assert training_panel_found \ No newline at end of file + assert training_panel_found + + @patch("builtins.input", return_value="") + def test_prompt_input_displays_agent_output(self, mock_input): + """Test that _prompt_input displays the agent output in a panel when provided.""" + provider = SyncHumanInputProvider() + crew = MagicMock() + crew._train = False + + formatter = event_listener.formatter + + with ( + patch.object(formatter, "pause_live_updates"), + patch.object(formatter, "resume_live_updates"), + patch.object(formatter.console, "print") as mock_console_print, + ): + provider._prompt_input(crew, agent_output="The sky is blue.") + + mock_console_print.assert_called() + call_args = mock_console_print.call_args_list + result_panel_found = any( + hasattr(call[0][0], "title") + and "Agent Final Answer" in str(call[0][0].title) + for call in call_args + if call[0] + ) + assert result_panel_found, ( + "Agent output panel should be displayed when agent_output is provided" + ) + + @patch("builtins.input", return_value="") + def test_prompt_input_no_output_panel_when_none(self, mock_input): + """Test that _prompt_input does not display the output panel when agent_output is None.""" + provider = SyncHumanInputProvider() + crew = MagicMock() + crew._train = False + + formatter = event_listener.formatter + + with ( + patch.object(formatter, "pause_live_updates"), + patch.object(formatter, "resume_live_updates"), + patch.object(formatter.console, "print") as mock_console_print, + ): + provider._prompt_input(crew, agent_output=None) + + call_args = mock_console_print.call_args_list + result_panel_found = any( + hasattr(call[0][0], "title") + and "Agent Final Answer" in str(call[0][0].title) + for call in call_args + if call[0] + ) + assert not result_panel_found, ( + "Agent output panel should NOT be displayed when agent_output is None" + ) + + @patch("builtins.input", return_value="looks good") + def test_handle_feedback_passes_output_to_prompt(self, mock_input): + """Test that handle_feedback passes the agent output to _prompt_input. + + This is the core fix for issue #6072: the feedback prompt should always + display the result being reviewed, regardless of verbose setting. + """ + from crewai.agents.parser import AgentFinish + + provider = SyncHumanInputProvider() + + formatted_answer = AgentFinish( + thought="I know the answer", + output="The sky is blue.", + text="Final Answer: The sky is blue.", + ) + + context = MagicMock() + context.crew = MagicMock() + context.crew._train = False + context._is_training_mode.return_value = False + context.ask_for_human_input = False + + formatter = event_listener.formatter + + with ( + patch.object(formatter, "pause_live_updates"), + patch.object(formatter, "resume_live_updates"), + patch.object(formatter.console, "print") as mock_console_print, + ): + provider.handle_feedback(formatted_answer, context) + + call_args = mock_console_print.call_args_list + result_panel_found = any( + hasattr(call[0][0], "title") + and "Agent Final Answer" in str(call[0][0].title) + for call in call_args + if call[0] + ) + assert result_panel_found, ( + "handle_feedback must display the agent result panel " + "so the user can see what they are reviewing (issue #6072)" + ) + + @patch("builtins.input", return_value="") + def test_prompt_input_displays_output_before_feedback_panel(self, mock_input): + """Test that the agent output panel appears before the feedback panel.""" + provider = SyncHumanInputProvider() + crew = MagicMock() + crew._train = False + + formatter = event_listener.formatter + + with ( + patch.object(formatter, "pause_live_updates"), + patch.object(formatter, "resume_live_updates"), + patch.object(formatter.console, "print") as mock_console_print, + ): + provider._prompt_input(crew, agent_output="Test output") + + call_args = mock_console_print.call_args_list + result_panel_idx = None + feedback_panel_idx = None + for i, call in enumerate(call_args): + if call[0] and hasattr(call[0][0], "title"): + title = str(call[0][0].title) + if "Agent Final Answer" in title: + result_panel_idx = i + elif "Human Feedback" in title: + feedback_panel_idx = i + + assert result_panel_idx is not None, "Agent output panel should be present" + assert feedback_panel_idx is not None, "Feedback panel should be present" + assert result_panel_idx < feedback_panel_idx, ( + "Agent output panel should appear before the feedback panel" + ) \ No newline at end of file