diff --git a/app/config.py b/app/config.py index bd78863..eb91cf4 100644 --- a/app/config.py +++ b/app/config.py @@ -38,7 +38,7 @@ class ProxySettings(BaseModel): class SearchSettings(BaseModel): engine: str = Field(default="Google", description="Search engine the llm to use") fallback_engines: List[str] = Field( - default_factory=lambda: ["DuckDuckGo", "Baidu"], + default_factory=lambda: ["DuckDuckGo", "Baidu", "Bing"], description="Fallback search engines to try if the primary engine fails", ) retry_delay: int = Field( @@ -49,6 +49,14 @@ class SearchSettings(BaseModel): default=3, description="Maximum number of times to retry all engines when all fail", ) + lang: str = Field( + default="en", + description="Language code for search results (e.g., en, zh, fr)", + ) + country: str = Field( + default="us", + description="Country code for search results (e.g., us, cn, uk)", + ) class BrowserSettings(BaseModel): diff --git a/app/tool/search/baidu_search.py b/app/tool/search/baidu_search.py index a6fca59..8beb330 100644 --- a/app/tool/search/baidu_search.py +++ b/app/tool/search/baidu_search.py @@ -1,10 +1,14 @@ +from typing import List + from baidusearch.baidusearch import search from app.tool.search.base import SearchItem, WebSearchEngine class BaiduSearchEngine(WebSearchEngine): - def perform_search(self, query, num_results=10, *args, **kwargs): + def perform_search( + self, query: str, num_results: int = 10, *args, **kwargs + ) -> List[SearchItem]: """ Baidu search engine. diff --git a/app/tool/search/base.py b/app/tool/search/base.py index 1f2aeb7..31d78b9 100644 --- a/app/tool/search/base.py +++ b/app/tool/search/base.py @@ -17,7 +17,11 @@ class SearchItem(BaseModel): return f"{self.title} - {self.url}" -class WebSearchEngine(object): +class WebSearchEngine(BaseModel): + """Base class for web search engines.""" + + model_config = {"arbitrary_types_allowed": True} + def perform_search( self, query: str, num_results: int = 10, *args, **kwargs ) -> List[SearchItem]: diff --git a/app/tool/search/bing_search.py b/app/tool/search/bing_search.py index 3a316aa..620620c 100644 --- a/app/tool/search/bing_search.py +++ b/app/tool/search/bing_search.py @@ -1,4 +1,4 @@ -from typing import List, Tuple +from typing import List, Optional, Tuple import requests from bs4 import BeautifulSoup @@ -36,7 +36,7 @@ BING_SEARCH_URL = "https://www.bing.com/search?q=" class BingSearchEngine(WebSearchEngine): - session: requests.Session = None + session: Optional[requests.Session] = None def __init__(self, **data): """Initialize the BingSearch tool with a requests session.""" @@ -133,7 +133,9 @@ class BingSearchEngine(WebSearchEngine): logger.warning(f"Error parsing HTML: {e}") return [], None - def perform_search(self, query, num_results=10, *args, **kwargs): + def perform_search( + self, query: str, num_results: int = 10, *args, **kwargs + ) -> List[SearchItem]: """ Bing search engine. diff --git a/app/tool/search/duckduckgo_search.py b/app/tool/search/duckduckgo_search.py index d7024fd..ca269b8 100644 --- a/app/tool/search/duckduckgo_search.py +++ b/app/tool/search/duckduckgo_search.py @@ -1,10 +1,14 @@ +from typing import List + from duckduckgo_search import DDGS from app.tool.search.base import SearchItem, WebSearchEngine class DuckDuckGoSearchEngine(WebSearchEngine): - def perform_search(self, query, num_results=10, *args, **kwargs): + def perform_search( + self, query: str, num_results: int = 10, *args, **kwargs + ) -> List[SearchItem]: """ DuckDuckGo search engine. @@ -18,14 +22,14 @@ class DuckDuckGoSearchEngine(WebSearchEngine): # If it's just a URL results.append( SearchItem( - title=f"DuckDuckGo Result {i+1}", url=item, description=None + title=f"DuckDuckGo Result {i + 1}", url=item, description=None ) ) elif isinstance(item, dict): # Extract data from the dictionary results.append( SearchItem( - title=item.get("title", f"DuckDuckGo Result {i+1}"), + title=item.get("title", f"DuckDuckGo Result {i + 1}"), url=item.get("href", ""), description=item.get("body", None), ) @@ -35,7 +39,7 @@ class DuckDuckGoSearchEngine(WebSearchEngine): try: results.append( SearchItem( - title=getattr(item, "title", f"DuckDuckGo Result {i+1}"), + title=getattr(item, "title", f"DuckDuckGo Result {i + 1}"), url=getattr(item, "href", ""), description=getattr(item, "body", None), ) @@ -44,7 +48,7 @@ class DuckDuckGoSearchEngine(WebSearchEngine): # Fallback results.append( SearchItem( - title=f"DuckDuckGo Result {i+1}", + title=f"DuckDuckGo Result {i + 1}", url=str(item), description=None, ) diff --git a/app/tool/search/google_search.py b/app/tool/search/google_search.py index 03f21b5..bd0838e 100644 --- a/app/tool/search/google_search.py +++ b/app/tool/search/google_search.py @@ -1,10 +1,14 @@ +from typing import List + from googlesearch import search from app.tool.search.base import SearchItem, WebSearchEngine class GoogleSearchEngine(WebSearchEngine): - def perform_search(self, query, num_results=10, *args, **kwargs): + def perform_search( + self, query: str, num_results: int = 10, *args, **kwargs + ) -> List[SearchItem]: """ Google search engine. diff --git a/app/tool/web_search.py b/app/tool/web_search.py index 0ce504f..da15489 100644 --- a/app/tool/web_search.py +++ b/app/tool/web_search.py @@ -207,8 +207,8 @@ class WebSearch(BaseTool): self, query: str, num_results: int = 5, - lang: str = "en", - country: str = "us", + lang: Optional[str] = None, + country: Optional[str] = None, fetch_content: bool = False, ) -> SearchResponse: """ @@ -217,14 +217,14 @@ class WebSearch(BaseTool): Args: query: The search query to submit to the search engine num_results: The number of search results to return (default: 5) - lang: Language code for search results (default: en) - country: Country code for search results (default: us) + lang: Language code for search results (default from config) + country: Country code for search results (default from config) fetch_content: Whether to fetch content from result pages (default: False) Returns: A structured response containing search results and metadata """ - # Get retry settings from config + # Get settings from config retry_delay = ( getattr(config.search_config, "retry_delay", 60) if config.search_config @@ -235,6 +235,22 @@ class WebSearch(BaseTool): if config.search_config else 3 ) + + # Use config values for lang and country if not specified + if lang is None: + lang = ( + getattr(config.search_config, "lang", "en") + if config.search_config + else "en" + ) + + if country is None: + country = ( + getattr(config.search_config, "country", "us") + if config.search_config + else "us" + ) + search_params = {"lang": lang, "country": country} # Try searching with retries when all engines fail diff --git a/config/config.example.toml b/config/config.example.toml index 82a80c4..e456051 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -79,6 +79,10 @@ temperature = 0.0 # Controls randomness for vision mode #retry_delay = 60 # Maximum number of times to retry all engines when all fail. Default is 3. #max_retries = 3 +# Language code for search results. Options: "en" (English), "zh" (Chinese), etc. +#lang = "en" +# Country code for search results. Options: "us" (United States), "cn" (China), etc. +#country = "us" ## Sandbox configuration diff --git a/requirements.txt b/requirements.txt index e2867e9..3324283 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,3 +33,9 @@ httpx>=0.27.0 tomli>=2.0.0 boto3~=1.37.18 + +requests~=2.32.3 +beautifulsoup4~=4.13.3 + +huggingface-hub~=0.29.2 +setuptools~=75.8.0