diff --git a/metagpt/actions/intent_detect.py b/metagpt/actions/intent_detect.py index ee7a01e42..052547798 100644 --- a/metagpt/actions/intent_detect.py +++ b/metagpt/actions/intent_detect.py @@ -43,7 +43,7 @@ SOP_CONFIG = [ "Writes a PRD based on software requirements.", "Writes a design to the project repository, based on the PRD of the project.", "Writes a project plan to the project repository, based on the design of the project.", - "Writes codes to the project repository, based on the project plan of the project.", + "Writes code to implement designed features according to the project plan and adds them to the project repository.", # "Run QA test on the project repository.", "Stage and commit changes for the project repository using Git.", ], diff --git a/metagpt/logs.py b/metagpt/logs.py index c77617436..c46041af8 100644 --- a/metagpt/logs.py +++ b/metagpt/logs.py @@ -8,6 +8,7 @@ from __future__ import annotations +import json import sys from datetime import datetime from functools import partial @@ -17,7 +18,6 @@ from loguru import logger as _logger from pydantic import BaseModel, Field from metagpt.const import METAGPT_ROOT -from metagpt.schema import BaseEnum class ToolOutputItem(BaseModel): @@ -26,21 +26,6 @@ class ToolOutputItem(BaseModel): value: str -class ToolName(str, BaseEnum): - Terminal = "Terminal" - Plan = "Plan" - Browser = "Browser" - Files = "Files" - WritePRD = "WritePRD" - WriteDesign = "WriteDesign" - WriteProjectPlan = "WriteProjectPlan" - WriteCode = "WriteCode" - WriteUntTest = "WriteUntTest" - FixBug = "FixBug" - GitArchive = "GitArchive" - ImportRepo = "ImportRepo" - - def define_log_level(print_level="INFO", logfile_level="DEBUG", name: str = None): """Adjust the log level to above level""" current_date = datetime.now() @@ -66,7 +51,7 @@ def log_tool_output(output: ToolOutputItem | List[ToolOutputItem], tool_name: st return outputs = output if isinstance(output, list) else [output] - _tool_output_log(output=[i.model_dump() for i in outputs], tool_name=tool_name) + _tool_output_log(output=json.dumps([i.model_dump() for i in outputs]), tool_name=tool_name) def set_llm_stream_logfunc(func): @@ -82,4 +67,8 @@ def set_tool_output_logfunc(func): _llm_stream_log = partial(print, end="") -_tool_output_log = partial(print, end="") +def _default_tool_output_log(*args, **kwargs): + print(*args, str(kwargs), end="") + + +_tool_output_log = _default_tool_output_log diff --git a/metagpt/roles/di/mgx.py b/metagpt/roles/di/mgx.py index 5e2d897a2..e14930788 100644 --- a/metagpt/roles/di/mgx.py +++ b/metagpt/roles/di/mgx.py @@ -32,8 +32,8 @@ class MGX(DataInterpreter): sop_str = "\n".join([f"- {i}" for i in i.sop.sop]) markdown = ( f"### User Requirement Detail\n```text\n{intention_ref}\n````\n" - f"### Knowledge\nTo meet user requirements, the following standard operating procedure(SOP)" - f" must be used:\n" + f"### Knowledge\nTo meet user requirements, the following standard operating procedure(SOP) must be" + f" used. SOP descriptions cannot be modified; user requirements can only be appended to the end of corresponding steps.\n" f"{sop_str}" ) return markdown diff --git a/metagpt/schema.py b/metagpt/schema.py index 016e91c23..afd804670 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -46,7 +46,6 @@ from metagpt.const import ( SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO, ) -from metagpt.logs import log_tool_output, logger from metagpt.repo_parser import DotClassInfo from metagpt.utils.common import any_to_str, any_to_str_set, import_class from metagpt.utils.exceptions import handle_exception @@ -434,8 +433,12 @@ class Plan(BaseModel): final_tasks = self.tasks[:prefix_length] + new_tasks[prefix_length:] self.tasks = final_tasks + from metagpt.logs import ToolOutputItem, log_tool_output + log_tool_output( - {"output": "\n\n".join([f"Task {task.task_id}: {task.instruction}" for task in self.tasks])}, + ToolOutputItem( + name="output", value="\n\n".join([f"Task {task.task_id}: {task.instruction}" for task in self.tasks]) + ), tool_name="Plan", ) @@ -814,3 +817,18 @@ class BaseEnum(Enum): obj._value_ = value obj.desc = desc return obj + + +class ToolName(str, BaseEnum): + Terminal = "Terminal" + Plan = "Plan" + Browser = "Browser" + Files = "Files" + WritePRD = "WritePRD" + WriteDesign = "WriteDesign" + WriteProjectPlan = "WriteProjectPlan" + WriteCode = "WriteCode" + WriteUntTest = "WriteUntTest" + FixBug = "FixBug" + GitArchive = "GitArchive" + ImportRepo = "ImportRepo" diff --git a/metagpt/tools/libs/software_development.py b/metagpt/tools/libs/software_development.py index 92ac51f32..968917a53 100644 --- a/metagpt/tools/libs/software_development.py +++ b/metagpt/tools/libs/software_development.py @@ -6,8 +6,8 @@ from pathlib import Path from typing import Optional from metagpt.const import BUGFIX_FILENAME, REQUIREMENT_FILENAME -from metagpt.logs import ToolName, ToolOutputItem, log_tool_output -from metagpt.schema import BugFixContext, Message +from metagpt.logs import ToolOutputItem, log_tool_output +from metagpt.schema import BugFixContext, Message, ToolName from metagpt.tools.tool_registry import register_tool from metagpt.utils.common import any_to_str @@ -86,7 +86,8 @@ async def write_design(prd_path: str | Path) -> Path: from metagpt.roles import Architect ctx = Context() - project_path = Path(prd_path).parent.parent + prd_path = Path(prd_path) + project_path = (Path(prd_path) if not prd_path.is_file() else prd_path.parent) / "../.." ctx.set_repo_dir(project_path) role = Architect(context=ctx) @@ -132,7 +133,8 @@ async def write_project_plan(system_design_path: str | Path) -> Path: from metagpt.roles import ProjectManager ctx = Context() - project_path = Path(system_design_path).parent.parent + system_design_path = Path(system_design_path) + project_path = (system_design_path if not system_design_path.is_file() else system_design_path.parent) / "../.." ctx.set_repo_dir(project_path) role = ProjectManager(context=ctx) @@ -149,7 +151,7 @@ async def write_project_plan(system_design_path: str | Path) -> Path: @register_tool(tags=["software development", "Engineer"]) async def write_codes(task_path: str | Path, inc: bool = False) -> Path: - """Writes codes to the project repository, based on the project plan of the project. + """Writes code to implement designed features according to the project plan and adds them to the project repository. Args: task_path (str|Path): The path to task files under the project directory. @@ -179,7 +181,8 @@ async def write_codes(task_path: str | Path, inc: bool = False) -> Path: ctx = Context() ctx.config.inc = inc - project_path = Path(task_path).parent.parent + task_path = Path(task_path) + project_path = (task_path if not task_path.is_file() else task_path.parent) / "../.." ctx.set_repo_dir(project_path) role = Engineer(context=ctx) @@ -220,7 +223,8 @@ async def run_qa_test(src_path: str | Path) -> Path: from metagpt.roles import QaEngineer ctx = Context() - project_path = Path(src_path).parent + src_path = Path(src_path) + project_path = (src_path if not src_path.is_file() else src_path.parent) / ".." ctx.set_repo_dir(project_path) ctx.src_workspace = ctx.git_repo.workdir / ctx.git_repo.workdir.name diff --git a/tests/metagpt/roles/di/test_mgx.py b/tests/metagpt/roles/di/test_mgx.py index 2e67113b9..2ff886d3d 100644 --- a/tests/metagpt/roles/di/test_mgx.py +++ b/tests/metagpt/roles/di/test_mgx.py @@ -16,6 +16,10 @@ from tests.metagpt.actions.test_intent_detect import DEMO1_CONTENT, DEMO_CONTENT [ [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(role="user", content='Create a "2048 game"'), + Message(role="user", content='"IndentationError: expected an indented block"'), + ], ], ) async def test_mgx(user_messages: List[Message]):