From 11e1720be0899e43191330837d7892c45937b7e8 Mon Sep 17 00:00:00 2001 From: Tao Wang Date: Thu, 14 Nov 2024 01:23:53 -0800 Subject: [PATCH 1/2] Add DuckDuckGo News Search and Video Search --- .../builtin/duckduckgo/tools/ddgo_news.py | 83 ++++++++++++++++++ .../builtin/duckduckgo/tools/ddgo_news.yaml | 71 +++++++++++++++ .../builtin/duckduckgo/tools/ddgo_video.py | 61 +++++++++++++ .../builtin/duckduckgo/tools/ddgo_video.yaml | 86 +++++++++++++++++++ web/app/components/base/markdown.tsx | 2 +- 5 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.py create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.yaml create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.py create mode 100644 api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.yaml diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.py new file mode 100644 index 0000000000..1bc81a3a5f --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.py @@ -0,0 +1,83 @@ +from typing import Any + +from duckduckgo_search import DDGS + +from core.model_runtime.entities.message_entities import SystemPromptMessage +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + +SUMMARY_PROMPT = """ +User's query: +{query} + +Here are the news results: +{content} + +Please summarize the news in a few sentences. +""" + + +class DuckDuckGoNewsSearchTool(BuiltinTool): + """ + Tool for performing a news search using DuckDuckGo search engine. + """ + + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]: + query_dict = { + "keywords": tool_parameters.get("query"), + "timelimit": tool_parameters.get("timelimit"), + "max_results": tool_parameters.get("max_results"), + "safesearch": "moderate", + "region": "wt-wt" + } + try: + response = list(DDGS().news(**query_dict)) + if not response: + return [self.create_text_message("No news found matching your criteria.")] + except Exception as e: + return [self.create_text_message(f"Error searching news: {str(e)}")] + + require_summary = tool_parameters.get("require_summary", False) + + if require_summary: + results = "\n".join([f"{res.get('title')}: {res.get('body')}" for res in response]) + results = self.summary_results(user_id=user_id, content=results, query=query_dict["keywords"]) + return self.create_text_message(text=results) + + # Create rich markdown content for each news item + markdown_result = "\n\n" + json_result = [] + + for res in response: + markdown_result += f"### {res.get('title', 'Untitled')}\n\n" + if res.get('date'): + markdown_result += f"**Date:** {res.get('date')}\n\n" + if res.get('body'): + markdown_result += f"{res.get('body')}\n\n" + if res.get('source'): + markdown_result += f"*Source: {res.get('source')}*\n\n" + if res.get('image'): + markdown_result += f"![{res.get('title', '')}]({res.get('image')})\n\n" + markdown_result += f"[Read more]({res.get('url', '')})\n\n---\n\n" + + json_result.append(self.create_json_message({ + "title": res.get("title", ""), + "date": res.get("date", ""), + "body": res.get("body", ""), + "url": res.get("url", ""), + "image": res.get("image", ""), + "source": res.get("source", "") + })) + + return [self.create_text_message(markdown_result)] + json_result + + def summary_results(self, user_id: str, content: str, query: str) -> str: + prompt = SUMMARY_PROMPT.format(query=query, content=content) + summary = self.invoke_model( + user_id=user_id, + prompt_messages=[ + SystemPromptMessage(content=prompt), + ], + stop=[], + ) + return summary.message.content \ No newline at end of file diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.yaml new file mode 100644 index 0000000000..5e8eae3985 --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_news.yaml @@ -0,0 +1,71 @@ +identity: + name: ddgo_news + author: Assistant + label: + en_US: DuckDuckGo News Search + zh_Hans: DuckDuckGo 新闻搜索 +description: + human: + en_US: Perform news searches on DuckDuckGo and get results. + zh_Hans: 在 DuckDuckGo 上进行新闻搜索并获取结果。 + llm: Perform news searches on DuckDuckGo and get results. +parameters: + - name: query + type: string + required: true + label: + en_US: Query String + zh_Hans: 查询语句 + human_description: + en_US: 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: 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: require_summary + type: boolean + default: false + label: + en_US: Require Summary + zh_Hans: 是否总结 + human_description: + en_US: Whether to pass the news results to llm for summarization. + zh_Hans: 是否需要将新闻结果传给大模型总结 + form: form \ No newline at end of file diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.py b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.py new file mode 100644 index 0000000000..ec54f02d7f --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.py @@ -0,0 +1,61 @@ +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 DuckDuckGoVideoSearchTool(BuiltinTool): + """ + Tool for performing a video 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"), + "region": tool_parameters.get("region", "wt-wt"), + "safesearch": tool_parameters.get("safesearch", "moderate"), + "timelimit": tool_parameters.get("timelimit"), + "resolution": tool_parameters.get("resolution"), + "duration": tool_parameters.get("duration"), + "license_videos": tool_parameters.get("license_videos"), + "max_results": tool_parameters.get("max_results"), + } + + # Remove None values to use API defaults + query_dict = {k: v for k, v in query_dict.items() if v is not None} + + response = DDGS().videos(**query_dict) + + # Create HTML result with embedded iframes + markdown_result = "\n\n" + json_result = [] + + for res in response: + title = res.get('title', '') + embed_html = res.get('embed_html', '') + description = res.get('description', '') + + # Modify iframe to be responsive + if embed_html: + # Replace fixed dimensions with responsive wrapper and iframe + embed_html = """ +
+ +
""".format( + src=res.get('embed_url', '') + ) + + markdown_result += f"{title}\n\n" + markdown_result += f"{embed_html}\n\n" + markdown_result += "---\n\n" + + json_result.append(self.create_json_message(res)) + + return [self.create_text_message(markdown_result)] + json_result \ No newline at end of file diff --git a/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.yaml b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.yaml new file mode 100644 index 0000000000..c46b3a3bb1 --- /dev/null +++ b/api/core/tools/provider/builtin/duckduckgo/tools/ddgo_video.yaml @@ -0,0 +1,86 @@ +identity: + name: ddgo_video + author: Assistant + label: + en_US: DuckDuckGo Video Search + zh_Hans: DuckDuckGo 视频搜索 +description: + human: + en_US: Perform video searches on DuckDuckGo and get results with embedded videos. + zh_Hans: 在 DuckDuckGo 上进行视频搜索并获取可嵌入的视频结果。 + llm: Perform video searches on DuckDuckGo and get results with embedded videos. +parameters: + - name: query + type: string + required: true + label: + en_US: Query String + zh_Hans: 查询语句 + human_description: + en_US: Search Query + zh_Hans: 搜索查询语句。 + llm_description: Key words for searching + form: llm + - name: max_results + type: number + required: true + default: 3 + minimum: 1 + maximum: 10 + label: + en_US: Max Results + zh_Hans: 最大结果数量 + human_description: + en_US: The max results (1-10). + zh_Hans: 最大结果数量(1-10)。 + 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: duration + type: select + required: false + options: + - value: short + label: + en_US: Short (<4 minutes) + zh_Hans: 短视频(<4分钟) + - value: medium + label: + en_US: Medium (4-20 minutes) + zh_Hans: 中等(4-20分钟) + - value: long + label: + en_US: Long (>20 minutes) + zh_Hans: 长视频(>20分钟) + label: + en_US: Video Duration + zh_Hans: 视频时长 + human_description: + en_US: Filter videos by duration + zh_Hans: 按时长筛选视频 + form: form \ No newline at end of file diff --git a/web/app/components/base/markdown.tsx b/web/app/components/base/markdown.tsx index 58e54123dd..40de269a6f 100644 --- a/web/app/components/base/markdown.tsx +++ b/web/app/components/base/markdown.tsx @@ -265,7 +265,7 @@ export function Markdown(props: { content: string; className?: string }) { } }, ]} - disallowedElements={['script', 'iframe', 'head', 'html', 'meta', 'link', 'style', 'body']} + disallowedElements={['script', 'head', 'html', 'meta', 'link', 'style', 'body']} components={{ code: CodeBlock, img: Img, From 47bc23876d83f533371bd925a61e970456036612 Mon Sep 17 00:00:00 2001 From: Tao Wang <74752235+taowang1993@users.noreply.github.com> Date: Fri, 15 Nov 2024 03:28:05 -0800 Subject: [PATCH 2/2] Update markdown.tsx --- web/app/components/base/markdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/base/markdown.tsx b/web/app/components/base/markdown.tsx index 40de269a6f..58e54123dd 100644 --- a/web/app/components/base/markdown.tsx +++ b/web/app/components/base/markdown.tsx @@ -265,7 +265,7 @@ export function Markdown(props: { content: string; className?: string }) { } }, ]} - disallowedElements={['script', 'head', 'html', 'meta', 'link', 'style', 'body']} + disallowedElements={['script', 'iframe', 'head', 'html', 'meta', 'link', 'style', 'body']} components={{ code: CodeBlock, img: Img,