diff --git a/src/crewai/agents/crew_agent_executor.py b/src/crewai/agents/crew_agent_executor.py index 1937d4bc2..99bc8b004 100644 --- a/src/crewai/agents/crew_agent_executor.py +++ b/src/crewai/agents/crew_agent_executor.py @@ -372,14 +372,29 @@ class CrewAgentExecutor(CrewAgentExecutorMixin): name.casefold().strip() for name in self.tool_name_to_tool_map ]: tool_result = tool_usage.use(tool_calling, agent_action.text) - # Strip any trailing backticks from tool result - if isinstance(tool_result, str): - tool_result = tool_result.rstrip('`') + tool_result = self._clean_tool_result(tool_result) tool = self.tool_name_to_tool_map.get(tool_calling.tool_name) if tool: return ToolResult( result=tool_result, result_as_answer=tool.result_as_answer ) + + def _clean_tool_result(self, tool_result: Any) -> Any: + """Clean tool result by removing trailing backticks. + + This is particularly important in hierarchical mode where tool outputs + might contain markdown formatting that needs to be cleaned up. + + Args: + tool_result: The result from a tool execution, can be any type + + Returns: + The cleaned result with any trailing backticks removed if it's a string, + otherwise returns the original result unchanged + """ + if isinstance(tool_result, str): + return tool_result.rstrip('`').rstrip('```') + return tool_result else: tool_result = self._i18n.errors("wrong_tool_name").format( tool=tool_calling.tool_name, diff --git a/tests/crew_test.py b/tests/crew_test.py index 839421e0b..fe8a663f8 100644 --- a/tests/crew_test.py +++ b/tests/crew_test.py @@ -317,14 +317,26 @@ def test_sync_task_execution(): @pytest.mark.vcr(filter_headers=["authorization"]) -def test_hierarchical_tool_output_formatting(): - """Test that tool outputs in hierarchical mode don't have extra backticks""" +@pytest.mark.parametrize("tool_output,expected", [ + ("test result```", "test result"), + ("test result`", "test result"), + ("test result``````", "test result"), + ("test result", "test result"), + ("test ```result```", "test ```result"), # Only strip trailing backticks +]) +def test_hierarchical_tool_output_formatting(tool_output, expected): + """Test that tool outputs in hierarchical mode don't have extra backticks. + + This test verifies that the tool output cleaning functionality correctly handles + various scenarios of backtick formatting, ensuring only trailing backticks are + removed while preserving any inline markdown formatting. + """ class TestTool(BaseTool): name: str = "test_tool" description: str = "A test tool" def _run(self, *args: Any, **kwargs: Any) -> str: - return "test result```" # Intentionally add backticks to test stripping + return tool_output task = Task( description="Test task using test_tool", @@ -341,13 +353,12 @@ def test_hierarchical_tool_output_formatting(): with patch.object(Task, 'execute_sync', return_value=TaskOutput( description="Test task", - raw="test result", + raw=expected, agent="researcher" )) as mock_execute_sync: result = crew.kickoff() assert mock_execute_sync.called - assert not result.raw.endswith('```') - assert '```\n```' not in result.raw + assert result.raw == expected @pytest.mark.vcr(filter_headers=["authorization"]) def test_hierarchical_process():