merge main to dev

This commit is contained in:
mannaandpoem 2024-01-22 13:50:21 +08:00
commit 76d4451fc6
28 changed files with 1479 additions and 55 deletions

View file

@ -20,17 +20,27 @@
from __future__ import annotations
import json
import os
from collections import defaultdict
from pathlib import Path
from typing import Set
from typing import Literal, Set
from metagpt.actions import Action, WriteCode, WriteCodeReview, WriteTasks
from metagpt.actions.fix_bug import FixBug
from metagpt.actions.project_management_an import REFINED_TASK_LIST, TASK_LIST
from metagpt.actions.summarize_code import SummarizeCode
from metagpt.const import SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO
from metagpt.actions.write_code_plan_and_change_an import WriteCodePlanAndChange
from metagpt.const import (
CODE_PLAN_AND_CHANGE_FILE_REPO,
CODE_PLAN_AND_CHANGE_FILENAME,
REQUIREMENT_FILENAME,
SYSTEM_DESIGN_FILE_REPO,
TASK_FILE_REPO,
)
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import (
CodePlanAndChangeContext,
CodeSummarizeContext,
CodingContext,
Document,
@ -80,7 +90,7 @@ class Engineer(Role):
super().__init__(**kwargs)
self.set_actions([WriteCode])
self._watch([WriteTasks, SummarizeCode, WriteCode, WriteCodeReview, FixBug])
self._watch([WriteTasks, SummarizeCode, WriteCode, WriteCodeReview, FixBug, WriteCodePlanAndChange])
self.code_todos = []
self.summarize_todos = []
self.next_todo_action = any_to_name(WriteCode)
@ -88,9 +98,9 @@ class Engineer(Role):
@staticmethod
def _parse_tasks(task_msg: Document) -> list[str]:
m = json.loads(task_msg.content)
return m.get("Task list")
return m.get(TASK_LIST.key) or m.get(REFINED_TASK_LIST.key)
async def _act_sp_with_cr(self, review=False) -> Set[str]:
async def _act_sp_with_cr(self, review=False, mode: Literal["normal", "incremental"] = "normal") -> Set[str]:
changed_files = set()
for todo in self.code_todos:
"""
@ -106,9 +116,13 @@ class Engineer(Role):
action = WriteCodeReview(i_context=coding_context, context=self.context, llm=self.llm)
self._init_action(action)
coding_context = await action.run()
dependencies = {coding_context.design_doc.root_relative_path, coding_context.task_doc.root_relative_path}
if mode == "incremental":
dependencies.add(os.path.join(CODE_PLAN_AND_CHANGE_FILE_REPO, CODE_PLAN_AND_CHANGE_FILENAME))
await self.project_repo.srcs.save(
filename=coding_context.filename,
dependencies={coding_context.design_doc.root_relative_path, coding_context.task_doc.root_relative_path},
dependencies=dependencies,
content=coding_context.code_doc.content,
)
msg = Message(
@ -128,6 +142,9 @@ class Engineer(Role):
"""Determines the mode of action based on whether code review is used."""
if self.rc.todo is None:
return None
if isinstance(self.rc.todo, WriteCodePlanAndChange):
self.next_todo_action = any_to_name(WriteCode)
return await self._act_code_plan_and_change()
if isinstance(self.rc.todo, WriteCode):
self.next_todo_action = any_to_name(SummarizeCode)
return await self._act_write_code()
@ -187,6 +204,36 @@ class Engineer(Role):
content=json.dumps(tasks), role=self.profile, cause_by=SummarizeCode, send_to=self, sent_from=self
)
async def _act_code_plan_and_change(self):
"""Write code plan and change that guides subsequent WriteCode and WriteCodeReview"""
logger.info("Writing code plan and change..")
node = await self.rc.todo.run()
code_plan_and_change = node.instruct_content.model_dump_json()
dependencies = {
self.rc.todo.i_context.requirement_doc.filename,
self.rc.todo.i_context.prd_docs[0].filename,
self.rc.todo.i_context.design_docs[0].filename,
self.rc.todo.i_context.tasks_docs[0].filename,
}
code_plan_and_change_filename = os.path.join(CODE_PLAN_AND_CHANGE_FILE_REPO, CODE_PLAN_AND_CHANGE_FILENAME)
await self.project_repo.resources.code_plan_and_change.save(
filename=code_plan_and_change_filename, content=code_plan_and_change, dependencies=dependencies
)
await self.project_repo.docs.code_plan_and_change.save(
filename=Path(code_plan_and_change_filename).with_suffix(".md").name,
content=node.content,
dependencies=dependencies,
)
return Message(
content=code_plan_and_change,
role=self.profile,
cause_by=WriteCodePlanAndChange,
send_to=self,
sent_from=self,
)
async def _is_pass(self, summary) -> (str, str):
rsp = await self.llm.aask(msg=IS_PASS_PROMPT.format(context=summary), stream=False)
logger.info(rsp)
@ -197,11 +244,16 @@ class Engineer(Role):
async def _think(self) -> Action | None:
if not self.src_workspace:
self.src_workspace = self.git_repo.workdir / self.git_repo.workdir.name
write_code_filters = any_to_str_set([WriteTasks, SummarizeCode, FixBug])
write_plan_and_change_filters = any_to_str_set([WriteTasks])
write_code_filters = any_to_str_set([WriteTasks, WriteCodePlanAndChange, SummarizeCode, FixBug])
summarize_code_filters = any_to_str_set([WriteCode, WriteCodeReview])
if not self.rc.news:
return None
msg = self.rc.news[0]
if self.config.inc and msg.cause_by in write_plan_and_change_filters:
logger.debug(f"TODO WriteCodePlanAndChange:{msg.model_dump_json()}")
await self._new_code_plan_and_change_action()
return self.rc.todo
if msg.cause_by in write_code_filters:
logger.debug(f"TODO WriteCode:{msg.model_dump_json()}")
await self._new_code_actions(bug_fix=msg.cause_by == any_to_str(FixBug))
@ -296,6 +348,21 @@ class Engineer(Role):
if self.summarize_todos:
self.set_todo(self.summarize_todos[0])
async def _new_code_plan_and_change_action(self):
"""Create a WriteCodePlanAndChange action for subsequent to-do actions."""
requirement_doc = await self.project_repo.docs.requirement.get(REQUIREMENT_FILENAME)
prd_docs = await self.project_repo.docs.prd.get_all()
design_docs = await self.project_repo.docs.system_design.get_all()
task_docs = await self.project_repo.docs.task.get_all()
code_plan_and_change_context = CodePlanAndChangeContext(
requirement_doc=requirement_doc,
prd_docs=prd_docs,
design_docs=design_docs,
task_docs=task_docs,
)
self.rc.todo = WriteCodePlanAndChange(i_context=code_plan_and_change_context, llm=self.llm)
@property
def todo(self) -> str:
"""AgentStore uses this attribute to display to the user what actions the current role should take."""