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