From 443a893ffd56ac144b033b56b72c6b95f5a1aae6 Mon Sep 17 00:00:00 2001 From: Zander Chase <130414180+vowelparrot@users.noreply.github.com> Date: Wed, 26 Apr 2023 16:21:34 -0700 Subject: [PATCH] Align names of search tools (#3620) Tools for Bing, DDG and Google weren't consistent even though the underlying implementations were. All three services now have the same tools and implementations to easily switch and experiment when building chains. --- docs/modules/agents/tools/examples/ddg.ipynb | 4 +- .../autonomous_agents/marathon_times.ipynb | 6 +-- langchain/agents/load_tools.py | 4 +- langchain/tools/__init__.py | 18 ++++++--- langchain/tools/bing_search/tool.py | 21 +++++++++++ langchain/tools/ddg_search/__init__.py | 4 +- langchain/tools/ddg_search/tool.py | 37 ++++++++++++++++++- langchain/utilities/duckduckgo_search.py | 6 +-- .../utilities/test_duckduckdgo_search_api.py | 4 +- 9 files changed, 84 insertions(+), 20 deletions(-) diff --git a/docs/modules/agents/tools/examples/ddg.ipynb b/docs/modules/agents/tools/examples/ddg.ipynb index ea7a66ea..b37e2cdf 100644 --- a/docs/modules/agents/tools/examples/ddg.ipynb +++ b/docs/modules/agents/tools/examples/ddg.ipynb @@ -27,7 +27,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.tools import DuckDuckGoSearchTool" + "from langchain.tools import DuckDuckGoSearchRun" ] }, { @@ -37,7 +37,7 @@ "metadata": {}, "outputs": [], "source": [ - "search = DuckDuckGoSearchTool()" + "search = DuckDuckGoSearchRun()" ] }, { diff --git a/docs/use_cases/autonomous_agents/marathon_times.ipynb b/docs/use_cases/autonomous_agents/marathon_times.ipynb index 49d75761..aba7a9e4 100644 --- a/docs/use_cases/autonomous_agents/marathon_times.ipynb +++ b/docs/use_cases/autonomous_agents/marathon_times.ipynb @@ -219,7 +219,7 @@ }, "outputs": [], "source": [ - "from langchain.tools import BaseTool, DuckDuckGoSearchTool\n", + "from langchain.tools import BaseTool, DuckDuckGoSearchRun\n", "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", "\n", "from pydantic import Field\n", @@ -321,7 +321,7 @@ "outputs": [], "source": [ "# !pip install duckduckgo_search\n", - "web_search = DuckDuckGoSearchTool()" + "web_search = DuckDuckGoSearchRun()" ] }, { @@ -618,7 +618,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.8.16" } }, "nbformat": 4, diff --git a/langchain/agents/load_tools.py b/langchain/agents/load_tools.py index de057275..ade98c96 100644 --- a/langchain/agents/load_tools.py +++ b/langchain/agents/load_tools.py @@ -15,7 +15,7 @@ from langchain.requests import TextRequestsWrapper from langchain.tools.arxiv.tool import ArxivQueryRun from langchain.tools.base import BaseTool from langchain.tools.bing_search.tool import BingSearchRun -from langchain.tools.ddg_search.tool import DuckDuckGoSearchTool +from langchain.tools.ddg_search.tool import DuckDuckGoSearchRun from langchain.tools.google_search.tool import GoogleSearchResults, GoogleSearchRun from langchain.tools.human.tool import HumanInputRun from langchain.tools.python.tool import PythonREPLTool @@ -219,7 +219,7 @@ def _get_bing_search(**kwargs: Any) -> BaseTool: def _get_ddg_search(**kwargs: Any) -> BaseTool: - return DuckDuckGoSearchTool(api_wrapper=DuckDuckGoSearchAPIWrapper(**kwargs)) + return DuckDuckGoSearchRun(api_wrapper=DuckDuckGoSearchAPIWrapper(**kwargs)) def _get_human_tool(**kwargs: Any) -> BaseTool: diff --git a/langchain/tools/__init__.py b/langchain/tools/__init__.py index 3c034f83..7ffcd0db 100644 --- a/langchain/tools/__init__.py +++ b/langchain/tools/__init__.py @@ -1,19 +1,27 @@ """Core toolkit implementations.""" from langchain.tools.base import BaseTool -from langchain.tools.ddg_search.tool import DuckDuckGoSearchTool +from langchain.tools.bing_search.tool import BingSearchResults, BingSearchRun +from langchain.tools.ddg_search.tool import DuckDuckGoSearchResults, DuckDuckGoSearchRun from langchain.tools.google_places.tool import GooglePlacesTool +from langchain.tools.google_search.tool import GoogleSearchResults, GoogleSearchRun from langchain.tools.ifttt import IFTTTWebhook from langchain.tools.openapi.utils.api_models import APIOperation from langchain.tools.openapi.utils.openapi_utils import OpenAPISpec from langchain.tools.plugin import AIPluginTool __all__ = [ - "BaseTool", - "IFTTTWebhook", "AIPluginTool", - "OpenAPISpec", "APIOperation", + "BingSearchResults", + "BingSearchRun", + "DuckDuckGoSearchResults", + "DuckDuckGoSearchRun", + "DuckDuckGoSearchRun", "GooglePlacesTool", - "DuckDuckGoSearchTool", + "GoogleSearchResults", + "GoogleSearchRun", + "IFTTTWebhook", + "OpenAPISpec", + "BaseTool", ] diff --git a/langchain/tools/bing_search/tool.py b/langchain/tools/bing_search/tool.py index c851c3ee..dd57295c 100644 --- a/langchain/tools/bing_search/tool.py +++ b/langchain/tools/bing_search/tool.py @@ -22,3 +22,24 @@ class BingSearchRun(BaseTool): async def _arun(self, query: str) -> str: """Use the tool asynchronously.""" raise NotImplementedError("BingSearchRun does not support async") + + +class BingSearchResults(BaseTool): + """Tool that has capability to query the Bing Search API and get back json.""" + + name = "Bing Search Results JSON" + description = ( + "A wrapper around Bing Search. " + "Useful for when you need to answer questions about current events. " + "Input should be a search query. Output is a JSON array of the query results" + ) + num_results: int = 4 + api_wrapper: BingSearchAPIWrapper + + def _run(self, query: str) -> str: + """Use the tool.""" + return str(self.api_wrapper.results(query, self.num_results)) + + async def _arun(self, query: str) -> str: + """Use the tool asynchronously.""" + raise NotImplementedError("BingSearchResults does not support async") diff --git a/langchain/tools/ddg_search/__init__.py b/langchain/tools/ddg_search/__init__.py index 931d4a89..157811df 100644 --- a/langchain/tools/ddg_search/__init__.py +++ b/langchain/tools/ddg_search/__init__.py @@ -1,5 +1,5 @@ """DuckDuckGo Search API toolkit.""" -from langchain.tools.ddg_search.tool import DuckDuckGoSearchTool +from langchain.tools.ddg_search.tool import DuckDuckGoSearchRun -__all__ = ["DuckDuckGoSearchTool"] +__all__ = ["DuckDuckGoSearchRun"] diff --git a/langchain/tools/ddg_search/tool.py b/langchain/tools/ddg_search/tool.py index 33044241..5948756f 100644 --- a/langchain/tools/ddg_search/tool.py +++ b/langchain/tools/ddg_search/tool.py @@ -1,12 +1,15 @@ """Tool for the DuckDuckGo search API.""" +import warnings +from typing import Any + from pydantic import Field from langchain.tools.base import BaseTool from langchain.utilities.duckduckgo_search import DuckDuckGoSearchAPIWrapper -class DuckDuckGoSearchTool(BaseTool): +class DuckDuckGoSearchRun(BaseTool): """Tool that adds the capability to query the DuckDuckGo search API.""" name = "DuckDuckGo Search" @@ -26,3 +29,35 @@ class DuckDuckGoSearchTool(BaseTool): async def _arun(self, query: str) -> str: """Use the tool asynchronously.""" raise NotImplementedError("DuckDuckGoSearch does not support async") + + +class DuckDuckGoSearchResults(BaseTool): + """Tool that queries the Duck Duck Go Search API and get back json.""" + + name = "DuckDuckGo Results JSON" + description = ( + "A wrapper around Duck Duck Go Search. " + "Useful for when you need to answer questions about current events. " + "Input should be a search query. Output is a JSON array of the query results" + ) + num_results: int = 4 + api_wrapper: DuckDuckGoSearchAPIWrapper = Field( + default_factory=DuckDuckGoSearchAPIWrapper + ) + + def _run(self, query: str) -> str: + """Use the tool.""" + return str(self.api_wrapper.results(query, self.num_results)) + + async def _arun(self, query: str) -> str: + """Use the tool asynchronously.""" + raise NotImplementedError("DuckDuckGoSearchResults does not support async") + + +def DuckDuckGoSearchTool(*args: Any, **kwargs: Any) -> DuckDuckGoSearchRun: + warnings.warn( + "DuckDuckGoSearchTool will be deprecated in the future. " + "Please use DuckDuckGoSearchRun instead.", + DeprecationWarning, + ) + return DuckDuckGoSearchRun(*args, **kwargs) diff --git a/langchain/utilities/duckduckgo_search.py b/langchain/utilities/duckduckgo_search.py index 547847b9..ff6ba6e2 100644 --- a/langchain/utilities/duckduckgo_search.py +++ b/langchain/utilities/duckduckgo_search.py @@ -41,7 +41,7 @@ class DuckDuckGoSearchAPIWrapper(BaseModel): def run(self, query: str) -> str: from duckduckgo_search import ddg - """Run query through DuckDuckGo and return results.""" + """Run query through DuckDuckGo and return concatenated results.""" results = ddg( query, region=self.region, @@ -54,7 +54,7 @@ class DuckDuckGoSearchAPIWrapper(BaseModel): snippets = [result["body"] for result in results] return " ".join(snippets) - def results(self, query: str, num_results: int) -> List[Dict]: + def results(self, query: str, num_results: int) -> List[Dict[str, str]]: """Run query through DuckDuckGo and return metadata. Args: @@ -80,7 +80,7 @@ class DuckDuckGoSearchAPIWrapper(BaseModel): if results is None or len(results) == 0: return [{"Result": "No good DuckDuckGo Search Result was found"}] - def to_metadata(result: Dict) -> Dict: + def to_metadata(result: Dict) -> Dict[str, str]: return { "snippet": result["body"], "title": result["title"], diff --git a/tests/integration_tests/utilities/test_duckduckdgo_search_api.py b/tests/integration_tests/utilities/test_duckduckdgo_search_api.py index a6397251..8d228e57 100644 --- a/tests/integration_tests/utilities/test_duckduckdgo_search_api.py +++ b/tests/integration_tests/utilities/test_duckduckdgo_search_api.py @@ -1,6 +1,6 @@ import pytest -from langchain.tools.ddg_search.tool import DuckDuckGoSearchTool +from langchain.tools.ddg_search.tool import DuckDuckGoSearchRun def ddg_installed() -> bool: @@ -16,7 +16,7 @@ def ddg_installed() -> bool: @pytest.mark.skipif(not ddg_installed(), reason="requires duckduckgo-search package") def test_ddg_search_tool() -> None: keywords = "Bella Ciao" - tool = DuckDuckGoSearchTool() + tool = DuckDuckGoSearchRun() result = tool(keywords) print(result) assert len(result.split()) > 20