mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-15 11:02:36 +02:00
Merge branch 'feature/explit_io' into 'mgx_ops'
feat: Implemenet of RFC236 #3 See merge request pub/MetaGPT!110
This commit is contained in:
commit
fccbc9d9da
34 changed files with 1060 additions and 432 deletions
|
|
@ -303,5 +303,4 @@ def test_action_node_from_pydantic_and_print_everything():
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_create_model_class()
|
||||
test_create_model_class_with_mapping()
|
||||
pytest.main([__file__, "-s"])
|
||||
|
|
|
|||
|
|
@ -6,37 +6,104 @@
|
|||
@File : test_design_api.py
|
||||
@Modifiled By: mashenquan, 2023-12-6. According to RFC 135
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from metagpt.actions.design_api import WriteDesign
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.const import DEFAULT_WORKSPACE_ROOT, METAGPT_ROOT
|
||||
from metagpt.logs import logger
|
||||
from metagpt.schema import Message
|
||||
from metagpt.schema import AIMessage, Message
|
||||
from metagpt.utils.project_repo import ProjectRepo
|
||||
from tests.data.incremental_dev_project.mock import DESIGN_SAMPLE, REFINED_PRD_JSON
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_design_api(context):
|
||||
inputs = ["我们需要一个音乐播放器,它应该有播放、暂停、上一曲、下一曲等功能。"] # PRD_SAMPLE
|
||||
for prd in inputs:
|
||||
await context.repo.docs.prd.save(filename="new_prd.txt", content=prd)
|
||||
async def test_design(context):
|
||||
# Mock new design env
|
||||
prd = "我们需要一个音乐播放器,它应该有播放、暂停、上一曲、下一曲等功能。"
|
||||
context.kwargs.project_path = context.config.project_path
|
||||
context.kwargs.inc = False
|
||||
filename = "prd.txt"
|
||||
repo = ProjectRepo(context.kwargs.project_path)
|
||||
await repo.docs.prd.save(filename=filename, content=prd)
|
||||
kvs = {
|
||||
"project_path": str(context.kwargs.project_path),
|
||||
"changed_prd_filenames": [str(repo.docs.prd.workdir / filename)],
|
||||
}
|
||||
instruct_content = AIMessage.create_instruct_value(kvs=kvs, class_name="WritePRDOutput")
|
||||
|
||||
design_api = WriteDesign(context=context)
|
||||
|
||||
result = await design_api.run(Message(content=prd, instruct_content=None))
|
||||
logger.info(result)
|
||||
|
||||
assert result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_refined_design_api(context):
|
||||
await context.repo.docs.prd.save(filename="1.txt", content=str(REFINED_PRD_JSON))
|
||||
await context.repo.docs.system_design.save(filename="1.txt", content=DESIGN_SAMPLE)
|
||||
|
||||
design_api = WriteDesign(context=context, llm=LLM())
|
||||
|
||||
result = await design_api.run(Message(content="", instruct_content=None))
|
||||
design_api = WriteDesign(context=context)
|
||||
result = await design_api.run([Message(content=prd, instruct_content=instruct_content)])
|
||||
logger.info(result)
|
||||
|
||||
assert result
|
||||
assert isinstance(result, AIMessage)
|
||||
assert result.instruct_content
|
||||
assert repo.docs.system_design.changed_files
|
||||
|
||||
# Mock incremental design env
|
||||
context.kwargs.inc = True
|
||||
await repo.docs.prd.save(filename=filename, content=str(REFINED_PRD_JSON))
|
||||
await repo.docs.system_design.save(filename=filename, content=DESIGN_SAMPLE)
|
||||
|
||||
result = await design_api.run([Message(content="", instruct_content=instruct_content)])
|
||||
logger.info(result)
|
||||
assert result
|
||||
assert isinstance(result, AIMessage)
|
||||
assert result.instruct_content
|
||||
assert repo.docs.system_design.changed_files
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("user_requirement", "prd_filename", "legacy_design_filename"),
|
||||
[
|
||||
("我们需要一个音乐播放器,它应该有播放、暂停、上一曲、下一曲等功能。", None, None),
|
||||
("write 2048 game", str(METAGPT_ROOT / "tests/data/prd.json"), None),
|
||||
(
|
||||
"write 2048 game",
|
||||
str(METAGPT_ROOT / "tests/data/prd.json"),
|
||||
str(METAGPT_ROOT / "tests/data/system_design.json"),
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
async def test_design_api(context, user_requirement, prd_filename, legacy_design_filename):
|
||||
action = WriteDesign()
|
||||
result = await action.run(
|
||||
user_requirement=user_requirement, prd_filename=prd_filename, legacy_design_filename=legacy_design_filename
|
||||
)
|
||||
assert isinstance(result, AIMessage)
|
||||
assert result.content
|
||||
assert str(DEFAULT_WORKSPACE_ROOT) in result.content
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("user_requirement", "prd_filename", "legacy_design_filename"),
|
||||
[
|
||||
("我们需要一个音乐播放器,它应该有播放、暂停、上一曲、下一曲等功能。", None, None),
|
||||
("write 2048 game", str(METAGPT_ROOT / "tests/data/prd.json"), None),
|
||||
(
|
||||
"write 2048 game",
|
||||
str(METAGPT_ROOT / "tests/data/prd.json"),
|
||||
str(METAGPT_ROOT / "tests/data/system_design.json"),
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
async def test_design_api_dir(context, user_requirement, prd_filename, legacy_design_filename):
|
||||
action = WriteDesign()
|
||||
result = await action.run(
|
||||
user_requirement=user_requirement,
|
||||
prd_filename=prd_filename,
|
||||
legacy_design_filename=legacy_design_filename,
|
||||
output_pathname=str(Path(context.config.project_path) / "1.txt"),
|
||||
)
|
||||
assert isinstance(result, AIMessage)
|
||||
assert result.content
|
||||
assert str(context.config.project_path) in result.content
|
||||
assert result.instruct_content
|
||||
assert result.instruct_content.changed_system_design_filenames
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-s"])
|
||||
|
|
|
|||
|
|
@ -5,13 +5,15 @@
|
|||
@Author : alexanderwu
|
||||
@File : test_project_management.py
|
||||
"""
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from metagpt.actions.project_management import WriteTasks
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.const import METAGPT_ROOT
|
||||
from metagpt.logs import logger
|
||||
from metagpt.schema import Message
|
||||
from metagpt.schema import AIMessage, Message
|
||||
from metagpt.utils.project_repo import ProjectRepo
|
||||
from tests.data.incremental_dev_project.mock import (
|
||||
REFINED_DESIGN_JSON,
|
||||
REFINED_PRD_JSON,
|
||||
|
|
@ -22,29 +24,46 @@ from tests.metagpt.actions.mock_json import DESIGN, PRD
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_task(context):
|
||||
await context.repo.docs.prd.save("1.txt", content=str(PRD))
|
||||
await context.repo.docs.system_design.save("1.txt", content=str(DESIGN))
|
||||
logger.info(context.git_repo)
|
||||
# Mock write tasks env
|
||||
context.kwargs.project_path = context.config.project_path
|
||||
context.kwargs.inc = False
|
||||
repo = ProjectRepo(context.kwargs.project_path)
|
||||
filename = "1.txt"
|
||||
await repo.docs.prd.save(filename=filename, content=str(PRD))
|
||||
await repo.docs.system_design.save(filename=filename, content=str(DESIGN))
|
||||
kvs = {
|
||||
"project_path": context.kwargs.project_path,
|
||||
"changed_system_design_filenames": [str(repo.docs.system_design.workdir / filename)],
|
||||
}
|
||||
instruct_content = AIMessage.create_instruct_value(kvs=kvs, class_name="WriteDesignOutput")
|
||||
|
||||
action = WriteTasks(context=context)
|
||||
|
||||
result = await action.run(Message(content="", instruct_content=None))
|
||||
result = await action.run([Message(content="", instruct_content=instruct_content)])
|
||||
logger.info(result)
|
||||
|
||||
assert result
|
||||
assert result.instruct_content.changed_task_filenames
|
||||
|
||||
# Mock incremental env
|
||||
context.kwargs.inc = True
|
||||
await repo.docs.prd.save(filename=filename, content=str(REFINED_PRD_JSON))
|
||||
await repo.docs.system_design.save(filename=filename, content=str(REFINED_DESIGN_JSON))
|
||||
await repo.docs.task.save(filename=filename, content=TASK_SAMPLE)
|
||||
|
||||
result = await action.run([Message(content="", instruct_content=instruct_content)])
|
||||
logger.info(result)
|
||||
assert result
|
||||
assert result.instruct_content.changed_task_filenames
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_refined_task(context):
|
||||
await context.repo.docs.prd.save("2.txt", content=str(REFINED_PRD_JSON))
|
||||
await context.repo.docs.system_design.save("2.txt", content=str(REFINED_DESIGN_JSON))
|
||||
await context.repo.docs.task.save("2.txt", content=TASK_SAMPLE)
|
||||
|
||||
logger.info(context.git_repo)
|
||||
|
||||
action = WriteTasks(context=context, llm=LLM())
|
||||
|
||||
result = await action.run(Message(content="", instruct_content=None))
|
||||
logger.info(result)
|
||||
|
||||
async def test_task_api(context):
|
||||
action = WriteTasks()
|
||||
result = await action.run(design_filename=str(METAGPT_ROOT / "tests/data/system_design.json"))
|
||||
assert result
|
||||
assert result.content
|
||||
m = json.loads(result.content)
|
||||
assert m
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-s"])
|
||||
|
|
|
|||
|
|
@ -26,12 +26,7 @@ from tests.metagpt.actions.mock_markdown import TASKS_2, WRITE_CODE_PROMPT_SAMPL
|
|||
|
||||
def setup_inc_workdir(context, inc: bool = False):
|
||||
"""setup incremental workdir for testing"""
|
||||
context.src_workspace = context.git_repo.workdir / "src"
|
||||
if inc:
|
||||
context.config.inc = inc
|
||||
context.repo.old_workspace = context.repo.git_repo.workdir / "old"
|
||||
context.config.project_path = "old"
|
||||
|
||||
context.config.inc = inc
|
||||
return context
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,25 +6,26 @@
|
|||
@File : test_write_prd.py
|
||||
@Modified By: mashenquan, 2023-11-1. According to Chapter 2.2.1 and 2.2.2 of RFC 116, replace `handle` with `run`.
|
||||
"""
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from metagpt.actions import UserRequirement, WritePRD
|
||||
from metagpt.const import REQUIREMENT_FILENAME
|
||||
from metagpt.const import DEFAULT_WORKSPACE_ROOT, REQUIREMENT_FILENAME
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles.product_manager import ProductManager
|
||||
from metagpt.roles.role import RoleReactMode
|
||||
from metagpt.schema import Message
|
||||
from metagpt.schema import AIMessage, Message
|
||||
from metagpt.utils.common import any_to_str
|
||||
from tests.data.incremental_dev_project.mock import NEW_REQUIREMENT_SAMPLE, PRD_SAMPLE
|
||||
from tests.metagpt.actions.test_write_code import setup_inc_workdir
|
||||
from metagpt.utils.project_repo import ProjectRepo
|
||||
from tests.data.incremental_dev_project.mock import NEW_REQUIREMENT_SAMPLE
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_prd(new_filename, context):
|
||||
product_manager = ProductManager(context=context)
|
||||
requirements = "开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结"
|
||||
await context.repo.docs.save(filename=REQUIREMENT_FILENAME, content=requirements)
|
||||
product_manager.rc.react_mode = RoleReactMode.BY_ORDER
|
||||
prd = await product_manager.run(Message(content=requirements, cause_by=UserRequirement))
|
||||
assert prd.cause_by == any_to_str(WritePRD)
|
||||
|
|
@ -34,38 +35,39 @@ async def test_write_prd(new_filename, context):
|
|||
# Assert the prd is not None or empty
|
||||
assert prd is not None
|
||||
assert prd.content != ""
|
||||
assert product_manager.context.repo.docs.prd.changed_files
|
||||
repo = ProjectRepo(context.kwargs.project_path)
|
||||
assert repo.docs.prd.changed_files
|
||||
repo.git_repo.archive()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_prd_inc(new_filename, context, git_dir):
|
||||
context = setup_inc_workdir(context, inc=True)
|
||||
await context.repo.docs.prd.save("1.txt", PRD_SAMPLE)
|
||||
await context.repo.docs.save(filename=REQUIREMENT_FILENAME, content=NEW_REQUIREMENT_SAMPLE)
|
||||
# Mock incremental requirement
|
||||
context.config.inc = True
|
||||
context.config.project_path = context.kwargs.project_path
|
||||
repo = ProjectRepo(context.config.project_path)
|
||||
await repo.docs.save(filename=REQUIREMENT_FILENAME, content=NEW_REQUIREMENT_SAMPLE)
|
||||
|
||||
action = WritePRD(context=context)
|
||||
prd = await action.run(Message(content=NEW_REQUIREMENT_SAMPLE, instruct_content=None))
|
||||
prd = await action.run([Message(content=NEW_REQUIREMENT_SAMPLE, instruct_content=None)])
|
||||
logger.info(NEW_REQUIREMENT_SAMPLE)
|
||||
logger.info(prd)
|
||||
|
||||
# Assert the prd is not None or empty
|
||||
assert prd is not None
|
||||
assert prd.content != ""
|
||||
assert "Refined Requirements" in prd.content
|
||||
assert repo.git_repo.changed_files
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fix_debug(new_filename, context, git_dir):
|
||||
context.src_workspace = context.git_repo.workdir / context.git_repo.workdir.name
|
||||
# Mock legacy project
|
||||
context.kwargs.project_path = str(git_dir)
|
||||
repo = ProjectRepo(context.kwargs.project_path)
|
||||
repo.with_src_path(git_dir.name)
|
||||
await repo.srcs.save(filename="main.py", content='if __name__ == "__main__":\nmain()')
|
||||
requirements = "ValueError: undefined variable `st`."
|
||||
await repo.docs.save(filename=REQUIREMENT_FILENAME, content=requirements)
|
||||
|
||||
await context.repo.with_src_path(context.src_workspace).srcs.save(
|
||||
filename="main.py", content='if __name__ == "__main__":\nmain()'
|
||||
)
|
||||
requirements = "Please fix the bug in the code."
|
||||
await context.repo.docs.save(filename=REQUIREMENT_FILENAME, content=requirements)
|
||||
action = WritePRD(context=context)
|
||||
|
||||
prd = await action.run(Message(content=requirements, instruct_content=None))
|
||||
prd = await action.run([Message(content=requirements, instruct_content=None)])
|
||||
logger.info(prd)
|
||||
|
||||
# Assert the prd is not None or empty
|
||||
|
|
@ -73,5 +75,40 @@ async def test_fix_debug(new_filename, context, git_dir):
|
|||
assert prd.content != ""
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_prd_api(context):
|
||||
action = WritePRD()
|
||||
result = await action.run(user_requirement="write a snake game.")
|
||||
assert isinstance(result, AIMessage)
|
||||
assert result.content
|
||||
assert str(DEFAULT_WORKSPACE_ROOT) in result.content
|
||||
|
||||
result = await action.run(
|
||||
user_requirement="write a snake game.",
|
||||
output_pathname=str(Path(context.config.project_path) / f"{uuid.uuid4().hex}.json"),
|
||||
)
|
||||
assert isinstance(result, AIMessage)
|
||||
assert result.content
|
||||
assert result.instruct_content
|
||||
assert str(context.config.project_path) in result.content
|
||||
|
||||
legacy_prd_filename = result.instruct_content.changed_prd_filenames[-1]
|
||||
|
||||
result = await action.run(user_requirement="Add moving enemy.", legacy_prd_filename=legacy_prd_filename)
|
||||
assert isinstance(result, AIMessage)
|
||||
assert result.content
|
||||
assert str(DEFAULT_WORKSPACE_ROOT) in result.content
|
||||
|
||||
result = await action.run(
|
||||
user_requirement="Add moving enemy.",
|
||||
output_pathname=str(Path(context.config.project_path) / f"{uuid.uuid4().hex}.json"),
|
||||
legacy_prd_filename=legacy_prd_filename,
|
||||
)
|
||||
assert isinstance(result, AIMessage)
|
||||
assert result.content
|
||||
assert result.instruct_content
|
||||
assert str(context.config.project_path) in result.content
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-s"])
|
||||
|
|
|
|||
|
|
@ -392,5 +392,11 @@ async def test_parse_resources(context, content: str, key_descriptions):
|
|||
assert k in result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("name", "value"), [("c1", {"age": 10, "name": "Alice"}), ("", {"path": __file__})])
|
||||
def test_create_instruct_value(name, value):
|
||||
obj = Message.create_instruct_value(kvs=value, class_name=name)
|
||||
assert obj.model_dump() == value
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-s"])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue