mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-07-02 16:01:04 +02:00
1. add mock.py in tests\data\incremental_dev_project
2. add mock for test case of ActionNode 3. add path of Guideline file in const.py 4. update engineer.py
This commit is contained in:
parent
8831177a22
commit
a6d56bd748
9 changed files with 599 additions and 853 deletions
|
|
@ -39,7 +39,7 @@ FILE_LIST = ActionNode(
|
|||
)
|
||||
|
||||
REFINED_FILE_LIST = ActionNode(
|
||||
key="Refined File List",
|
||||
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.",
|
||||
|
|
@ -56,7 +56,7 @@ DATA_STRUCTURES_AND_INTERFACES = ActionNode(
|
|||
)
|
||||
|
||||
REFINED_DATA_STRUCTURES_AND_INTERFACES = ActionNode(
|
||||
key="Refined Data Structures and Interfaces",
|
||||
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 "
|
||||
|
|
@ -108,7 +108,7 @@ REFINE_NODES = [
|
|||
]
|
||||
|
||||
DESIGN_API_NODE = ActionNode.from_children("DesignAPI", NODES)
|
||||
REFINED_DESIGN_NODES = ActionNode.from_children("Refined_Design_API", REFINE_NODES)
|
||||
REFINED_DESIGN_NODES = ActionNode.from_children("RefinedDesignAPI", REFINE_NODES)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
@Author : mannaandpoem
|
||||
@File : write_code_guideline_an.py
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
from metagpt.actions.action import Action
|
||||
from metagpt.actions.action_node import ActionNode
|
||||
from metagpt.logs import logger
|
||||
from metagpt.actions.action_node import ActionNode, dict_to_markdown
|
||||
from metagpt.config import CONFIG
|
||||
from metagpt.const import CODE_GUIDELINE_FILE_REPO, CODE_GUIDELINE_PDF_FILE_REPO
|
||||
|
||||
GUIDELINES_AND_INCREMENTAL_CHANGE = ActionNode(
|
||||
key="Guidelines and Incremental Change",
|
||||
|
|
@ -123,271 +123,6 @@ CODE_GUIDELINE_CONTEXT = """
|
|||
{code}
|
||||
"""
|
||||
|
||||
CODE_GUIDELINE_CONTEXT_EXAMPLE = """
|
||||
## New Requirements
|
||||
Add subtraction, multiplication and division operations to the calculator.
|
||||
The current calculator can only perform basic addition operations, and it is necessary to introduce subtraction, multiplication, division operation into the calculator
|
||||
|
||||
## Design
|
||||
{
|
||||
"Refined Implementation Approach": "To accommodate the new requirements, we will extend the existing Python-based calculator application. We will enhance the Tkinter-based UI to include buttons for subtraction, multiplication, and division, alongside the existing addition functionality. We will also implement input validation to handle edge cases such as division by zero. The architecture will be modular, with separate components for the UI, calculation logic, and error handling to maintain simplicity and facilitate future enhancements such as a history feature.",
|
||||
"File list": [
|
||||
"main.py",
|
||||
"calculator.py",
|
||||
"interface.py",
|
||||
"operations.py"
|
||||
],
|
||||
"Refined Data Structures and Interfaces": "classDiagram\n class CalculatorApp {\n +main() None\n }\n class Calculator {\n -result float\n +add(number1: float, number2: float) float\n +subtract(number1: float, number2: float) float\n +multiply(number1: float, number2: float) float\n +divide(number1: float, number2: float) float\n +clear() None\n }\n class Interface {\n -calculator Calculator\n +start() None\n +display_result(result: float) None\n +get_input() float\n +show_error(message: str) None\n +update_operation(operation: str) None\n }\n class Operations {\n +perform_operation(operation: str, number1: float, number2: float) float\n }\n CalculatorApp --> Interface\n Interface --> Calculator\n Calculator --> Operations",
|
||||
"Refined Program call flow": "sequenceDiagram\n participant CA as CalculatorApp\n participant I as Interface\n participant C as Calculator\n participant O as Operations\n CA->>I: start()\n I->>I: get_input()\n I->>I: update_operation(operation)\n loop For Each Operation\n I->>C: perform_operation(operation, number1, number2)\n C->>O: perform_operation(operation, number1, number2)\n O-->>C: return result\n C-->>I: return result\n I->>I: display_result(result)\n end\n I->>I: show_error(message)",
|
||||
"Anything UNCLEAR": "The requirement for a history feature is mentioned but not prioritized. It is unclear whether this should be implemented now or in the future. Additionally, there is no specification on the limit to the size of the numbers or the number of operations that can be performed in sequence. These aspects will need clarification for complete implementation."
|
||||
}
|
||||
|
||||
## Tasks
|
||||
{
|
||||
"Required Python packages": [
|
||||
"tkinter"
|
||||
],
|
||||
"Required Other language third-party packages": [
|
||||
"No third-party dependencies required"
|
||||
],
|
||||
"Refined Logic Analysis": [
|
||||
[
|
||||
"main.py",
|
||||
"Entry point of the application, creates an instance of the Interface class and starts the application."
|
||||
],
|
||||
[
|
||||
"calculator.py",
|
||||
"Contains the Calculator class with add, subtract, multiply, divide and clear methods for performing arithmetic operations."
|
||||
],
|
||||
[
|
||||
"interface.py",
|
||||
"Contains the Interface class responsible for the GUI, interacts with Calculator for the logic and displays results or errors."
|
||||
],
|
||||
[
|
||||
"operations.py",
|
||||
"Contains the Operations class with perform_operation method that delegates the arithmetic operation based on the operation argument."
|
||||
]
|
||||
],
|
||||
"Refined Task list": [
|
||||
"operations.py",
|
||||
"calculator.py",
|
||||
"interface.py",
|
||||
"main.py"
|
||||
],
|
||||
"Full API spec": "",
|
||||
"Refined Shared Knowledge": "`interface.py` will use the Calculator class from `calculator.py` to perform operations and display results. `main.py` will be the starting point that initializes the Interface. `calculator.py` will now also interact with `operations.py` to perform the arithmetic operations.",
|
||||
"Anything UNCLEAR": "The requirement for a history feature is mentioned but not prioritized. It is unclear whether this should be implemented now or in the future. Additionally, there is no specification on the limit to the size of the numbers or the number of operations that can be performed in sequence. These aspects will need clarification for complete implementation."
|
||||
}
|
||||
|
||||
## Legacy Code
|
||||
----- calculator.py
|
||||
```## calculator.py
|
||||
|
||||
class Calculator:
|
||||
def __init__(self):
|
||||
self.result = 0.0 # Default value for the result
|
||||
|
||||
def add(self, number1: float, number2: float) -> float:
|
||||
'''
|
||||
Adds two numbers and returns the result.
|
||||
|
||||
Args:
|
||||
number1 (float): The first number to add.
|
||||
number2 (float): The second number to add.
|
||||
|
||||
Returns:
|
||||
float: The sum of number1 and number2.
|
||||
'''
|
||||
self.result = number1 + number2
|
||||
return self.result
|
||||
|
||||
def clear(self) -> None:
|
||||
'''
|
||||
Clears the result to its default value.
|
||||
'''
|
||||
self.result = 0.0
|
||||
```
|
||||
|
||||
---- interface.py
|
||||
```## interface.py
|
||||
import tkinter as tk
|
||||
from calculator import Calculator
|
||||
|
||||
class Interface:
|
||||
def __init__(self):
|
||||
self.calculator = Calculator()
|
||||
self.root = tk.Tk()
|
||||
self.root.title("Calculator")
|
||||
self.create_widgets()
|
||||
|
||||
def create_widgets(self):
|
||||
self.result_var = tk.StringVar()
|
||||
self.result_display = tk.Entry(self.root, textvariable=self.result_var, state='readonly', justify='right', font=('Arial', 24))
|
||||
self.result_display.grid(row=0, column=0, columnspan=4, sticky='nsew')
|
||||
|
||||
self.entry_number1 = tk.Entry(self.root, justify='right', font=('Arial', 18))
|
||||
self.entry_number1.grid(row=1, column=0, columnspan=2, sticky='nsew')
|
||||
|
||||
self.entry_number2 = tk.Entry(self.root, justify='right', font=('Arial', 18))
|
||||
self.entry_number2.grid(row=1, column=2, columnspan=2, sticky='nsew')
|
||||
|
||||
self.add_button = tk.Button(self.root, text='+', command=self.add, font=('Arial', 18))
|
||||
self.add_button.grid(row=2, column=0, sticky='nsew')
|
||||
|
||||
self.clear_button = tk.Button(self.root, text='C', command=self.clear, font=('Arial', 18))
|
||||
self.clear_button.grid(row=2, column=1, sticky='nsew')
|
||||
|
||||
self.quit_button = tk.Button(self.root, text='Quit', command=self.root.quit, font=('Arial', 18))
|
||||
self.quit_button.grid(row=2, column=2, columnspan=2, sticky='nsew')
|
||||
|
||||
self.root.grid_rowconfigure(1, weight=1)
|
||||
self.root.grid_columnconfigure(0, weight=1)
|
||||
|
||||
def start(self):
|
||||
self.root.mainloop()
|
||||
|
||||
def display_result(self, result: float):
|
||||
self.result_var.set(str(result))
|
||||
|
||||
def get_input(self):
|
||||
try:
|
||||
number1 = float(self.entry_number1.get())
|
||||
number2 = float(self.entry_number2.get())
|
||||
return number1, number2
|
||||
except ValueError:
|
||||
self.show_error("Invalid input! Please enter valid numbers.")
|
||||
return None, None
|
||||
|
||||
def add(self):
|
||||
number1, number2 = self.get_input()
|
||||
if number1 is not None and number2 is not None:
|
||||
result = self.calculator.add(number1, number2)
|
||||
self.display_result(result)
|
||||
|
||||
def clear(self):
|
||||
self.entry_number1.delete(0, tk.END)
|
||||
self.entry_number2.delete(0, tk.END)
|
||||
self.result_var.set("")
|
||||
|
||||
def show_error(self, message: str):
|
||||
tk.messagebox.showerror("Error", message)
|
||||
|
||||
# This code is meant to be used as a module and not as a standalone script.
|
||||
# The Interface class will be instantiated and started by the main.py file.
|
||||
```
|
||||
|
||||
---- main.py
|
||||
```## main.py
|
||||
from interface import Interface
|
||||
|
||||
|
||||
class CalculatorApp:
|
||||
@staticmethod
|
||||
def main():
|
||||
interface = Interface()
|
||||
interface.start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
CalculatorApp.main()
|
||||
```
|
||||
"""
|
||||
|
||||
REFINE_CODE_SCRIPT_EXAMPLE = """
|
||||
----- calculator.py
|
||||
```## calculator.py
|
||||
|
||||
class Calculator:
|
||||
def __init__(self):
|
||||
self.result = 0.0 # Default value for the result
|
||||
|
||||
def add(self, number1: float, number2: float) -> float:
|
||||
'''
|
||||
Adds two numbers and returns the result.
|
||||
|
||||
Args:
|
||||
number1 (float): The first number to add.
|
||||
number2 (float): The second number to add.
|
||||
|
||||
Returns:
|
||||
float: The sum of number1 and number2.
|
||||
'''
|
||||
self.result = number1 + number2
|
||||
return self.result
|
||||
|
||||
def clear(self) -> None:
|
||||
'''
|
||||
Clears the result to its default value.
|
||||
'''
|
||||
self.result = 0.0
|
||||
```
|
||||
|
||||
---- Now, interface.py to be rewritten
|
||||
```## interface.py
|
||||
import tkinter as tk
|
||||
from calculator import Calculator
|
||||
|
||||
class Interface:
|
||||
def __init__(self):
|
||||
self.calculator = Calculator()
|
||||
self.root = tk.Tk()
|
||||
self.root.title("Calculator")
|
||||
self.create_widgets()
|
||||
|
||||
def create_widgets(self):
|
||||
self.result_var = tk.StringVar()
|
||||
self.result_display = tk.Entry(self.root, textvariable=self.result_var, state='readonly', justify='right', font=('Arial', 24))
|
||||
self.result_display.grid(row=0, column=0, columnspan=4, sticky='nsew')
|
||||
|
||||
self.entry_number1 = tk.Entry(self.root, justify='right', font=('Arial', 18))
|
||||
self.entry_number1.grid(row=1, column=0, columnspan=2, sticky='nsew')
|
||||
|
||||
self.entry_number2 = tk.Entry(self.root, justify='right', font=('Arial', 18))
|
||||
self.entry_number2.grid(row=1, column=2, columnspan=2, sticky='nsew')
|
||||
|
||||
self.add_button = tk.Button(self.root, text='+', command=self.add, font=('Arial', 18))
|
||||
self.add_button.grid(row=2, column=0, sticky='nsew')
|
||||
|
||||
self.clear_button = tk.Button(self.root, text='C', command=self.clear, font=('Arial', 18))
|
||||
self.clear_button.grid(row=2, column=1, sticky='nsew')
|
||||
|
||||
self.quit_button = tk.Button(self.root, text='Quit', command=self.root.quit, font=('Arial', 18))
|
||||
self.quit_button.grid(row=2, column=2, columnspan=2, sticky='nsew')
|
||||
|
||||
self.root.grid_rowconfigure(1, weight=1)
|
||||
self.root.grid_columnconfigure(0, weight=1)
|
||||
|
||||
def start(self):
|
||||
self.root.mainloop()
|
||||
|
||||
def display_result(self, result: float):
|
||||
self.result_var.set(str(result))
|
||||
|
||||
def get_input(self):
|
||||
try:
|
||||
number1 = float(self.entry_number1.get())
|
||||
number2 = float(self.entry_number2.get())
|
||||
return number1, number2
|
||||
except ValueError:
|
||||
self.show_error("Invalid input! Please enter valid numbers.")
|
||||
return None, None
|
||||
|
||||
def add(self):
|
||||
number1, number2 = self.get_input()
|
||||
if number1 is not None and number2 is not None:
|
||||
result = self.calculator.add(number1, number2)
|
||||
self.display_result(result)
|
||||
|
||||
def clear(self):
|
||||
self.entry_number1.delete(0, tk.END)
|
||||
self.entry_number2.delete(0, tk.END)
|
||||
self.result_var.set("")
|
||||
|
||||
def show_error(self, message: str):
|
||||
tk.messagebox.showerror("Error", message)
|
||||
```
|
||||
"""
|
||||
|
||||
REFINED_CODE_TEMPLATE = """
|
||||
NOTICE
|
||||
Role: You are a professional engineer; The main goal is to complete incremental development by combining legacy code and Guidelines and Incremental Change, ensuring the integration of new features.
|
||||
|
|
@ -451,13 +186,21 @@ class WriteCodeGuideline(Action):
|
|||
"meticulously craft comprehensive incremental development guidelines and deliver detailed Incremental Change"
|
||||
return await WRITE_CODE_GUIDELINE_NODE.fill(context=context, llm=self.llm, schema="json")
|
||||
|
||||
@staticmethod
|
||||
async def save(guideline):
|
||||
await WriteCodeGuideline.save_json(guideline)
|
||||
await WriteCodeGuideline.save_md(guideline)
|
||||
|
||||
async def main():
|
||||
write_code_guideline = WriteCodeGuideline()
|
||||
node = await write_code_guideline.run(CODE_GUIDELINE_CONTEXT_EXAMPLE)
|
||||
guideline = node.instruct_content.model_dump_json()
|
||||
logger.info(guideline)
|
||||
@staticmethod
|
||||
async def save_json(guideline):
|
||||
filename = "code_guideline.json"
|
||||
await CONFIG.git_repo.new_file_repository(CODE_GUIDELINE_FILE_REPO).save(
|
||||
filename=filename, content=str(guideline)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@staticmethod
|
||||
async def save_md(guideline):
|
||||
filename = "code_guideline.md"
|
||||
await CONFIG.git_repo.new_file_repository(CODE_GUIDELINE_PDF_FILE_REPO).save(
|
||||
filename=filename, content=dict_to_markdown(guideline)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -92,12 +92,14 @@ DOCS_FILE_REPO = "docs"
|
|||
PRDS_FILE_REPO = "docs/prds"
|
||||
SYSTEM_DESIGN_FILE_REPO = "docs/system_design"
|
||||
TASK_FILE_REPO = "docs/tasks"
|
||||
CODE_GUIDELINE_FILE_REPO = "docs/code_guideline"
|
||||
COMPETITIVE_ANALYSIS_FILE_REPO = "resources/competitive_analysis"
|
||||
DATA_API_DESIGN_FILE_REPO = "resources/data_api_design"
|
||||
SEQ_FLOW_FILE_REPO = "resources/seq_flow"
|
||||
SYSTEM_DESIGN_PDF_FILE_REPO = "resources/system_design"
|
||||
PRD_PDF_FILE_REPO = "resources/prd"
|
||||
TASK_PDF_FILE_REPO = "resources/api_spec_and_tasks"
|
||||
CODE_GUIDELINE_PDF_FILE_REPO = "resources/code_guideline"
|
||||
TEST_CODES_FILE_REPO = "tests"
|
||||
TEST_OUTPUTS_FILE_REPO = "test_outputs"
|
||||
CODE_SUMMARIES_FILE_REPO = "docs/code_summaries"
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ from pathlib import Path
|
|||
from typing import Set
|
||||
|
||||
from metagpt.actions import Action, WriteCode, WriteCodeReview, WriteTasks
|
||||
from metagpt.actions.action_node import dict_to_markdown
|
||||
from metagpt.actions.fix_bug import FixBug
|
||||
from metagpt.actions.summarize_code import SummarizeCode
|
||||
from metagpt.actions.write_code_guideline_an import (
|
||||
|
|
@ -34,6 +35,7 @@ from metagpt.actions.write_code_guideline_an import (
|
|||
)
|
||||
from metagpt.config import CONFIG
|
||||
from metagpt.const import (
|
||||
CODE_GUIDELINE_PDF_FILE_REPO,
|
||||
CODE_SUMMARIES_FILE_REPO,
|
||||
CODE_SUMMARIES_PDF_FILE_REPO,
|
||||
PRDS_FILE_REPO,
|
||||
|
|
@ -101,7 +103,7 @@ class Engineer(Role):
|
|||
m = json.loads(task_msg.content)
|
||||
return m.get("Task list") or m.get("Refined Task list")
|
||||
|
||||
async def _act_sp_with_cr(self, review=False, guideline="") -> Set[str]:
|
||||
async def _act_sp_with_cr(self, review=False, guideline=Document()) -> Set[str]:
|
||||
changed_files = set()
|
||||
src_file_repo = CONFIG.git_repo.new_file_repository(CONFIG.src_workspace)
|
||||
for todo in self.code_todos:
|
||||
|
|
@ -112,16 +114,16 @@ class Engineer(Role):
|
|||
3. Do we need other codes (currently needed)?
|
||||
TODO: The goal is not to need it. After clear task decomposition, based on the design idea, you should be able to write a single file without needing other codes. If you can't, it means you need a clearer definition. This is the key to writing longer code.
|
||||
"""
|
||||
coding_context = await todo.run(guideline=guideline)
|
||||
coding_context = await todo.run(guideline=guideline.content)
|
||||
# Code review
|
||||
if review:
|
||||
action = WriteCodeReview(context=coding_context, llm=self.llm)
|
||||
self._init_action_system_message(action)
|
||||
coding_context = await action.run(guideline=guideline)
|
||||
coding_context = await action.run(guideline=guideline.content)
|
||||
|
||||
dependencies = {coding_context.design_doc.root_relative_path, coding_context.task_doc.root_relative_path}
|
||||
if guideline:
|
||||
dependencies.add("code_guideline.json")
|
||||
if guideline.content:
|
||||
dependencies.add(guideline.root_relative_path)
|
||||
await src_file_repo.save(
|
||||
coding_context.filename,
|
||||
dependencies=dependencies,
|
||||
|
|
@ -364,12 +366,11 @@ class Engineer(Role):
|
|||
code=old_codes,
|
||||
)
|
||||
node = await WriteCodeGuideline().run(context=context)
|
||||
guideline = node.instruct_content.model_dump_json()
|
||||
guideline = node.instruct_content.model_dump()
|
||||
await WriteCodeGuideline.save(guideline)
|
||||
guideline = dict_to_markdown(guideline)
|
||||
|
||||
await CONFIG.git_repo.new_file_repository(CONFIG.git_repo.workdir).save(
|
||||
filename="code_guideline.json", content=guideline
|
||||
)
|
||||
return guideline
|
||||
return Document(root_path=CODE_GUIDELINE_PDF_FILE_REPO, filename="code_guideline.md", content=guideline)
|
||||
|
||||
@staticmethod
|
||||
async def get_old_codes() -> str:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue