mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-18 13:55:17 +02:00
merge main to dev
This commit is contained in:
commit
76d4451fc6
28 changed files with 1479 additions and 55 deletions
|
|
@ -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("")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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__":
|
||||
|
|
|
|||
|
|
@ -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 project’s 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)
|
||||
|
|
|
|||
207
metagpt/actions/write_code_plan_and_change_an.py
Normal file
207
metagpt/actions/write_code_plan_and_change_an.py
Normal 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)
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue