diff --git a/app/agent/manus.py b/app/agent/manus.py index e11ca45..7cd012c 100644 --- a/app/agent/manus.py +++ b/app/agent/manus.py @@ -8,7 +8,9 @@ from app.tool import Terminate, ToolCollection from app.tool.browser_use_tool import BrowserUseTool from app.tool.file_saver import FileSaver from app.tool.google_search import GoogleSearch +from app.tool.baidu_search import BaiduSearch from app.tool.python_execute import PythonExecute +from app.config import config class Manus(ToolCallAgent): @@ -34,9 +36,22 @@ class Manus(ToolCallAgent): # Add general-purpose tools to the tool collection available_tools: ToolCollection = Field( default_factory=lambda: ToolCollection( - PythonExecute(), GoogleSearch(), BrowserUseTool(), FileSaver(), Terminate() + PythonExecute(), Manus.get_search_tool(), BrowserUseTool(), FileSaver(), Terminate() ) ) + + @staticmethod + def get_search_tool(): + """Determines the search tool to use based on the configuration.""" + print(config.search_config) + if config.search_config is None: + return GoogleSearch() + else: + # Check search engine + engine = config.search_config.engine.lower() + if engine == "baidu": + return BaiduSearch() + return GoogleSearch() async def _handle_special_tool(self, name: str, result: Any, **kwargs): await self.available_tools.get_tool(BrowserUseTool().name).cleanup() diff --git a/app/config.py b/app/config.py index 64f478d..81e1e81 100644 --- a/app/config.py +++ b/app/config.py @@ -30,6 +30,8 @@ class ProxySettings(BaseModel): username: Optional[str] = Field(None, description="Proxy username") password: Optional[str] = Field(None, description="Proxy password") +class SearchSettings(BaseModel): + engine: str = Field(default='Google', description="Search engine the llm to use") class BrowserSettings(BaseModel): headless: bool = Field(False, description="Whether to run browser in headless mode") @@ -58,6 +60,9 @@ class AppConfig(BaseModel): browser_config: Optional[BrowserSettings] = Field( None, description="Browser configuration" ) + search_config: Optional[SearchSettings] = Field( + None, description="Search configuration" + ) class Config: arbitrary_types_allowed = True @@ -149,6 +154,12 @@ class Config: if valid_browser_params: browser_settings = BrowserSettings(**valid_browser_params) + search_config = raw_config.get("search", {}) + search_settings = None + if search_config: + search_settings = SearchSettings(**search_config) + print("search setting", search_settings) + config_dict = { "llm": { "default": default_settings, @@ -158,6 +169,7 @@ class Config: }, }, "browser_config": browser_settings, + "search_config": search_settings, } self._config = AppConfig(**config_dict) @@ -169,6 +181,10 @@ class Config: @property def browser_config(self) -> Optional[BrowserSettings]: return self._config.browser_config + + @property + def search_config(self) -> Optional[SearchSettings]: + return self._config.search_config config = Config() diff --git a/app/tool/baidu_search.py b/app/tool/baidu_search.py new file mode 100644 index 0000000..93ba50f --- /dev/null +++ b/app/tool/baidu_search.py @@ -0,0 +1,48 @@ +import asyncio +from typing import List + +from baidusearch.baidusearch import search + +from app.tool.base import BaseTool + + +class BaiduSearch(BaseTool): + name: str = "baidu_search" + description: str = """Perform a Baidu search and return a list of relevant links. +Use this tool when you need to find information on the web, get up-to-date data, or research specific topics. +The tool returns a list of URLs that match the search query. +""" + parameters: dict = { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "(required) The search query to submit to Baidu.", + }, + "num_results": { + "type": "integer", + "description": "(optional) The number of search results to return. Default is 10.", + "default": 10, + }, + }, + "required": ["query"], + } + + async def execute(self, query: str, num_results: int = 10) -> List[str]: + """ + Execute a Baidu search and return a list of URLs. + + Args: + query (str): The search query to submit to Baidu. + num_results (int, optional): The number of search results to return. Default is 10. + + Returns: + List[str]: A list of URLs matching the search query. + """ + # Run the search in a thread pool to prevent blocking + loop = asyncio.get_event_loop() + links = await loop.run_in_executor( + None, lambda: list(search(query, num_results=num_results)) + ) + + return links diff --git a/config/config.example.toml b/config/config.example.toml index 13648dd..ac8af62 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -42,3 +42,8 @@ api_key = "sk-..." # server = "http://proxy-server:port" # username = "proxy-username" # password = "proxy-password" + +# Optional configuration, Search settings. +# [search] +# Search engine for agent to use. Default is "Google", can be set to "Baidu". +#engine = "Google" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 7ce4b52..c275e65 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ uvicorn~=0.34.0 unidiff~=0.7.5 browser-use~=0.1.40 googlesearch-python~=1.3.0 +baidusearch~=1.0.3 aiofiles~=24.1.0 pydantic_core~=2.27.2