Compare commits

...

1 Commits

Author SHA1 Message Date
Devin AI
5878627ca6 Fix #6072: Display agent output in feedback prompt regardless of verbose setting
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 <joao@crewai.com>
2026-06-08 20:25:30 +00:00
2 changed files with 171 additions and 7 deletions

View File

@@ -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"

View File

@@ -138,4 +138,136 @@ class TestFlowHumanInputIntegration:
for call in call_args
if call[0]
)
assert training_panel_found
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"
)