Merge branch 'main' into incremental_development

This commit is contained in:
mannaandpoem 2023-12-27 14:43:29 +08:00
commit 2cb06a7888
15 changed files with 185 additions and 27 deletions

View file

@ -85,7 +85,7 @@ class CollectLinks(Action):
llm: BaseGPTAPI = Field(default_factory=LLM)
desc: str = "Collect links from a search engine."
search_engine: SearchEngine = Field(default_factory=SearchEngine)
rank_func: Union[Callable[[list[str]], None], None] = None
rank_func: Optional[Callable[[list[str]], None]] = None
async def run(
self,
@ -180,16 +180,18 @@ class WebBrowseAndSummarize(Action):
llm: BaseGPTAPI = Field(default_factory=LLM)
desc: str = "Explore the web and provide summaries of articles and webpages."
browse_func: Union[Callable[[list[str]], None], None] = None
web_browser_engine: WebBrowserEngine = WebBrowserEngine(
engine=WebBrowserEngineType.CUSTOM if browse_func else None,
run_func=browse_func,
)
web_browser_engine: Optional[WebBrowserEngine] = None
def __init__(self, **kwargs):
super().__init__(**kwargs)
if CONFIG.model_for_researcher_summary:
self.llm.model = CONFIG.model_for_researcher_summary
self.web_browser_engine = WebBrowserEngine(
engine=WebBrowserEngineType.CUSTOM if self.browse_func else None,
run_func=self.browse_func,
)
async def run(
self,
url: str,

View file

@ -107,7 +107,9 @@ class Config(metaclass=Singleton):
self.gemini_api_key = self._get("GEMINI_API_KEY")
self.ollama_api_base = self._get("OLLAMA_API_BASE")
self.ollama_api_model = self._get("OLLAMA_API_MODEL")
_ = self.get_default_llm_provider_enum()
if not self._get("DISABLE_LLM_PROVIDER_CHECK"):
_ = self.get_default_llm_provider_enum()
self.openai_base_url = self._get("OPENAI_BASE_URL")
self.openai_proxy = self._get("OPENAI_PROXY") or self.global_proxy

View file

@ -6,6 +6,8 @@
@File : llm.py
"""
from typing import Optional
from metagpt.config import CONFIG, LLMProviderEnum
from metagpt.provider.base_gpt_api import BaseGPTAPI
from metagpt.provider.human_provider import HumanProvider
@ -14,6 +16,9 @@ from metagpt.provider.llm_provider_registry import LLM_REGISTRY
_ = HumanProvider() # Avoid pre-commit error
def LLM(provider: LLMProviderEnum = CONFIG.get_default_llm_provider_enum()) -> BaseGPTAPI:
def LLM(provider: Optional[LLMProviderEnum] = None) -> BaseGPTAPI:
"""get the default llm provider"""
if provider is None:
provider = CONFIG.get_default_llm_provider_enum()
return LLM_REGISTRY.get_provider(provider)

View file

@ -8,6 +8,7 @@
import sys
from datetime import datetime
from functools import partial
from loguru import logger as _logger
@ -26,3 +27,15 @@ def define_log_level(print_level="INFO", logfile_level="DEBUG"):
logger = define_log_level()
def log_llm_stream(msg):
_llm_stream_log(msg)
def set_llm_stream_logfunc(func):
global _llm_stream_log
_llm_stream_log = func
_llm_stream_log = partial(print, end="")

View file

@ -20,7 +20,7 @@ from tenacity import (
)
from metagpt.config import CONFIG, LLMProviderEnum
from metagpt.logs import logger
from metagpt.logs import log_llm_stream, logger
from metagpt.provider.base_gpt_api import BaseGPTAPI
from metagpt.provider.llm_provider_registry import register_provider
from metagpt.provider.openai_api import CostManager, log_and_reraise
@ -119,7 +119,7 @@ class GeminiGPTAPI(BaseGPTAPI):
collected_content = []
async for chunk in resp:
content = chunk.text
print(content, end="")
log_llm_stream(content)
collected_content.append(content)
full_content = "".join(collected_content)

View file

@ -15,7 +15,7 @@ from tenacity import (
from metagpt.config import CONFIG, LLMProviderEnum
from metagpt.const import LLM_API_TIMEOUT
from metagpt.logs import logger
from metagpt.logs import log_llm_stream, logger
from metagpt.provider.base_gpt_api import BaseGPTAPI
from metagpt.provider.general_api_requestor import GeneralAPIRequestor
from metagpt.provider.llm_provider_registry import register_provider
@ -127,7 +127,7 @@ class OllamaGPTAPI(BaseGPTAPI):
if not chunk.get("done", False):
content = self.get_choice_text(chunk)
collected_content.append(content)
print(content, end="")
log_llm_stream(content)
else:
# stream finished
usage = self.get_usage(chunk)

View file

@ -29,7 +29,7 @@ from tenacity import (
)
from metagpt.config import CONFIG, Config, LLMProviderEnum
from metagpt.logs import logger
from metagpt.logs import log_llm_stream, logger
from metagpt.provider.base_gpt_api import BaseGPTAPI
from metagpt.provider.constant import GENERAL_FUNCTION_SCHEMA, GENERAL_TOOL_CHOICE
from metagpt.provider.llm_provider_registry import register_provider
@ -222,7 +222,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter):
chunk_message = chunk.choices[0].delta # extract the message
collected_messages.append(chunk_message) # save the message
if chunk_message.content:
print(chunk_message.content, end="")
log_llm_stream(chunk_message.content)
print()
full_reply_content = "".join([m.content for m in collected_messages if m.content])

View file

@ -17,7 +17,7 @@ from tenacity import (
)
from metagpt.config import CONFIG, LLMProviderEnum
from metagpt.logs import logger
from metagpt.logs import log_llm_stream, logger
from metagpt.provider.base_gpt_api import BaseGPTAPI
from metagpt.provider.llm_provider_registry import register_provider
from metagpt.provider.openai_api import CostManager, log_and_reraise
@ -50,6 +50,8 @@ class ZhiPuAIGPTAPI(BaseGPTAPI):
assert config.zhipuai_api_key
zhipuai.api_key = config.zhipuai_api_key
openai.api_key = zhipuai.api_key # due to use openai sdk, set the api_key but it will't be used.
if config.openai_proxy:
openai.proxy = config.openai_proxy
def _const_kwargs(self, messages: list[dict]) -> dict:
kwargs = {"model": self.model, "prompt": messages, "temperature": 0.3}
@ -94,7 +96,7 @@ class ZhiPuAIGPTAPI(BaseGPTAPI):
if event.event == ZhiPuEvent.ADD.value:
content = event.data
collected_content.append(content)
print(content, end="")
log_llm_stream(content)
elif event.event == ZhiPuEvent.ERROR.value or event.event == ZhiPuEvent.INTERRUPTED.value:
content = event.data
logger.error(f"event error: {content}", end="")

View file

@ -5,6 +5,7 @@
"""
import asyncio
import re
from pydantic import BaseModel
@ -95,9 +96,11 @@ class Researcher(Role):
return msg
def write_report(self, topic: str, content: str):
filename = re.sub(r'[\\/:"*?<>|]+', " ", topic)
filename = filename.replace("\n", "")
if not RESEARCH_PATH.exists():
RESEARCH_PATH.mkdir(parents=True)
filepath = RESEARCH_PATH / f"{topic}.md"
filepath = RESEARCH_PATH / f"{filename}.md"
filepath.write_text(content)

View file

@ -152,7 +152,7 @@ class Role(BaseModel):
builtin_class_name: str = ""
_private_attributes = {
"_llm": LLM() if not is_human else HumanProvider(),
"_llm": None,
"_role_id": _role_id,
"_states": [],
"_actions": [],

View file

@ -7,13 +7,13 @@
@Modified By: mashenquan, 2023-11-1. In accordance with Chapter 2.2.1 and 2.2.2 of RFC 116, utilize the new message
distribution feature for message filtering.
"""
from typing import Any, Type
from pydantic import Field
from semantic_kernel import Kernel
from semantic_kernel.orchestration.sk_function_base import SKFunctionBase
from semantic_kernel.planning import SequentialPlanner
from semantic_kernel.planning.action_planner.action_planner import ActionPlanner
from semantic_kernel.planning.basic_planner import BasicPlanner, Plan
from semantic_kernel.planning.basic_planner import BasicPlanner
from metagpt.actions import UserRequirement
from metagpt.actions.execute_task import ExecuteTask
@ -41,13 +41,13 @@ class SkAgent(Role):
goal: str = "Execute task based on passed in task description"
constraints: str = ""
plan: Plan = None
planner_cls: BasicPlanner = BasicPlanner
planner: BasicPlanner = Field(default_factory=BasicPlanner)
plan: Any = None
planner_cls: Any = None
planner: Any = None
llm: BaseGPTAPI = Field(default_factory=LLM)
kernel: Kernel = Field(default_factory=Kernel)
import_semantic_skill_from_directory: str = ""
import_skill: dict[str, SKFunctionBase] = dict()
import_semantic_skill_from_directory: Type[Kernel.import_semantic_skill_from_directory] = None
import_skill: Type[Kernel.import_skill] = None
def __init__(self, **kwargs) -> None:
"""Initializes the Engineer role with given attributes."""
@ -57,8 +57,8 @@ class SkAgent(Role):
self.kernel = make_sk_kernel()
# how funny the interface is inconsistent
if self.planner_cls == BasicPlanner:
self.planner = self.planner_cls()
if self.planner_cls == BasicPlanner or self.planner_cls is None:
self.planner = BasicPlanner()
elif self.planner_cls in [SequentialPlanner, ActionPlanner]:
self.planner = self.planner_cls(self.kernel)
else:
@ -78,6 +78,7 @@ class SkAgent(Role):
async def _act(self) -> Message:
# how funny the interface is inconsistent
result = None
if isinstance(self.planner, BasicPlanner):
result = await self.planner.execute_plan_async(self.plan, self.kernel)
elif any(isinstance(self.planner, cls) for cls in [SequentialPlanner, ActionPlanner]):