From 3b36ba797f6e2c409526cb39a82493a9f07ef48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Wed, 12 Jun 2024 10:04:10 +0800 Subject: [PATCH] feat: add duckduckgo img search, translate, ai chat (#5074) --- .../provider/builtin/duckduckgo/duckduckgo.py | 2 +- .../builtin/duckduckgo/tools/ddgo_ai.py | 20 ++ .../builtin/duckduckgo/tools/ddgo_ai.yaml | 41 +++++ .../builtin/duckduckgo/tools/ddgo_img.py | 25 +++ .../builtin/duckduckgo/tools/ddgo_img.yaml | 88 +++++++++ .../builtin/duckduckgo/tools/ddgo_search.py | 29 +++ .../builtin/duckduckgo/tools/ddgo_search.yaml | 65 +++++++ .../duckduckgo/tools/ddgo_translate.py | 20 ++ .../duckduckgo/tools/ddgo_translate.yaml | 51 ++++++ .../duckduckgo/tools/duckduckgo_search.py | 171 ------------------ .../duckduckgo/tools/duckduckgo_search.yaml | 23 --- 11 files changed, 340 insertions(+), 195 deletions(-) create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.py create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.yaml create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.py create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.yaml create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.py create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.yaml create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.py create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.yaml delete mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/duckduckgo_search.py delete mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/duckduckgo_search.yaml diff --git a/api/core/tools/provider/builtin/duckduckgo/duckduckgo.py b/api/core/tools/provider/builtin/duckduckgo/duckduckgo.py index 6df8678d30..2292e89fa6 100644 --- a/api/core/tools/provider/builtin/duckduckgo/duckduckgo.py +++ b/api/core/tools/provider/builtin/duckduckgo/duckduckgo.py @@ -1,5 +1,5 @@ from core.tools.errors import ToolProviderCredentialValidationError -from core.tools.provider.builtin.duckduckgo.tools.duckduckgo_search import DuckDuckGoSearchTool +from core.tools.provider.builtin.duckduckgo.tools.ddgo_search import DuckDuckGoSearchTool from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.py new file mode 100644 index 0000000000..878b0d8645 --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.py @@ -0,0 +1,20 @@ +from typing import Any + +from duckduckgo_search import DDGS + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class DuckDuckGoAITool(BuiltinTool): + """ + Tool for performing a search using DuckDuckGo search engine. + """ + + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + query_dict = { + "keywords": tool_parameters.get('query'), + "model": tool_parameters.get('model'), + } + response = DDGS().chat(**query_dict) + return self.create_text_message(text=response) diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.yaml new file mode 100644 index 0000000000..1ca16e660f --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_ai.yaml @@ -0,0 +1,41 @@ +identity: + name: ddgo_ai + author: hjlarry + label: + en_US: DuckDuckGo AI Chat + zh_Hans: DuckDuckGo AI聊天 +description: + human: + en_US: Use the anonymous private chat provided by DuckDuckGo. + zh_Hans: 使用DuckDuckGo提供的匿名私密聊天。 + llm: Use the anonymous private chat provided by DuckDuckGo. +parameters: + - name: query + type: string + required: true + label: + en_US: Chat Content + zh_Hans: 聊天内容 + human_description: + en_US: The chat content. + zh_Hans: 要聊天的内容。 + llm_description: Key words for chat + form: llm + - name: model + type: select + required: true + options: + - value: gpt-3.5 + label: + en_US: GPT-3.5 + - value: claude-3-haiku + label: + en_US: Claude 3 + default: gpt-3.5 + label: + en_US: Choose Model + zh_Hans: 选择模型 + human_description: + en_US: used to select the model for AI chat. + zh_Hans: 用于选择使用AI聊天的模型 + form: form diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.py new file mode 100644 index 0000000000..660611e8a6 --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.py @@ -0,0 +1,25 @@ +from typing import Any + +from duckduckgo_search import DDGS + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class DuckDuckGoImageSearchTool(BuiltinTool): + """ + Tool for performing an image search using DuckDuckGo search engine. + """ + + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> list[ToolInvokeMessage]: + query_dict = { + "keywords": tool_parameters.get('query'), + "timelimit": tool_parameters.get('timelimit'), + "size": tool_parameters.get('size'), + "max_results": tool_parameters.get('max_results'), + } + response = DDGS().images(**query_dict) + results = [] + for res in response: + results.append(self.create_image_message(image=res.get("image"))) + return results diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.yaml new file mode 100644 index 0000000000..168cface22 --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_img.yaml @@ -0,0 +1,88 @@ +identity: + name: ddgo_img + author: hjlarry + label: + en_US: DuckDuckGo Image Search + zh_Hans: DuckDuckGo 图片搜索 +description: + human: + en_US: Perform image searches on DuckDuckGo and get results. + zh_Hans: 在 DuckDuckGo 上进行图片搜索并获取结果。 + llm: Perform image searches on DuckDuckGo and get results. +parameters: + - name: query + type: string + required: true + label: + en_US: Query string + zh_Hans: 查询语句 + human_description: + en_US: The search query. + zh_Hans: 搜索查询语句。 + llm_description: Key words for searching + form: llm + - name: max_results + type: number + required: true + default: 3 + label: + en_US: Max results + zh_Hans: 最大结果数量 + human_description: + en_US: The max results. + zh_Hans: 最大结果数量 + form: form + - name: timelimit + type: select + required: false + options: + - value: Day + label: + en_US: current day + zh_Hans: 当天 + - value: Week + label: + en_US: current week + zh_Hans: 本周 + - value: Month + label: + en_US: current month + zh_Hans: 当月 + - value: Year + label: + en_US: current year + zh_Hans: 今年 + label: + en_US: Result time limit + zh_Hans: 结果时间限制 + human_description: + en_US: Use when querying results within a specific time range only. + zh_Hans: 只查询一定时间范围内的结果时使用 + form: form + - name: size + type: select + required: false + options: + - value: Small + label: + en_US: small + zh_Hans: 小 + - value: Medium + label: + en_US: medium + zh_Hans: 中 + - value: Large + label: + en_US: large + zh_Hans: 大 + - value: Wallpaper + label: + en_US: xl + zh_Hans: 超大 + label: + en_US: image size + zh_Hans: 图片大小 + human_description: + en_US: The size of the image to be searched. + zh_Hans: 要搜索的图片的大小 + form: form diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.py new file mode 100644 index 0000000000..bf963f3442 --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.py @@ -0,0 +1,29 @@ +from typing import Any + +from duckduckgo_search import DDGS + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class DuckDuckGoSearchTool(BuiltinTool): + """ + Tool for performing a search using DuckDuckGo search engine. + """ + + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + query = tool_parameters.get('query', '') + result_type = tool_parameters.get('result_type', 'text') + max_results = tool_parameters.get('max_results', 10) + require_summary = tool_parameters.get('require_summary', False) + response = DDGS().text(query, max_results=max_results) + + if result_type == 'link': + results = [f"[{res.get('title')}]({res.get('href')})" for res in response] + results = "\n".join(results) + return self.create_link_message(link=results) + results = [res.get("body") for res in response] + results = "\n".join(results) + if require_summary: + results = self.summary(user_id=user_id, content=results) + return self.create_text_message(text=results) diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.yaml new file mode 100644 index 0000000000..a8248d595b --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_search.yaml @@ -0,0 +1,65 @@ +identity: + name: ddgo_search + author: Yash Parmar + label: + en_US: DuckDuckGo Search + zh_Hans: DuckDuckGo 搜索 +description: + human: + en_US: Perform searches on DuckDuckGo and get results. + zh_Hans: 在 DuckDuckGo 上进行搜索并获取结果。 + llm: Perform searches on DuckDuckGo and get results. +parameters: + - name: query + type: string + required: true + label: + en_US: Query string + zh_Hans: 查询语句 + human_description: + en_US: The search query. + zh_Hans: 搜索查询语句。 + llm_description: Key words for searching + form: llm + - name: max_results + type: number + required: true + default: 5 + label: + en_US: Max results + zh_Hans: 最大结果数量 + human_description: + en_US: The max results. + zh_Hans: 最大结果数量 + form: form + - name: result_type + type: select + required: true + options: + - value: text + label: + en_US: text + zh_Hans: 文本 + - value: link + label: + en_US: link + zh_Hans: 链接 + default: text + label: + en_US: Result type + zh_Hans: 结果类型 + human_description: + en_US: used for selecting the result type, text or link + zh_Hans: 用于选择结果类型,使用文本还是链接进行展示 + form: form + - name: require_summary + type: boolean + required: true + default: false + label: + en_US: Require Summary + zh_Hans: 是否总结 + human_description: + en_US: Whether to pass the search results to llm for summarization. + zh_Hans: 是否需要将搜索结果传给大模型总结 + form: form diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.py new file mode 100644 index 0000000000..9822b37cf0 --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.py @@ -0,0 +1,20 @@ +from typing import Any + +from duckduckgo_search import DDGS + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class DuckDuckGoTranslateTool(BuiltinTool): + """ + Tool for performing a search using DuckDuckGo search engine. + """ + + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + query_dict = { + "keywords": tool_parameters.get('query'), + "to": tool_parameters.get('translate_to'), + } + response = DDGS().translate(**query_dict)[0].get('translated', 'Unable to translate!') + return self.create_text_message(text=response) diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.yaml new file mode 100644 index 0000000000..78b5d0b022 --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_translate.yaml @@ -0,0 +1,51 @@ +identity: + name: ddgo_translate + author: hjlarry + label: + en_US: DuckDuckGo Translate + zh_Hans: DuckDuckGo 翻译 +description: + human: + en_US: Use DuckDuckGo's translation feature. + zh_Hans: 使用DuckDuckGo的翻译功能。 + llm: Use DuckDuckGo's translation feature. +parameters: + - name: query + type: string + required: true + label: + en_US: Translate Content + zh_Hans: 翻译内容 + human_description: + en_US: The translate content. + zh_Hans: 要翻译的内容。 + llm_description: Key words for translate + form: llm + - name: translate_to + type: select + required: true + options: + - value: en + label: + en_US: English + zh_Hans: 英语 + - value: zh-Hans + label: + en_US: Simplified Chinese + zh_Hans: 简体中文 + - value: zh-Hant + label: + en_US: Traditional Chinese + zh_Hans: 繁体中文 + - value: ja + label: + en_US: Japanese + zh_Hans: 日语 + default: en + label: + en_US: Choose Language + zh_Hans: 选择语言 + human_description: + en_US: select the language to translate. + zh_Hans: 选择要翻译的语言 + form: form diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/duckduckgo_search.py b/api/core/tools/provider/builtin/duckduckgo/tools/duckduckgo_search.py deleted file mode 100644 index 80722a4d6e..0000000000 --- a/api/core/tools/provider/builtin/duckduckgo/tools/duckduckgo_search.py +++ /dev/null @@ -1,171 +0,0 @@ -from typing import Any, Optional - -from pydantic import BaseModel, Field - -from core.tools.entities.tool_entities import ToolInvokeMessage -from core.tools.tool.builtin_tool import BuiltinTool - - -class DuckDuckGoSearchAPIWrapper(BaseModel): - """Wrapper for DuckDuckGo Search API. - - Free and does not require any setup. - """ - - region: Optional[str] = "wt-wt" - safesearch: str = "moderate" - time: Optional[str] = "y" - max_results: int = 5 - - def get_snippets(self, query: str) -> list[str]: - """Run query through DuckDuckGo and return concatenated results.""" - from duckduckgo_search import DDGS - - with DDGS() as ddgs: - results = ddgs.text( - query, - region=self.region, - safesearch=self.safesearch, - timelimit=self.time, - ) - if results is None: - return ["No good DuckDuckGo Search Result was found"] - snippets = [] - for i, res in enumerate(results, 1): - if res is not None: - snippets.append(res["body"]) - if len(snippets) == self.max_results: - break - return snippets - - def run(self, query: str) -> str: - snippets = self.get_snippets(query) - return " ".join(snippets) - - def results( - self, query: str, num_results: int, backend: str = "api" - ) -> list[dict[str, str]]: - """Run query through DuckDuckGo and return metadata. - - Args: - query: The query to search for. - num_results: The number of results to return. - - Returns: - A list of dictionaries with the following keys: - snippet - The description of the result. - title - The title of the result. - link - The link to the result. - """ - from duckduckgo_search import DDGS - - with DDGS() as ddgs: - results = ddgs.text( - query, - region=self.region, - safesearch=self.safesearch, - timelimit=self.time, - backend=backend, - ) - if results is None: - return [{"Result": "No good DuckDuckGo Search Result was found"}] - - def to_metadata(result: dict) -> dict[str, str]: - if backend == "news": - return { - "date": result["date"], - "title": result["title"], - "snippet": result["body"], - "source": result["source"], - "link": result["url"], - } - return { - "snippet": result["body"], - "title": result["title"], - "link": result["href"], - } - - formatted_results = [] - for i, res in enumerate(results, 1): - if res is not None: - formatted_results.append(to_metadata(res)) - if len(formatted_results) == num_results: - break - return formatted_results - - -class DuckDuckGoSearchRun(BaseModel): - """Tool that queries the DuckDuckGo search API.""" - - name = "duckduckgo_search" - description = ( - "A wrapper around DuckDuckGo Search. " - "Useful for when you need to answer questions about current events. " - "Input should be a search query." - ) - api_wrapper: DuckDuckGoSearchAPIWrapper = Field( - default_factory=DuckDuckGoSearchAPIWrapper - ) - - def _run( - self, - query: str, - ) -> str: - """Use the tool.""" - return self.api_wrapper.run(query) - - -class DuckDuckGoSearchResults(BaseModel): - """Tool that queries the DuckDuckGo search API and gets 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 - ) - backend: str = "api" - - def _run( - self, - query: str, - ) -> str: - """Use the tool.""" - res = self.api_wrapper.results(query, self.num_results, backend=self.backend) - res_strs = [", ".join([f"{k}: {v}" for k, v in d.items()]) for d in res] - return ", ".join([f"[{rs}]" for rs in res_strs]) - -class DuckDuckGoInput(BaseModel): - query: str = Field(..., description="Search query.") - -class DuckDuckGoSearchTool(BuiltinTool): - """ - Tool for performing a search using DuckDuckGo search engine. - """ - - def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]: - """ - Invoke the DuckDuckGo search tool. - - Args: - user_id (str): The ID of the user invoking the tool. - tool_parameters (dict[str, Any]): The parameters for the tool invocation. - - Returns: - ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation. - """ - query = tool_parameters.get('query', '') - - if not query: - return self.create_text_message('Please input query') - - tool = DuckDuckGoSearchRun(args_schema=DuckDuckGoInput) - - result = tool._run(query) - - return self.create_text_message(self.summary(user_id=user_id, content=result)) - \ No newline at end of file diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/duckduckgo_search.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/duckduckgo_search.yaml deleted file mode 100644 index 93c857010c..0000000000 --- a/api/core/tools/provider/builtin/duckduckgo/tools/duckduckgo_search.yaml +++ /dev/null @@ -1,23 +0,0 @@ -identity: - name: duckduckgo_search - author: Yash Parmar - label: - en_US: DuckDuckGo Search - zh_Hans: DuckDuckGo 搜索 -description: - human: - en_US: Perform searches on DuckDuckGo and get results. - zh_Hans: 在 DuckDuckGo 上进行搜索并获取结果。 - llm: Perform searches on DuckDuckGo and get results. -parameters: - - name: query - type: string - required: true - label: - en_US: Query string - zh_Hans: 查询语句 - human_description: - en_US: The search query. - zh_Hans: 搜索查询语句。 - llm_description: Key words for searching - form: llm