From eec69e64a3131c9c58b38920906a2fd4f0477dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sun, 7 Apr 2024 13:01:03 +0800 Subject: [PATCH 01/11] fixbug: type error --- metagpt/actions/di/detect_intent.py | 2 ++ metagpt/roles/di/mgx.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/metagpt/actions/di/detect_intent.py b/metagpt/actions/di/detect_intent.py index 8f56f4ae8..d41317321 100644 --- a/metagpt/actions/di/detect_intent.py +++ b/metagpt/actions/di/detect_intent.py @@ -90,6 +90,8 @@ You should follow the following Standard Operating Procedure: class DetectIntent(Action): async def run(self, user_requirement: str) -> Tuple[str, str]: + if not isinstance(user_requirement, str): + raise ValueError(f"str type error: {user_requirement}") intentions = "\n".join([f"{si.type_name}: {si.value.description}" for si in SOPItem]) prompt = DETECT_PROMPT.format(user_requirement=user_requirement, intentions=intentions) diff --git a/metagpt/roles/di/mgx.py b/metagpt/roles/di/mgx.py index b2caa930b..33377e5f1 100644 --- a/metagpt/roles/di/mgx.py +++ b/metagpt/roles/di/mgx.py @@ -16,7 +16,8 @@ class MGX(DataInterpreter): async def _detect_intent(self, user_msgs: List[Message] = None, **kwargs): todo = DetectIntent(context=self.context) - request_with_sop, sop_type = await todo.run(user_msgs) + user_requirement = "\n".join([f"> {i.role}: {i.content}" for i in user_msgs]) + request_with_sop, sop_type = await todo.run(user_requirement) logger.info(f"{sop_type} {request_with_sop}") return request_with_sop From ef3563500f9aa409f7a35df89ebed49e014d0185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sun, 7 Apr 2024 13:12:34 +0800 Subject: [PATCH 02/11] fixbug: type error --- metagpt/actions/di/detect_intent.py | 24 ++++--------------- metagpt/roles/di/mgx.py | 3 +-- .../metagpt/actions/di/test_detect_intent.py | 3 ++- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/metagpt/actions/di/detect_intent.py b/metagpt/actions/di/detect_intent.py index d41317321..ba42660b6 100644 --- a/metagpt/actions/di/detect_intent.py +++ b/metagpt/actions/di/detect_intent.py @@ -1,12 +1,12 @@ from __future__ import annotations -import asyncio from enum import Enum -from typing import Tuple +from typing import List, Tuple from pydantic import BaseModel from metagpt.actions import Action +from metagpt.schema import Message class SOPItemDef(BaseModel): @@ -89,9 +89,8 @@ You should follow the following Standard Operating Procedure: class DetectIntent(Action): - async def run(self, user_requirement: str) -> Tuple[str, str]: - if not isinstance(user_requirement, str): - raise ValueError(f"str type error: {user_requirement}") + async def run(self, user_msgs: List[Message]) -> Tuple[str, str]: + user_requirement = "\n".join([f"> {i.role}: {i.content}" for i in user_msgs]) intentions = "\n".join([f"{si.type_name}: {si.value.description}" for si in SOPItem]) prompt = DETECT_PROMPT.format(user_requirement=user_requirement, intentions=intentions) @@ -105,18 +104,3 @@ class DetectIntent(Action): ) return req_with_sop, sop_type - - -async def main(): - # Example usage of the DetectIntent action - user_requirements = ["Develop a 2048 game.", "Run data analysis on sklearn wine dataset"] - detect_intent = DetectIntent() - - for user_requirement in user_requirements: - req_with_sop, sop_type = await detect_intent.run(user_requirement) - print(req_with_sop) - print(f"Detected SOP Type: {sop_type}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/metagpt/roles/di/mgx.py b/metagpt/roles/di/mgx.py index 33377e5f1..b2caa930b 100644 --- a/metagpt/roles/di/mgx.py +++ b/metagpt/roles/di/mgx.py @@ -16,8 +16,7 @@ class MGX(DataInterpreter): async def _detect_intent(self, user_msgs: List[Message] = None, **kwargs): todo = DetectIntent(context=self.context) - user_requirement = "\n".join([f"> {i.role}: {i.content}" for i in user_msgs]) - request_with_sop, sop_type = await todo.run(user_requirement) + request_with_sop, sop_type = await todo.run(user_msgs) logger.info(f"{sop_type} {request_with_sop}") return request_with_sop diff --git a/tests/metagpt/actions/di/test_detect_intent.py b/tests/metagpt/actions/di/test_detect_intent.py index 7c9cf9eba..dfb25c343 100644 --- a/tests/metagpt/actions/di/test_detect_intent.py +++ b/tests/metagpt/actions/di/test_detect_intent.py @@ -1,6 +1,7 @@ import pytest from metagpt.actions.di.detect_intent import DetectIntent +from metagpt.schema import Message SOFTWARE_DEV_REQ1 = """ I'd like to create a personalized website that features the 'Game of Life' simulation. @@ -51,5 +52,5 @@ git clone 'https://github.com/spec-first/connexion' and format to MetaGPT projec ) async def test_detect_intent(requirement, expected_intent_type): di = DetectIntent() - _, intent_type = await di.run(requirement) + _, intent_type = await di.run([Message(role="user", content=requirement)]) assert intent_type == expected_intent_type From ffa0a6223dfcb1a12e8ea5f5fa0acfedfafd59bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sun, 7 Apr 2024 13:27:10 +0800 Subject: [PATCH 03/11] fixbug: type error --- metagpt/actions/di/detect_intent.py | 23 ++++++++++++++++--- metagpt/roles/di/mgx.py | 8 +++---- .../metagpt/actions/di/test_detect_intent.py | 2 +- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/metagpt/actions/di/detect_intent.py b/metagpt/actions/di/detect_intent.py index ba42660b6..a09541cf6 100644 --- a/metagpt/actions/di/detect_intent.py +++ b/metagpt/actions/di/detect_intent.py @@ -1,7 +1,8 @@ from __future__ import annotations +import asyncio from enum import Enum -from typing import List, Tuple +from typing import Tuple from pydantic import BaseModel @@ -89,8 +90,9 @@ You should follow the following Standard Operating Procedure: class DetectIntent(Action): - async def run(self, user_msgs: List[Message]) -> Tuple[str, str]: - user_requirement = "\n".join([f"> {i.role}: {i.content}" for i in user_msgs]) + async def run(self, with_message: Message, **kwargs) -> Tuple[str, str]: + ref_content = with_message.content.replace("\n", "\n> ") + user_requirement = f"> {with_message.role}: {ref_content}" intentions = "\n".join([f"{si.type_name}: {si.value.description}" for si in SOPItem]) prompt = DETECT_PROMPT.format(user_requirement=user_requirement, intentions=intentions) @@ -104,3 +106,18 @@ class DetectIntent(Action): ) return req_with_sop, sop_type + + +async def main(): + # Example usage of the DetectIntent action + user_requirements = ["Develop a 2048 game.", "Run data analysis on sklearn wine dataset"] + detect_intent = DetectIntent() + + for user_requirement in user_requirements: + req_with_sop, sop_type = await detect_intent.run(Message(role="user", content=user_requirement)) + print(req_with_sop) + print(f"Detected SOP Type: {sop_type}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/metagpt/roles/di/mgx.py b/metagpt/roles/di/mgx.py index b2caa930b..90e88303e 100644 --- a/metagpt/roles/di/mgx.py +++ b/metagpt/roles/di/mgx.py @@ -2,7 +2,7 @@ # @Author : stellahong (stellahong@fuzhi.ai) # @Desc : import asyncio -from typing import Dict, List +from typing import Dict from metagpt.actions.di.detect_intent import DetectIntent from metagpt.logs import logger @@ -14,9 +14,9 @@ class MGX(DataInterpreter): use_intent: bool = True intents: Dict = {} - async def _detect_intent(self, user_msgs: List[Message] = None, **kwargs): + async def _detect_intent(self, user_msg: Message = None, **kwargs): todo = DetectIntent(context=self.context) - request_with_sop, sop_type = await todo.run(user_msgs) + request_with_sop, sop_type = await todo.run(user_msg) logger.info(f"{sop_type} {request_with_sop}") return request_with_sop @@ -27,7 +27,7 @@ class MGX(DataInterpreter): goal = self.rc.memory.get()[-1].content # retreive latest user requirement if self.use_intent: # add mode user_message = Message(content=goal, role="user") - goal = await self._detect_intent(user_msgs=[user_message]) + goal = await self._detect_intent(user_message) logger.info(f"Goal is {goal}") await self.planner.update_plan(goal=goal) diff --git a/tests/metagpt/actions/di/test_detect_intent.py b/tests/metagpt/actions/di/test_detect_intent.py index dfb25c343..e555680ef 100644 --- a/tests/metagpt/actions/di/test_detect_intent.py +++ b/tests/metagpt/actions/di/test_detect_intent.py @@ -52,5 +52,5 @@ git clone 'https://github.com/spec-first/connexion' and format to MetaGPT projec ) async def test_detect_intent(requirement, expected_intent_type): di = DetectIntent() - _, intent_type = await di.run([Message(role="user", content=requirement)]) + _, intent_type = await di.run(Message(role="user", content=requirement)) assert intent_type == expected_intent_type From b91905ce581dd91f86039d82e64138394530727b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sun, 7 Apr 2024 13:28:46 +0800 Subject: [PATCH 04/11] fixbug: type error --- metagpt/roles/di/mgx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/di/mgx.py b/metagpt/roles/di/mgx.py index 90e88303e..0034dfe93 100644 --- a/metagpt/roles/di/mgx.py +++ b/metagpt/roles/di/mgx.py @@ -14,7 +14,7 @@ class MGX(DataInterpreter): use_intent: bool = True intents: Dict = {} - async def _detect_intent(self, user_msg: Message = None, **kwargs): + async def _detect_intent(self, user_msg: Message, **kwargs): todo = DetectIntent(context=self.context) request_with_sop, sop_type = await todo.run(user_msg) logger.info(f"{sop_type} {request_with_sop}") From ada3c1554ce58623a00320d0a57934a79a864467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sun, 7 Apr 2024 13:29:07 +0800 Subject: [PATCH 05/11] fixbug: type error --- metagpt/roles/di/mgx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/di/mgx.py b/metagpt/roles/di/mgx.py index 0034dfe93..1002a559c 100644 --- a/metagpt/roles/di/mgx.py +++ b/metagpt/roles/di/mgx.py @@ -14,7 +14,7 @@ class MGX(DataInterpreter): use_intent: bool = True intents: Dict = {} - async def _detect_intent(self, user_msg: Message, **kwargs): + async def _detect_intent(self, user_msg: Message): todo = DetectIntent(context=self.context) request_with_sop, sop_type = await todo.run(user_msg) logger.info(f"{sop_type} {request_with_sop}") From 4455b298fcaf03f12dc079490490bd755430ae94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sun, 7 Apr 2024 13:39:19 +0800 Subject: [PATCH 06/11] fixbug: type error --- metagpt/actions/di/detect_intent.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/metagpt/actions/di/detect_intent.py b/metagpt/actions/di/detect_intent.py index a09541cf6..9109c01bb 100644 --- a/metagpt/actions/di/detect_intent.py +++ b/metagpt/actions/di/detect_intent.py @@ -91,8 +91,7 @@ You should follow the following Standard Operating Procedure: class DetectIntent(Action): async def run(self, with_message: Message, **kwargs) -> Tuple[str, str]: - ref_content = with_message.content.replace("\n", "\n> ") - user_requirement = f"> {with_message.role}: {ref_content}" + user_requirement = with_message.content intentions = "\n".join([f"{si.type_name}: {si.value.description}" for si in SOPItem]) prompt = DETECT_PROMPT.format(user_requirement=user_requirement, intentions=intentions) From 7ec3b5c627a647bda28774f578ad957f8a6b5fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Sun, 7 Apr 2024 14:26:44 +0800 Subject: [PATCH 07/11] AttributeError: 'NoneType' object has no attribute 'sop' --- metagpt/actions/di/detect_intent.py | 3 ++- metagpt/tools/libs/software_development.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/metagpt/actions/di/detect_intent.py b/metagpt/actions/di/detect_intent.py index 9109c01bb..92d4aeb54 100644 --- a/metagpt/actions/di/detect_intent.py +++ b/metagpt/actions/di/detect_intent.py @@ -98,7 +98,8 @@ class DetectIntent(Action): sop_type = await self._aask(prompt) sop_type = sop_type.strip() - sop = SOPItem.get_type(sop_type).sop + item = SOPItem.get_type(sop_type) + sop = item.sop if item else None req_with_sop = ( REQ_WITH_SOP.format(user_requirement=user_requirement, sop="\n".join(sop)) if sop else user_requirement diff --git a/metagpt/tools/libs/software_development.py b/metagpt/tools/libs/software_development.py index acc3716b1..04da431b7 100644 --- a/metagpt/tools/libs/software_development.py +++ b/metagpt/tools/libs/software_development.py @@ -43,7 +43,7 @@ async def write_prd(idea: str, project_path: Optional[str | Path] = None) -> Pat from metagpt.roles import ProductManager ctx = Context() - if project_path: + if project_path and Path(project_path).exists(): ctx.config.project_path = Path(project_path) ctx.config.inc = True role = ProductManager(context=ctx) From 52592ad1667714115469efafefb9fc81d0d4a24c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Mon, 8 Apr 2024 12:40:20 +0800 Subject: [PATCH 08/11] fixbug: intent type error --- metagpt/actions/di/detect_intent.py | 31 +++++++++++++++++++---------- metagpt/roles/di/mgx.py | 4 ++-- tests/metagpt/roles/di/test_mgx.py | 6 +++--- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/metagpt/actions/di/detect_intent.py b/metagpt/actions/di/detect_intent.py index 92d4aeb54..1fb1c9089 100644 --- a/metagpt/actions/di/detect_intent.py +++ b/metagpt/actions/di/detect_intent.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import re from enum import Enum from typing import Tuple @@ -78,13 +79,16 @@ DETECT_PROMPT = """ # Intentions {intentions} # Task -Classify user requirement into one type of the above intentions, output the name of the intention directly. -Intention name: +Classify user requirement into one type of the above intentions, output the index of the intention directly. +Intention index: """ REQ_WITH_SOP = """ {user_requirement} -You should follow the following Standard Operating Procedure: +## Knowledge +To meet user requirements, the following standard operating procedure(SOP) must be used. +SOP descriptions cannot be modified; user requirements can only be appended to the end of corresponding steps. + {sop} """ @@ -92,17 +96,24 @@ You should follow the following Standard Operating Procedure: class DetectIntent(Action): async def run(self, with_message: Message, **kwargs) -> Tuple[str, str]: user_requirement = with_message.content - intentions = "\n".join([f"{si.type_name}: {si.value.description}" for si in SOPItem]) + mappings = {i + 1: si for i, si in enumerate(SOPItem)} + intentions = "\n".join([f"{i+1}. {si.type_name}: {si.value.description}" for i, si in enumerate(SOPItem)]) prompt = DETECT_PROMPT.format(user_requirement=user_requirement, intentions=intentions) - sop_type = await self._aask(prompt) - sop_type = sop_type.strip() - - item = SOPItem.get_type(sop_type) - sop = item.sop if item else None + rsp = await self._aask(prompt) + match = re.search(r"\d+", rsp) + index = len(SOPItem) + 1 # 1-based + if match: + index = int(match.group()) # 1-based + sop = mappings[index].value.sop if index in mappings else None + sop_type = mappings[index].type_name if index in mappings else SOPItem.OTHER.type_name req_with_sop = ( - REQ_WITH_SOP.format(user_requirement=user_requirement, sop="\n".join(sop)) if sop else user_requirement + REQ_WITH_SOP.format( + user_requirement=user_requirement, sop="\n".join([f"{i+1}. {v}" for i, v in enumerate(sop)]) + ) + if sop + else user_requirement ) return req_with_sop, sop_type diff --git a/metagpt/roles/di/mgx.py b/metagpt/roles/di/mgx.py index 1002a559c..0fa7c77b6 100644 --- a/metagpt/roles/di/mgx.py +++ b/metagpt/roles/di/mgx.py @@ -14,7 +14,7 @@ class MGX(DataInterpreter): use_intent: bool = True intents: Dict = {} - async def _detect_intent(self, user_msg: Message): + async def _detect_intent(self, user_msg: Message) -> str: todo = DetectIntent(context=self.context) request_with_sop, sop_type = await todo.run(user_msg) logger.info(f"{sop_type} {request_with_sop}") @@ -24,7 +24,7 @@ class MGX(DataInterpreter): """first plan, then execute an action sequence, i.e. _think (of a plan) -> _act -> _act -> ... Use llm to come up with the plan dynamically.""" # create initial plan and update it until confirmation - goal = self.rc.memory.get()[-1].content # retreive latest user requirement + goal = self.rc.memory.get()[-1].content # retrieve latest user requirement if self.use_intent: # add mode user_message = Message(content=goal, role="user") goal = await self._detect_intent(user_message) diff --git a/tests/metagpt/roles/di/test_mgx.py b/tests/metagpt/roles/di/test_mgx.py index 534e32499..3c08fb356 100644 --- a/tests/metagpt/roles/di/test_mgx.py +++ b/tests/metagpt/roles/di/test_mgx.py @@ -139,11 +139,11 @@ DEMO3_CONTENT = [ "user_messages", [ [Message.model_validate(i) for i in DEMO2_CONTENT if i["role"] == "user"], - [Message.model_validate(i) for i in DEMO_CONTENT if i["role"] == "user"], - [Message.model_validate(i) for i in DEMO1_CONTENT if i["role"] == "user"], + # [Message.model_validate(i) for i in DEMO_CONTENT if i["role"] == "user"], + # [Message.model_validate(i) for i in DEMO1_CONTENT if i["role"] == "user"], ], ) -@pytest.mark.skip +# @pytest.mark.skip async def test_mgx(user_messages: List[Message], context): mgx = MGX(context=context, tools=[""]) From c9a42e85bbf775300c38a685e66f3fe9e97d9b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Mon, 8 Apr 2024 12:43:19 +0800 Subject: [PATCH 09/11] fixbug: intent type error --- tests/metagpt/roles/di/test_mgx.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/metagpt/roles/di/test_mgx.py b/tests/metagpt/roles/di/test_mgx.py index 3c08fb356..534e32499 100644 --- a/tests/metagpt/roles/di/test_mgx.py +++ b/tests/metagpt/roles/di/test_mgx.py @@ -139,11 +139,11 @@ DEMO3_CONTENT = [ "user_messages", [ [Message.model_validate(i) for i in DEMO2_CONTENT if i["role"] == "user"], - # [Message.model_validate(i) for i in DEMO_CONTENT if i["role"] == "user"], - # [Message.model_validate(i) for i in DEMO1_CONTENT if i["role"] == "user"], + [Message.model_validate(i) for i in DEMO_CONTENT if i["role"] == "user"], + [Message.model_validate(i) for i in DEMO1_CONTENT if i["role"] == "user"], ], ) -# @pytest.mark.skip +@pytest.mark.skip async def test_mgx(user_messages: List[Message], context): mgx = MGX(context=context, tools=[""]) From 0251e7ad20202011d77de8a443762bf91d108cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Mon, 8 Apr 2024 14:58:50 +0800 Subject: [PATCH 10/11] feat: +tool log --- metagpt/tools/libs/software_development.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/metagpt/tools/libs/software_development.py b/metagpt/tools/libs/software_development.py index 04da431b7..c5b38e00a 100644 --- a/metagpt/tools/libs/software_development.py +++ b/metagpt/tools/libs/software_development.py @@ -51,13 +51,21 @@ async def write_prd(idea: str, project_path: Optional[str | Path] = None) -> Pat await role.run(with_message=msg) outputs = [ - ToolLogItem(name="PRD File", value=str(ctx.repo.docs.prd.workdir / i)) + ToolLogItem(name="Intermedia PRD File", value=str(ctx.repo.docs.prd.workdir / i)) for i in ctx.repo.docs.prd.changed_files.keys() ] - for i in ctx.repo.resources.competitive_analysis.changed_files.keys(): - outputs.append( + outputs.extend( + [ + ToolLogItem(name="PRD File", value=str(ctx.repo.resources.prd.workdir / i)) + for i in ctx.repo.resources.prd.changed_files.keys() + ] + ) + outputs.extend( + [ ToolLogItem(name="Competitive Analysis", value=str(ctx.repo.resources.competitive_analysis.workdir / i)) - ) + for i in ctx.repo.resources.competitive_analysis.changed_files.keys() + ] + ) log_tool_output(output=outputs, tool_name=write_prd.__name__) return ctx.repo.docs.prd.workdir @@ -327,9 +335,12 @@ async def git_archive(project_path: str | Path) -> str: ctx = Context() ctx.set_repo_dir(project_path) + files = " ".join(ctx.git_repo.changed_files.keys()) + outputs = [ToolLogItem(name="cmd", value=f"git add {files}")] + log_tool_output(output=outputs, tool_name=git_archive.__name__) ctx.git_repo.archive() - outputs = [ToolLogItem(name="Git Commit", value=str(ctx.repo.workdir))] + outputs = [ToolLogItem(name="cmd", value="git commit -m 'Archive'")] log_tool_output(output=outputs, tool_name=git_archive.__name__) return ctx.git_repo.log() From a221f8cced5e867cc29f458326b44fc5a2247f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Mon, 8 Apr 2024 16:22:31 +0800 Subject: [PATCH 11/11] feat: +task markdown --- metagpt/actions/project_management.py | 1 + metagpt/const.py | 3 ++ metagpt/tools/libs/software_development.py | 39 ++++++++++++++++++++-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/metagpt/actions/project_management.py b/metagpt/actions/project_management.py index 67a614d6f..b52616e37 100644 --- a/metagpt/actions/project_management.py +++ b/metagpt/actions/project_management.py @@ -70,6 +70,7 @@ class WriteTasks(Action): dependencies={system_design_doc.root_relative_path}, ) await self._update_requirements(task_doc) + await self.repo.resources.api_spec_and_task.save_pdf(doc=task_doc) return task_doc async def _run_new_tasks(self, context): diff --git a/metagpt/const.py b/metagpt/const.py index e4cebfd96..484987a03 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -135,3 +135,6 @@ AGGREGATION = "Aggregate" # Timeout USE_CONFIG_TIMEOUT = 0 # Using llm.timeout configuration. LLM_API_TIMEOUT = 300 + +# Assistant alias +ASSISTANT_ALIAS = "response" diff --git a/metagpt/tools/libs/software_development.py b/metagpt/tools/libs/software_development.py index 04da431b7..726f46e12 100644 --- a/metagpt/tools/libs/software_development.py +++ b/metagpt/tools/libs/software_development.py @@ -5,7 +5,7 @@ from __future__ import annotations from pathlib import Path from typing import Optional -from metagpt.const import BUGFIX_FILENAME, REQUIREMENT_FILENAME +from metagpt.const import ASSISTANT_ALIAS, BUGFIX_FILENAME, REQUIREMENT_FILENAME from metagpt.logs import ToolLogItem, log_tool_output from metagpt.schema import BugFixContext, Message from metagpt.tools.tool_registry import register_tool @@ -42,6 +42,8 @@ async def write_prd(idea: str, project_path: Optional[str | Path] = None) -> Pat from metagpt.context import Context from metagpt.roles import ProductManager + log_tool_output(output=[ToolLogItem(name=ASSISTANT_ALIAS, value=write_prd.__name__)], tool_name=write_prd.__name__) + ctx = Context() if project_path and Path(project_path).exists(): ctx.config.project_path = Path(project_path) @@ -85,6 +87,10 @@ async def write_design(prd_path: str | Path) -> Path: from metagpt.context import Context from metagpt.roles import Architect + log_tool_output( + output=[ToolLogItem(name=ASSISTANT_ALIAS, value=write_design.__name__)], tool_name=write_design.__name__ + ) + ctx = Context() prd_path = Path(prd_path) project_path = (Path(prd_path) if not prd_path.is_file() else prd_path.parent) / "../.." @@ -132,6 +138,11 @@ async def write_project_plan(system_design_path: str | Path) -> Path: from metagpt.context import Context from metagpt.roles import ProjectManager + log_tool_output( + output=[ToolLogItem(name=ASSISTANT_ALIAS, value=write_project_plan.__name__)], + tool_name=write_project_plan.__name__, + ) + ctx = Context() system_design_path = Path(system_design_path) project_path = (system_design_path if not system_design_path.is_file() else system_design_path.parent) / "../.." @@ -141,9 +152,15 @@ async def write_project_plan(system_design_path: str | Path) -> Path: await role.run(with_message=Message(content="", cause_by=WriteDesign)) outputs = [ - ToolLogItem(name="Project Plan", value=str(ctx.repo.docs.task.workdir / i)) + ToolLogItem(name="Intermedia Project Plan", value=str(ctx.repo.docs.task.workdir / i)) for i in ctx.repo.docs.task.changed_files.keys() ] + outputs.extend( + [ + ToolLogItem(name="Project Plan", value=str(ctx.repo.resources.api_spec_and_task.workdir / i)) + for i in ctx.repo.resources.api_spec_and_task.changed_files.keys() + ] + ) log_tool_output(output=outputs, tool_name=write_project_plan.__name__) return ctx.repo.docs.task.workdir @@ -179,6 +196,10 @@ async def write_codes(task_path: str | Path, inc: bool = False) -> Path: from metagpt.context import Context from metagpt.roles import Engineer + log_tool_output( + output=[ToolLogItem(name=ASSISTANT_ALIAS, value=write_codes.__name__)], tool_name=write_codes.__name__ + ) + ctx = Context() ctx.config.inc = inc task_path = Path(task_path) @@ -222,6 +243,10 @@ async def run_qa_test(src_path: str | Path) -> Path: from metagpt.environment import Environment from metagpt.roles import QaEngineer + log_tool_output( + output=[ToolLogItem(name=ASSISTANT_ALIAS, value=run_qa_test.__name__)], tool_name=run_qa_test.__name__ + ) + ctx = Context() src_path = Path(src_path) project_path = (src_path if not src_path.is_file() else src_path.parent) / ".." @@ -270,6 +295,8 @@ async def fix_bug(project_path: str | Path, issue: str) -> Path: from metagpt.context import Context from metagpt.roles import Engineer + log_tool_output(output=[ToolLogItem(name=ASSISTANT_ALIAS, value=fix_bug.__name__)], tool_name=fix_bug.__name__) + ctx = Context() ctx.set_repo_dir(project_path) ctx.src_workspace = ctx.git_repo.workdir / ctx.git_repo.workdir.name @@ -325,6 +352,10 @@ async def git_archive(project_path: str | Path) -> str: """ from metagpt.context import Context + log_tool_output( + output=[ToolLogItem(name=ASSISTANT_ALIAS, value=git_archive.__name__)], tool_name=git_archive.__name__ + ) + ctx = Context() ctx.set_repo_dir(project_path) ctx.git_repo.archive() @@ -358,6 +389,10 @@ async def import_git_repo(url: str) -> Path: from metagpt.actions.import_repo import ImportRepo from metagpt.context import Context + log_tool_output( + output=[ToolLogItem(name=ASSISTANT_ALIAS, value=import_git_repo.__name__)], tool_name=import_git_repo.__name__ + ) + ctx = Context() action = ImportRepo(repo_path=url, context=ctx) await action.run()