From 832294809b097793dff3472b1183aed37f8f5c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 11:01:27 +0800 Subject: [PATCH 01/68] feat: + LLMType --- metagpt/llm.py | 24 ++++++++++++++++++++++-- metagpt/provider/__init__.py | 4 +++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/metagpt/llm.py b/metagpt/llm.py index 6a9a9132f..0ef23d0be 100644 --- a/metagpt/llm.py +++ b/metagpt/llm.py @@ -4,17 +4,37 @@ @Time : 2023/5/11 14:45 @Author : alexanderwu @File : llm.py +@Modified By: mashenquan, 2023 """ +from enum import Enum from metagpt.provider.anthropic_api import Claude2 as Claude from metagpt.provider.openai_api import OpenAIGPTAPI as LLM + +class LLMType(Enum): + OPENAI = "OpenAI" + METAGPT = "MetaGPT" + UNKNOWN = "UNKNOWN" + + @classmethod + def get(cls, value): + for member in cls: + if member.value == value: + return member + return cls.UNKNOWN + + @property + def UNKNOWN(self): + return LLMType.UNKNOWN + + DEFAULT_LLM = LLM() CLAUDE_LLM = Claude() async def ai_func(prompt): """使用LLM进行QA - QA with LLMs - """ + QA with LLMs + """ return await DEFAULT_LLM.aask(prompt) diff --git a/metagpt/provider/__init__.py b/metagpt/provider/__init__.py index 56dc19b4b..9895aa7fc 100644 --- a/metagpt/provider/__init__.py +++ b/metagpt/provider/__init__.py @@ -4,9 +4,11 @@ @Time : 2023/5/5 22:59 @Author : alexanderwu @File : __init__.py +@Modified By: mashenquan, 2023/9/8. Add `MetaGPTLLMAPI` """ from metagpt.provider.openai_api import OpenAIGPTAPI +from metagpt.provider.metagpt_llm_api import MetaGPTLLMAPI -__all__ = ["OpenAIGPTAPI"] +__all__ = ["OpenAIGPTAPI", "MetaGPTLLMAPI"] From 154f67c5e32467a9a21b5cb979aed25fa7e32520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 11:06:39 +0800 Subject: [PATCH 02/68] feat: + LLMType --- metagpt/llm.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/metagpt/llm.py b/metagpt/llm.py index 0ef23d0be..e31eee908 100644 --- a/metagpt/llm.py +++ b/metagpt/llm.py @@ -9,7 +9,8 @@ from enum import Enum from metagpt.provider.anthropic_api import Claude2 as Claude -from metagpt.provider.openai_api import OpenAIGPTAPI as LLM +from metagpt.provider.metagpt_llm_api import MetaGPTLLMAPI as MetaGPT_LLM +from metagpt.provider.openai_api import OpenAIGPTAPI as OpenAI_LLM class LLMType(Enum): @@ -29,7 +30,8 @@ class LLMType(Enum): return LLMType.UNKNOWN -DEFAULT_LLM = LLM() +DEFAULT_LLM = OpenAI_LLM() +DEFAULT_METAGPT_LLM = MetaGPT_LLM() CLAUDE_LLM = Claude() From e316fe4d60ac8b2de96729fdc998d0db6069d1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 11:20:27 +0800 Subject: [PATCH 03/68] feat: + LLMType --- metagpt/llm.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/metagpt/llm.py b/metagpt/llm.py index e31eee908..87ce8fa5b 100644 --- a/metagpt/llm.py +++ b/metagpt/llm.py @@ -8,6 +8,7 @@ """ from enum import Enum +from metagpt.config import CONFIG from metagpt.provider.anthropic_api import Claude2 as Claude from metagpt.provider.metagpt_llm_api import MetaGPTLLMAPI as MetaGPT_LLM from metagpt.provider.openai_api import OpenAIGPTAPI as OpenAI_LLM @@ -40,3 +41,10 @@ async def ai_func(prompt): QA with LLMs """ return await DEFAULT_LLM.aask(prompt) + + +class LLMFactory: + @staticmethod + async def new_llm() -> object: + llm = OpenAI_LLM() if CONFIG.LLM_TYPE == LLMType.OPENAI.value else MetaGPT_LLM() + return llm From c513712928e58ae3782819b29accf515ff366de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 11:43:34 +0800 Subject: [PATCH 04/68] feat: + kwargs --- metagpt/provider/openai_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 863475f52..64267975e 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -223,7 +223,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): return CONFIG.max_tokens_rsp return get_max_completion_tokens(messages, self.model, CONFIG.max_tokens_rsp) - async def get_summary(self, text: str, max_words=200, keep_language: bool = False): + async def get_summary(self, text: str, max_words=200, keep_language: bool = False, **kwargs): max_token_count = DEFAULT_MAX_TOKENS max_count = 100 text_length = len(text) @@ -262,7 +262,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): logger.debug(f"summary rsp: {response}") return response - async def get_context_title(self, text: str, max_words=5) -> str: + async def get_context_title(self, text: str, max_words=5, **kwargs) -> str: """Generate text title""" summary = await self.get_summary(text, max_words=500) From a41fe2494e34af249ebdd530f0a8cecb2b3a259c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 11:55:36 +0800 Subject: [PATCH 05/68] feat: +LLMType --- metagpt/llm.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/metagpt/llm.py b/metagpt/llm.py index 87ce8fa5b..93cbcaaf6 100644 --- a/metagpt/llm.py +++ b/metagpt/llm.py @@ -26,10 +26,6 @@ class LLMType(Enum): return member return cls.UNKNOWN - @property - def UNKNOWN(self): - return LLMType.UNKNOWN - DEFAULT_LLM = OpenAI_LLM() DEFAULT_METAGPT_LLM = MetaGPT_LLM() From 3a4f31b51787f2e60bed4efda45f63d49e1637ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 12:00:30 +0800 Subject: [PATCH 06/68] feat: +LLMType --- metagpt/actions/action.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/metagpt/actions/action.py b/metagpt/actions/action.py index e4b9613ad..c52caaa40 100644 --- a/metagpt/actions/action.py +++ b/metagpt/actions/action.py @@ -5,6 +5,7 @@ @Author : alexanderwu @File : action.py @Modified By: mashenquan, 2023/8/20. Add function return annotations. +@Modified By: mashenquan, 2023/9/8. Replace LLM with LLMFactory """ from __future__ import annotations @@ -14,16 +15,17 @@ from typing import Optional from tenacity import retry, stop_after_attempt, wait_fixed from metagpt.actions.action_output import ActionOutput -from metagpt.llm import LLM +from metagpt.llm import LLMFactory from metagpt.logs import logger +from metagpt.provider.base_gpt_api import BaseGPTAPI from metagpt.utils.common import OutputParser class Action(ABC): - def __init__(self, name: str = "", context=None, llm: LLM = None): + def __init__(self, name: str = "", context=None, llm: BaseGPTAPI = None): self.name: str = name if llm is None: - llm = LLM() + llm = LLMFactory.new_llm() self.llm = llm self.context = context self.prefix = "" From c7bc975cf20c926dcc52756efcc3038c9b6b30f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 12:02:43 +0800 Subject: [PATCH 07/68] fixbug: LLM() --- metagpt/roles/role.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index b1ace19fa..6d774b0b4 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -16,7 +16,7 @@ from pydantic import BaseModel, Field from metagpt.actions import Action, ActionOutput from metagpt.config import CONFIG from metagpt.const import OPTIONS -from metagpt.llm import LLM +from metagpt.llm import LLMFactory from metagpt.logs import logger from metagpt.memory import LongTermMemory, Memory from metagpt.schema import Message, MessageTag @@ -113,7 +113,7 @@ class Role: constraints = Role.format_value(constraints) desc = Role.format_value(desc) - self._llm = LLM() + self._llm = LLMFactory.new_llm() self._setting = RoleSetting(name=name, profile=profile, goal=goal, constraints=constraints, desc=desc) self._states = [] self._actions = [] From 2324c1c6dcc334cfe368b3e4252db3060dbfbba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 12:44:49 +0800 Subject: [PATCH 08/68] fixbug: LLM() --- metagpt/llm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/llm.py b/metagpt/llm.py index 93cbcaaf6..4772d2e6e 100644 --- a/metagpt/llm.py +++ b/metagpt/llm.py @@ -41,6 +41,6 @@ async def ai_func(prompt): class LLMFactory: @staticmethod - async def new_llm() -> object: + def new_llm() -> object: llm = OpenAI_LLM() if CONFIG.LLM_TYPE == LLMType.OPENAI.value else MetaGPT_LLM() return llm From ef485e7400546e5577f3ca59fcc089c811e13f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 13:52:36 +0800 Subject: [PATCH 09/68] feat: + summary --- metagpt/provider/metagpt_llm_api.py | 21 +++++++++++++++++++++ metagpt/roles/assistant.py | 4 +++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/metagpt/provider/metagpt_llm_api.py b/metagpt/provider/metagpt_llm_api.py index c27e7132d..f8c4ac1ed 100644 --- a/metagpt/provider/metagpt_llm_api.py +++ b/metagpt/provider/metagpt_llm_api.py @@ -5,10 +5,13 @@ @File : metagpt_llm_api.py @Desc : MetaGPT LLM related APIs """ +import json import openai +from pydantic import BaseModel from metagpt.config import CONFIG +from metagpt.memory.brain_memory import BrainMemory from metagpt.provider import OpenAIGPTAPI from metagpt.provider.openai_api import RateLimiter @@ -31,3 +34,21 @@ class MetaGPTLLMAPI(OpenAIGPTAPI): openai.api_type = CONFIG.METAGPT_API_TYPE openai.api_version = CONFIG.METAGPT_API_VERSION self.rpm = int(CONFIG.RPM) if CONFIG.RPM else 10 + + async def get_summary(self, memory: BrainMemory, max_words=200, keep_language: bool = False, **kwargs): + summary = [] + + class QuweryAnswerPair(BaseModel): + ask: str + answer: str + + rh = reversed(memory.history) + ix = 0 + while ix < len(rh): + t = rh[ix] + print(t) + # 如果 t是ask, continue + pass + + data = json.dumps(summary) + return data diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index cd1932f82..0a796ac11 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -121,7 +121,9 @@ class Assistant(Role): return None if history_text == "": return last_talk - history_summary = await self._llm.get_summary(history_text, max_words=800, keep_language=True) + history_summary = await self._llm.get_summary( + history_text, max_words=800, keep_language=True, memory=self.memory + ) await self.memory.set_history_summary( history_summary=history_summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS ) From 6848d189cfd5c5d5df05d53fb825c64a85121090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 14:15:03 +0800 Subject: [PATCH 10/68] feat: + summary --- metagpt/provider/metagpt_llm_api.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/metagpt/provider/metagpt_llm_api.py b/metagpt/provider/metagpt_llm_api.py index f8c4ac1ed..0688e1878 100644 --- a/metagpt/provider/metagpt_llm_api.py +++ b/metagpt/provider/metagpt_llm_api.py @@ -27,12 +27,12 @@ class MetaGPTLLMAPI(OpenAIGPTAPI): RateLimiter.__init__(self, rpm=self.rpm) def __init_openai(self, *args, **kwargs): - openai.api_key = CONFIG.METAGPT_API_KEY - if CONFIG.METAGPT_API_BASE: - openai.api_base = CONFIG.METAGPT_API_BASE - if CONFIG.METAGPT_API_TYPE: - openai.api_type = CONFIG.METAGPT_API_TYPE - openai.api_version = CONFIG.METAGPT_API_VERSION + # openai.api_key = CONFIG.METAGPT_API_KEY + # if CONFIG.METAGPT_API_BASE: + # openai.api_base = CONFIG.METAGPT_API_BASE + # if CONFIG.METAGPT_API_TYPE: + # openai.api_type = CONFIG.METAGPT_API_TYPE + # openai.api_version = CONFIG.METAGPT_API_VERSION self.rpm = int(CONFIG.RPM) if CONFIG.RPM else 10 async def get_summary(self, memory: BrainMemory, max_words=200, keep_language: bool = False, **kwargs): From 451b3510552fc22eef69b4e6f44e0a4caea7f75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 14:18:19 +0800 Subject: [PATCH 11/68] feat: + summary --- metagpt/provider/metagpt_llm_api.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/metagpt/provider/metagpt_llm_api.py b/metagpt/provider/metagpt_llm_api.py index 0688e1878..17c2b3ab8 100644 --- a/metagpt/provider/metagpt_llm_api.py +++ b/metagpt/provider/metagpt_llm_api.py @@ -20,20 +20,11 @@ class MetaGPTLLMAPI(OpenAIGPTAPI): """MetaGPT LLM api""" def __init__(self): - self.__init_openai() self.llm = openai self.model = CONFIG.METAGPT_API_MODEL self.auto_max_tokens = False - RateLimiter.__init__(self, rpm=self.rpm) - - def __init_openai(self, *args, **kwargs): - # openai.api_key = CONFIG.METAGPT_API_KEY - # if CONFIG.METAGPT_API_BASE: - # openai.api_base = CONFIG.METAGPT_API_BASE - # if CONFIG.METAGPT_API_TYPE: - # openai.api_type = CONFIG.METAGPT_API_TYPE - # openai.api_version = CONFIG.METAGPT_API_VERSION self.rpm = int(CONFIG.RPM) if CONFIG.RPM else 10 + RateLimiter.__init__(self, rpm=self.rpm) async def get_summary(self, memory: BrainMemory, max_words=200, keep_language: bool = False, **kwargs): summary = [] From 098027d249e709c4a939d8feb042e76f26ef0116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 14:23:36 +0800 Subject: [PATCH 12/68] feat: + summary --- metagpt/const.py | 6 ------ metagpt/provider/metagpt_llm_api.py | 9 +-------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/metagpt/const.py b/metagpt/const.py index e9fa118d7..2323e3b6d 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -48,12 +48,6 @@ BRAIN_MEMORY = "BRAIN_MEMORY" SKILL_PATH = "SKILL_PATH" SERPER_API_KEY = "SERPER_API_KEY" -# Key Definitions for MetaGPT LLM -METAGPT_API_MODEL = "METAGPT_API_MODEL" -METAGPT_API_KEY = "METAGPT_API_KEY" -METAGPT_API_BASE = "METAGPT_API_BASE" -METAGPT_API_TYPE = "METAGPT_API_TYPE" -METAGPT_API_VERSION = "METAGPT_API_VERSION" # format BASE64_FORMAT = "base64" diff --git a/metagpt/provider/metagpt_llm_api.py b/metagpt/provider/metagpt_llm_api.py index 17c2b3ab8..c21ffd650 100644 --- a/metagpt/provider/metagpt_llm_api.py +++ b/metagpt/provider/metagpt_llm_api.py @@ -7,24 +7,17 @@ """ import json -import openai from pydantic import BaseModel -from metagpt.config import CONFIG from metagpt.memory.brain_memory import BrainMemory from metagpt.provider import OpenAIGPTAPI -from metagpt.provider.openai_api import RateLimiter class MetaGPTLLMAPI(OpenAIGPTAPI): """MetaGPT LLM api""" def __init__(self): - self.llm = openai - self.model = CONFIG.METAGPT_API_MODEL - self.auto_max_tokens = False - self.rpm = int(CONFIG.RPM) if CONFIG.RPM else 10 - RateLimiter.__init__(self, rpm=self.rpm) + super().__init__() async def get_summary(self, memory: BrainMemory, max_words=200, keep_language: bool = False, **kwargs): summary = [] From 239f68d40d1c49b94736344a94e8459fee43c535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 14:30:36 +0800 Subject: [PATCH 13/68] feat: + summary --- metagpt/actions/action.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metagpt/actions/action.py b/metagpt/actions/action.py index c52caaa40..92608f448 100644 --- a/metagpt/actions/action.py +++ b/metagpt/actions/action.py @@ -15,7 +15,6 @@ from typing import Optional from tenacity import retry, stop_after_attempt, wait_fixed from metagpt.actions.action_output import ActionOutput -from metagpt.llm import LLMFactory from metagpt.logs import logger from metagpt.provider.base_gpt_api import BaseGPTAPI from metagpt.utils.common import OutputParser @@ -25,6 +24,8 @@ class Action(ABC): def __init__(self, name: str = "", context=None, llm: BaseGPTAPI = None): self.name: str = name if llm is None: + from metagpt.llm import LLMFactory + llm = LLMFactory.new_llm() self.llm = llm self.context = context From bda4132a9062a995616b1fd6ac93d35d218812dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 14:59:40 +0800 Subject: [PATCH 14/68] feat: + summary --- metagpt/schema.py | 1 + 1 file changed, 1 insertion(+) diff --git a/metagpt/schema.py b/metagpt/schema.py index 8f8e4030f..9bf85174b 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -76,6 +76,7 @@ class Message: "sent_from": self.sent_from, "send_to": self.send_to, "tags": self.tags, + "id": self.id, } m = {"content": self.content} From 7723df1455b2a646e431d721ca9326d7872bb67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 15:55:50 +0800 Subject: [PATCH 15/68] feat: + summary --- metagpt/provider/metagpt_llm_api.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/metagpt/provider/metagpt_llm_api.py b/metagpt/provider/metagpt_llm_api.py index c21ffd650..06476f63b 100644 --- a/metagpt/provider/metagpt_llm_api.py +++ b/metagpt/provider/metagpt_llm_api.py @@ -6,10 +6,10 @@ @Desc : MetaGPT LLM related APIs """ import json +from typing import Dict, List from pydantic import BaseModel -from metagpt.memory.brain_memory import BrainMemory from metagpt.provider import OpenAIGPTAPI @@ -19,17 +19,22 @@ class MetaGPTLLMAPI(OpenAIGPTAPI): def __init__(self): super().__init__() - async def get_summary(self, memory: BrainMemory, max_words=200, keep_language: bool = False, **kwargs): + async def get_summary(self, history: List[Dict], max_words=200, keep_language: bool = False, **kwargs): summary = [] + class HisMsg(BaseModel): + content: str + tags: set + id: str + class QuweryAnswerPair(BaseModel): ask: str answer: str - rh = reversed(memory.history) + rh = reversed(history) ix = 0 while ix < len(rh): - t = rh[ix] + t = HisMsg(**rh[ix]) print(t) # 如果 t是ask, continue pass From a0ad7872f7dfc946ddc27d5e106fc3ab82130dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 15:57:24 +0800 Subject: [PATCH 16/68] feat: + summary --- metagpt/roles/assistant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index 0a796ac11..5d04c2d6f 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -122,7 +122,7 @@ class Assistant(Role): if history_text == "": return last_talk history_summary = await self._llm.get_summary( - history_text, max_words=800, keep_language=True, memory=self.memory + history_text, max_words=800, keep_language=True, history=self.memory.history ) await self.memory.set_history_summary( history_summary=history_summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS From 8a0644a496fb956106dbcbc5697cd48617b85009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 15:58:13 +0800 Subject: [PATCH 17/68] feat: + summary --- metagpt/roles/assistant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index 5d04c2d6f..2f9059210 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -122,7 +122,7 @@ class Assistant(Role): if history_text == "": return last_talk history_summary = await self._llm.get_summary( - history_text, max_words=800, keep_language=True, history=self.memory.history + text=history_text, max_words=800, keep_language=True, history=self.memory.history ) await self.memory.set_history_summary( history_summary=history_summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS From 4c9a5d8dda1238dbe0056243d3ef1860f6be0d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 16:45:01 +0800 Subject: [PATCH 18/68] feat: truncated history --- metagpt/provider/metagpt_llm_api.py | 53 ++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/metagpt/provider/metagpt_llm_api.py b/metagpt/provider/metagpt_llm_api.py index 06476f63b..d8d06aeaa 100644 --- a/metagpt/provider/metagpt_llm_api.py +++ b/metagpt/provider/metagpt_llm_api.py @@ -10,34 +10,55 @@ from typing import Dict, List from pydantic import BaseModel +from metagpt.memory.brain_memory import MessageType from metagpt.provider import OpenAIGPTAPI +class HisMsg(BaseModel): + content: str + tags: set + id: str + + +class Conversion(BaseModel): + """See: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb""" + + role: str + content: str + + class MetaGPTLLMAPI(OpenAIGPTAPI): """MetaGPT LLM api""" def __init__(self): super().__init__() - async def get_summary(self, history: List[Dict], max_words=200, keep_language: bool = False, **kwargs): + async def get_summary(self, history: List[Dict], max_words=200, keep_language: bool = False, **kwargs) -> str: + """ + Return string in the following format: + [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Knock knock."}, + {"role": "assistant", "content": "Who's there?"}, + {"role": "user", "content": "Orange."}, + ] + """ summary = [] - class HisMsg(BaseModel): - content: str - tags: set - id: str + total_length = 0 + for m in reversed(history): + msg = HisMsg(**m) + c = Conversion(role="user" if MessageType.Talk.value in msg.tags else "assistant", content=msg.content) + length_delta = len(msg.content) + if total_length + length_delta > max_words: + left = max_words - total_length + if left > 0: + c.content = msg.content[0:left] + summary.insert(0, c.dict()) + break - class QuweryAnswerPair(BaseModel): - ask: str - answer: str - - rh = reversed(history) - ix = 0 - while ix < len(rh): - t = HisMsg(**rh[ix]) - print(t) - # 如果 t是ask, continue - pass + total_length += length_delta + summary.insert(0, c.dict()) data = json.dumps(summary) return data From 05532426c08a250ec4d7661fbecf79bd918b1ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 17:02:11 +0800 Subject: [PATCH 19/68] feat: truncated history --- metagpt/memory/brain_memory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index f309b532e..d83611af1 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -107,7 +107,7 @@ class BrainMemory(pydantic.BaseModel): def add_history(self, msg: Message): if msg.id: - if self.to_int(msg.id, 0) < self.to_int(self.last_history_id, -1): + if self.to_int(msg.id, 0) <= self.to_int(self.last_history_id, -1): return self.history.append(msg.dict()) self.is_dirty = True From 92402bedd4e2fe171e9ee9732b9ad120075e0da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 17:09:12 +0800 Subject: [PATCH 20/68] feat: truncated history --- metagpt/memory/brain_memory.py | 1 + 1 file changed, 1 insertion(+) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index d83611af1..04ae6593a 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -110,6 +110,7 @@ class BrainMemory(pydantic.BaseModel): if self.to_int(msg.id, 0) <= self.to_int(self.last_history_id, -1): return self.history.append(msg.dict()) + self.last_history_id = str(msg.id) self.is_dirty = True def exists(self, text) -> bool: From 4c82298e8864f9e8f3712aa9bb6333079a015749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 18:21:10 +0800 Subject: [PATCH 21/68] feat: truncated history --- metagpt/memory/brain_memory.py | 62 ++++++++++++++++++++++++----- metagpt/provider/metagpt_llm_api.py | 41 ++----------------- metagpt/roles/assistant.py | 2 +- 3 files changed, 56 insertions(+), 49 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 04ae6593a..e8a98c55b 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -15,6 +15,7 @@ import pydantic from metagpt import Message from metagpt.logs import logger +from metagpt.schema import RawMessage from metagpt.utils.redis import Redis @@ -54,17 +55,21 @@ class BrainMemory(pydantic.BaseModel): def history_text(self): if len(self.history) == 0 and not self.historical_summary: return "" - texts = [self.historical_summary] if self.historical_summary else [] - for m in self.history[:-1]: - if isinstance(m, Dict): - t = Message(**m).content - elif isinstance(m, Message): - t = m.content - else: - continue - texts.append(t) + try: + self.loads_raw_messages() + return self.dumps_raw_messages() + except: + texts = [self.historical_summary] if self.historical_summary else [] + for m in self.history[:-1]: + if isinstance(m, Dict): + t = Message(**m).content + elif isinstance(m, Message): + t = m.content + else: + continue + texts.append(t) - return "\n".join(texts) + return "\n".join(texts) @staticmethod async def loads(redis_key: str, redis_conf: Dict = None) -> "BrainMemory": @@ -130,3 +135,40 @@ class BrainMemory(pydantic.BaseModel): v = self.last_talk self.last_talk = None return v + + def loads_raw_messages(self): + if not self.historical_summary: + return + vv = json.loads(self.historical_summary) + msgs = [] + for v in vv: + tag = set([MessageType.Talk.value]) if v.get("role") == "user" else set([MessageType.Answer.value]) + m = Message(content=v.get("content"), tags=tag) + msgs.append(m) + msgs.extend(self.history) + self.history = msgs + self.is_dirty = True + + def dumps_raw_messages(self, max_length: int = 0) -> str: + summary = [] + + total_length = 0 + for m in reversed(self.history): + msg = Message(**m) + c = RawMessage(role="user" if MessageType.Talk.value in msg.tags else "assistant", content=msg.content) + length_delta = len(msg.content) + if max_length > 0: + if total_length + length_delta > max_length: + left = max_length - total_length + if left > 0: + c.content = msg.content[0:left] + summary.insert(0, c) + break + + total_length += length_delta + summary.insert(0, c) + + self.historical_summary = json.dumps(summary) + self.history = [] + self.is_dirty = True + return self.historical_summary diff --git a/metagpt/provider/metagpt_llm_api.py b/metagpt/provider/metagpt_llm_api.py index d8d06aeaa..3ae65a623 100644 --- a/metagpt/provider/metagpt_llm_api.py +++ b/metagpt/provider/metagpt_llm_api.py @@ -5,35 +5,18 @@ @File : metagpt_llm_api.py @Desc : MetaGPT LLM related APIs """ -import json -from typing import Dict, List -from pydantic import BaseModel - -from metagpt.memory.brain_memory import MessageType +from metagpt.memory.brain_memory import BrainMemory from metagpt.provider import OpenAIGPTAPI -class HisMsg(BaseModel): - content: str - tags: set - id: str - - -class Conversion(BaseModel): - """See: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb""" - - role: str - content: str - - class MetaGPTLLMAPI(OpenAIGPTAPI): """MetaGPT LLM api""" def __init__(self): super().__init__() - async def get_summary(self, history: List[Dict], max_words=200, keep_language: bool = False, **kwargs) -> str: + async def get_summary(self, memory: BrainMemory, max_words=200, keep_language: bool = False, **kwargs) -> str: """ Return string in the following format: [ @@ -43,22 +26,4 @@ class MetaGPTLLMAPI(OpenAIGPTAPI): {"role": "user", "content": "Orange."}, ] """ - summary = [] - - total_length = 0 - for m in reversed(history): - msg = HisMsg(**m) - c = Conversion(role="user" if MessageType.Talk.value in msg.tags else "assistant", content=msg.content) - length_delta = len(msg.content) - if total_length + length_delta > max_words: - left = max_words - total_length - if left > 0: - c.content = msg.content[0:left] - summary.insert(0, c.dict()) - break - - total_length += length_delta - summary.insert(0, c.dict()) - - data = json.dumps(summary) - return data + return memory.dumps_raw_messages(max_length=max_words) diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index 2f9059210..2fcb6f584 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -122,7 +122,7 @@ class Assistant(Role): if history_text == "": return last_talk history_summary = await self._llm.get_summary( - text=history_text, max_words=800, keep_language=True, history=self.memory.history + text=history_text, max_words=800, keep_language=True, memory=self.memory ) await self.memory.set_history_summary( history_summary=history_summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS From 530d2f5b308a9c280853a20f51c2fac929c95134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 19:03:41 +0800 Subject: [PATCH 22/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 113 +++++++++++++++++++++++++++++++++ metagpt/provider/openai_api.py | 110 -------------------------------- metagpt/roles/assistant.py | 20 +++--- 3 files changed, 123 insertions(+), 120 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index e8a98c55b..7eda9c601 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -8,12 +8,16 @@ @Modified By: mashenquan, 2023/9/4. + redis memory cache. """ import json +import re from enum import Enum from typing import Dict, List +import openai import pydantic from metagpt import Message +from metagpt.config import CONFIG +from metagpt.const import DEFAULT_LANGUAGE, DEFAULT_MAX_TOKENS from metagpt.logs import logger from metagpt.schema import RawMessage from metagpt.utils.redis import Redis @@ -36,6 +40,7 @@ class BrainMemory(pydantic.BaseModel): last_history_id: str = "" is_dirty: bool = False last_talk: str = None + llm_type: str def add_talk(self, msg: Message): msg.add_tag(MessageType.Talk.value) @@ -172,3 +177,111 @@ class BrainMemory(pydantic.BaseModel): self.history = [] self.is_dirty = True return self.historical_summary + + async def get_summary(self, text: str, llm, max_words=200, keep_language: bool = False, **kwargs): + max_token_count = DEFAULT_MAX_TOKENS + max_count = 100 + text_length = len(text) + while max_count > 0: + if text_length < max_token_count: + return await self._get_summary(text=text, llm=llm, max_words=max_words, keep_language=keep_language) + + padding_size = 20 if max_token_count > 20 else 0 + text_windows = self.split_texts(text, window_size=max_token_count - padding_size) + part_max_words = min(int(max_words / len(text_windows)) + 1, 100) + summaries = [] + for ws in text_windows: + response = await self._get_summary(text=ws, max_words=part_max_words, keep_language=keep_language) + summaries.append(response) + if len(summaries) == 1: + return summaries[0] + + # Merged and retry + text = "\n".join(summaries) + text_length = len(text) + + max_count -= 1 # safeguard + raise openai.error.InvalidRequestError("text too long") + + async def _get_summary(self, text: str, llm, max_words=20, keep_language: bool = False): + """Generate text summary""" + if len(text) < max_words: + return text + if keep_language: + command = f".Translate the above content into a summary of less than {max_words} words in language of the content strictly." + else: + command = f"Translate the above content into a summary of less than {max_words} words." + msg = text + "\n\n" + command + logger.debug(f"summary ask:{msg}") + response = await llm.aask(msg=msg, system_msgs=[]) + logger.debug(f"summary rsp: {response}") + return response + + async def get_title(self, text: str, llm, max_words=5, **kwargs) -> str: + """Generate text title""" + summary = await self.get_summary(text, max_words=500) + + language = CONFIG.language or DEFAULT_LANGUAGE + command = f"Translate the above summary into a {language} title of less than {max_words} words." + summaries = [summary, command] + msg = "\n".join(summaries) + logger.debug(f"title ask:{msg}") + response = await llm.aask(msg=msg, system_msgs=[]) + logger.debug(f"title rsp: {response}") + return response + + async def is_related(self, text1, text2, llm): + # command = f"{text1}\n{text2}\n\nIf the two sentences above are related, return [TRUE] brief and clear. Otherwise, return [FALSE]." + command = f"{text2}\n\nIs there any sentence above related to the following sentence: {text1}.\nIf is there any relevance, return [TRUE] brief and clear. Otherwise, return [FALSE] brief and clear." + rsp = await llm.aask(msg=command, system_msgs=[]) + result = True if "TRUE" in rsp else False + p2 = text2.replace("\n", "") + p1 = text1.replace("\n", "") + logger.info(f"IS_RELATED:\nParagraph 1: {p2}\nParagraph 2: {p1}\nRESULT: {result}\n") + return result + + async def rewrite(self, sentence: str, context: str, llm): + # command = ( + # f"{context}\n\nConsidering the content above, rewrite and return this sentence brief and clear:\n{sentence}" + # ) + command = f"{context}\n\nExtract relevant information from every preceding sentence and use it to succinctly supplement or rewrite the following text in brief and clear:\n{sentence}" + rsp = await llm.aask(msg=command, system_msgs=[]) + logger.info(f"REWRITE:\nCommand: {command}\nRESULT: {rsp}\n") + return rsp + + @staticmethod + def split_texts(text: str, window_size) -> List[str]: + """Splitting long text into sliding windows text""" + if window_size <= 0: + window_size = BrainMemory.DEFAULT_TOKEN_SIZE + total_len = len(text) + if total_len <= window_size: + return [text] + + padding_size = 20 if window_size > 20 else 0 + windows = [] + idx = 0 + data_len = window_size - padding_size + while idx < total_len: + if window_size + idx > total_len: # 不足一个滑窗 + windows.append(text[idx:]) + break + # 每个窗口少算padding_size自然就可实现滑窗功能, 比如: [1, 2, 3, 4, 5, 6, 7, ....] + # window_size=3, padding_size=1: + # [1, 2, 3], [3, 4, 5], [5, 6, 7], .... + # idx=2, | idx=5 | idx=8 | ... + w = text[idx : idx + window_size] + windows.append(w) + idx += data_len + + return windows + + @staticmethod + def extract_info(input_string, pattern=r"\[([A-Z]+)\]:\s*(.+)"): + match = re.match(pattern, input_string) + if match: + return match.group(1), match.group(2) + else: + return None, input_string + + DEFAULT_TOKEN_SIZE = 500 diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 64267975e..231b568c7 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -8,10 +8,8 @@ """ import asyncio import random -import re import time import traceback -from typing import List import openai from openai.error import APIConnectionError @@ -24,7 +22,6 @@ from tenacity import ( ) from metagpt.config import CONFIG -from metagpt.const import DEFAULT_LANGUAGE, DEFAULT_MAX_TOKENS from metagpt.logs import logger from metagpt.provider.base_gpt_api import BaseGPTAPI from metagpt.utils.cost_manager import Costs @@ -223,112 +220,6 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): return CONFIG.max_tokens_rsp return get_max_completion_tokens(messages, self.model, CONFIG.max_tokens_rsp) - async def get_summary(self, text: str, max_words=200, keep_language: bool = False, **kwargs): - max_token_count = DEFAULT_MAX_TOKENS - max_count = 100 - text_length = len(text) - while max_count > 0: - if text_length < max_token_count: - return await self._get_summary(text=text, max_words=max_words, keep_language=keep_language) - - padding_size = 20 if max_token_count > 20 else 0 - text_windows = self.split_texts(text, window_size=max_token_count - padding_size) - part_max_words = min(int(max_words / len(text_windows)) + 1, 100) - summaries = [] - for ws in text_windows: - response = await self._get_summary(text=ws, max_words=part_max_words, keep_language=keep_language) - summaries.append(response) - if len(summaries) == 1: - return summaries[0] - - # Merged and retry - text = "\n".join(summaries) - text_length = len(text) - - max_count -= 1 # safeguard - raise openai.error.InvalidRequestError("text too long") - - async def _get_summary(self, text: str, max_words=20, keep_language: bool = False): - """Generate text summary""" - if len(text) < max_words: - return text - if keep_language: - command = f".Translate the above content into a summary of less than {max_words} words in language of the content strictly." - else: - command = f"Translate the above content into a summary of less than {max_words} words." - msg = text + "\n\n" + command - logger.debug(f"summary ask:{msg}") - response = await self.aask(msg=msg, system_msgs=[]) - logger.debug(f"summary rsp: {response}") - return response - - async def get_context_title(self, text: str, max_words=5, **kwargs) -> str: - """Generate text title""" - summary = await self.get_summary(text, max_words=500) - - language = CONFIG.language or DEFAULT_LANGUAGE - command = f"Translate the above summary into a {language} title of less than {max_words} words." - summaries = [summary, command] - msg = "\n".join(summaries) - logger.debug(f"title ask:{msg}") - response = await self.aask(msg=msg, system_msgs=[]) - logger.debug(f"title rsp: {response}") - return response - - async def is_related(self, text1, text2): - # command = f"{text1}\n{text2}\n\nIf the two sentences above are related, return [TRUE] brief and clear. Otherwise, return [FALSE]." - command = f"{text2}\n\nIs there any sentence above related to the following sentence: {text1}.\nIf is there any relevance, return [TRUE] brief and clear. Otherwise, return [FALSE] brief and clear." - rsp = await self.aask(msg=command, system_msgs=[]) - result = True if "TRUE" in rsp else False - p2 = text2.replace("\n", "") - p1 = text1.replace("\n", "") - logger.info(f"IS_RELATED:\nParagraph 1: {p2}\nParagraph 2: {p1}\nRESULT: {result}\n") - return result - - async def rewrite(self, sentence: str, context: str): - # command = ( - # f"{context}\n\nConsidering the content above, rewrite and return this sentence brief and clear:\n{sentence}" - # ) - command = f"{context}\n\nExtract relevant information from every preceding sentence and use it to succinctly supplement or rewrite the following text in brief and clear:\n{sentence}" - rsp = await self.aask(msg=command, system_msgs=[]) - logger.info(f"REWRITE:\nCommand: {command}\nRESULT: {rsp}\n") - return rsp - - @staticmethod - def split_texts(text: str, window_size) -> List[str]: - """Splitting long text into sliding windows text""" - if window_size <= 0: - window_size = OpenAIGPTAPI.DEFAULT_TOKEN_SIZE - total_len = len(text) - if total_len <= window_size: - return [text] - - padding_size = 20 if window_size > 20 else 0 - windows = [] - idx = 0 - data_len = window_size - padding_size - while idx < total_len: - if window_size + idx > total_len: # 不足一个滑窗 - windows.append(text[idx:]) - break - # 每个窗口少算padding_size自然就可实现滑窗功能, 比如: [1, 2, 3, 4, 5, 6, 7, ....] - # window_size=3, padding_size=1: - # [1, 2, 3], [3, 4, 5], [5, 6, 7], .... - # idx=2, | idx=5 | idx=8 | ... - w = text[idx : idx + window_size] - windows.append(w) - idx += data_len - - return windows - - @staticmethod - def extract_info(input_string, pattern=r"\[([A-Z]+)\]:\s*(.+)"): - match = re.match(pattern, input_string) - if match: - return match.group(1), match.group(2) - else: - return None, input_string - @staticmethod async def async_retry_call(func, *args, **kwargs): for i in range(OpenAIGPTAPI.MAX_TRY): @@ -371,7 +262,6 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): raise openai.error.OpenAIError("Exceeds the maximum retries") MAX_TRY = 5 - DEFAULT_TOKEN_SIZE = 500 if __name__ == "__main__": diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index 2fcb6f584..d5467cafb 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -121,23 +121,23 @@ class Assistant(Role): return None if history_text == "": return last_talk - history_summary = await self._llm.get_summary( - text=history_text, max_words=800, keep_language=True, memory=self.memory + history_summary = await self.memory.get_summary( + text=history_text, max_words=800, keep_language=True, llm=self._llm ) - await self.memory.set_history_summary( - history_summary=history_summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS - ) - if last_talk and await self._llm.is_related(last_talk, history_summary): # Merge relevant content. - last_talk = await self._llm.rewrite(sentence=last_talk, context=history_text) + # await self.memory.set_history_summary( + # history_summary=history_summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS + # ) + if last_talk and await self.memory.is_related( + text1=last_talk, text2=history_summary, llm=self._llm + ): # Merge relevant content. + last_talk = await self.memory.rewrite(sentence=last_talk, context=history_text, llm=self._llm) return last_talk return last_talk @staticmethod def extract_info(input_string): - from metagpt.provider.openai_api import OpenAIGPTAPI - - return OpenAIGPTAPI.extract_info(input_string) + return BrainMemory.extract_info(input_string) def get_memory(self) -> str: return self.memory.json() From 4c873a91584286ea8bfb37a635a37b82eb5b3b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 19:13:23 +0800 Subject: [PATCH 23/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 14 +++++++++++--- metagpt/roles/assistant.py | 20 +++++--------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 7eda9c601..fea3b2512 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -178,13 +178,16 @@ class BrainMemory(pydantic.BaseModel): self.is_dirty = True return self.historical_summary - async def get_summary(self, text: str, llm, max_words=200, keep_language: bool = False, **kwargs): + async def summerize(self, llm, max_words=200, keep_language: bool = False, **kwargs): max_token_count = DEFAULT_MAX_TOKENS max_count = 100 + text = self.history_text text_length = len(text) + summary = "" while max_count > 0: if text_length < max_token_count: - return await self._get_summary(text=text, llm=llm, max_words=max_words, keep_language=keep_language) + summary = await self._get_summary(text=text, llm=llm, max_words=max_words, keep_language=keep_language) + break padding_size = 20 if max_token_count > 20 else 0 text_windows = self.split_texts(text, window_size=max_token_count - padding_size) @@ -194,13 +197,18 @@ class BrainMemory(pydantic.BaseModel): response = await self._get_summary(text=ws, max_words=part_max_words, keep_language=keep_language) summaries.append(response) if len(summaries) == 1: - return summaries[0] + summary = summaries[0] + break # Merged and retry text = "\n".join(summaries) text_length = len(text) max_count -= 1 # safeguard + if not summary: + await self.set_history_summary(history_summary=summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS) + return summary + raise openai.error.InvalidRequestError("text too long") async def _get_summary(self, text: str, llm, max_words=20, keep_language: bool = False): diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index d5467cafb..26711486f 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -45,7 +45,7 @@ class Assistant(Role): name=name, profile=profile, goal=goal, constraints=constraints, desc=desc, *args, **kwargs ) brain_memory = CONFIG.BRAIN_MEMORY - self.memory = BrainMemory(**brain_memory) if brain_memory else BrainMemory() + self.memory = BrainMemory(**brain_memory) if brain_memory else BrainMemory(llm_type=CONFIG.LLM_TYPE) skill_path = Path(CONFIG.SKILL_PATH) if CONFIG.SKILL_PATH else None self.skills = SkillLoader(skill_yaml_file_name=skill_path) @@ -83,7 +83,7 @@ class Assistant(Role): self.memory.add_talk(Message(content=text)) async def _plan(self, rsp: str, **kwargs) -> bool: - skill, text = Assistant.extract_info(input_string=rsp) + skill, text = BrainMemory.extract_info(input_string=rsp) handlers = { MessageType.Talk.value: self.talk_handler, MessageType.Skill.value: self.skill_handler, @@ -121,24 +121,14 @@ class Assistant(Role): return None if history_text == "": return last_talk - history_summary = await self.memory.get_summary( - text=history_text, max_words=800, keep_language=True, llm=self._llm - ) - # await self.memory.set_history_summary( - # history_summary=history_summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS - # ) - if last_talk and await self.memory.is_related( - text1=last_talk, text2=history_summary, llm=self._llm - ): # Merge relevant content. + history_summary = await self.memory.summerize(max_words=800, keep_language=True, llm=self._llm) + if last_talk and await self.memory.is_related(text1=last_talk, text2=history_summary, llm=self._llm): + # Merge relevant content. last_talk = await self.memory.rewrite(sentence=last_talk, context=history_text, llm=self._llm) return last_talk return last_talk - @staticmethod - def extract_info(input_string): - return BrainMemory.extract_info(input_string) - def get_memory(self) -> str: return self.memory.json() From 44706ba1416805083caec6683787157ec8df38ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 19:23:09 +0800 Subject: [PATCH 24/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index fea3b2512..adb1f0114 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -80,7 +80,7 @@ class BrainMemory(pydantic.BaseModel): async def loads(redis_key: str, redis_conf: Dict = None) -> "BrainMemory": redis = Redis(conf=redis_conf) if not redis.is_valid() or not redis_key: - return BrainMemory() + return BrainMemory(llm_type=CONFIG.LLM_TYPE) v = await redis.get(key=redis_key) logger.debug(f"REDIS GET {redis_key} {v}") if v: @@ -88,9 +88,11 @@ class BrainMemory(pydantic.BaseModel): bm = BrainMemory(**data) bm.is_dirty = False return bm - return BrainMemory() + return BrainMemory(llm_type=CONFIG.LLM_TYPE) async def dumps(self, redis_key: str, timeout_sec: int = 30 * 60, redis_conf: Dict = None): + if not self.is_dirty: + return redis = Redis(conf=redis_conf) if not redis.is_valid() or not redis_key: return False From 948d1577e4a51f673768f8c16c51e378e435c732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 19:30:35 +0800 Subject: [PATCH 25/68] refactor: brain memory --- metagpt/memory/memory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metagpt/memory/memory.py b/metagpt/memory/memory.py index bf9f0541c..f9dd5c1a3 100644 --- a/metagpt/memory/memory.py +++ b/metagpt/memory/memory.py @@ -8,7 +8,6 @@ from collections import defaultdict from typing import Iterable, Type -from metagpt.actions import Action from metagpt.schema import Message @@ -17,6 +16,8 @@ class Memory: def __init__(self): """Initialize an empty storage list and an empty index dictionary""" + from metagpt.actions import Action + self.storage: list[Message] = [] self.index: dict[Type[Action], list[Message]] = defaultdict(list) From c66012d087b9c80b207a256238142e6daf8e4a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 19:45:42 +0800 Subject: [PATCH 26/68] refactor: brain memory --- metagpt/memory/memory.py | 3 +-- metagpt/provider/metagpt_llm_api.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/metagpt/memory/memory.py b/metagpt/memory/memory.py index f9dd5c1a3..bf9f0541c 100644 --- a/metagpt/memory/memory.py +++ b/metagpt/memory/memory.py @@ -8,6 +8,7 @@ from collections import defaultdict from typing import Iterable, Type +from metagpt.actions import Action from metagpt.schema import Message @@ -16,8 +17,6 @@ class Memory: def __init__(self): """Initialize an empty storage list and an empty index dictionary""" - from metagpt.actions import Action - self.storage: list[Message] = [] self.index: dict[Type[Action], list[Message]] = defaultdict(list) diff --git a/metagpt/provider/metagpt_llm_api.py b/metagpt/provider/metagpt_llm_api.py index 3ae65a623..95514cf53 100644 --- a/metagpt/provider/metagpt_llm_api.py +++ b/metagpt/provider/metagpt_llm_api.py @@ -6,7 +6,6 @@ @Desc : MetaGPT LLM related APIs """ -from metagpt.memory.brain_memory import BrainMemory from metagpt.provider import OpenAIGPTAPI @@ -16,7 +15,7 @@ class MetaGPTLLMAPI(OpenAIGPTAPI): def __init__(self): super().__init__() - async def get_summary(self, memory: BrainMemory, max_words=200, keep_language: bool = False, **kwargs) -> str: + async def get_summary(self, memory, max_words=200, keep_language: bool = False, **kwargs) -> str: """ Return string in the following format: [ From 0c21aa810f64743ac3a484d53c005bf654cbf3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 19:49:51 +0800 Subject: [PATCH 27/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index adb1f0114..596928a4c 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -10,7 +10,7 @@ import json import re from enum import Enum -from typing import Dict, List +from typing import Dict, List, Optional import openai import pydantic @@ -40,7 +40,7 @@ class BrainMemory(pydantic.BaseModel): last_history_id: str = "" is_dirty: bool = False last_talk: str = None - llm_type: str + llm_type: Optional[str] = None def add_talk(self, msg: Message): msg.add_tag(MessageType.Talk.value) From 415e6d5686a231201cd9c2a92c0cefdda12893ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 20:25:50 +0800 Subject: [PATCH 28/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 38 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 596928a4c..4f99de3c7 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -56,25 +56,25 @@ class BrainMemory(pydantic.BaseModel): texts = [Message(**m).content for m in self.knowledge] return "\n".join(texts) - @property - def history_text(self): - if len(self.history) == 0 and not self.historical_summary: - return "" - try: - self.loads_raw_messages() - return self.dumps_raw_messages() - except: - texts = [self.historical_summary] if self.historical_summary else [] - for m in self.history[:-1]: - if isinstance(m, Dict): - t = Message(**m).content - elif isinstance(m, Message): - t = m.content - else: - continue - texts.append(t) - - return "\n".join(texts) + # @property + # def history_text(self): + # if len(self.history) == 0 and not self.historical_summary: + # return "" + # try: + # self.loads_raw_messages() + # return self.dumps_raw_messages() + # except: + # texts = [self.historical_summary] if self.historical_summary else [] + # for m in self.history[:-1]: + # if isinstance(m, Dict): + # t = Message(**m).content + # elif isinstance(m, Message): + # t = m.content + # else: + # continue + # texts.append(t) + # + # return "\n".join(texts) @staticmethod async def loads(redis_key: str, redis_conf: Dict = None) -> "BrainMemory": From 7abb1a3b9368c704dbec747755e107a72cc138ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 20:29:59 +0800 Subject: [PATCH 29/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 4f99de3c7..805ef1b27 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -44,12 +44,12 @@ class BrainMemory(pydantic.BaseModel): def add_talk(self, msg: Message): msg.add_tag(MessageType.Talk.value) - self.history.append(msg.dict()) + self.add_history(msg) self.is_dirty = True def add_answer(self, msg: Message): msg.add_tag(MessageType.Answer.value) - self.history.append(msg.dict()) + self.add_history(msg) self.is_dirty = True def get_knowledge(self) -> str: From c36e1d6f1a85c7d1fb4ad124efcfe2d40917d7b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 20:41:46 +0800 Subject: [PATCH 30/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 805ef1b27..45a7c0691 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -103,7 +103,7 @@ class BrainMemory(pydantic.BaseModel): @staticmethod def to_redis_key(prefix: str, user_id: str, chat_id: str): - return f"{prefix}:{chat_id}:{user_id}" + return f"{prefix}:{user_id}:{chat_id}" async def set_history_summary(self, history_summary, redis_key, redis_conf): if self.historical_summary == history_summary: @@ -294,4 +294,9 @@ class BrainMemory(pydantic.BaseModel): else: return None, input_string + def set_llm_type(self, v): + if v: + self.llm_type = v + self.is_dirty = True + DEFAULT_TOKEN_SIZE = 500 From 2be79730a020e0c455810087eb2e771df9d59f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 20:49:36 +0800 Subject: [PATCH 31/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 9 +++++++-- metagpt/roles/assistant.py | 7 +++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 45a7c0691..b06bf1036 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -240,7 +240,8 @@ class BrainMemory(pydantic.BaseModel): logger.debug(f"title rsp: {response}") return response - async def is_related(self, text1, text2, llm): + @staticmethod + async def is_related(text1, text2, llm): # command = f"{text1}\n{text2}\n\nIf the two sentences above are related, return [TRUE] brief and clear. Otherwise, return [FALSE]." command = f"{text2}\n\nIs there any sentence above related to the following sentence: {text1}.\nIf is there any relevance, return [TRUE] brief and clear. Otherwise, return [FALSE] brief and clear." rsp = await llm.aask(msg=command, system_msgs=[]) @@ -295,8 +296,12 @@ class BrainMemory(pydantic.BaseModel): return None, input_string def set_llm_type(self, v): - if v: + if v and v != self.llm_type: self.llm_type = v self.is_dirty = True + @property + def is_history_available(self): + return self.history or self.historical_summary + DEFAULT_TOKEN_SIZE = 500 diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index 26711486f..54c1e2f43 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -115,16 +115,15 @@ class Assistant(Role): return True async def refine_memory(self) -> str: - history_text = self.memory.history_text last_talk = self.memory.pop_last_talk() if last_talk is None: # No user feedback, unsure if past conversation is finished. return None - if history_text == "": + if not self.memory.is_history_available: return last_talk history_summary = await self.memory.summerize(max_words=800, keep_language=True, llm=self._llm) - if last_talk and await self.memory.is_related(text1=last_talk, text2=history_summary, llm=self._llm): + if last_talk and await BrainMemory.is_related(text1=last_talk, text2=history_summary, llm=self._llm): # Merge relevant content. - last_talk = await self.memory.rewrite(sentence=last_talk, context=history_text, llm=self._llm) + last_talk = await self.memory.rewrite(sentence=last_talk, llm=self._llm) return last_talk return last_talk From 0703c29030587cb0c0b6a57907c5112c8fe84d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 21:21:03 +0800 Subject: [PATCH 32/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 103 ++++++++++++++------------------- 1 file changed, 44 insertions(+), 59 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index b06bf1036..a9677bd66 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -18,6 +18,7 @@ import pydantic from metagpt import Message from metagpt.config import CONFIG from metagpt.const import DEFAULT_LANGUAGE, DEFAULT_MAX_TOKENS +from metagpt.llm import LLMType from metagpt.logs import logger from metagpt.schema import RawMessage from metagpt.utils.redis import Redis @@ -56,26 +57,6 @@ class BrainMemory(pydantic.BaseModel): texts = [Message(**m).content for m in self.knowledge] return "\n".join(texts) - # @property - # def history_text(self): - # if len(self.history) == 0 and not self.historical_summary: - # return "" - # try: - # self.loads_raw_messages() - # return self.dumps_raw_messages() - # except: - # texts = [self.historical_summary] if self.historical_summary else [] - # for m in self.history[:-1]: - # if isinstance(m, Dict): - # t = Message(**m).content - # elif isinstance(m, Message): - # t = m.content - # else: - # continue - # texts.append(t) - # - # return "\n".join(texts) - @staticmethod async def loads(redis_key: str, redis_conf: Dict = None) -> "BrainMemory": redis = Redis(conf=redis_conf) @@ -143,47 +124,19 @@ class BrainMemory(pydantic.BaseModel): self.last_talk = None return v - def loads_raw_messages(self): - if not self.historical_summary: - return - vv = json.loads(self.historical_summary) - msgs = [] - for v in vv: - tag = set([MessageType.Talk.value]) if v.get("role") == "user" else set([MessageType.Answer.value]) - m = Message(content=v.get("content"), tags=tag) - msgs.append(m) - msgs.extend(self.history) - self.history = msgs - self.is_dirty = True + async def summarize(self, llm, max_words=200, keep_language: bool = False, **kwargs): + if self.llm_type == LLMType.METAGPT.value: + return await self._metagpt_summarize(llm=llm, max_words=max_words, keep_language=keep_language, **kwargs) - def dumps_raw_messages(self, max_length: int = 0) -> str: - summary = [] + return await self._openai_summarize(llm=llm, max_words=max_words, keep_language=keep_language, **kwargs) - total_length = 0 - for m in reversed(self.history): - msg = Message(**m) - c = RawMessage(role="user" if MessageType.Talk.value in msg.tags else "assistant", content=msg.content) - length_delta = len(msg.content) - if max_length > 0: - if total_length + length_delta > max_length: - left = max_length - total_length - if left > 0: - c.content = msg.content[0:left] - summary.insert(0, c) - break - - total_length += length_delta - summary.insert(0, c) - - self.historical_summary = json.dumps(summary) - self.history = [] - self.is_dirty = True - return self.historical_summary - - async def summerize(self, llm, max_words=200, keep_language: bool = False, **kwargs): + async def _openai_summarize(self, llm, max_words=200, keep_language: bool = False, **kwargs): max_token_count = DEFAULT_MAX_TOKENS max_count = 100 - text = self.history_text + texts = [self.historical_summary] + for m in self.history: + texts.append(m.content) + text = "\n".join(texts) text_length = len(text) summary = "" while max_count > 0: @@ -210,9 +163,41 @@ class BrainMemory(pydantic.BaseModel): if not summary: await self.set_history_summary(history_summary=summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS) return summary - raise openai.error.InvalidRequestError("text too long") + async def _metagpt_summarize(self, max_words=200, **kwargs): + if not self.history: + return "" + + total_length = 0 + msgs = [] + for m in reversed(self.history): + delta = len(m.content) + if total_length + delta > max_words: + left = max_words - total_length + if left == 0: + break + m.content = m.content[0:left] + msgs.append(m) + break + msgs.append(m) + total_length += delta + self.history = msgs + self.is_dirty = True + await self.dumps(redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS_CONF) + self.is_dirty = False + + return BrainMemory.to_metagpt_history_format(self.history) + + @staticmethod + def to_metagpt_history_format(history) -> str: + mmsg = [] + for m in reversed(history): + msg = Message(**m) + r = RawMessage(role="user" if MessageType.Talk.value in msg.tags else "assistant", content=msg.content) + mmsg.append(r) + return json.dumps(mmsg) + async def _get_summary(self, text: str, llm, max_words=20, keep_language: bool = False): """Generate text summary""" if len(text) < max_words: @@ -302,6 +287,6 @@ class BrainMemory(pydantic.BaseModel): @property def is_history_available(self): - return self.history or self.historical_summary + return bool(self.history or self.historical_summary) DEFAULT_TOKEN_SIZE = 500 From 8e30dfd84a8e516fe7a6ad7d993a7883ec728b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 21:21:38 +0800 Subject: [PATCH 33/68] refactor: brain memory --- metagpt/roles/assistant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index 54c1e2f43..66daef403 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -120,7 +120,7 @@ class Assistant(Role): return None if not self.memory.is_history_available: return last_talk - history_summary = await self.memory.summerize(max_words=800, keep_language=True, llm=self._llm) + history_summary = await self.memory.summarize(max_words=800, keep_language=True, llm=self._llm) if last_talk and await BrainMemory.is_related(text1=last_talk, text2=history_summary, llm=self._llm): # Merge relevant content. last_talk = await self.memory.rewrite(sentence=last_talk, llm=self._llm) From 12b2fcd4be85b2dd013fc040d046dda938c38b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 21:30:15 +0800 Subject: [PATCH 34/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index a9677bd66..3d713ddfb 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -171,16 +171,17 @@ class BrainMemory(pydantic.BaseModel): total_length = 0 msgs = [] - for m in reversed(self.history): + for i in reversed(self.history): + m = Message(**i) delta = len(m.content) if total_length + delta > max_words: left = max_words - total_length if left == 0: break m.content = m.content[0:left] - msgs.append(m) + msgs.append(m.dict()) break - msgs.append(m) + msgs.append(m.dict()) total_length += delta self.history = msgs self.is_dirty = True @@ -198,7 +199,8 @@ class BrainMemory(pydantic.BaseModel): mmsg.append(r) return json.dumps(mmsg) - async def _get_summary(self, text: str, llm, max_words=20, keep_language: bool = False): + @staticmethod + async def _get_summary(text: str, llm, max_words=20, keep_language: bool = False): """Generate text summary""" if len(text) < max_words: return text @@ -214,7 +216,7 @@ class BrainMemory(pydantic.BaseModel): async def get_title(self, text: str, llm, max_words=5, **kwargs) -> str: """Generate text title""" - summary = await self.get_summary(text, max_words=500) + summary = await self.summarize(text, max_words=500) language = CONFIG.language or DEFAULT_LANGUAGE command = f"Translate the above summary into a {language} title of less than {max_words} words." From 24a3e725726338ed3b5a611489ce1af481692e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 21:35:48 +0800 Subject: [PATCH 35/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 3d713ddfb..09a4915fc 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -181,7 +181,7 @@ class BrainMemory(pydantic.BaseModel): m.content = m.content[0:left] msgs.append(m.dict()) break - msgs.append(m.dict()) + msgs.append(i) total_length += delta self.history = msgs self.is_dirty = True From a4f36e0852f0804c327cc6cce016e00e28d0591c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 21:42:35 +0800 Subject: [PATCH 36/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 09a4915fc..e65459f1a 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -183,7 +183,7 @@ class BrainMemory(pydantic.BaseModel): break msgs.append(i) total_length += delta - self.history = msgs + self.history = msgs.reverse() self.is_dirty = True await self.dumps(redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS_CONF) self.is_dirty = False From 5b3f6e0b6857210dacf115e171418d5893afdcf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 21:43:28 +0800 Subject: [PATCH 37/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index e65459f1a..39e2ec43d 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -183,7 +183,8 @@ class BrainMemory(pydantic.BaseModel): break msgs.append(i) total_length += delta - self.history = msgs.reverse() + msgs.reverse() + self.history = msgs self.is_dirty = True await self.dumps(redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS_CONF) self.is_dirty = False From 1df4121b12863793b23dcd7a11d6855b35eb752d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 22:06:54 +0800 Subject: [PATCH 38/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 39e2ec43d..2d191ccaa 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -194,7 +194,7 @@ class BrainMemory(pydantic.BaseModel): @staticmethod def to_metagpt_history_format(history) -> str: mmsg = [] - for m in reversed(history): + for m in history: msg = Message(**m) r = RawMessage(role="user" if MessageType.Talk.value in msg.tags else "assistant", content=msg.content) mmsg.append(r) From 1ce9ad54fd6dbe52f726b8977b18aae19049f23c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 22:24:20 +0800 Subject: [PATCH 39/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 2d191ccaa..e0e2ae1a0 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -228,8 +228,17 @@ class BrainMemory(pydantic.BaseModel): logger.debug(f"title rsp: {response}") return response + async def is_related(self, text1, text2, llm): + if self.llm_type == LLMType.METAGPT.value: + return await self._metagpt_is_related(text1=text1, text2=text2, llm=llm) + return await self._openai_is_related(text1=text1, text2=text2, llm=llm) + @staticmethod - async def is_related(text1, text2, llm): + async def _metagpt_is_related(**kwargs): + return False + + @staticmethod + async def _openai_is_related(text1, text2, llm, **kwargs): # command = f"{text1}\n{text2}\n\nIf the two sentences above are related, return [TRUE] brief and clear. Otherwise, return [FALSE]." command = f"{text2}\n\nIs there any sentence above related to the following sentence: {text1}.\nIf is there any relevance, return [TRUE] brief and clear. Otherwise, return [FALSE] brief and clear." rsp = await llm.aask(msg=command, system_msgs=[]) @@ -240,6 +249,14 @@ class BrainMemory(pydantic.BaseModel): return result async def rewrite(self, sentence: str, context: str, llm): + if self.llm_type == LLMType.METAGPT.value: + return await self._metagpt_rewrite(sentence=sentence, context=context, llm=llm) + return await self._openai_rewrite(sentence=sentence, context=context, llm=llm) + + async def _metagpt_rewrite(self, sentence: str, **kwargs): + return sentence + + async def _openai_rewrite(self, sentence: str, context: str, llm, **kwargs): # command = ( # f"{context}\n\nConsidering the content above, rewrite and return this sentence brief and clear:\n{sentence}" # ) From 7c6b0325d8a2001491d2ec25167ef638f417aa7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 22:27:49 +0800 Subject: [PATCH 40/68] refactor: brain memory --- metagpt/roles/assistant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index 66daef403..397ddc94b 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -121,7 +121,7 @@ class Assistant(Role): if not self.memory.is_history_available: return last_talk history_summary = await self.memory.summarize(max_words=800, keep_language=True, llm=self._llm) - if last_talk and await BrainMemory.is_related(text1=last_talk, text2=history_summary, llm=self._llm): + if last_talk and await self.memory.is_related(text1=last_talk, text2=history_summary, llm=self._llm): # Merge relevant content. last_talk = await self.memory.rewrite(sentence=last_talk, llm=self._llm) return last_talk From f2da313548b07f81ce8e9299b2d96bb067ba7e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 22:58:00 +0800 Subject: [PATCH 41/68] refactor: brain memory --- metagpt/actions/talk_action.py | 11 +++++++++++ metagpt/memory/brain_memory.py | 24 ++++++++++++++++++++++++ metagpt/provider/base_gpt_api.py | 19 +++++++++++++++---- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/metagpt/actions/talk_action.py b/metagpt/actions/talk_action.py index 0e3762798..baef47eeb 100644 --- a/metagpt/actions/talk_action.py +++ b/metagpt/actions/talk_action.py @@ -6,10 +6,12 @@ @File : talk_action.py @Desc : Act as it’s a talk """ +import json from metagpt.actions import Action, ActionOutput from metagpt.config import CONFIG from metagpt.const import DEFAULT_LANGUAGE +from metagpt.llm import LLMType from metagpt.logs import logger @@ -63,6 +65,15 @@ class TalkAction(Action): return prompt async def run(self, *args, **kwargs) -> ActionOutput: + if CONFIG.LLM_TYPE == LLMType.METAGPT.value: + rsp = await self.llm.aask( + msg=self._talk, + knowledge_msgs=[{"knowledge": self._knowledge}] if self._knowledge else None, + history_msgs=json.loads(self._history_summary) if self._history_summary else None, + ) + self._rsp = ActionOutput(content=rsp) + return self._rsp + prompt = self.prompt rsp = await self.llm.aask(msg=prompt, system_msgs=[]) logger.debug(f"PROMPT:{prompt}\nRESULT:{rsp}\n") diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index e0e2ae1a0..0f9c1dbb6 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -309,4 +309,28 @@ class BrainMemory(pydantic.BaseModel): def is_history_available(self): return bool(self.history or self.historical_summary) + @property + def history_text(self): + if self.llm_type == LLMType.METAGPT.value: + return self._get_metagpt_history_text() + return self._get_openai_history_text() + + def _get_metagpt_history_text(self): + return BrainMemory.to_metagpt_history_format(self.history) + + def _get_openai_history_text(self): + if len(self.history) == 0 and not self.historical_summary: + return "" + texts = [self.historical_summary] if self.historical_summary else [] + for m in self.history[:-1]: + if isinstance(m, Dict): + t = Message(**m).content + elif isinstance(m, Message): + t = m.content + else: + continue + texts.append(t) + + return "\n".join(texts) + DEFAULT_TOKEN_SIZE = 500 diff --git a/metagpt/provider/base_gpt_api.py b/metagpt/provider/base_gpt_api.py index 7351e6916..f405ae902 100644 --- a/metagpt/provider/base_gpt_api.py +++ b/metagpt/provider/base_gpt_api.py @@ -38,11 +38,22 @@ class BaseGPTAPI(BaseChatbot): rsp = self.completion(message) return self.get_choice_text(rsp) - async def aask(self, msg: str, system_msgs: Optional[list[str]] = None, generator: bool = False) -> str: + async def aask( + self, + msg: str, + system_msgs: Optional[list[str]] = None, + history_msgs: Optional[list[dict[str, str]]] = None, + knowledge_msgs: Optional[list[dict[str, str]]] = None, + generator: bool = False, + ) -> str: + message = [] if system_msgs: - message = self._system_msgs(system_msgs) + [self._user_msg(msg)] - else: - message = [self._default_system_msg(), self._user_msg(msg)] + message = self._system_msgs(system_msgs) + if knowledge_msgs: + message.extend(knowledge_msgs) + if history_msgs: + message.extend(history_msgs) + message.append(self._user_msg(msg)) try: rsp = await self.acompletion_text(message, stream=True, generator=generator) except Exception as e: From f92aeb0e506e852d3551f7cf67b3574448e91712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 23:05:30 +0800 Subject: [PATCH 42/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 0f9c1dbb6..7677a9144 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -134,7 +134,8 @@ class BrainMemory(pydantic.BaseModel): max_token_count = DEFAULT_MAX_TOKENS max_count = 100 texts = [self.historical_summary] - for m in self.history: + for i in self.history: + m = Message(**i) texts.append(m.content) text = "\n".join(texts) text_length = len(text) From 1b267d34dc986e8f18be63423783421d88e72eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 23:08:46 +0800 Subject: [PATCH 43/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 7677a9144..f3a3e3563 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -161,7 +161,7 @@ class BrainMemory(pydantic.BaseModel): text_length = len(text) max_count -= 1 # safeguard - if not summary: + if summary: await self.set_history_summary(history_summary=summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS) return summary raise openai.error.InvalidRequestError("text too long") From 270b14e0360c41ede5fadaf71ea4b6e04b384dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 23:29:08 +0800 Subject: [PATCH 44/68] refactor: brain memory --- metagpt/actions/talk_action.py | 33 ++++++++++++++++++++++---------- metagpt/provider/base_gpt_api.py | 9 +++------ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/metagpt/actions/talk_action.py b/metagpt/actions/talk_action.py index baef47eeb..2d473c7cf 100644 --- a/metagpt/actions/talk_action.py +++ b/metagpt/actions/talk_action.py @@ -64,22 +64,35 @@ class TalkAction(Action): logger.info(f"PROMPT: {prompt}") return prompt - async def run(self, *args, **kwargs) -> ActionOutput: - if CONFIG.LLM_TYPE == LLMType.METAGPT.value: - rsp = await self.llm.aask( - msg=self._talk, - knowledge_msgs=[{"knowledge": self._knowledge}] if self._knowledge else None, - history_msgs=json.loads(self._history_summary) if self._history_summary else None, - ) - self._rsp = ActionOutput(content=rsp) - return self._rsp - + async def run_old(self, *args, **kwargs) -> ActionOutput: prompt = self.prompt rsp = await self.llm.aask(msg=prompt, system_msgs=[]) logger.debug(f"PROMPT:{prompt}\nRESULT:{rsp}\n") self._rsp = ActionOutput(content=rsp) return self._rsp + async def run(self, *args, **kwargs) -> ActionOutput: + language = CONFIG.language or DEFAULT_LANGUAGE + system_msgs = [ + f"You are {CONFIG.agent_description}.", + "Your responses should align with the role-play agreement, " + "maintaining the character's persona and habits. When faced with unrelated questions, playfully " + "decline to answer without revealing your AI nature to preserve the character's image.", + "If the information is insufficient, you can search in the context or knowledge.", + f"Answer the following questions strictly in {language}, and the answers must follow the Markdown format.", + ] + format_msgs = [] + if self._knowledge: + format_msgs.append({"knowledge": self._knowledge}) + if self._history_summary: + if CONFIG.LLM_TYPE == LLMType.METAGPT.value: + format_msgs.append(json.loads(self._history_summary)) + else: + format_msgs.append({"context": self._history_summary}) + rsp = await self.llm.aask(msg=self._talk, format_msgs=format_msgs, system_msgs=system_msgs) + self._rsp = ActionOutput(content=rsp) + return self._rsp + __FORMATION__ = """Formation: "Capacity and role" defines the role you are currently playing; "[HISTORY_BEGIN]" and "[HISTORY_END]" tags enclose the historical conversation; "[KNOWLEDGE_BEGIN]" and "[KNOWLEDGE_END]" tags enclose the knowledge may help for your responses; diff --git a/metagpt/provider/base_gpt_api.py b/metagpt/provider/base_gpt_api.py index f405ae902..19f5fcfff 100644 --- a/metagpt/provider/base_gpt_api.py +++ b/metagpt/provider/base_gpt_api.py @@ -42,17 +42,14 @@ class BaseGPTAPI(BaseChatbot): self, msg: str, system_msgs: Optional[list[str]] = None, - history_msgs: Optional[list[dict[str, str]]] = None, - knowledge_msgs: Optional[list[dict[str, str]]] = None, + format_msgs: Optional[list[dict[str, str]]] = None, generator: bool = False, ) -> str: message = [] if system_msgs: message = self._system_msgs(system_msgs) - if knowledge_msgs: - message.extend(knowledge_msgs) - if history_msgs: - message.extend(history_msgs) + if format_msgs: + message.extend(format_msgs) message.append(self._user_msg(msg)) try: rsp = await self.acompletion_text(message, stream=True, generator=generator) From b49c7f2d70e7b7f45d4e632c203b9fcecbfe52ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 23:38:04 +0800 Subject: [PATCH 45/68] refactor: brain memory --- metagpt/actions/talk_action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/actions/talk_action.py b/metagpt/actions/talk_action.py index 2d473c7cf..3c3db0841 100644 --- a/metagpt/actions/talk_action.py +++ b/metagpt/actions/talk_action.py @@ -88,7 +88,7 @@ class TalkAction(Action): if CONFIG.LLM_TYPE == LLMType.METAGPT.value: format_msgs.append(json.loads(self._history_summary)) else: - format_msgs.append({"context": self._history_summary}) + format_msgs.append({"knowledge": self._history_summary}) rsp = await self.llm.aask(msg=self._talk, format_msgs=format_msgs, system_msgs=system_msgs) self._rsp = ActionOutput(content=rsp) return self._rsp From d906bd1c81d534dbd1edee9d03e9e556a2805c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 23:41:31 +0800 Subject: [PATCH 46/68] refactor: brain memory --- metagpt/actions/talk_action.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/actions/talk_action.py b/metagpt/actions/talk_action.py index 3c3db0841..b5282c3e5 100644 --- a/metagpt/actions/talk_action.py +++ b/metagpt/actions/talk_action.py @@ -83,12 +83,12 @@ class TalkAction(Action): ] format_msgs = [] if self._knowledge: - format_msgs.append({"knowledge": self._knowledge}) + format_msgs.append({"role": "knowledge", "content": self._knowledge}) if self._history_summary: if CONFIG.LLM_TYPE == LLMType.METAGPT.value: format_msgs.append(json.loads(self._history_summary)) else: - format_msgs.append({"knowledge": self._history_summary}) + format_msgs.append({"role": "context", "content": self._history_summary}) rsp = await self.llm.aask(msg=self._talk, format_msgs=format_msgs, system_msgs=system_msgs) self._rsp = ActionOutput(content=rsp) return self._rsp From b1f7aa396895723b121f184d2fff559a72eb52be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 23:44:01 +0800 Subject: [PATCH 47/68] refactor: brain memory --- metagpt/actions/talk_action.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/actions/talk_action.py b/metagpt/actions/talk_action.py index b5282c3e5..85d99db49 100644 --- a/metagpt/actions/talk_action.py +++ b/metagpt/actions/talk_action.py @@ -83,12 +83,12 @@ class TalkAction(Action): ] format_msgs = [] if self._knowledge: - format_msgs.append({"role": "knowledge", "content": self._knowledge}) + format_msgs.append({"role": "assistant", "content": self._knowledge}) if self._history_summary: if CONFIG.LLM_TYPE == LLMType.METAGPT.value: format_msgs.append(json.loads(self._history_summary)) else: - format_msgs.append({"role": "context", "content": self._history_summary}) + format_msgs.append({"role": "assistant", "content": self._history_summary}) rsp = await self.llm.aask(msg=self._talk, format_msgs=format_msgs, system_msgs=system_msgs) self._rsp = ActionOutput(content=rsp) return self._rsp From f4eea02866cc76d9e3ceb809c466451abae91af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 23:54:56 +0800 Subject: [PATCH 48/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index f3a3e3563..cdf3d7fbb 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -164,7 +164,7 @@ class BrainMemory(pydantic.BaseModel): if summary: await self.set_history_summary(history_summary=summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS) return summary - raise openai.error.InvalidRequestError("text too long") + raise openai.error.InvalidRequestError(message="text too long", param=None) async def _metagpt_summarize(self, max_words=200, **kwargs): if not self.history: From 20fb71b0a3a6f2abebea6d81edf73c0a59f26afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 7 Sep 2023 23:58:30 +0800 Subject: [PATCH 49/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index cdf3d7fbb..3dfa050b3 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -216,9 +216,9 @@ class BrainMemory(pydantic.BaseModel): logger.debug(f"summary rsp: {response}") return response - async def get_title(self, text: str, llm, max_words=5, **kwargs) -> str: + async def get_title(self, llm, max_words=5, **kwargs) -> str: """Generate text title""" - summary = await self.summarize(text, max_words=500) + summary = await self.summarize(max_words=500) language = CONFIG.language or DEFAULT_LANGUAGE command = f"Translate the above summary into a {language} title of less than {max_words} words." From 42d0281fbbba5ccd8f5646c2e7303ba1d5aa6f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 00:00:41 +0800 Subject: [PATCH 50/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 3dfa050b3..a995244a6 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -218,7 +218,7 @@ class BrainMemory(pydantic.BaseModel): async def get_title(self, llm, max_words=5, **kwargs) -> str: """Generate text title""" - summary = await self.summarize(max_words=500) + summary = await self.summarize(llm=llm, max_words=500) language = CONFIG.language or DEFAULT_LANGUAGE command = f"Translate the above summary into a {language} title of less than {max_words} words." From 8b5d83956d7cbc852fbeeb6e4006bc8d0712088e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 10:05:44 +0800 Subject: [PATCH 51/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 10 +++++++--- metagpt/provider/openai_api.py | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index a995244a6..9878fa750 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -124,13 +124,15 @@ class BrainMemory(pydantic.BaseModel): self.last_talk = None return v - async def summarize(self, llm, max_words=200, keep_language: bool = False, **kwargs): + async def summarize(self, llm, max_words=200, keep_language: bool = False, limit: int = -1, **kwargs): if self.llm_type == LLMType.METAGPT.value: return await self._metagpt_summarize(llm=llm, max_words=max_words, keep_language=keep_language, **kwargs) - return await self._openai_summarize(llm=llm, max_words=max_words, keep_language=keep_language, **kwargs) + return await self._openai_summarize( + llm=llm, max_words=max_words, keep_language=keep_language, limit=limit, **kwargs + ) - async def _openai_summarize(self, llm, max_words=200, keep_language: bool = False, **kwargs): + async def _openai_summarize(self, llm, max_words=200, keep_language: bool = False, limit: int = -1, **kwargs): max_token_count = DEFAULT_MAX_TOKENS max_count = 100 texts = [self.historical_summary] @@ -139,6 +141,8 @@ class BrainMemory(pydantic.BaseModel): texts.append(m.content) text = "\n".join(texts) text_length = len(text) + if limit > 0 and text_length < limit: + return text summary = "" while max_count > 0: if text_length < max_token_count: diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 231b568c7..9dbbaf7e5 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -22,7 +22,9 @@ from tenacity import ( ) from metagpt.config import CONFIG +from metagpt.llm import LLMType from metagpt.logs import logger +from metagpt.memory.brain_memory import BrainMemory from metagpt.provider.base_gpt_api import BaseGPTAPI from metagpt.utils.cost_manager import Costs from metagpt.utils.token_counter import ( @@ -261,6 +263,19 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): raise e raise openai.error.OpenAIError("Exceeds the maximum retries") + async def get_summary(self, text: str, max_words=200, keep_language: bool = False, **kwargs) -> str: + """ + Return string in the following format: + [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Knock knock."}, + {"role": "assistant", "content": "Who's there?"}, + {"role": "user", "content": "Orange."}, + ] + """ + memory = BrainMemory(llm_type=LLMType.OPENAI.value, historical_summary=text) + return await memory.summarize(llm=self._llm, max_length=max_words, keep_language=keep_language) + MAX_TRY = 5 @@ -269,4 +284,3 @@ if __name__ == "__main__": as dfas sad lkf sdkl sakdfsdk sjd jsk sdl sk dd sd asd fa sdf sad dd - .gitlab-ci.yml & base_test.py """ - OpenAIGPTAPI.split_texts(txt, 30) From 827505fca9838f7df57970174ae018df911f258d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 10:07:46 +0800 Subject: [PATCH 52/68] refactor: brain memory --- metagpt/provider/openai_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 9dbbaf7e5..85dfe8436 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -274,7 +274,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): ] """ memory = BrainMemory(llm_type=LLMType.OPENAI.value, historical_summary=text) - return await memory.summarize(llm=self._llm, max_length=max_words, keep_language=keep_language) + return await memory.summarize(llm=self, max_length=max_words, keep_language=keep_language) MAX_TRY = 5 From 6942cc91619e35a626cbfc5b33f5e27f856ebc42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 10:16:07 +0800 Subject: [PATCH 53/68] refactor: brain memory --- metagpt/llm.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/metagpt/llm.py b/metagpt/llm.py index 4772d2e6e..67ae42d62 100644 --- a/metagpt/llm.py +++ b/metagpt/llm.py @@ -8,15 +8,15 @@ """ from enum import Enum +import openai + from metagpt.config import CONFIG -from metagpt.provider.anthropic_api import Claude2 as Claude -from metagpt.provider.metagpt_llm_api import MetaGPTLLMAPI as MetaGPT_LLM -from metagpt.provider.openai_api import OpenAIGPTAPI as OpenAI_LLM class LLMType(Enum): OPENAI = "OpenAI" METAGPT = "MetaGPT" + CLAUDE = "Claude" UNKNOWN = "UNKNOWN" @classmethod @@ -27,20 +27,18 @@ class LLMType(Enum): return cls.UNKNOWN -DEFAULT_LLM = OpenAI_LLM() -DEFAULT_METAGPT_LLM = MetaGPT_LLM() -CLAUDE_LLM = Claude() - - -async def ai_func(prompt): - """使用LLM进行QA - QA with LLMs - """ - return await DEFAULT_LLM.aask(prompt) - - class LLMFactory: @staticmethod def new_llm() -> object: - llm = OpenAI_LLM() if CONFIG.LLM_TYPE == LLMType.OPENAI.value else MetaGPT_LLM() - return llm + from metagpt.provider.anthropic_api import Claude2 as Claude + from metagpt.provider.metagpt_llm_api import MetaGPTLLMAPI as MetaGPT_LLM + from metagpt.provider.openai_api import OpenAIGPTAPI as OpenAI_LLM + + if CONFIG.LLM_TYPE == LLMType.OPENAI.value: + return OpenAI_LLM() + if CONFIG.LLM_TYPE == LLMType.METAGPT.value: + return MetaGPT_LLM() + if CONFIG.LLM_TYPE == LLMType.CLAUDE.value: + return Claude() + + raise openai.InvalidRequestError(message=f"Unsupported LLM TYPE: {CONFIG.LLM_TYPE}") From 525ca29c89d7279f082f0e7d237a6445dbdd61df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 10:17:52 +0800 Subject: [PATCH 54/68] refactor: brain memory --- metagpt/provider/openai_api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 85dfe8436..de640aed7 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -24,7 +24,6 @@ from tenacity import ( from metagpt.config import CONFIG from metagpt.llm import LLMType from metagpt.logs import logger -from metagpt.memory.brain_memory import BrainMemory from metagpt.provider.base_gpt_api import BaseGPTAPI from metagpt.utils.cost_manager import Costs from metagpt.utils.token_counter import ( @@ -273,6 +272,8 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): {"role": "user", "content": "Orange."}, ] """ + from metagpt.memory.brain_memory import BrainMemory + memory = BrainMemory(llm_type=LLMType.OPENAI.value, historical_summary=text) return await memory.summarize(llm=self, max_length=max_words, keep_language=keep_language) From 1254f93467ca8cd9cad34e3c6791ce9ffef3d633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 10:22:31 +0800 Subject: [PATCH 55/68] refactor: brain memory --- metagpt/memory/brain_memory.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 9878fa750..b8f9a2a15 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -42,6 +42,7 @@ class BrainMemory(pydantic.BaseModel): is_dirty: bool = False last_talk: str = None llm_type: Optional[str] = None + cacheable: bool = True def add_talk(self, msg: Message): msg.add_tag(MessageType.Talk.value) @@ -78,8 +79,9 @@ class BrainMemory(pydantic.BaseModel): if not redis.is_valid() or not redis_key: return False v = self.json() - await redis.set(key=redis_key, data=v, timeout_sec=timeout_sec) - logger.debug(f"REDIS SET {redis_key} {v}") + if self.cacheable: + await redis.set(key=redis_key, data=v, timeout_sec=timeout_sec) + logger.debug(f"REDIS SET {redis_key} {v}") self.is_dirty = False @staticmethod From 348cafa0b86096f96b4cb41fef197f04b5814256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 10:24:08 +0800 Subject: [PATCH 56/68] refactor: brain memory --- metagpt/provider/openai_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index de640aed7..514671488 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -274,7 +274,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): """ from metagpt.memory.brain_memory import BrainMemory - memory = BrainMemory(llm_type=LLMType.OPENAI.value, historical_summary=text) + memory = BrainMemory(llm_type=LLMType.OPENAI.value, historical_summary=text, cacheable=False) return await memory.summarize(llm=self, max_length=max_words, keep_language=keep_language) MAX_TRY = 5 From bed3d8c841dcd1c6901e9ecfcac5e855b4413164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 11:54:26 +0800 Subject: [PATCH 57/68] refactor: brain memory --- metagpt/actions/talk_action.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/metagpt/actions/talk_action.py b/metagpt/actions/talk_action.py index 85d99db49..f9ff76015 100644 --- a/metagpt/actions/talk_action.py +++ b/metagpt/actions/talk_action.py @@ -71,7 +71,8 @@ class TalkAction(Action): self._rsp = ActionOutput(content=rsp) return self._rsp - async def run(self, *args, **kwargs) -> ActionOutput: + @property + def aask_args(self): language = CONFIG.language or DEFAULT_LANGUAGE system_msgs = [ f"You are {CONFIG.agent_description}.", @@ -89,7 +90,11 @@ class TalkAction(Action): format_msgs.append(json.loads(self._history_summary)) else: format_msgs.append({"role": "assistant", "content": self._history_summary}) - rsp = await self.llm.aask(msg=self._talk, format_msgs=format_msgs, system_msgs=system_msgs) + return self._talk, format_msgs, system_msgs + + async def run(self, *args, **kwargs) -> ActionOutput: + msg, format_msgs, system_msgs = self.aask_args + rsp = await self.llm.aask(msg=msg, format_msgs=format_msgs, system_msgs=system_msgs) self._rsp = ActionOutput(content=rsp) return self._rsp From 5f3931820ec17d2de2aeef77b4294bfd3dc67b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 12:08:05 +0800 Subject: [PATCH 58/68] refactor: brain memory --- metagpt/actions/talk_action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/actions/talk_action.py b/metagpt/actions/talk_action.py index f9ff76015..eb619cb7e 100644 --- a/metagpt/actions/talk_action.py +++ b/metagpt/actions/talk_action.py @@ -87,7 +87,7 @@ class TalkAction(Action): format_msgs.append({"role": "assistant", "content": self._knowledge}) if self._history_summary: if CONFIG.LLM_TYPE == LLMType.METAGPT.value: - format_msgs.append(json.loads(self._history_summary)) + format_msgs.extend(json.loads(self._history_summary)) else: format_msgs.append({"role": "assistant", "content": self._history_summary}) return self._talk, format_msgs, system_msgs From 5903b3efbc33f3ec5ba68953980dac4c5c83dd3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 13:03:05 +0800 Subject: [PATCH 59/68] refactor: brain memory --- metagpt/llm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/llm.py b/metagpt/llm.py index 67ae42d62..eeb665872 100644 --- a/metagpt/llm.py +++ b/metagpt/llm.py @@ -41,4 +41,4 @@ class LLMFactory: if CONFIG.LLM_TYPE == LLMType.CLAUDE.value: return Claude() - raise openai.InvalidRequestError(message=f"Unsupported LLM TYPE: {CONFIG.LLM_TYPE}") + raise openai.InvalidRequestError(message=f"Unsupported LLM TYPE: {CONFIG.LLM_TYPE}", param=None) From ce6619a10c5aac43a715cfb53a6844c3c732e7d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 13:07:21 +0800 Subject: [PATCH 60/68] refactor: brain memory --- metagpt/provider/base_gpt_api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metagpt/provider/base_gpt_api.py b/metagpt/provider/base_gpt_api.py index 19f5fcfff..59da67d5b 100644 --- a/metagpt/provider/base_gpt_api.py +++ b/metagpt/provider/base_gpt_api.py @@ -48,6 +48,8 @@ class BaseGPTAPI(BaseChatbot): message = [] if system_msgs: message = self._system_msgs(system_msgs) + else: + message = [self._default_system_msg()] if format_msgs: message.extend(format_msgs) message.append(self._user_msg(msg)) From 1b71081c745f469a5f4529c30558f565a59bbe8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 13:08:29 +0800 Subject: [PATCH 61/68] refactor: brain memory --- metagpt/provider/base_gpt_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/metagpt/provider/base_gpt_api.py b/metagpt/provider/base_gpt_api.py index 59da67d5b..1b1187b72 100644 --- a/metagpt/provider/base_gpt_api.py +++ b/metagpt/provider/base_gpt_api.py @@ -45,7 +45,6 @@ class BaseGPTAPI(BaseChatbot): format_msgs: Optional[list[dict[str, str]]] = None, generator: bool = False, ) -> str: - message = [] if system_msgs: message = self._system_msgs(system_msgs) else: From 2c3ab2fae4be572d62e7fd5a54392e25993327b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 13:10:01 +0800 Subject: [PATCH 62/68] refactor: brain memory --- metagpt/provider/metagpt_llm_api.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/metagpt/provider/metagpt_llm_api.py b/metagpt/provider/metagpt_llm_api.py index 95514cf53..7e79f0ae5 100644 --- a/metagpt/provider/metagpt_llm_api.py +++ b/metagpt/provider/metagpt_llm_api.py @@ -14,15 +14,3 @@ class MetaGPTLLMAPI(OpenAIGPTAPI): def __init__(self): super().__init__() - - async def get_summary(self, memory, max_words=200, keep_language: bool = False, **kwargs) -> str: - """ - Return string in the following format: - [ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "Knock knock."}, - {"role": "assistant", "content": "Who's there?"}, - {"role": "user", "content": "Orange."}, - ] - """ - return memory.dumps_raw_messages(max_length=max_words) From dda55aec96ee25b5f44297b42b2075939b63f683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 15:13:25 +0800 Subject: [PATCH 63/68] fixbug: llm missing --- metagpt/memory/brain_memory.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index b8f9a2a15..59d108a7d 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -156,7 +156,9 @@ class BrainMemory(pydantic.BaseModel): part_max_words = min(int(max_words / len(text_windows)) + 1, 100) summaries = [] for ws in text_windows: - response = await self._get_summary(text=ws, max_words=part_max_words, keep_language=keep_language) + response = await self._get_summary( + text=ws, llm=llm, max_words=part_max_words, keep_language=keep_language + ) summaries.append(response) if len(summaries) == 1: summary = summaries[0] From b58d2ff2d3ff64b4fd6a7c2279a6520a04e8e958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 15:19:09 +0800 Subject: [PATCH 64/68] fixbug: llm missing --- metagpt/provider/openai_api.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 514671488..81be1975a 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -263,15 +263,6 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): raise openai.error.OpenAIError("Exceeds the maximum retries") async def get_summary(self, text: str, max_words=200, keep_language: bool = False, **kwargs) -> str: - """ - Return string in the following format: - [ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "Knock knock."}, - {"role": "assistant", "content": "Who's there?"}, - {"role": "user", "content": "Orange."}, - ] - """ from metagpt.memory.brain_memory import BrainMemory memory = BrainMemory(llm_type=LLMType.OPENAI.value, historical_summary=text, cacheable=False) From 95a5f1b9f1edc484b280b2b24277c9bf52926d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 8 Sep 2023 16:29:41 +0800 Subject: [PATCH 65/68] fixbug: context missing --- metagpt/roles/assistant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index 397ddc94b..84ca07c9a 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -123,7 +123,7 @@ class Assistant(Role): history_summary = await self.memory.summarize(max_words=800, keep_language=True, llm=self._llm) if last_talk and await self.memory.is_related(text1=last_talk, text2=history_summary, llm=self._llm): # Merge relevant content. - last_talk = await self.memory.rewrite(sentence=last_talk, llm=self._llm) + last_talk = await self.memory.rewrite(sentence=last_talk, context=history_summary, llm=self._llm) return last_talk return last_talk From 85dc0ad7d4522df3c2fc8bdb58c50f9029f25f33 Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Sat, 9 Sep 2023 14:28:46 +0800 Subject: [PATCH 66/68] wait_exponential if RateLimitError --- metagpt/provider/base_gpt_api.py | 9 +--- metagpt/provider/openai_api.py | 71 ++++++-------------------------- 2 files changed, 13 insertions(+), 67 deletions(-) diff --git a/metagpt/provider/base_gpt_api.py b/metagpt/provider/base_gpt_api.py index 1b1187b72..e334e8a5d 100644 --- a/metagpt/provider/base_gpt_api.py +++ b/metagpt/provider/base_gpt_api.py @@ -9,7 +9,6 @@ from abc import abstractmethod from typing import Optional -from metagpt.logs import logger from metagpt.provider.base_chatbot import BaseChatbot @@ -52,13 +51,7 @@ class BaseGPTAPI(BaseChatbot): if format_msgs: message.extend(format_msgs) message.append(self._user_msg(msg)) - try: - rsp = await self.acompletion_text(message, stream=True, generator=generator) - except Exception as e: - logger.exception(f"{e}") - logger.info(f"ask:{msg}, error:{e}") - raise e - logger.info(f"ask:{msg}, anwser:{rsp}") + rsp = await self.acompletion_text(message, stream=True, generator=generator) return rsp def _extract_assistant_rsp(self, context): diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 81be1975a..7fc8b867a 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -7,17 +7,16 @@ Change cost control from global to company level. """ import asyncio -import random import time -import traceback import openai -from openai.error import APIConnectionError +from openai.error import APIConnectionError, RateLimitError from tenacity import ( after_log, retry, retry_if_exception_type, stop_after_attempt, + wait_exponential, wait_fixed, ) @@ -75,16 +74,13 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): """ def __init__(self): - self.llm = openai self.model = CONFIG.openai_api_model self.auto_max_tokens = False self.rpm = int(CONFIG.get("RPM", 10)) RateLimiter.__init__(self, rpm=self.rpm) async def _achat_completion_stream(self, messages: list[dict]) -> str: - response = await self.async_retry_call( - openai.ChatCompletion.acreate, **self._cons_kwargs(messages), stream=True - ) + response = await openai.ChatCompletion.acreate(**self._cons_kwargs(messages), stream=True) # iterate through the stream of events async for chunk in response: chunk_message = chunk["choices"][0]["delta"] # extract the message @@ -118,12 +114,12 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): return kwargs async def _achat_completion(self, messages: list[dict]) -> dict: - rsp = await self.async_retry_call(self.llm.ChatCompletion.acreate, **self._cons_kwargs(messages)) + rsp = await openai.ChatCompletion.acreate(**self._cons_kwargs(messages)) self._update_costs(rsp.get("usage")) return rsp def _chat_completion(self, messages: list[dict]) -> dict: - rsp = self.retry_call(self.llm.ChatCompletion.create, **self._cons_kwargs(messages)) + rsp = openai.ChatCompletion.create(**self._cons_kwargs(messages)) self._update_costs(rsp) return rsp @@ -144,6 +140,13 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): retry=retry_if_exception_type(APIConnectionError), retry_error_callback=log_and_reraise, ) + @retry( + stop=stop_after_attempt(6), + wait=wait_exponential(1), + after=after_log(logger, logger.level("WARNING").name), + retry=retry_if_exception_type(RateLimitError), + reraise=True, + ) async def acompletion_text(self, messages: list[dict], stream=False, generator: bool = False) -> str: """when streaming, print each token in place.""" if stream: @@ -221,58 +224,8 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): return CONFIG.max_tokens_rsp return get_max_completion_tokens(messages, self.model, CONFIG.max_tokens_rsp) - @staticmethod - async def async_retry_call(func, *args, **kwargs): - for i in range(OpenAIGPTAPI.MAX_TRY): - try: - rsp = await func(*args, **kwargs) - return rsp - except openai.error.RateLimitError as e: - random_time = random.uniform(0, 3) # 生成0到5秒之间的随机时间 - rounded_time = round(random_time, 1) # 保留一位小数,以实现0.1秒的精度 - logger.warning(f"Exception:{e}, sleeping for {rounded_time} seconds") - await asyncio.sleep(rounded_time) - continue - except Exception as e: - error_str = traceback.format_exc() - logger.error(f"Exception:{e}, stack:{error_str}") - raise e - raise openai.error.OpenAIError("Exceeds the maximum retries") - - @staticmethod - def retry_call(func, *args, **kwargs): - for i in range(OpenAIGPTAPI.MAX_TRY): - try: - rsp = func(*args, **kwargs) - return rsp - except openai.error.RateLimitError as e: - logger.warning(f"Exception:{e}") - continue - except ( - openai.error.AuthenticationError, - openai.error.PermissionError, - openai.error.InvalidAPIType, - openai.error.SignatureVerificationError, - ) as e: - logger.warning(f"Exception:{e}") - raise e - except Exception as e: - error_str = traceback.format_exc() - logger.error(f"Exception:{e}, stack:{error_str}") - raise e - raise openai.error.OpenAIError("Exceeds the maximum retries") - async def get_summary(self, text: str, max_words=200, keep_language: bool = False, **kwargs) -> str: from metagpt.memory.brain_memory import BrainMemory memory = BrainMemory(llm_type=LLMType.OPENAI.value, historical_summary=text, cacheable=False) return await memory.summarize(llm=self, max_length=max_words, keep_language=keep_language) - - MAX_TRY = 5 - - -if __name__ == "__main__": - txt = """ -as dfas sad lkf sdkl sakdfsdk sjd jsk sdl sk dd sd asd fa sdf sad dd -- .gitlab-ci.yml & base_test.py - """ From 19e78ff13e109b55aebb59ca2da2c9f02bcd78a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sat, 9 Sep 2023 16:38:43 +0800 Subject: [PATCH 67/68] fixbug: get_title --- metagpt/memory/brain_memory.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 59d108a7d..78eeac758 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -226,6 +226,9 @@ class BrainMemory(pydantic.BaseModel): async def get_title(self, llm, max_words=5, **kwargs) -> str: """Generate text title""" + if self.llm_type == LLMType.METAGPT.value: + return self.history[0] if self.history else "New" + summary = await self.summarize(llm=llm, max_words=500) language = CONFIG.language or DEFAULT_LANGUAGE From 1b6b24077e2f4b9fa37af2ee742a3e578c8efeee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sat, 9 Sep 2023 16:43:42 +0800 Subject: [PATCH 68/68] fixbug: get_title --- metagpt/memory/brain_memory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index 78eeac758..be3736100 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -227,7 +227,7 @@ class BrainMemory(pydantic.BaseModel): async def get_title(self, llm, max_words=5, **kwargs) -> str: """Generate text title""" if self.llm_type == LLMType.METAGPT.value: - return self.history[0] if self.history else "New" + return Message(**self.history[0]).content if self.history else "New" summary = await self.summarize(llm=llm, max_words=500)