1. remove INC prompt in some ActionNode

2. replace Code archive from data to tests/data/incremental_dev_project
3. update test case for ActionNode
This commit is contained in:
mannaandpoem 2024-01-16 15:42:48 +08:00
parent f460771978
commit f4e39a462c
19 changed files with 194 additions and 420 deletions

View file

@ -18,14 +18,6 @@ IMPLEMENTATION_APPROACH = ActionNode(
example="We will ...",
)
INCREMENTAL_IMPLEMENTATION_APPROACH = ActionNode(
key="Incremental Implementation approach",
expected_type=str,
instruction="Analyze the challenging aspects of the requirements and select a suitable open-source framework. "
"Outline the incremental steps involved in the implementation process with the detailed strategies.",
example="we will ...",
)
REFINED_IMPLEMENTATION_APPROACH = ActionNode(
key="Refined Implementation Approach",
expected_type=str,
@ -63,16 +55,6 @@ DATA_STRUCTURES_AND_INTERFACES = ActionNode(
example=MMC1,
)
INCREMENTAL_DATA_STRUCTURES_AND_INTERFACES = ActionNode(
key="Incremental Data structures and interfaces",
expected_type=str,
instruction="Extend the existing mermaid classDiagram code syntax to incorporate new classes, "
"methods (including __init__), and functions with precise type annotations. Clearly delineate additional "
"relationships between classes, maintaining adherence to PEP8 standards. Enhance the level of detail in data "
"structures, ensuring a comprehensive API design that seamlessly integrates with the existing structure.",
example=MMC1_REFINE,
)
REFINED_DATA_STRUCTURES_AND_INTERFACES = ActionNode(
key="Refined Data Structures and Interfaces",
expected_type=str,
@ -108,32 +90,6 @@ ANYTHING_UNCLEAR = ActionNode(
example="Clarification needed on third-party API integration, ...",
)
INC_DESIGN_CONTEXT = """
## Legacy Content
{old_design}
## New Requirements
{requirements}
## PRD Increment Content
{prd_increment}
"""
MERGE_DESIGN_CONTEXT = """
Role: You are a professional Architect tasked with overseeing incremental development.
Based on new requirements, review and refine the system design. Integrate existing architecture with incremental design changes, ensuring the refined design encompasses all architectural elements, enhancements, and adjustments. Retain content unrelated to incremental development needs for coherence and clarity.
# Context
## New Requirements
{requirements}
## Legacy Content
{old_design}
## Design Increment Content
{design_increment}
"""
NODES = [
IMPLEMENTATION_APPROACH,
# PROJECT_NAME,
@ -143,8 +99,6 @@ NODES = [
ANYTHING_UNCLEAR,
]
INC_NODES = [INCREMENTAL_IMPLEMENTATION_APPROACH, INCREMENTAL_DATA_STRUCTURES_AND_INTERFACES, REFINED_PROGRAM_CALL_FLOW]
REFINE_NODES = [
REFINED_IMPLEMENTATION_APPROACH,
REFINED_FILE_LIST,
@ -154,7 +108,6 @@ REFINE_NODES = [
]
DESIGN_API_NODE = ActionNode.from_children("DesignAPI", NODES)
INCREMENTAL_DESIGN_NODES = ActionNode.from_children("Incremental_Design_API", INC_NODES)
REFINED_DESIGN_NODES = ActionNode.from_children("Refined_Design_API", REFINE_NODES)

View file

@ -35,24 +35,12 @@ LOGIC_ANALYSIS = ActionNode(
],
)
INCREMENTAL_LOGIC_ANALYSIS = ActionNode(
key="Incremental Logic Analysis",
expected_type=List[List[str]],
instruction="Provide a list of files with the classes/methods/functions to be implemented or modified "
"incrementally. Include thorough dependency analysis, consider potential impacts on existing code, and document"
" necessary imports.",
example=[
["new_feature.py", "Introduces NewFeature class and related functions"],
["utils.py", "Modifies existing utility functions to support incremental changes"],
],
)
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 thorough dependency analysis, consider potential impacts on existing code, and document necessary imports.",
"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"],
@ -68,15 +56,6 @@ TASK_LIST = ActionNode(
example=["game.py", "main.py"],
)
INCREMENTAL_TASK_LIST = ActionNode(
key="Incremental Task list",
expected_type=List[str],
instruction="Break down the incremental development tasks into a prioritized list of filenames."
"Organize the tasks based on dependency order, ensuring a systematic and efficient implementation."
"Only output filename! Do not include comments in the list ",
example=["new_feature.py", "main.py"],
)
REFINED_TASK_LIST = ActionNode(
key="Refined Task list",
expected_type=List[str],
@ -101,14 +80,6 @@ SHARED_KNOWLEDGE = ActionNode(
example="`game.py` contains functions shared across the project.",
)
INCREMENTAL_SHARED_KNOWLEDGE = ActionNode(
key="Incremental Shared Knowledge",
expected_type=str,
instruction="Document any new shared knowledge generated during incremental development. This includes common "
"utility functions, configuration variables, or any information vital for team collaboration.",
example="`new_module.py` introduces shared utility functions for improved code reusability.",
)
REFINED_SHARED_KNOWLEDGE = ActionNode(
key="Refined Shared Knowledge",
expected_type=str,
@ -126,32 +97,6 @@ ANYTHING_UNCLEAR_PM = ActionNode(
example="Clarification needed on how to start and initialize third-party libraries.",
)
INC_PM_CONTEXT = """
### Legacy Content
{old_tasks}
### New Requirements
{requirements}
### Design Increment Content
{design_increment}
"""
MERGE_PM_CONTEXT = """
Role: You are a professional Project Manager tasked with overseeing incremental development.
Based on New Requirements, refine the project context to account for incremental development. Ensure the context offers a comprehensive overview of the project's evolving scope, covering both legacy content and incremental content. Retain any content unrelated to incremental development.
# Context
## New Requirements
{requirements}
## Legacy Content
{old_tasks}
## Increment Content
{tasks_increment}
"""
NODES = [
REQUIRED_PYTHON_PACKAGES,
REQUIRED_OTHER_LANGUAGE_PACKAGES,
@ -162,8 +107,6 @@ NODES = [
ANYTHING_UNCLEAR_PM,
]
INC_NODES = [INCREMENTAL_LOGIC_ANALYSIS, INCREMENTAL_TASK_LIST, INCREMENTAL_SHARED_KNOWLEDGE]
REFINE_NODES = [
REQUIRED_PYTHON_PACKAGES,
REQUIRED_OTHER_LANGUAGE_PACKAGES,
@ -175,7 +118,6 @@ REFINE_NODES = [
]
PM_NODE = ActionNode.from_children("PM_NODE", NODES)
INCREMENTAL_PM_NODES = ActionNode.from_children("Incremental_PM_NODES", INC_NODES)
REFINED_PM_NODES = ActionNode.from_children("Refined_PM_NODES", REFINE_NODES)

View file

@ -157,6 +157,24 @@ class WriteCode(Action):
@staticmethod
async def get_codes(task_doc, exclude, mode="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.
mode (str): Specifies the mode, either "normal" or "guide" (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 "guide", it returns code snippets for incremental development,
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:
@ -173,6 +191,8 @@ class WriteCode(Action):
union_files_list = list(set(src_files) | set(old_files))
for filename in union_files_list:
if filename == exclude:
# 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)

View file

@ -9,6 +9,7 @@ import asyncio
from metagpt.actions.action import Action
from metagpt.actions.action_node import ActionNode
from metagpt.logs import logger
GUIDELINES_AND_INCREMENTAL_CHANGE = ActionNode(
key="Guidelines and Incremental Change",
@ -455,7 +456,7 @@ async def main():
write_code_guideline = WriteCodeGuideline()
node = await write_code_guideline.run(CODE_GUIDELINE_CONTEXT_EXAMPLE)
guideline = node.instruct_content.model_dump_json()
print(guideline)
logger.info(guideline)
if __name__ == "__main__":

View file

@ -130,14 +130,6 @@ REQUIREMENT_ANALYSIS = ActionNode(
example="",
)
INCREMENTAL_REQUIREMENT_ANALYSIS = ActionNode(
key="Incremental Requirement Analysis",
expected_type=List[str],
instruction="Propose the comprehensive incremental development requirement analysis on new features and enhanced "
"features for New Requirements.",
example=["Require add/update/modify ..."],
)
REFINED_REQUIREMENT_ANALYSIS = ActionNode(
key="Refined Requirement Analysis",
expected_type=List[str],
@ -194,22 +186,6 @@ REASON = ActionNode(
key="reason", expected_type=str, instruction="Explain the reasoning process from question to answer", example="..."
)
INCREMENTAL_PRD_CONTEXT = """
Role: You are a professional Product Manager tasked with overseeing incremental development.
Based on New Requirements, output a New PRD that seamlessly integrates both the Legacy Content and the Incremental Content. Ensure the resulting document captures the complete scope of features, enhancements, and retain content unrelated to incremental development needs for coherence and clarity.
# Context
## New Requirements
{requirements}
## Legacy Content
{old_prd}
## PRD Incremental Content
{prd_increment}
"""
REFINE_PRD_TEMPLATE = """
### Project Name
{project_name}
@ -255,11 +231,8 @@ REFINE_NODES = [
ANYTHING_UNCLEAR,
]
INCREMENT_PRD_NODES = [INCREMENTAL_REQUIREMENT_ANALYSIS, REQUIREMENT_POOL]
WRITE_PRD_NODE = ActionNode.from_children("WritePRD", NODES)
REFINE_PRD_NODE = ActionNode.from_children("RefinePRD", REFINE_NODES)
INCREMENTAL_PRD_NODE = ActionNode.from_children("IncrementalPRD", INCREMENT_PRD_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])

View file

@ -119,18 +119,9 @@ class Engineer(Role):
self._init_action_system_message(action)
coding_context = await action.run(guideline=guideline)
# Get dependencies
dependencies = {coding_context.design_doc.root_relative_path, coding_context.task_doc.root_relative_path}
if guideline:
dependencies = {
coding_context.design_doc.root_relative_path,
coding_context.task_doc.root_relative_path,
"code_guideline.json",
}
else:
dependencies = {
coding_context.design_doc.root_relative_path,
coding_context.task_doc.root_relative_path,
}
dependencies.add("code_guideline.json")
await src_file_repo.save(
coding_context.filename,
dependencies=dependencies,
@ -344,6 +335,7 @@ class Engineer(Role):
return self.next_todo_action
async def _write_code_guideline(self):
"""Write some guidelines that guides subsequent WriteCode and WriteCodeReview"""
logger.info("Writing code guideline..")
user_requirement = str(self.rc.memory.get_by_role("Human")[0])

View file

@ -0,0 +1,3 @@
# Code archive
This folder contains a compressed package for the test_incremental_dev.py file, which is used to demonstrate the process of incremental development.

View file

@ -101,4 +101,7 @@ def llm():
async def test_write_design_an():
node = await REFINED_DESIGN_NODES.fill(CONTEXT, llm)
assert node.instruct_content
assert "Refined Implementation Approach" in node.instruct_content.model_dump_json()
assert "Refined File List" in node.instruct_content.model_dump_json()
assert "Refined Data Structures and Interfaces" in node.instruct_content.model_dump_json()
assert "Refined Program call flow" in node.instruct_content.model_dump_json()

View file

@ -141,4 +141,8 @@ def llm():
async def test_project_management_an(llm):
node = await REFINED_PM_NODES.fill(CONTEXT, llm)
assert node.instruct_content
assert "Required Python Packages" in node.instruct_content.model_dump_json()
assert "Required Other Language Packages" in node.instruct_content.model_dump_json()
assert "Refined Logic Analysis" in node.instruct_content.model_dump_json()
assert "Refined Task List" in node.instruct_content.model_dump_json()
assert "Refined Shared Knowledge" in node.instruct_content.model_dump_json()

View file

@ -185,9 +185,94 @@ if __name__ == "__main__":
CalculatorApp.main()
```"""
INCREMENTAL_CHANGE_EXAMPLE = """
GUIDELINES_AND_INCREMENTAL_CHANGE_EXAMPLE = """
{
"Incremental Change": "- operations.py: Implement the Operations class with a method to perform the requested arithmetic operation. This class will be used by the Calculator class to execute the operations.\n```python\n## operations.py\nclass Operations:\n @staticmethod\n def perform_operation(operation: str, number1: float, number2: float) -> float:\n if operation == 'add':\n return number1 + number2\n elif operation == 'subtract':\n return number1 - number2\n elif operation == 'multiply':\n return number1 * number2\n elif operation == 'divide':\n if number2 == 0:\n raise ValueError('Cannot divide by zero')\n return number1 / number2\n else:\n raise ValueError('Invalid operation')\n```\n\n- calculator.py: Extend the Calculator class to include methods for subtraction, multiplication, and division. These methods will utilize the Operations class to perform the actual calculations.\n```python\n## calculator.py\nfrom operations import Operations\nclass Calculator:\n ...\n def subtract(self, number1: float, number2: float) -> float:\n return Operations.perform_operation('subtract', number1, number2)\n\n def multiply(self, number1: float, number2: float) -> float:\n return Operations.perform_operation('multiply', number1, number2)\n\n def divide(self, number1: float, number2: float) -> float:\n return Operations.perform_operation('divide', number1, number2)\n```\n\n- interface.py: Update the Interface class to include buttons for subtraction, multiplication, and division, and link them to the corresponding methods in the Calculator class. Also, handle the display of errors such as division by zero.\n```python\n## interface.py\nimport tkinter as tk\nfrom tkinter import messagebox\nfrom calculator import Calculator\n...\nclass Interface:\n ...\n def create_widgets(self):\n ...\n self.subtract_button = tk.Button(self.root, text='-', command=self.subtract, font=('Arial', 18))\n self.subtract_button.grid(row=3, column=0, sticky='nsew')\n\n self.multiply_button = tk.Button(self.root, text='*', command=self.multiply, font=('Arial', 18))\n self.multiply_button.grid(row=3, column=1, sticky='nsew')\n\n self.divide_button = tk.Button(self.root, text='/', command=self.divide, font=('Arial', 18))\n self.divide_button.grid(row=3, column=2, sticky='nsew')\n ...\n\n def subtract(self):\n number1, number2 = self.get_input()\n if number1 is not None and number2 is not None:\n result = self.calculator.subtract(number1, number2)\n self.display_result(result)\n\n def multiply(self):\n number1, number2 = self.get_input()\n if number1 is not None and number2 is not None:\n result = self.calculator.multiply(number1, number2)\n self.display_result(result)\n\n def divide(self):\n number1, number2 = self.get_input()\n if number1 is not None and number2 is not None:\n try:\n result = self.calculator.divide(number1, number2)\n except ValueError as e:\n self.show_error(str(e))\n return\n self.display_result(result)\n```\n\n- main.py: No changes needed in main.py as it serves as the entry point and will run the updated Interface class.\n```python\n## main.py\nfrom interface import Interface\n...\n```\n\nNote: Ensure that the new operations buttons in the Interface class are properly arranged and that the grid layout is adjusted accordingly. Also, make sure to import the messagebox module from tkinter for error handling."
"Guidelines and Incremental Change": "
1. Guideline 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. Guideline 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()
```"
}
"""
@ -207,7 +292,7 @@ async def test_write_code_guideline_an():
async def test_refine_code():
prompt = REFINED_CODE_TEMPLATE.format(
requirement=REQUIREMENT_EXAMPLE,
guideline=INCREMENTAL_CHANGE_EXAMPLE,
guideline=GUIDELINES_AND_INCREMENTAL_CHANGE_EXAMPLE,
design=DESIGN_EXAMPLE,
tasks=TASKS_EXAMPLE,
code=REFINE_CODE_SCRIPT_EXAMPLE,

View file

@ -85,4 +85,8 @@ def llm():
async def test_write_prd_an(llm):
node = await REFINE_PRD_NODE.fill(CONTEXT, llm)
assert node.instruct_content
assert "Refined Requirements" in node.instruct_content.model_dump_json()
assert "Refined Product Goals" in node.instruct_content.model_dump_json()
assert "Refined User Stories" in node.instruct_content.model_dump_json()
assert "Refined Requirement Analysis" in node.instruct_content.model_dump_json()
assert "Refined Requirement Pool" in node.instruct_content.model_dump_json()

View file

@ -11,24 +11,73 @@ import subprocess
import pytest
from typer.testing import CliRunner
from metagpt.const import DATA_PATH
from metagpt.const import TEST_DATA_PATH
from metagpt.logs import logger
from metagpt.startup import app
runner = CliRunner()
IDEAS = [
"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",
"Add a feature to remove deprecated words from the word cloud. The current word cloud generator does not support removing deprecated words. Now, The word cloud generator should support removing deprecated words. Customize deactivated words to exclude them from word cloud. Let users see all the words in the text file, and allow users to select the words they want to remove.",
"Add an AI opponent with fixed difficulty levels. Currently, the game only allows players to compete against themselves. Implement an AI algorithm that can playing with player. This will provide a more engaging and challenging experience for players.",
"Add functionality to view the history of scores. The original dice rolling game could only display the current game result, but the new requirement allows players to view the history of scores",
"Add functionality to view the history of scores and perform statistical analysis on them. The original dice rolling game could only display the current game result, but the new requirement allows players to view the history of scores and display the statistical analysis results of the current score",
"Changed score target for 2048 game from 2048 to 4096. Please change the game's score target from 2048 to 4096, and change the interface size from 4*4 to 8*8",
"Display the history score of the player in the 2048 game. Add a record board that can display players' historical score records so that players can trace their scores",
"Add limited time mode. The original game only had a default classic mode. The improved game should be able to support limited-time mode, allowing users to choose classic mode or limited-time mode from the available options before starting the game.",
"Incremental Idea Gradually increase the speed of the snake as the game progresses. In the current version of the game, the snakes speed remains constant throughout the gameplay. Implement a feature where the snakes speed gradually increases over time, making the game more challenging and intense as the player progresses.",
"Introduce power-ups and obstacles to the game. The current version of the game only involves eating food and growing the snake. Add new elements such as power-ups that can enhance the snakes speed or make it invincible for a short duration. At the same time, introduce obstacles like walls or enemies that the snake must avoid or overcome to continue growing.",
]
def test_refined_simple_calculator():
project_path = f"{DATA_PATH}/simple_add_calculator"
check_or_create_base_tag(project_path)
PROJECT_NAMES = [
"calculator",
"word_cloud",
"Gomoku",
"dice_simulator_new",
"dice_simulator_new",
"pygame_2048",
"pygame_2048",
"pygame_2048",
"snake_game",
"snake_game",
]
args = [
"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",
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
def test_refined_calculator():
result = get_incremental_dev_result(IDEAS[0], PROJECT_NAMES[0])
log_and_check_result(result)
def test_refined_word_cloud():
result = get_incremental_dev_result(IDEAS[1], PROJECT_NAMES[1])
log_and_check_result(result)
def test_refined_gomoku():
result = get_incremental_dev_result(IDEAS[2], PROJECT_NAMES[2])
log_and_check_result(result)
def test_refined_dice_simulator_new():
for idea, project_name in zip(IDEAS[3:5], PROJECT_NAMES[3:5]):
result = get_incremental_dev_result(idea, project_name)
log_and_check_result(result)
def test_refined_pygame_2048():
for idea, project_name in zip(IDEAS[5:8], PROJECT_NAMES[5:8]):
result = get_incremental_dev_result(idea, project_name)
log_and_check_result(result)
def test_refined_snake_game():
for idea, project_name in zip(IDEAS[8:10], PROJECT_NAMES[8:10]):
result = get_incremental_dev_result(idea, project_name)
log_and_check_result(result)
def log_and_check_result(result):
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
@ -46,274 +95,19 @@ def test_refined_simple_calculator():
raise e
def test_refined_number_guessing_game():
project_path = f"{DATA_PATH}/number_guessing_game"
def get_incremental_dev_result(idea, project_name):
project_path = TEST_DATA_PATH / "incremental_dev_project" / project_name
if not os.path.exists(project_path):
raise Exception(f"Project {project_name} not exists")
check_or_create_base_tag(project_path)
args = [
"Adding graphical interface functionality to enhance the user experience in the number-guessing game. The existing number-guessing game currently relies on command-line input for numbers. The goal is to introduce a graphical interface to improve the game's usability and visual appeal",
idea,
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
assert False
else:
tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, text=True).stdout.strip()
if tag == "base":
assert False
else:
assert True
try:
subprocess.run(["git", "tag", "refine"], check=True)
except subprocess.CalledProcessError as e:
raise e
def test_refined_word_cloud():
project_path = f"{DATA_PATH}/word_cloud"
check_or_create_base_tag(project_path)
args = [
"Add a feature to remove deprecated words from the word cloud. The current word cloud generator does not support removing deprecated words. Now, The word cloud generator should support removing deprecated words. Customize deactivated words to exclude them from word cloud. Let users see all the words in the text file, and allow users to select the words they want to remove.",
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
assert False
else:
tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, text=True).stdout.strip()
if tag == "base":
assert False
else:
assert True
try:
subprocess.run(["git", "tag", "refine"], check=True)
except subprocess.CalledProcessError as e:
raise e
def test_refined_gomoku():
project_path = f"{DATA_PATH}/Gomoku"
check_or_create_base_tag(project_path)
args = [
"Add an AI opponent with fixed difficulty levels. Currently, the game only allows players to compete against themselves. Implement an AI algorithm that can playing with player. This will provide a more engaging and challenging experience for players.",
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
assert False
else:
tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, text=True).stdout.strip()
if tag == "base":
assert False
else:
assert True
try:
subprocess.run(["git", "tag", "refine"], check=True)
except subprocess.CalledProcessError as e:
raise e
def test_refined_dice_simulator_1():
project_path = f"{DATA_PATH}/dice_simulator_new"
check_or_create_base_tag(project_path)
args = [
"Add functionality to view the history of scores. The original dice rolling game could only display the current game result, but the new requirement allows players to view the history of scores",
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
assert False
else:
tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, text=True).stdout.strip()
if tag == "base":
assert False
else:
assert True
try:
subprocess.run(["git", "tag", "refine_1"], check=True)
except subprocess.CalledProcessError as e:
raise e
def test_refined_dice_simulator_2():
project_path = f"{DATA_PATH}/dice_simulator_new"
check_or_create_base_tag(project_path)
args = [
"Add functionality to view the history of scores and perform statistical analysis on them. The original dice rolling game could only display the current game result, but the new requirement allows players to view the history of scores and display the statistical analysis results of the current score",
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
assert False
else:
tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, text=True).stdout.strip()
if tag == "base":
assert False
else:
assert True
try:
subprocess.run(["git", "tag", "refine_2"], check=True)
except subprocess.CalledProcessError as e:
raise e
def test_refined_pygame_2048_1():
project_path = f"{DATA_PATH}/pygame_2048"
check_or_create_base_tag(project_path)
args = [
"Changed score target for 2048 game from 2048 to 4096. Please change the game's score target from 2048 to 4096, and change the interface size from 4*4 to 8*8",
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
assert False
else:
tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, text=True).stdout.strip()
if tag == "base":
assert False
else:
assert True
try:
subprocess.run(["git", "tag", "refine_1"], check=True)
except subprocess.CalledProcessError as e:
raise e
def test_refined_pygame_2048_2():
project_path = f"{DATA_PATH}/pygame_2048"
check_or_create_base_tag(project_path)
args = [
"Display the history score of the player in the 2048 game. Add a record board that can display players' historical score records so that players can trace their scores",
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
assert False
else:
tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, text=True).stdout.strip()
if tag == "base":
assert False
else:
assert True
try:
subprocess.run(["git", "tag", "refine_2"], check=True)
except subprocess.CalledProcessError as e:
raise e
def test_refined_pygame_2048_3():
project_path = f"{DATA_PATH}/pygame_2048"
check_or_create_base_tag(project_path)
args = [
"Add limited time mode. The original game only had a default classic mode. The improved game should be able to support limited-time mode, allowing users to choose classic mode or limited-time mode from the available options before starting the game.",
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
assert False
else:
tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, text=True).stdout.strip()
if tag == "base":
assert False
else:
assert True
try:
subprocess.run(["git", "tag", "refine_3"], check=True)
except subprocess.CalledProcessError as e:
raise e
def test_refined_snake_game_1():
project_path = f"{DATA_PATH}/snake_game"
check_or_create_base_tag(project_path)
args = [
"Incremental Idea Gradually increase the speed of the snake as the game progresses. In the current version of the game, the snakes speed remains constant throughout the gameplay. Implement a feature where the snakes speed gradually increases over time, making the game more challenging and intense as the player progresses.",
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
assert False
else:
tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, text=True).stdout.strip()
if tag == "base":
assert False
else:
assert True
try:
subprocess.run(["git", "tag", "refine_1"], check=True)
except subprocess.CalledProcessError as e:
raise e
def test_refined_snake_game_2():
project_path = f"{DATA_PATH}/snake_game"
check_or_create_base_tag(project_path)
args = [
"Introduce power-ups and obstacles to the game. The current version of the game only involves eating food and growing the snake. Add new elements such as power-ups that can enhance the snakes speed or make it invincible for a short duration. At the same time, introduce obstacles like walls or enemies that the snake must avoid or overcome to continue growing.",
"--inc",
"--project-path",
project_path,
]
result = runner.invoke(app, args)
logger.info(result)
logger.info(result.output)
if "Aborting" in result.output:
assert False
else:
tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, text=True).stdout.strip()
if tag == "base":
assert False
else:
assert True
try:
subprocess.run(["git", "tag", "refine_2"], check=True)
except subprocess.CalledProcessError as e:
raise e
return result
def check_or_create_base_tag(project_path):