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

@ -14,7 +14,14 @@ from pathlib import Path
from typing import Optional
from metagpt.actions import Action, ActionOutput
from metagpt.actions.design_api_an import DESIGN_API_NODE
from metagpt.actions.design_api_an import (
DATA_STRUCTURES_AND_INTERFACES,
DESIGN_API_NODE,
PROGRAM_CALL_FLOW,
REFINED_DATA_STRUCTURES_AND_INTERFACES,
REFINED_DESIGN_NODE,
REFINED_PROGRAM_CALL_FLOW,
)
from metagpt.const import DATA_API_DESIGN_FILE_REPO, SEQ_FLOW_FILE_REPO
from metagpt.logs import logger
from metagpt.schema import Document, Documents, Message
@ -68,7 +75,7 @@ class WriteDesign(Action):
async def _merge(self, prd_doc, system_design_doc):
context = NEW_REQ_TEMPLATE.format(old_design=system_design_doc.content, context=prd_doc.content)
node = await DESIGN_API_NODE.fill(context=context, llm=self.llm)
node = await REFINED_DESIGN_NODE.fill(context=context, llm=self.llm)
system_design_doc.content = node.instruct_content.model_dump_json()
return system_design_doc
@ -92,7 +99,7 @@ class WriteDesign(Action):
async def _save_data_api_design(self, design_doc):
m = json.loads(design_doc.content)
data_api_design = m.get("Data structures and interfaces")
data_api_design = m.get(DATA_STRUCTURES_AND_INTERFACES.key) or m.get(REFINED_DATA_STRUCTURES_AND_INTERFACES.key)
if not data_api_design:
return
pathname = self.repo.workdir / DATA_API_DESIGN_FILE_REPO / Path(design_doc.filename).with_suffix("")
@ -101,7 +108,7 @@ class WriteDesign(Action):
async def _save_seq_flow(self, design_doc):
m = json.loads(design_doc.content)
seq_flow = m.get("Program call flow")
seq_flow = m.get(PROGRAM_CALL_FLOW.key) or m.get(REFINED_PROGRAM_CALL_FLOW.key)
if not seq_flow:
return
pathname = self.repo.workdir / Path(SEQ_FLOW_FILE_REPO) / Path(design_doc.filename).with_suffix("")

View file

@ -8,6 +8,7 @@
from typing import List
from metagpt.actions.action_node import ActionNode
from metagpt.logs import logger
from metagpt.utils.mermaid import MMC1, MMC2
IMPLEMENTATION_APPROACH = ActionNode(
@ -17,6 +18,15 @@ IMPLEMENTATION_APPROACH = ActionNode(
example="We will ...",
)
REFINED_IMPLEMENTATION_APPROACH = ActionNode(
key="Refined Implementation Approach",
expected_type=str,
instruction="Update and extend the original implementation approach to reflect the evolving challenges and "
"requirements due to incremental development. Outline the steps involved in the implementation process with the "
"detailed strategies.",
example="We will refine ...",
)
PROJECT_NAME = ActionNode(
key="Project name", expected_type=str, instruction="The project name with underline", example="game_2048"
)
@ -28,6 +38,14 @@ FILE_LIST = ActionNode(
example=["main.py", "game.py"],
)
REFINED_FILE_LIST = ActionNode(
key="Refined File list",
expected_type=List[str],
instruction="Update and expand the original file list including only relative paths. Up to 2 files can be added."
"Ensure that the refined file list reflects the evolving structure of the project.",
example=["main.py", "game.py", "new_feature.py"],
)
DATA_STRUCTURES_AND_INTERFACES = ActionNode(
key="Data structures and interfaces",
expected_type=str,
@ -37,6 +55,16 @@ DATA_STRUCTURES_AND_INTERFACES = ActionNode(
example=MMC1,
)
REFINED_DATA_STRUCTURES_AND_INTERFACES = ActionNode(
key="Refined Data structures and interfaces",
expected_type=str,
instruction="Update and extend the existing mermaid classDiagram code syntax to incorporate new classes, "
"methods (including __init__), and functions with precise type annotations. Delineate additional "
"relationships between classes, ensuring clarity and adherence to PEP8 standards."
"Retain content that is not related to incremental development but important for consistency and clarity.",
example=MMC1,
)
PROGRAM_CALL_FLOW = ActionNode(
key="Program call flow",
expected_type=str,
@ -45,6 +73,16 @@ PROGRAM_CALL_FLOW = ActionNode(
example=MMC2,
)
REFINED_PROGRAM_CALL_FLOW = ActionNode(
key="Refined Program call flow",
expected_type=str,
instruction="Extend the existing sequenceDiagram code syntax with detailed information, accurately covering the"
"CRUD and initialization of each object. Ensure correct syntax usage and reflect the incremental changes introduced"
"in the classes and API defined above. "
"Retain content that is not related to incremental development but important for consistency and clarity.",
example=MMC2,
)
ANYTHING_UNCLEAR = ActionNode(
key="Anything UNCLEAR",
expected_type=str,
@ -61,4 +99,24 @@ NODES = [
ANYTHING_UNCLEAR,
]
REFINED_NODES = [
REFINED_IMPLEMENTATION_APPROACH,
REFINED_FILE_LIST,
REFINED_DATA_STRUCTURES_AND_INTERFACES,
REFINED_PROGRAM_CALL_FLOW,
ANYTHING_UNCLEAR,
]
DESIGN_API_NODE = ActionNode.from_children("DesignAPI", NODES)
REFINED_DESIGN_NODE = ActionNode.from_children("RefinedDesignAPI", REFINED_NODES)
def main():
prompt = DESIGN_API_NODE.compile(context="")
logger.info(prompt)
prompt = REFINED_DESIGN_NODE.compile(context="")
logger.info(prompt)
if __name__ == "__main__":
main()

View file

@ -15,7 +15,7 @@ from typing import Optional
from metagpt.actions.action import Action
from metagpt.actions.action_output import ActionOutput
from metagpt.actions.project_management_an import PM_NODE
from metagpt.actions.project_management_an import PM_NODE, REFINED_PM_NODE
from metagpt.const import PACKAGE_REQUIREMENTS_FILENAME
from metagpt.logs import logger
from metagpt.schema import Document, Documents
@ -78,7 +78,7 @@ class WriteTasks(Action):
async def _merge(self, system_design_doc, task_doc) -> Document:
context = NEW_REQ_TEMPLATE.format(context=system_design_doc.content, old_tasks=task_doc.content)
node = await PM_NODE.fill(context, self.llm, schema=self.prompt_schema)
node = await REFINED_PM_NODE.fill(context, self.llm, schema=self.prompt_schema)
task_doc.content = node.instruct_content.model_dump_json()
return task_doc

View file

@ -35,6 +35,20 @@ LOGIC_ANALYSIS = ActionNode(
],
)
REFINED_LOGIC_ANALYSIS = ActionNode(
key="Refined Logic Analysis",
expected_type=List[List[str]],
instruction="Review and refine the logic analysis by merging the Legacy Content and Incremental Content. "
"Provide a comprehensive list of files with classes/methods/functions to be implemented or modified incrementally. "
"Include dependency analysis, consider potential impacts on existing code, and document necessary imports.",
example=[
["game.py", "Contains Game class and ... functions"],
["main.py", "Contains main function, from game import Game"],
["new_feature.py", "Introduces NewFeature class and related functions"],
["utils.py", "Modifies existing utility functions to support incremental changes"],
],
)
TASK_LIST = ActionNode(
key="Task list",
expected_type=List[str],
@ -42,6 +56,15 @@ TASK_LIST = ActionNode(
example=["game.py", "main.py"],
)
REFINED_TASK_LIST = ActionNode(
key="Refined Task list",
expected_type=List[str],
instruction="Review and refine the combined task list after the merger of Legacy Content and Incremental Content, "
"and consistent with Refined File List. Ensure that tasks are organized in a logical and prioritized order, "
"considering dependencies for a streamlined and efficient development process. ",
example=["new_feature.py", "utils", "game.py", "main.py"],
)
FULL_API_SPEC = ActionNode(
key="Full API spec",
expected_type=str,
@ -54,9 +77,19 @@ SHARED_KNOWLEDGE = ActionNode(
key="Shared Knowledge",
expected_type=str,
instruction="Detail any shared knowledge, like common utility functions or configuration variables.",
example="'game.py' contains functions shared across the project.",
example="`game.py` contains functions shared across the project.",
)
REFINED_SHARED_KNOWLEDGE = ActionNode(
key="Refined Shared Knowledge",
expected_type=str,
instruction="Update and expand shared knowledge to reflect any new elements introduced. This includes common "
"utility functions, configuration variables for team collaboration. Retain content that is not related to "
"incremental development but important for consistency and clarity.",
example="`new_module.py` enhances shared utility functions for improved code reusability and collaboration.",
)
ANYTHING_UNCLEAR_PM = ActionNode(
key="Anything UNCLEAR",
expected_type=str,
@ -74,13 +107,25 @@ NODES = [
ANYTHING_UNCLEAR_PM,
]
REFINED_NODES = [
REQUIRED_PYTHON_PACKAGES,
REQUIRED_OTHER_LANGUAGE_PACKAGES,
REFINED_LOGIC_ANALYSIS,
REFINED_TASK_LIST,
FULL_API_SPEC,
REFINED_SHARED_KNOWLEDGE,
ANYTHING_UNCLEAR_PM,
]
PM_NODE = ActionNode.from_children("PM_NODE", NODES)
REFINED_PM_NODE = ActionNode.from_children("REFINED_PM_NODE", REFINED_NODES)
def main():
prompt = PM_NODE.compile(context="")
logger.info(prompt)
prompt = REFINED_PM_NODE.compile(context="")
logger.info(prompt)
if __name__ == "__main__":

View file

@ -16,15 +16,23 @@
"""
import json
from typing import Literal
from pydantic import Field
from tenacity import retry, stop_after_attempt, wait_random_exponential
from metagpt.actions.action import Action
from metagpt.const import BUGFIX_FILENAME
from metagpt.actions.project_management_an import REFINED_TASK_LIST, TASK_LIST
from metagpt.actions.write_code_plan_and_change_an import REFINED_TEMPLATE
from metagpt.const import (
BUGFIX_FILENAME,
CODE_PLAN_AND_CHANGE_FILENAME,
REQUIREMENT_FILENAME,
)
from metagpt.logs import logger
from metagpt.schema import CodingContext, Document, RunCodeResult
from metagpt.utils.common import CodeParser
from metagpt.utils.project_repo import ProjectRepo
PROMPT_TEMPLATE = """
NOTICE
@ -91,6 +99,9 @@ class WriteCode(Action):
bug_feedback = await self.repo.docs.get(filename=BUGFIX_FILENAME)
coding_context = CodingContext.loads(self.i_context.content)
test_doc = await self.repo.test_outputs.get(filename="test_" + coding_context.filename + ".json")
code_plan_and_change_doc = await self.repo.docs.code_plan_and_change.get(filename=CODE_PLAN_AND_CHANGE_FILENAME)
code_plan_and_change = code_plan_and_change_doc.content if code_plan_and_change_doc else ""
requirement_doc = await self.repo.docs.requirement.get(filename=REQUIREMENT_FILENAME)
summary_doc = None
if coding_context.design_doc and coding_context.design_doc.filename:
summary_doc = await self.repo.docs.code_summary.get(filename=coding_context.design_doc.filename)
@ -101,6 +112,10 @@ class WriteCode(Action):
if bug_feedback:
code_context = coding_context.code_doc.content
elif code_plan_and_change:
code_context = await self.get_codes(
coding_context.task_doc, exclude=self.context.filename, project_repo=self.repo, mode="incremental"
)
else:
code_context = await self.get_codes(
coding_context.task_doc,
@ -108,15 +123,28 @@ class WriteCode(Action):
project_repo=self.repo.with_src_path(self.context.src_workspace),
)
prompt = PROMPT_TEMPLATE.format(
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,
feedback=bug_feedback.content if bug_feedback else "",
filename=self.i_context.filename,
summary_log=summary_doc.content if summary_doc else "",
)
if code_plan_and_change:
prompt = REFINED_TEMPLATE.format(
user_requirement=requirement_doc.content if requirement_doc else "",
code_plan_and_change=code_plan_and_change,
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,
feedback=bug_feedback.content if bug_feedback else "",
filename=self.context.filename,
summary_log=summary_doc.content if summary_doc else "",
)
else:
prompt = PROMPT_TEMPLATE.format(
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,
feedback=bug_feedback.content if bug_feedback else "",
filename=self.i_context.filename,
summary_log=summary_doc.content if summary_doc else "",
)
logger.info(f"Writing {coding_context.filename}..")
code = await self.write_code(prompt)
if not coding_context.code_doc:
@ -127,20 +155,71 @@ class WriteCode(Action):
return coding_context
@staticmethod
async def get_codes(task_doc, exclude, project_repo) -> str:
async def get_codes(
task_doc: Document, exclude: str, project_repo: ProjectRepo, mode: Literal["normal", "incremental"] = "normal"
) -> str:
"""
Get code snippets based on different modes.
Attributes:
task_doc (Document): Document object of the task file.
exclude (str): Specifies the filename to be excluded from the code snippets.
project_repo (ProjectRepo): ProjectRepo object of the project.
mode (str): Specifies the mode, either "normal" or "incremental" (default is "normal").
Returns:
str: Code snippets.
Description:
If mode is set to "normal", it returns code snippets for the regular coding phase,
i.e., all the code generated before writing the current file.
If mode is set to "incremental", it returns code snippets for generating the code plan and change,
building upon the existing code in the "normal" mode and adding code for the current file's older versions.
"""
if not task_doc:
return ""
if not task_doc.content:
task_doc = project_repo.docs.task.get(filename=task_doc.filename)
m = json.loads(task_doc.content)
code_filenames = m.get("Task list", [])
code_filenames = m.get(TASK_LIST.key, []) if mode == "normal" else m.get(REFINED_TASK_LIST.key, [])
codes = []
src_file_repo = project_repo.srcs
for filename in code_filenames:
if filename == exclude:
continue
doc = await src_file_repo.get(filename=filename)
if not doc:
continue
codes.append(f"----- {filename}\n" + doc.content)
if mode == "incremental":
src_files = src_file_repo.all_files
old_file_repo = project_repo.git_repo.new_file_repository(relative_path=project_repo.old_workspace)
old_files = old_file_repo.all_files
# Get the union of the files in the src and old workspaces
union_files_list = list(set(src_files) | set(old_files))
for filename in union_files_list:
# Exclude the current file from the all code snippets to get the context code snippets for generating
if filename == exclude:
# If the file is in the old workspace, use the legacy code
# Exclude unnecessary code to maintain a clean and focused main.py file, ensuring only relevant and
# essential functionality is included for the projects requirements
if filename in old_files and filename != "main.py":
# Use legacy code
doc = await old_file_repo.get(filename=filename)
# If the file is in the src workspace, skip it
else:
continue
codes.insert(0, f"-----Now, {filename} to be rewritten\n```{doc.content}```\n=====")
# The context code snippets are generated from the src workspace
else:
doc = await src_file_repo.get(filename=filename)
# If the file does not exist in the src workspace, skip it
if not doc:
continue
codes.append(f"----- {filename}\n```{doc.content}```")
elif mode == "normal":
for filename in code_filenames:
# Exclude the current file to get the context code snippets for generating the current file
if filename == exclude:
continue
doc = await src_file_repo.get(filename=filename)
if not doc:
continue
codes.append(f"----- {filename}\n```{doc.content}```")
return "\n".join(codes)

View file

@ -0,0 +1,207 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/12/26
@Author : mannaandpoem
@File : write_code_plan_and_change_an.py
"""
import os
from pydantic import Field
from metagpt.actions.action import Action
from metagpt.actions.action_node import ActionNode
from metagpt.schema import CodePlanAndChangeContext
CODE_PLAN_AND_CHANGE = ActionNode(
key="Code Plan And Change",
expected_type=str,
instruction="Developing comprehensive and step-by-step incremental development plan, and write Incremental "
"Change by making a code draft that how to implement incremental development including detailed steps based on the "
"context. Note: Track incremental changes using mark of '+' or '-' for add/modify/delete code, and conforms to the "
"output format of git diff",
example="""
1. Plan for calculator.py: Enhance the functionality of `calculator.py` by extending it to incorporate methods for subtraction, multiplication, and division. Additionally, implement robust error handling for the division operation to mitigate potential issues related to division by zero.
```python
class Calculator:
self.result = number1 + number2
return self.result
- def sub(self, number1, number2) -> float:
+ def subtract(self, number1: float, number2: float) -> float:
+ '''
+ Subtracts the second number from the first and returns the result.
+
+ Args:
+ number1 (float): The number to be subtracted from.
+ number2 (float): The number to subtract.
+
+ Returns:
+ float: The difference of number1 and number2.
+ '''
+ self.result = number1 - number2
+ return self.result
+
def multiply(self, number1: float, number2: float) -> float:
- pass
+ '''
+ Multiplies two numbers and returns the result.
+
+ Args:
+ number1 (float): The first number to multiply.
+ number2 (float): The second number to multiply.
+
+ Returns:
+ float: The product of number1 and number2.
+ '''
+ self.result = number1 * number2
+ return self.result
+
def divide(self, number1: float, number2: float) -> float:
- pass
+ '''
+ ValueError: If the second number is zero.
+ '''
+ if number2 == 0:
+ raise ValueError('Cannot divide by zero')
+ self.result = number1 / number2
+ return self.result
+
- def reset_result(self):
+ def clear(self):
+ if self.result != 0.0:
+ print("Result is not zero, clearing...")
+ else:
+ print("Result is already zero, no need to clear.")
+
self.result = 0.0
```
2. Plan for main.py: Integrate new API endpoints for subtraction, multiplication, and division into the existing codebase of `main.py`. Then, ensure seamless integration with the overall application architecture and maintain consistency with coding standards.
```python
def add_numbers():
result = calculator.add_numbers(num1, num2)
return jsonify({'result': result}), 200
-# TODO: Implement subtraction, multiplication, and division operations
+@app.route('/subtract_numbers', methods=['POST'])
+def subtract_numbers():
+ data = request.get_json()
+ num1 = data.get('num1', 0)
+ num2 = data.get('num2', 0)
+ result = calculator.subtract_numbers(num1, num2)
+ return jsonify({'result': result}), 200
+
+@app.route('/multiply_numbers', methods=['POST'])
+def multiply_numbers():
+ data = request.get_json()
+ num1 = data.get('num1', 0)
+ num2 = data.get('num2', 0)
+ try:
+ result = calculator.divide_numbers(num1, num2)
+ except ValueError as e:
+ return jsonify({'error': str(e)}), 400
+ return jsonify({'result': result}), 200
+
if __name__ == '__main__':
app.run()
```""",
)
CODE_PLAN_AND_CHANGE_CONTEXT = """
## User New Requirements
{requirement}
## PRD
{prd}
## Design
{design}
## Tasks
{tasks}
## Legacy Code
{code}
"""
REFINED_TEMPLATE = """
NOTICE
Role: You are a professional engineer; The main goal is to complete incremental development by combining legacy code and plan and Incremental Change, ensuring the integration of new features.
# Context
## User New Requirements
{user_requirement}
## Code Plan And Change
{code_plan_and_change}
## Design
{design}
## Tasks
{tasks}
## Legacy Code
```Code
{code}
```
## Debug logs
```text
{logs}
{summary_log}
```
## Bug Feedback logs
```text
{feedback}
```
# Format example
## Code: {filename}
```python
## {filename}
...
```
# Instruction: Based on the context, follow "Format example", write or rewrite code.
## Write/Rewrite Code: Only write one file {filename}, write or rewrite complete code using triple quotes 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. Follow Code Plan And Change: If there is any Incremental Change or Legacy Code files contain "{filename} to be rewritten", you must merge it into the code file according to the plan.
6. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.
7. Before using a external variable/module, make sure you import it first.
8. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.
9. Attention: Retain content that is not related to incremental development but important for consistency and clarity.".
"""
WRITE_CODE_PLAN_AND_CHANGE_NODE = ActionNode.from_children("WriteCodePlanAndChange", [CODE_PLAN_AND_CHANGE])
class WriteCodePlanAndChange(Action):
name: str = "WriteCodePlanAndChange"
i_context: CodePlanAndChangeContext = Field(default_factory=CodePlanAndChangeContext)
async def run(self, *args, **kwargs):
self.llm.system_prompt = "You are a professional software engineer, your primary responsibility is to "
"meticulously craft comprehensive incremental development plan and deliver detailed incremental change"
requirement = self.i_context.requirement_doc.content
prd = "\n".join([doc.content for doc in self.i_context.prd_docs])
design = "\n".join([doc.content for doc in self.i_context.design_docs])
tasks = "\n".join([doc.content for doc in self.i_context.tasks_docs])
code_text = await self.get_old_codes()
context = CODE_PLAN_AND_CHANGE_CONTEXT.format(
requirement=requirement, prd=prd, design=design, tasks=tasks, code=code_text
)
return await WRITE_CODE_PLAN_AND_CHANGE_NODE.fill(context=context, llm=self.llm, schema="json")
async def get_old_codes(self) -> str:
self.repo.old_workspace = self.repo.git_repo.workdir / os.path.basename(self.config.project_path)
old_file_repo = self.repo.git_repo.new_file_repository(relative_path=self.repo.old_workspace)
old_codes = await old_file_repo.get_all()
codes = [f"----- {code.filename}\n```{code.content}```" for code in old_codes]
return "\n".join(codes)

View file

@ -13,6 +13,7 @@ from tenacity import retry, stop_after_attempt, wait_random_exponential
from metagpt.actions import WriteCode
from metagpt.actions.action import Action
from metagpt.const import CODE_PLAN_AND_CHANGE_FILENAME, REQUIREMENT_FILENAME
from metagpt.logs import logger
from metagpt.schema import CodingContext
from metagpt.utils.common import CodeParser
@ -137,6 +138,9 @@ class WriteCodeReview(Action):
async def run(self, *args, **kwargs) -> CodingContext:
iterative_code = self.i_context.code_doc.content
k = self.context.config.code_review_k_times or 1
code_plan_and_change_doc = await self.repo.get(filename=CODE_PLAN_AND_CHANGE_FILENAME)
code_plan_and_change = code_plan_and_change_doc.content if code_plan_and_change_doc else ""
mode = "incremental" if code_plan_and_change else "normal"
for i in range(k):
format_example = FORMAT_EXAMPLE.format(filename=self.i_context.code_doc.filename)
task_content = self.i_context.task_doc.content if self.i_context.task_doc else ""
@ -144,14 +148,31 @@ class WriteCodeReview(Action):
self.i_context.task_doc,
exclude=self.i_context.filename,
project_repo=self.repo.with_src_path(self.context.src_workspace),
mode=mode,
)
context = "\n".join(
[
"## System Design\n" + str(self.i_context.design_doc) + "\n",
"## Tasks\n" + task_content + "\n",
"## Code Files\n" + code_context + "\n",
]
)
if not code_plan_and_change:
context = "\n".join(
[
"## System Design\n" + str(self.context.design_doc) + "\n",
"## Tasks\n" + task_content + "\n",
"## Code Files\n" + code_context + "\n",
]
)
else:
requirement_doc = await self.repo.get(filename=REQUIREMENT_FILENAME)
user_requirement = requirement_doc.content if requirement_doc else ""
context = "\n".join(
[
"## User New Requirements\n" + user_requirement + "\n",
"## Code Plan And Change\n" + code_plan_and_change + "\n",
"## System Design\n" + str(self.context.design_doc) + "\n",
"## Tasks\n" + task_content + "\n",
"## Code Files\n" + code_context + "\n",
]
)
context_prompt = PROMPT_TEMPLATE.format(
context=context,
code=iterative_code,

View file

@ -20,7 +20,9 @@ from metagpt.actions import Action, ActionOutput
from metagpt.actions.action_node import ActionNode
from metagpt.actions.fix_bug import FixBug
from metagpt.actions.write_prd_an import (
COMPETITIVE_QUADRANT_CHART,
PROJECT_NAME,
REFINED_PRD_NODE,
WP_IS_RELATIVE_NODE,
WP_ISSUE_TYPE_NODE,
WRITE_PRD_NODE,
@ -138,7 +140,7 @@ class WritePRD(Action):
if not self.project_name:
self.project_name = Path(self.project_path).name
prompt = NEW_REQ_TEMPLATE.format(requirements=req.content, old_prd=related_doc.content)
node = await WRITE_PRD_NODE.fill(context=prompt, llm=self.llm, schema=self.prompt_schema)
node = await REFINED_PRD_NODE.fill(context=prompt, llm=self.llm, schema=self.prompt_schema)
related_doc.content = node.instruct_content.model_dump_json()
await self._rename_workspace(node)
return related_doc
@ -152,7 +154,7 @@ class WritePRD(Action):
async def _save_competitive_analysis(self, prd_doc: Document):
m = json.loads(prd_doc.content)
quadrant_chart = m.get("Competitive Quadrant Chart")
quadrant_chart = m.get(COMPETITIVE_QUADRANT_CHART.key)
if not quadrant_chart:
return
pathname = self.repo.workdir / COMPETITIVE_ANALYSIS_FILE_REPO / Path(prd_doc.filename).stem

View file

@ -30,6 +30,13 @@ ORIGINAL_REQUIREMENTS = ActionNode(
example="Create a 2048 game",
)
REFINED_REQUIREMENTS = ActionNode(
key="Refined Requirements",
expected_type=str,
instruction="Place the New user's requirements here.",
example="Create a 2048 game with a new feature that ...",
)
PROJECT_NAME = ActionNode(
key="Project Name",
expected_type=str,
@ -45,6 +52,18 @@ PRODUCT_GOALS = ActionNode(
example=["Create an engaging user experience", "Improve accessibility, be responsive", "More beautiful UI"],
)
REFINED_PRODUCT_GOALS = ActionNode(
key="Refined Product Goals",
expected_type=List[str],
instruction="Update and expand the original product goals to reflect the evolving needs due to incremental "
"development.Ensure that the refined goals align with the current project direction and contribute to its success.",
example=[
"Enhance user engagement through new features",
"Optimize performance for scalability",
"Integrate innovative UI enhancements",
],
)
USER_STORIES = ActionNode(
key="User Stories",
expected_type=List[str],
@ -58,6 +77,20 @@ USER_STORIES = ActionNode(
],
)
REFINED_USER_STORIES = ActionNode(
key="Refined User Stories",
expected_type=List[str],
instruction="Update and expand the original scenario-based user stories to reflect the evolving needs due to "
"incremental development. Ensure that the refined user stories capture incremental features and improvements. ",
example=[
"As a player, I want to choose difficulty levels to challenge my skills",
"As a player, I want a visually appealing score display after each game for a better gaming experience",
"As a player, I want a convenient restart button displayed when I lose to quickly start a new game",
"As a player, I want an enhanced and aesthetically pleasing UI to elevate the overall gaming experience",
"As a player, I want the ability to play the game seamlessly on my mobile phone for on-the-go entertainment",
],
)
COMPETITIVE_ANALYSIS = ActionNode(
key="Competitive Analysis",
expected_type=List[str],
@ -97,6 +130,15 @@ REQUIREMENT_ANALYSIS = ActionNode(
example="",
)
REFINED_REQUIREMENT_ANALYSIS = ActionNode(
key="Refined Requirement Analysis",
expected_type=List[str],
instruction="Review and refine the existing requirement analysis to align with the evolving needs of the project "
"due to incremental development. Ensure the analysis comprehensively covers the new features and enhancements "
"required for the refined project scope.",
example=["Require add/update/modify ..."],
)
REQUIREMENT_POOL = ActionNode(
key="Requirement Pool",
expected_type=List[List[str]],
@ -104,6 +146,14 @@ REQUIREMENT_POOL = ActionNode(
example=[["P0", "The main code ..."], ["P0", "The game algorithm ..."]],
)
REFINED_REQUIREMENT_POOL = ActionNode(
key="Refined Requirement Pool",
expected_type=List[List[str]],
instruction="List down the top 5 to 7 requirements with their priority (P0, P1, P2). "
"Cover both legacy content and incremental content. Retain content unrelated to incremental development",
example=[["P0", "The main code ..."], ["P0", "The game algorithm ..."]],
)
UI_DESIGN_DRAFT = ActionNode(
key="UI Design draft",
expected_type=str,
@ -152,6 +202,22 @@ NODES = [
ANYTHING_UNCLEAR,
]
REFINED_NODES = [
LANGUAGE,
PROGRAMMING_LANGUAGE,
REFINED_REQUIREMENTS,
PROJECT_NAME,
REFINED_PRODUCT_GOALS,
REFINED_USER_STORIES,
COMPETITIVE_ANALYSIS,
COMPETITIVE_QUADRANT_CHART,
REFINED_REQUIREMENT_ANALYSIS,
REFINED_REQUIREMENT_POOL,
UI_DESIGN_DRAFT,
ANYTHING_UNCLEAR,
]
WRITE_PRD_NODE = ActionNode.from_children("WritePRD", NODES)
REFINED_PRD_NODE = ActionNode.from_children("RefinedPRD", REFINED_NODES)
WP_ISSUE_TYPE_NODE = ActionNode.from_children("WP_ISSUE_TYPE", [ISSUE_TYPE, REASON])
WP_IS_RELATIVE_NODE = ActionNode.from_children("WP_IS_RELATIVE", [IS_RELATIVE, REASON])