feat: resolve conflicts

This commit is contained in:
geekan 2023-12-14 23:54:38 +08:00 committed by 莘权 马
parent ad0ac94093
commit b97ca3af7e
12 changed files with 120 additions and 90 deletions

View file

@ -44,6 +44,7 @@ class Action(ABC):
self.prefix = prefix
self.profile = profile
self.llm.system_prompt = prefix
return self
def __str__(self):
return self.__class__.__name__

View file

@ -243,7 +243,7 @@ class ActionNode:
)
return prompt
@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6))
@retry(wait=wait_random_exponential(min=1, max=10), stop=stop_after_attempt(6))
async def _aask_v1(
self,
prompt: str,

View file

@ -99,7 +99,7 @@ class WriteTasks(Action):
async def _merge(self, system_design_doc, task_doc, format=CONFIG.prompt_format) -> Document:
context = NEW_REQ_TEMPLATE.format(context=system_design_doc.content, old_tasks=task_doc.content)
node = await PM_NODE.fill(context, self.llm, format)
task_doc.content = node.content
task_doc.content = node.instruct_content.json(ensure_ascii=False)
return task_doc
@staticmethod

View file

@ -34,59 +34,52 @@ from metagpt.utils.file_repository import FileRepository
PROMPT_TEMPLATE = """
NOTICE
Role: You are a professional engineer; the main goal is to write PEP8 compliant, elegant, modular, easy to read and maintain Python 3.9 code (but you can also use other programming language)
Role: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code
Language: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.
ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
-----
# Design
```json
# Context
## Design
{design}
```
-----
# Tasks
```json
## Tasks
{tasks}
```
-----
# Legacy Code
```python
## Legacy Code
```Code
{code}
```
-----
# Debug logs
## Debug logs
```text
{logs}
{summary_log}
```
-----
# Bug Feedback logs
## Bug Feedback logs
```text
{feedback}
```
-----
## Code: {filename} Write code with triple quoto, based on the following list and context.
1. Do your best to implement THIS ONLY ONE FILE. ONLY USE EXISTING API. IF NO API, IMPLEMENT IT.
2. Requirement: Based on the context, implement one following code file, note to return only in code form, your code will be part of the entire project, so please implement complete, reliable, reusable code snippets
3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE.
4. Follow design: YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN.
5. Think before writing: What should be implemented and provided in this document?
6. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.
7. Do not use public member functions that do not exist in your design.
8. Before using a variable, make sure you reference it first
9. Write out EVERY DETAIL, DON'T LEAVE TODO.
## Format example
-----
# Format example
## Code: {filename}
```python
## {filename}
...
```
-----
# Instruction: Based on the context, follow "Format example", write code.
## Code: {filename}. Write code with triple quoto, based on the following attentions and context.
1. Only One file: do your best to implement THIS ONLY ONE FILE.
2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.
3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.
4. Follow design: YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.
5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.
6. Before using a external variable/module, make sure you import it first.
7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.
"""
@ -107,7 +100,7 @@ class WriteCode(Action):
filename="test_" + coding_context.filename + ".json", relative_path=TEST_OUTPUTS_FILE_REPO
)
summary_doc = None
if coding_context.design_doc.filename:
if coding_context.design_doc and coding_context.design_doc.filename:
summary_doc = await FileRepository.get_file(
filename=coding_context.design_doc.filename, relative_path=CODE_SUMMARIES_FILE_REPO
)
@ -115,9 +108,14 @@ class WriteCode(Action):
if test_doc:
test_detail = RunCodeResult.loads(test_doc.content)
logs = test_detail.stderr
code_context = await self.get_codes(coding_context.task_doc, exclude=self.context.filename)
if bug_feedback:
code_context = coding_context.code_doc.content
else:
code_context = await self.get_codes(coding_context.task_doc, exclude=self.context.filename)
prompt = PROMPT_TEMPLATE.format(
design=coding_context.design_doc.content,
design=coding_context.design_doc.content if coding_context.design_doc else "",
tasks=coding_context.task_doc.content if coding_context.task_doc else "",
code=code_context,
logs=logs,
@ -148,5 +146,5 @@ class WriteCode(Action):
doc = await src_file_repo.get(filename=filename)
if not doc:
continue
codes.append(doc.content)
return "\n----------\n".join(codes)
codes.append(f"----- {filename}\n" + doc.content)
return "\n".join(codes)

View file

@ -18,8 +18,8 @@ from metagpt.schema import CodingContext
from metagpt.utils.common import CodeParser
PROMPT_TEMPLATE = """
NOTICE
Role: You are a professional software engineer, and your main task is to review the code. You need to ensure that the code conforms to the PEP8 standards, is elegantly designed and modularized, easy to read and maintain, and is written in Python 3.9 (or in another programming language).
# System
Role: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.
Language: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.
ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
@ -27,53 +27,52 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenc
{context}
## Code to be Reviewed: {filename}
```
```Code
{code}
```
"""
-----
## Code Review: Based on the "Code to be Reviewed", provide key, clear, concise, and specific code modification suggestions, up to 5.
EXAMPLE_AND_INSTRUCTION = """
{format_example}
# Instruction: Based on the actual code situation, follow one of the "Format example".
## Code Review: Ordered List. Based on the "Code to be Reviewed", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.
1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.
2. Is the code logic completely correct? If there are errors, please indicate how to correct them.
3. Does the existing code follow the "Data structures and interfaces"?
4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.
5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported
6. Is the code implemented concisely enough? Are methods from other files being reused correctly?
6. Are methods from other files being reused correctly?
## Code Review Result: If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.
## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B
## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.
LGTM/LBTM
## Rewrite Code: if it still has some bugs, rewrite {filename} based on "Code Review" with triple quotes, try to get LGTM. Do your utmost to optimize THIS SINGLE FILE. Implement ALL TODO. RETURN ALL CODE, NEVER OMIT ANYTHING. 以任何方式省略代码都是不允许的。
```
```
## Format example
{format_example}
"""
FORMAT_EXAMPLE = """
-----
# EXAMPLE 1
# Format example 1
## Code Review: {filename}
1. No, we should add the logic of ...
1. No, we should fix the logic of class A due to ...
2. ...
3. ...
4. ...
4. No, function B is not implemented, ...
5. ...
6. ...
## Code Review Result: {filename}
## Actions
1. fix class A
2. implement function B
## Code Review Result
LBTM
## Rewrite Code: {filename}
```python
## {filename}
...
```
-----
# EXAMPLE 2
# Format example 2
## Code Review: {filename}
1. Yes.
2. Yes.
@ -82,12 +81,20 @@ LBTM
5. Yes.
6. Yes.
## Code Review Result: {filename}
LGTM
## Rewrite Code: {filename}
## Actions
pass
-----
## Code Review Result
LGTM
"""
REWRITE_CODE_TEMPLATE = """
# Instruction: rewrite code based on the Code Review and Actions
## Rewrite Code: CodeBlock. If it still has some bugs, rewrite {filename} with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes.
```Code
## {filename}
...
```
"""
@ -96,11 +103,15 @@ class WriteCodeReview(Action):
super().__init__(name, context, llm)
@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6))
async def write_code_review_and_rewrite(self, prompt):
code_rsp = await self._aask(prompt)
result = CodeParser.parse_block("Code Review Result", code_rsp)
async def write_code_review_and_rewrite(self, context_prompt, cr_prompt, filename):
cr_rsp = await self._aask(context_prompt + cr_prompt)
result = CodeParser.parse_block("Code Review Result", cr_rsp)
if "LGTM" in result:
return result, None
# if LBTM, rewrite code
rewrite_prompt = f"{context_prompt}\n{cr_rsp}\n{REWRITE_CODE_TEMPLATE.format(filename=filename)}"
code_rsp = await self._aask(rewrite_prompt)
code = CodeParser.parse_code(block="", text=code_rsp)
return result, code
@ -111,23 +122,23 @@ class WriteCodeReview(Action):
format_example = FORMAT_EXAMPLE.format(filename=self.context.code_doc.filename)
task_content = self.context.task_doc.content if self.context.task_doc else ""
code_context = await WriteCode.get_codes(self.context.task_doc, exclude=self.context.filename)
context = "\n----------\n".join(
context = "\n".join(
[
"```text\n" + self.context.design_doc.content + "```\n",
"```text\n" + task_content + "```\n",
"```python\n" + code_context + "```\n",
"## System Design\n" + str(self.context.design_doc) + "\n",
"## Tasks\n" + task_content + "\n",
"## Code Files\n" + code_context + "\n",
]
)
prompt = PROMPT_TEMPLATE.format(
context_prompt = PROMPT_TEMPLATE.format(
context=context,
code=iterative_code,
filename=self.context.code_doc.filename,
format_example=format_example,
)
cr_prompt = EXAMPLE_AND_INSTRUCTION.format(format_example=format_example, )
logger.info(
f"Code review and rewrite {self.context.code_doc.filename,}: {i+1}/{k} | {len(iterative_code)=}, {len(self.context.code_doc.content)=}"
f"Code review and rewrite {self.context.code_doc.filename}: {i+1}/{k} | {len(iterative_code)=}, {len(self.context.code_doc.content)=}"
)
result, rewrited_code = await self.write_code_review_and_rewrite(prompt)
result, rewrited_code = await self.write_code_review_and_rewrite(context_prompt, cr_prompt, self.context.code_doc.filename)
if "LBTM" in result:
iterative_code = rewrited_code
elif "LGTM" in result:

View file

@ -93,7 +93,7 @@ REQUIREMENT_POOL = ActionNode(
key="Requirement Pool",
expected_type=list[list[str]],
instruction="List down the requirements with their priority (P0, P1, P2).",
example=[["P0", "High priority requirement"], ["P1", "Medium priority requirement"]],
example=[["P0", "..."], ["P1", "..."]],
)
UI_DESIGN_DRAFT = ActionNode(

View file

@ -49,8 +49,8 @@ class BaseGPTAPI(BaseChatbot):
message = (
[self._default_system_msg(), self._user_msg(msg)] if self.use_system_prompt else [self._user_msg(msg)]
)
rsp = await self.acompletion_text(message, stream=stream)
logger.debug(message)
rsp = await self.acompletion_text(message, stream=stream)
# logger.debug(rsp)
return rsp

View file

@ -72,7 +72,7 @@ class Engineer(Role):
name: str = "Alex",
profile: str = "Engineer",
goal: str = "write elegant, readable, extensible, efficient code",
constraints: str = "the code should conform to standards like PEP8 and be modular and maintainable. "
constraints: str = "the code should conform to standards like google-style and be modular and maintainable. "
"Use same language as user requirement",
n_borg: int = 1,
use_code_review: bool = False,
@ -105,7 +105,9 @@ class Engineer(Role):
coding_context = await todo.run()
# Code review
if review:
coding_context = await WriteCodeReview(context=coding_context, llm=self._llm).run()
action = WriteCodeReview(context=coding_context, llm=self._llm)
self._init_action_system_message(action)
coding_context = await action.run()
await src_file_repo.save(
coding_context.filename,
dependencies={coding_context.design_doc.root_relative_path, coding_context.task_doc.root_relative_path},
@ -224,6 +226,7 @@ class Engineer(Role):
task_doc = await task_file_repo.get(i.name)
elif str(i.parent) == SYSTEM_DESIGN_FILE_REPO:
design_doc = await design_file_repo.get(i.name)
# FIXME: design doc没有加载进来是None
context = CodingContext(filename=filename, design_doc=design_doc, task_doc=task_doc, code_doc=old_code_doc)
return context

View file

@ -134,6 +134,7 @@ class Role:
self._setting = RoleSetting(
name=name, profile=profile, goal=goal, constraints=constraints, desc=desc, is_human=is_human
)
self._llm.system_prompt = self._get_prefix()
self._states = []
self._actions = []
self._role_id = str(self._setting)
@ -144,6 +145,9 @@ class Role:
self._states = []
self._actions = []
def _init_action_system_message(self, action: Action):
action.set_prefix(self._get_prefix(), self.profile)
def _init_actions(self, actions):
self._reset()
for idx, action in enumerate(actions):
@ -158,7 +162,7 @@ class Role:
)
i = action
# i.set_env(self._rc.env)
i.set_prefix(self._get_prefix(), self.profile)
self._init_action_system_message(i)
self._actions.append(i)
self._states.append(f"{idx}. {action}")
@ -408,7 +412,7 @@ class Role:
logger.debug(f"{self._setting}: no news. waiting.")
return
rsp = await self._react()
rsp = await self.react()
# Reset the next action to be taken.
self._rc.todo = None

View file

@ -74,6 +74,12 @@ class Document(BaseModel):
return None
return str(CONFIG.git_repo.workdir / self.root_path / self.filename)
def __str__(self):
return self.content
def __repr__(self):
return self.content
class Documents(BaseModel):
"""A class representing a collection of documents.
@ -259,7 +265,7 @@ class MessageQueue:
class CodingContext(BaseModel):
filename: str
design_doc: Document
design_doc: Optional[Document]
task_doc: Optional[Document]
code_doc: Optional[Document]

View file

@ -223,10 +223,15 @@ class CodeParser:
# 遍历所有的block
for block in blocks:
# 如果block不为空则继续处理
if block.strip() != "":
if block.strip() == "":
continue
if "\n" not in block:
block_title = block
block_content = ""
else:
# 将block的标题和内容分开并分别去掉前后的空白字符
block_title, block_content = block.split("\n", 1)
block_dict[block_title.strip()] = block_content.strip()
block_dict[block_title.strip()] = block_content.strip()
return block_dict

View file

@ -233,6 +233,8 @@ class GitRepository:
files = []
try:
directory_path = Path(self.workdir) / relative_path
if not directory_path.exists():
return []
for file_path in directory_path.iterdir():
if file_path.is_file():
rpath = file_path.relative_to(root_relative_path)