diff --git a/metagpt/actions/research.py b/metagpt/actions/research.py index bad8f139c..5086f10cf 100644 --- a/metagpt/actions/research.py +++ b/metagpt/actions/research.py @@ -10,6 +10,7 @@ from pydantic import TypeAdapter, model_validator from metagpt.actions import Action from metagpt.config2 import config from metagpt.logs import logger +from metagpt.tools.search_engine import SearchEngine from metagpt.tools.web_browser_engine import WebBrowserEngine from metagpt.utils.common import OutputParser from metagpt.utils.text import generate_prompt_chunk, reduce_message_length diff --git a/metagpt/tools/search_engine.py b/metagpt/tools/search_engine.py index 1372a8c7a..81629bb02 100644 --- a/metagpt/tools/search_engine.py +++ b/metagpt/tools/search_engine.py @@ -6,9 +6,9 @@ @File : search_engine.py """ import importlib -from typing import Callable, Coroutine, Literal, Optional, Union, overload +from typing import Annotated, Callable, Coroutine, Literal, Optional, Union, overload -from pydantic import BaseModel, ConfigDict, model_validator +from pydantic import BaseModel, ConfigDict, Field, model_validator from metagpt.configs.search_config import SearchConfig from metagpt.logs import logger @@ -29,7 +29,9 @@ class SearchEngine(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow") engine: SearchEngineType = SearchEngineType.SERPER_GOOGLE - run_func: Optional[Callable[[str, int, bool], Coroutine[None, None, Union[str, list[str]]]]] = None + run_func: Annotated[ + Optional[Callable[[str, int, bool], Coroutine[None, None, Union[str, list[str]]]]], Field(exclude=True) + ] = None api_key: Optional[str] = None proxy: Optional[str] = None diff --git a/metagpt/tools/web_browser_engine.py b/metagpt/tools/web_browser_engine.py index 01339e51a..ff9ad0fa6 100644 --- a/metagpt/tools/web_browser_engine.py +++ b/metagpt/tools/web_browser_engine.py @@ -3,9 +3,9 @@ from __future__ import annotations import importlib -from typing import Any, Callable, Coroutine, Optional, Union, overload +from typing import Annotated, Any, Callable, Coroutine, Optional, Union, overload -from pydantic import BaseModel, ConfigDict, model_validator +from pydantic import BaseModel, ConfigDict, Field, model_validator from metagpt.configs.browser_config import BrowserConfig from metagpt.tools import WebBrowserEngineType @@ -29,7 +29,10 @@ class WebBrowserEngine(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow") engine: WebBrowserEngineType = WebBrowserEngineType.PLAYWRIGHT - run_func: Optional[Callable[..., Coroutine[Any, Any, Union[WebPage, list[WebPage]]]]] = None + run_func: Annotated[ + Optional[Callable[..., Coroutine[Any, Any, Union[WebPage, list[WebPage]]]]], + Field(exclude=True), + ] = None proxy: Optional[str] = None @model_validator(mode="after") diff --git a/tests/metagpt/roles/test_researcher.py b/tests/metagpt/roles/test_researcher.py index ba05e1296..9ce3bc23b 100644 --- a/tests/metagpt/roles/test_researcher.py +++ b/tests/metagpt/roles/test_researcher.py @@ -1,3 +1,4 @@ +import tempfile from pathlib import Path from random import random from tempfile import TemporaryDirectory @@ -6,6 +7,7 @@ import pytest from metagpt.actions.research import CollectLinks from metagpt.roles import researcher +from metagpt.team import Team from metagpt.tools import SearchEngineType from metagpt.tools.search_engine import SearchEngine @@ -57,5 +59,13 @@ def test_write_report(mocker, context): assert (researcher.RESEARCH_PATH / f"{i+1}. metagpt.md").read_text().startswith("# Research Report") +@pytest.mark.asyncio +async def test_serialize(): + team = Team() + team.hire([researcher.Researcher()]) + with tempfile.TemporaryDirectory() as dirname: + team.serialize(Path(dirname) / "team.json") + + if __name__ == "__main__": pytest.main([__file__, "-s"])