| 
					
				 | 
			
			
				@@ -1,39 +1,20 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import os 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import sys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from typing import Any, Union 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-from serpapi import GoogleSearch 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import requests 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from core.tools.entities.tool_entities import ToolInvokeMessage 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from core.tools.tool.builtin_tool import BuiltinTool 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-class HiddenPrints: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    """Context manager to hide prints.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def __enter__(self) -> None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Open file to pipe stdout to.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self._original_stdout = sys.stdout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        sys.stdout = open(os.devnull, "w") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def __exit__(self, *_: Any) -> None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Close file that stdout was piped to.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        sys.stdout.close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        sys.stdout = self._original_stdout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+SERP_API_URL = "https://serpapi.com/search" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class SerpAPI: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     SerpAPI tool provider. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    search_engine: Any  #: :meta private: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    serpapi_api_key: str = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def __init__(self, api_key: str) -> None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """Initialize SerpAPI tool provider.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.serpapi_api_key = api_key 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.search_engine = GoogleSearch 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def run(self, query: str, **kwargs: Any) -> str: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """Run query through SerpAPI and parse result.""" 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -43,117 +24,76 @@ class SerpAPI: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def results(self, query: str) -> dict: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """Run query through SerpAPI and return the raw result.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         params = self.get_params(query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        with HiddenPrints(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            search = self.search_engine(params) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            res = search.get_dict() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return res 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        response = requests.get(url=SERP_API_URL, params=params) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        response.raise_for_status() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return response.json() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def get_params(self, query: str) -> dict[str, str]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """Get parameters for SerpAPI.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        _params = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        params = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             "api_key": self.serpapi_api_key, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             "q": query, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        params = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             "engine": "google", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             "google_domain": "google.com", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             "gl": "us", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            "hl": "en", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            **_params 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "hl": "en" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return params 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @staticmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def _process_response(res: dict, typ: str) -> str: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Process response from SerpAPI.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if "error" in res.keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Process response from SerpAPI. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        SerpAPI doc: https://serpapi.com/search-api 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Google search main results are called organic results 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if "error" in res: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             raise ValueError(f"Got error from SerpAPI: {res['error']}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        toret = "" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if typ == "text": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            toret = "" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if "answer_box" in res.keys() and type(res["answer_box"]) == list: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                res["answer_box"] = res["answer_box"][0] + "\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if "answer_box" in res.keys() and "answer" in res["answer_box"].keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret += res["answer_box"]["answer"] + "\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if "answer_box" in res.keys() and "snippet" in res["answer_box"].keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret += res["answer_box"]["snippet"] + "\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                "answer_box" in res.keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                and "snippet_highlighted_words" in res["answer_box"].keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for item in res["answer_box"]["snippet_highlighted_words"]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    toret += item + "\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                "sports_results" in res.keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                and "game_spotlight" in res["sports_results"].keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret += res["sports_results"]["game_spotlight"] + "\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                "shopping_results" in res.keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                and "title" in res["shopping_results"][0].keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret += res["shopping_results"][:3] + "\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                "knowledge_graph" in res.keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                and "description" in res["knowledge_graph"].keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret = res["knowledge_graph"]["description"] + "\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if "snippet" in res["organic_results"][0].keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret = "\n".join( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    f"content: {item['snippet']}\nlink: {item['link']}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if "knowledge_graph" in res and "description" in res["knowledge_graph"]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                toret += res["knowledge_graph"]["description"] + "\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if "organic_results" in res: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                snippets = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    f"content: {item.get('snippet')}\nlink: {item.get('link')}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     for item in res["organic_results"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if "snippet" in item and "link" in item 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                "images_results" in res.keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                and "thumbnail" in res["images_results"][0].keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                thumbnails = [item["thumbnail"] for item in res["images_results"][:10]] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret = thumbnails 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if toret == "": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret = "No good search result found" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if "snippet" in item 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                toret += "\n".join(snippets) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         elif typ == "link": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if "knowledge_graph" in res.keys() and "title" in res["knowledge_graph"].keys() \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    and "description_link" in res["knowledge_graph"].keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret = res["knowledge_graph"]["description_link"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            elif "knowledge_graph" in res.keys() and "see_results_about" in res["knowledge_graph"].keys() \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                and len(res["knowledge_graph"]["see_results_about"]) > 0: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                see_result_about = res["knowledge_graph"]["see_results_about"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret = "" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for item in see_result_about: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if "name" not in item.keys() or "link" not in item.keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    toret += f"[{item['name']}]({item['link']})\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            elif "organic_results" in res.keys() and len(res["organic_results"]) > 0: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                organic_results = res["organic_results"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret = "" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for item in organic_results: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if "title" not in item.keys() or "link" not in item.keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    toret += f"[{item['title']}]({item['link']})\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            elif "related_questions" in res.keys() and len(res["related_questions"]) > 0: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                related_questions = res["related_questions"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret = "" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for item in related_questions: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if "question" not in item.keys() or "link" not in item.keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    toret += f"[{item['question']}]({item['link']})\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            elif "related_searches" in res.keys() and len(res["related_searches"]) > 0: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                related_searches = res["related_searches"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret = "" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for item in related_searches: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if "query" not in item.keys() or "link" not in item.keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    toret += f"[{item['query']}]({item['link']})\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                toret = "No good search result found" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if "knowledge_graph" in res and "source" in res["knowledge_graph"]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                toret += res["knowledge_graph"]["source"]["link"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif "organic_results" in res: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                links = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    f"[{item['title']}]({item['link']})\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    for item in res["organic_results"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if "title" in item and "link" in item 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                toret += "\n".join(links) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif "related_questions" in res: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                questions = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    f"[{item['question']}]({item['link']})\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    for item in res["related_questions"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if "question" in item and "link" in item 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                toret += "\n".join(questions) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif "related_searches" in res: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                searches = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    f"[{item['query']}]({item['link']})\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    for item in res["related_searches"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if "query" in item and "link" in item 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                toret += "\n".join(searches) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if not toret: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            toret = "No good search result found" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return toret 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class GoogleSearchTool(BuiltinTool): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def _invoke(self,  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _invoke(self, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 user_id: str, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-               tool_parameters: dict[str, Any],  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                tool_parameters: dict[str, Any], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             invoke tools 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """ 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -164,4 +104,3 @@ class GoogleSearchTool(BuiltinTool): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if result_type == 'text': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return self.create_text_message(text=result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return self.create_link_message(link=result) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-     
			 |