mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-01 11:56:24 +02:00
feat: merge geekan/dev
This commit is contained in:
commit
aa0909525e
62 changed files with 1733 additions and 154 deletions
|
|
@ -15,6 +15,7 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|||
from metagpt.actions.action_node import ActionNode
|
||||
from metagpt.context_mixin import ContextMixin
|
||||
from metagpt.schema import (
|
||||
CodePlanAndChangeContext,
|
||||
CodeSummarizeContext,
|
||||
CodingContext,
|
||||
RunCodeContext,
|
||||
|
|
@ -28,7 +29,9 @@ class Action(SerializationMixin, ContextMixin, BaseModel):
|
|||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
name: str = ""
|
||||
i_context: Union[dict, CodingContext, CodeSummarizeContext, TestingContext, RunCodeContext, str, None] = ""
|
||||
i_context: Union[
|
||||
dict, CodingContext, CodeSummarizeContext, TestingContext, RunCodeContext, CodePlanAndChangeContext, str, None
|
||||
] = ""
|
||||
prefix: str = "" # aask*时会加上prefix,作为system_message
|
||||
desc: str = "" # for skill manager
|
||||
node: ActionNode = Field(default=None, exclude=True)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import json
|
|||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
||||
|
||||
from pydantic import BaseModel, create_model, model_validator
|
||||
from pydantic import BaseModel, Field, create_model, model_validator
|
||||
from tenacity import retry, stop_after_attempt, wait_random_exponential
|
||||
|
||||
from metagpt.actions.action_outcls_registry import register_action_outcls
|
||||
|
|
@ -186,11 +186,27 @@ class ActionNode:
|
|||
obj.add_children(nodes)
|
||||
return obj
|
||||
|
||||
def get_children_mapping(self, exclude=None) -> Dict[str, Tuple[Type, Any]]:
|
||||
def get_children_mapping_old(self, exclude=None) -> Dict[str, Tuple[Type, Any]]:
|
||||
"""获得子ActionNode的字典,以key索引"""
|
||||
exclude = exclude or []
|
||||
return {k: (v.expected_type, ...) for k, v in self.children.items() if k not in exclude}
|
||||
|
||||
def get_children_mapping(self, exclude=None) -> Dict[str, Tuple[Type, Any]]:
|
||||
"""获得子ActionNode的字典,以key索引,支持多级结构"""
|
||||
exclude = exclude or []
|
||||
mapping = {}
|
||||
|
||||
def _get_mapping(node: "ActionNode", prefix: str = ""):
|
||||
for key, child in node.children.items():
|
||||
if key in exclude:
|
||||
continue
|
||||
full_key = f"{prefix}{key}"
|
||||
mapping[full_key] = (child.expected_type, ...)
|
||||
_get_mapping(child, prefix=f"{full_key}.")
|
||||
|
||||
_get_mapping(self)
|
||||
return mapping
|
||||
|
||||
def get_self_mapping(self) -> Dict[str, Tuple[Type, Any]]:
|
||||
"""get self key: type mapping"""
|
||||
return {self.key: (self.expected_type, ...)}
|
||||
|
|
@ -616,3 +632,62 @@ class ActionNode:
|
|||
self.update_instruct_content(revise_contents)
|
||||
|
||||
return revise_contents
|
||||
|
||||
@classmethod
|
||||
def from_pydantic(cls, model: Type[BaseModel], key: str = None):
|
||||
"""
|
||||
Creates an ActionNode tree from a Pydantic model.
|
||||
|
||||
Args:
|
||||
model (Type[BaseModel]): The Pydantic model to convert.
|
||||
|
||||
Returns:
|
||||
ActionNode: The root node of the created ActionNode tree.
|
||||
"""
|
||||
key = key or model.__name__
|
||||
root_node = cls(key=model.__name__, expected_type=Type[model], instruction="", example="")
|
||||
|
||||
for field_name, field_model in model.model_fields.items():
|
||||
# Extracting field details
|
||||
expected_type = field_model.annotation
|
||||
instruction = field_model.description or ""
|
||||
example = field_model.default
|
||||
|
||||
# Check if the field is a Pydantic model itself.
|
||||
# Use isinstance to avoid typing.List, typing.Dict, etc. (they are instances of type, not subclasses)
|
||||
if isinstance(expected_type, type) and issubclass(expected_type, BaseModel):
|
||||
# Recursively process the nested model
|
||||
child_node = cls.from_pydantic(expected_type, key=field_name)
|
||||
else:
|
||||
child_node = cls(key=field_name, expected_type=expected_type, instruction=instruction, example=example)
|
||||
|
||||
root_node.add_child(child_node)
|
||||
|
||||
return root_node
|
||||
|
||||
|
||||
class ToolUse(BaseModel):
|
||||
tool_name: str = Field(default="a", description="tool name", examples=[])
|
||||
|
||||
|
||||
class Task(BaseModel):
|
||||
task_id: int = Field(default="1", description="task id", examples=[1, 2, 3])
|
||||
name: str = Field(default="Get data from ...", description="task name", examples=[])
|
||||
dependent_task_ids: List[int] = Field(default=[], description="dependent task ids", examples=[1, 2, 3])
|
||||
tool: ToolUse = Field(default=ToolUse(), description="tool use", examples=[])
|
||||
|
||||
|
||||
class Tasks(BaseModel):
|
||||
tasks: List[Task] = Field(default=[], description="tasks", examples=[])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
node = ActionNode.from_pydantic(Tasks)
|
||||
print("Tasks")
|
||||
print(Tasks.model_json_schema())
|
||||
print("Task")
|
||||
print(Task.model_json_schema())
|
||||
print(node)
|
||||
prompt = node.compile(context="")
|
||||
node.create_children_class()
|
||||
print(prompt)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -39,7 +46,7 @@ class WriteDesign(Action):
|
|||
)
|
||||
|
||||
async def run(self, with_messages: Message, schema: str = None):
|
||||
# Use `git status` to identify which PRD documents have been modified in the `docs/prds` directory.
|
||||
# Use `git status` to identify which PRD documents have been modified in the `docs/prd` directory.
|
||||
changed_prds = self.repo.docs.prd.changed_files
|
||||
# Use `git status` to identify which design documents in the `docs/system_designs` directory have undergone
|
||||
# changes.
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -48,5 +48,5 @@ class PrepareDocuments(Action):
|
|||
# Write the newly added requirements from the main parameter idea to `docs/requirement.txt`.
|
||||
doc = await self.repo.docs.save(filename=REQUIREMENT_FILENAME, content=with_messages[0].content)
|
||||
# Send a Message notification to the WritePRD action, instructing it to process requirements using
|
||||
# `docs/requirement.txt` and `docs/prds/`.
|
||||
# `docs/requirement.txt` and `docs/prd/`.
|
||||
return ActionOutput(content=doc.content, instruct_content=doc)
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ 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
|
||||
|
||||
NEW_REQ_TEMPLATE = """
|
||||
### Legacy Content
|
||||
{old_tasks}
|
||||
{old_task}
|
||||
|
||||
### New Requirements
|
||||
{context}
|
||||
|
|
@ -77,8 +77,8 @@ class WriteTasks(Action):
|
|||
return node
|
||||
|
||||
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)
|
||||
context = NEW_REQ_TEMPLATE.format(context=system_design_doc.content, old_task=task_doc.content)
|
||||
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,6 +16,7 @@
|
|||
class.
|
||||
"""
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Tuple
|
||||
|
||||
from pydantic import Field
|
||||
|
|
@ -150,11 +151,23 @@ class RunCode(Action):
|
|||
return subprocess.run(cmd, check=check, cwd=cwd, env=env)
|
||||
|
||||
@staticmethod
|
||||
def _install_dependencies(working_directory, env):
|
||||
def _install_requirements(working_directory, env):
|
||||
file_path = Path(working_directory) / "requirements.txt"
|
||||
if not file_path.exists():
|
||||
return
|
||||
if file_path.stat().st_size == 0:
|
||||
return
|
||||
install_command = ["python", "-m", "pip", "install", "-r", "requirements.txt"]
|
||||
logger.info(" ".join(install_command))
|
||||
RunCode._install_via_subprocess(install_command, check=True, cwd=working_directory, env=env)
|
||||
|
||||
@staticmethod
|
||||
def _install_pytest(working_directory, env):
|
||||
install_pytest_command = ["python", "-m", "pip", "install", "pytest"]
|
||||
logger.info(" ".join(install_pytest_command))
|
||||
RunCode._install_via_subprocess(install_pytest_command, check=True, cwd=working_directory, env=env)
|
||||
|
||||
@staticmethod
|
||||
def _install_dependencies(working_directory, env):
|
||||
RunCode._install_requirements(working_directory, env)
|
||||
RunCode._install_pytest(working_directory, env)
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenc
|
|||
{system_design}
|
||||
```
|
||||
-----
|
||||
# Tasks
|
||||
# Task
|
||||
```text
|
||||
{tasks}
|
||||
{task}
|
||||
```
|
||||
-----
|
||||
{code_blocks}
|
||||
|
|
@ -110,7 +110,7 @@ class SummarizeCode(Action):
|
|||
format_example = FORMAT_EXAMPLE
|
||||
prompt = PROMPT_TEMPLATE.format(
|
||||
system_design=design_doc.content,
|
||||
tasks=task_doc.content,
|
||||
task=task_doc.content,
|
||||
code_blocks="\n".join(code_blocks),
|
||||
format_example=format_example,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,10 +21,17 @@ 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
|
||||
|
|
@ -36,8 +43,8 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenc
|
|||
## Design
|
||||
{design}
|
||||
|
||||
## Tasks
|
||||
{tasks}
|
||||
## Task
|
||||
{task}
|
||||
|
||||
## Legacy Code
|
||||
```Code
|
||||
|
|
@ -91,6 +98,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.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 +111,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.i_context.filename, project_repo=self.repo, use_inc=True
|
||||
)
|
||||
else:
|
||||
code_context = await self.get_codes(
|
||||
coding_context.task_doc,
|
||||
|
|
@ -108,15 +122,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 "",
|
||||
task=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 "",
|
||||
)
|
||||
else:
|
||||
prompt = PROMPT_TEMPLATE.format(
|
||||
design=coding_context.design_doc.content if coding_context.design_doc else "",
|
||||
task=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 +154,66 @@ 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, use_inc: bool = False) -> str:
|
||||
"""
|
||||
Get codes for generating the exclude file in various scenarios.
|
||||
|
||||
Attributes:
|
||||
task_doc (Document): Document object of the task file.
|
||||
exclude (str): The file to be generated. Specifies the filename to be excluded from the code snippets.
|
||||
project_repo (ProjectRepo): ProjectRepo object of the project.
|
||||
use_inc (bool): Indicates whether the scenario involves incremental development. Defaults to False.
|
||||
|
||||
Returns:
|
||||
str: Codes for generating the exclude file.
|
||||
"""
|
||||
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 use_inc 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)
|
||||
|
||||
# Incremental development scenario
|
||||
if use_inc:
|
||||
src_files = src_file_repo.all_files
|
||||
# Get the old workspace contained the old codes and old workspace are created in previous CodePlanAndChange
|
||||
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
|
||||
if filename == exclude:
|
||||
# If the file is in the old workspace, use the old 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 old 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 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}```")
|
||||
|
||||
# Normal scenario
|
||||
else:
|
||||
for filename in code_filenames:
|
||||
# Exclude the current file to get the 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)
|
||||
|
|
|
|||
210
metagpt/actions/write_code_plan_and_change_an.py
Normal file
210
metagpt/actions/write_code_plan_and_change_an.py
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
#!/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}
|
||||
|
||||
## Task
|
||||
{task}
|
||||
|
||||
## 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}
|
||||
|
||||
## Task
|
||||
{task}
|
||||
|
||||
## 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 that is marked by the git diff format using '+' and '-' for add/modify/delete code, 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 details that are not related to incremental development but are important for maintaining the consistency and clarity of the old code.
|
||||
"""
|
||||
|
||||
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"
|
||||
prd_doc = await self.repo.docs.prd.get(filename=self.i_context.prd_filename)
|
||||
design_doc = await self.repo.docs.system_design.get(filename=self.i_context.design_filename)
|
||||
task_doc = await self.repo.docs.task.get(filename=self.i_context.task_filename)
|
||||
code_text = await self.get_old_codes()
|
||||
context = CODE_PLAN_AND_CHANGE_CONTEXT.format(
|
||||
requirement=self.i_context.requirement,
|
||||
prd=prd_doc.content,
|
||||
design=design_doc.content,
|
||||
task=task_doc.content,
|
||||
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,7 @@ 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
|
||||
|
||||
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 +146,30 @@ class WriteCodeReview(Action):
|
|||
self.i_context.task_doc,
|
||||
exclude=self.i_context.filename,
|
||||
project_repo=self.repo.with_src_path(self.context.src_workspace),
|
||||
use_inc=self.config.inc,
|
||||
)
|
||||
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 self.config.inc:
|
||||
context = "\n".join(
|
||||
[
|
||||
"## System Design\n" + str(self.i_context.design_doc) + "\n",
|
||||
"## Task\n" + task_content + "\n",
|
||||
"## Code Files\n" + code_context + "\n",
|
||||
]
|
||||
)
|
||||
else:
|
||||
requirement_doc = await self.repo.docs.get(filename=REQUIREMENT_FILENAME)
|
||||
code_plan_and_change_doc = await self.repo.get(filename=CODE_PLAN_AND_CHANGE_FILENAME)
|
||||
context = "\n".join(
|
||||
[
|
||||
"## User New Requirements\n" + str(requirement_doc) + "\n",
|
||||
"## Code Plan And Change\n" + str(code_plan_and_change_doc) + "\n",
|
||||
"## System Design\n" + str(self.i_context.design_doc) + "\n",
|
||||
"## Task\n" + task_content + "\n",
|
||||
"## Code Files\n" + code_context + "\n",
|
||||
]
|
||||
)
|
||||
|
||||
context_prompt = PROMPT_TEMPLATE.format(
|
||||
context=context,
|
||||
code=iterative_code,
|
||||
|
|
@ -161,7 +179,7 @@ class WriteCodeReview(Action):
|
|||
format_example=format_example,
|
||||
)
|
||||
len1 = len(iterative_code) if iterative_code else 0
|
||||
len2 = len(self.context.code_doc.content) if self.context.code_doc.content else 0
|
||||
len2 = len(self.i_context.code_doc.content) if self.i_context.code_doc.content else 0
|
||||
logger.info(
|
||||
f"Code review and rewrite {self.i_context.code_doc.filename}: {i + 1}/{k} | len(iterative_code)={len1}, "
|
||||
f"len(self.i_context.code_doc.content)={len2}"
|
||||
|
|
|
|||
|
|
@ -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,21 +140,21 @@ 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
|
||||
|
||||
async def _update_prd(self, req: Document, prd_doc: Document) -> Document:
|
||||
new_prd_doc: Document = await self._merge(req, prd_doc)
|
||||
self.repo.docs.prd.save_doc(doc=new_prd_doc)
|
||||
await self.repo.docs.prd.save_doc(doc=new_prd_doc)
|
||||
await self._save_competitive_analysis(new_prd_doc)
|
||||
await self.repo.resources.prd.save_pdf(doc=new_prd_doc)
|
||||
return new_prd_doc
|
||||
|
||||
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 original 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