File size: 11,266 Bytes
6bf2a1e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# coding: utf-8

import json
import os

import requests

from typing import Tuple, Any, List, Dict

from examples.tools.tool_action import SearchAction
from aworld.core.tool.action_factory import ActionFactory
from aworld.core.common import ActionModel, ActionResult
from aworld.logs.util import logger
from aworld.utils import import_package
from aworld.core.tool.action import ExecutableAction


@ActionFactory.register(name=SearchAction.WIKI.value.name,
                        desc=SearchAction.WIKI.value.desc,
                        tool_name='search_api')
class SearchWiki(ExecutableAction):
    def __init__(self):
        import_package("wikipedia")

    def act(self, action: ActionModel, **kwargs) -> Tuple[ActionResult, Any]:
        import wikipedia

        query = action.params.get("query")
        logger.info(f"Calling search_wiki api with query: {query}")

        result: str = ''
        try:
            page = wikipedia.page(query)
            result_dict = {
                'url': page.url,
                'title': page.title,
                'content': page.content,
            }
            result = str(result_dict)
        except wikipedia.exceptions.DisambiguationError as e:
            result = wikipedia.summary(
                e.options[0], sentences=5, auto_suggest=False
            )
        except wikipedia.exceptions.PageError:
            result = (
                "There is no page in Wikipedia corresponding to entity "
                f"{query}, please specify another word to describe the"
                " entity to be searched."
            )
        except Exception as e:
            logger.error(f"An exception occurred during the search: {e}")
            result = f"An exception occurred during the search: {e}"
        logger.debug(f"wiki result: {result}")
        return ActionResult(content=result, keep=True, is_done=True), None


@ActionFactory.register(name=SearchAction.DUCK_GO.value.name,
                        desc=SearchAction.DUCK_GO.value.desc,
                        tool_name="search_api")
class Duckduckgo(ExecutableAction):
    def __init__(self):
        import_package("duckduckgo_search")

    def act(self, action: ActionModel, **kwargs) -> Tuple[ActionResult, Any]:
        r"""Use DuckDuckGo search engine to search information for
        the given query.

        This function queries the DuckDuckGo API for related topics to
        the given search term. The results are formatted into a list of
        dictionaries, each representing a search result.

        Args:
            query (str): The query to be searched.
            source (str): The type of information to query (e.g., "text",
                "images", "videos"). Defaults to "text".
            max_results (int): Max number of results, defaults to `5`.

        Returns:
            List[Dict[str, Any]]: A list of dictionaries where each dictionary
                represents a search result.
        """

        from duckduckgo_search import DDGS

        params = action.params
        query = params.get("query")
        max_results = params.get("max_results", 5)
        source = params.get("source", "text")

        logger.debug(f"Calling search_duckduckgo function with query: {query}")

        ddgs = DDGS()
        responses: List[Dict[str, Any]] = []

        if source == "text":
            try:
                results = ddgs.text(keywords=query, max_results=max_results)
            except Exception as e:
                # Handle specific exceptions or general request exceptions
                responses.append({"error": f"duckduckgo search failed.{e}"})
                return ActionResult(content="duckduckgo search failed", keep=True), responses

            for i, result in enumerate(results, start=1):
                # Creating a response object with a similar structure
                response = {
                    "result_id": i,
                    "title": result["title"],
                    "description": result["body"],
                    "url": result["href"],
                }
                responses.append(response)
        elif source == "images":
            try:
                results = ddgs.images(keywords=query, max_results=max_results)
            except Exception as e:
                # Handle specific exceptions or general request exceptions
                responses.append({"error": f"duckduckgo search failed.{e}"})
                return ActionResult(content="duckduckgo search failed", keep=True), responses

            # Iterate over results found
            for i, result in enumerate(results, start=1):
                # Creating a response object with a similar structure
                response = {
                    "result_id": i,
                    "title": result["title"],
                    "image": result["image"],
                    "url": result["url"],
                    "source": result["source"],
                }
                responses.append(response)
        elif source == "videos":
            try:
                results = ddgs.videos(keywords=query, max_results=max_results)
            except Exception as e:
                # Handle specific exceptions or general request exceptions
                responses.append({"error": f"duckduckgo search failed.{e}"})
                return ActionResult(content="duckduckgo search failed", keep=True), responses

            # Iterate over results found
            for i, result in enumerate(results, start=1):
                # Creating a response object with a similar structure
                response = {
                    "result_id": i,
                    "title": result["title"],
                    "description": result["description"],
                    "embed_url": result["embed_url"],
                    "publisher": result["publisher"],
                    "duration": result["duration"],
                    "published": result["published"],
                }
                responses.append(response)
        logger.debug(f"Search results: {responses}")
        return ActionResult(content=json.dumps(responses), keep=True, is_done=True), None


@ActionFactory.register(name=SearchAction.GOOGLE.value.name,
                        desc=SearchAction.GOOGLE.value.desc,
                        tool_name="search_api")
class SearchGoogle(ExecutableAction):
    def act(self, action: ActionModel, **kwargs) -> Tuple[ActionResult, Any]:
        query = action.params.get("query")
        num_result_pages = action.params.get("num_result_pages", 6)
        # https://developers.google.com/custom-search/v1/overview
        api_key = action.params.get("api_key", os.environ.get("GOOGLE_API_KEY"))
        # https://cse.google.com/cse/all
        engine_id = action.params.get("engine_id", os.environ.get("GOOGLE_ENGINE_ID"))
        logger.debug(f"Calling search_google function with query: {query}")

        # Using the first page
        start_page_idx = 1
        # Different language may get different result
        search_language = "en"
        # How many pages to return
        num_result_pages = num_result_pages
        # Constructing the URL
        # Doc: https://developers.google.com/custom-search/v1/using_rest
        url = f"https://www.googleapis.com/customsearch/v1?key={api_key}&cx={engine_id}&q={query}&start={start_page_idx}&lr={search_language}&num={num_result_pages}"
        responses = []
        try:
            result = requests.get(url)
            result.raise_for_status()
            data = result.json()

            # Get the result items
            if "items" in data:
                search_items = data.get("items")

                for i, search_item in enumerate(search_items, start=1):
                    # Check metatags are present
                    if "pagemap" not in search_item:
                        continue
                    if "metatags" not in search_item["pagemap"]:
                        continue
                    if "og:description" in search_item["pagemap"]["metatags"][0]:
                        long_description = search_item["pagemap"]["metatags"][0]["og:description"]
                    else:
                        long_description = "N/A"
                    # Get the page title
                    title = search_item.get("title")
                    # Page snippet
                    snippet = search_item.get("snippet")

                    # Extract the page url
                    link = search_item.get("link")
                    response = {
                        "result_id": i,
                        "title": title,
                        "description": snippet,
                        "long_description": long_description,
                        "url": link,
                    }
                    if "huggingface.co" in link:
                        logger.warning(f"Filter out the link: {link}")
                        continue
                    responses.append(response)
            else:
                responses.append({"error": f"google search failed with response: {data}"})
        except Exception as e:
            logger.error(f"Google search failed with error: {e}")
            responses.append({"error": f"google search failed with error: {e}"})

        if len(responses) == 0:
            responses.append(
                "No relevant webpages found. Please simplify your query and expand the search space as much as you can, then try again.")
        logger.debug(f"search result: {responses}")
        responses.append(
            "If the search result does not contain the information you want, please make reflection on your query: what went well, what didn't, then refine your search plan.")
        return ActionResult(content=json.dumps(responses), keep=True, is_done=True), None


@ActionFactory.register(name=SearchAction.BAIDU.value.name,
                        desc=SearchAction.BAIDU.value.desc,
                        tool_name="search_api")
class SearchBaidu(ExecutableAction):
    def __init__(self):
        import_package("baidusearch")

    def act(self, action: ActionModel, **kwargs) -> Tuple[ActionResult, Any]:
        from baidusearch.baidusearch import search

        query = action.params.get("query")
        num_results = action.params.get("num_results", 6)
        num_results = int(num_results)
        logger.debug(f"Calling search_baidu with query: {query}")

        responses = []
        try:
            responses = search(query, num_results=num_results)
        except Exception as e:
            logger.error(f"Baidu search failed with error: {e}")
            responses.append({"error": f"baidu search failed with error: {e}"})

        if len(responses) == 0:
            responses.append(
                "No relevant webpages found. Please simplify your query and expand the search space as much as you can, then try again.")
        logger.debug(f"search result: {responses}")
        responses.append(
            "If the search result does not contain the information you want, please make reflection on your query: what went well, what didn't, then refine your search plan.")
        return ActionResult(content=json.dumps(responses), keep=True, is_done=True), None