From 1386a4325ba52e178af0d28d1a8bf8e43723157a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:58:19 +0000 Subject: [PATCH] fix: remove $ prefix from f-strings in BrightDataSearchTool.get_search_url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The get_search_url method used JavaScript template literal syntax (${query}) instead of Python f-string syntax ({query}), causing a literal '$' character to be prepended to all search queries for Google, Bing, and Yandex. Fixes #5269 Co-Authored-By: João --- .../tools/brightdata_tool/brightdata_serp.py | 6 +-- .../tests/tools/brightdata_serp_tool_test.py | 54 +++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_serp.py b/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_serp.py index 884a8ac8e6..56bda13c3c 100644 --- a/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_serp.py +++ b/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_serp.py @@ -131,10 +131,10 @@ def __init__( def get_search_url(self, engine: str, query: str) -> str: if engine == "yandex": - return f"https://yandex.com/search/?text=${query}" + return f"https://yandex.com/search/?text={query}" if engine == "bing": - return f"https://www.bing.com/search?q=${query}" - return f"https://www.google.com/search?q=${query}" + return f"https://www.bing.com/search?q={query}" + return f"https://www.google.com/search?q={query}" def _run( self, diff --git a/lib/crewai-tools/tests/tools/brightdata_serp_tool_test.py b/lib/crewai-tools/tests/tools/brightdata_serp_tool_test.py index 11ca018e8d..80d830d45a 100644 --- a/lib/crewai-tools/tests/tools/brightdata_serp_tool_test.py +++ b/lib/crewai-tools/tests/tools/brightdata_serp_tool_test.py @@ -45,6 +45,60 @@ def test_run_with_request_exception(self, mock_post): result = self.tool._run(query="AI", search_engine="google") self.assertIn("Error", result) + @patch.dict( + "os.environ", + {"BRIGHT_DATA_API_KEY": "test_api_key", "BRIGHT_DATA_ZONE": "test_zone"}, + ) + def test_get_search_url_google_no_dollar_prefix(self): + """Test that Google search URL does not contain a '$' prefix before the query.""" + tool = BrightDataSearchTool() + url = tool.get_search_url("google", "AI news") + self.assertEqual(url, "https://www.google.com/search?q=AI news") + self.assertNotIn("$", url) + + @patch.dict( + "os.environ", + {"BRIGHT_DATA_API_KEY": "test_api_key", "BRIGHT_DATA_ZONE": "test_zone"}, + ) + def test_get_search_url_bing_no_dollar_prefix(self): + """Test that Bing search URL does not contain a '$' prefix before the query.""" + tool = BrightDataSearchTool() + url = tool.get_search_url("bing", "AI news") + self.assertEqual(url, "https://www.bing.com/search?q=AI news") + self.assertNotIn("$", url) + + @patch.dict( + "os.environ", + {"BRIGHT_DATA_API_KEY": "test_api_key", "BRIGHT_DATA_ZONE": "test_zone"}, + ) + def test_get_search_url_yandex_no_dollar_prefix(self): + """Test that Yandex search URL does not contain a '$' prefix before the query.""" + tool = BrightDataSearchTool() + url = tool.get_search_url("yandex", "AI news") + self.assertEqual(url, "https://yandex.com/search/?text=AI news") + self.assertNotIn("$", url) + + @patch.dict( + "os.environ", + {"BRIGHT_DATA_API_KEY": "test_api_key", "BRIGHT_DATA_ZONE": "test_zone"}, + ) + @patch("requests.post") + def test_run_search_url_no_dollar_prefix(self, mock_post): + """Test that the full _run flow produces URLs without '$' prefix in the query.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = "mock response text" + mock_post.return_value = mock_response + + tool = BrightDataSearchTool() + tool._run(query="test query", search_engine="google") + + call_args = mock_post.call_args + request_body = call_args[1]["json"] if "json" in call_args[1] else call_args[0][1] + url_sent = request_body["url"] + self.assertNotIn("$", url_sent.split("?q=")[1]) + self.assertIn("q=test%20query", url_sent) + def tearDown(self): # Clean up env vars pass