Merge branch 'role_zero_impr' into 'mgx_ops'

save doc in folder by default, change WriteTasks.run, add run examples

See merge request pub/MetaGPT!153
This commit is contained in:
林义章 2024-06-11 08:01:31 +00:00
commit 214688dc77
10 changed files with 159 additions and 123 deletions

View file

@ -11,7 +11,6 @@
@Modified By: mashenquan, 2024/5/31. Implement Chapter 3 of RFC 236.
"""
import json
import uuid
from pathlib import Path
from typing import List, Optional, Union
@ -87,84 +86,48 @@ class WriteDesign(Action):
output_pathname (str, optional): The output path name of file that the system design should be saved to.
Returns:
AIMessage: An AIMessage object containing the system design.
str: The file path of the generated system design.
Example:
# Write a new system design.
>>> user_requirement = "Your user requirements"
>>> extra_info = "Your extra information"
>>> action = WriteDesign()
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info)
>>> print(result)
System Design filename: "/path/to/design/filename"
# Modify an exists system design.
>>> user_requirement = "Your user requirements"
>>> extra_info = "Your extra information"
>>> legacy_design_filename = "/path/to/exists/design/filename"
>>> action = WriteDesign()
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, legacy_design_filename=legacy_design_filename)
>>> print(result)
System Design filename: "/path/to/design/filename"
# Write a new system design with the given PRD(Product Requirement Document).
>>> user_requirement = "Your user requirements"
>>> extra_info = "Your extra information"
>>> prd_filename = "/path/to/prd/filename"
>>> action = WriteDesign()
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, prd_filename=prd_filename)
>>> print(result)
System Design filename: "/path/to/design/filename"
# Modify an exists system design with the given PRD(Product Requirement Document).
>>> user_requirement = "Your user requirements"
>>> extra_info = "Your extra information"
>>> prd_filename = "/path/to/prd/filename"
>>> legacy_design_filename = "/path/to/exists/design/filename"
>>> action = WriteDesign()
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, legacy_design_filename=legacy_design_filename, prd_filename=prd_filename)
>>> print(result)
TSystem Design filename: "/path/to/design/filename"
# Write a new system design and save to the path name.
>>> user_requirement = "Your user requirements"
>>> user_requirement = "Write system design for a snake game"
>>> extra_info = "Your extra information"
>>> output_pathname = "/path/to/design/filename"
>>> output_pathname = "snake_game/docs/system_design.json"
>>> action = WriteDesign()
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, output_pathname=output_pathname)
>>> print(result)
System Design filename: "/path/to/design/filename"
System Design filename: "/absolute/path/to/snake_game/docs/system_design.json"
# Modify an exists system design and save to the path name.
>>> user_requirement = "Your user requirements"
# Rewrite an existing system design and save to the path name.
>>> user_requirement = "Write system design for a snake game, include new features such as a web UI"
>>> extra_info = "Your extra information"
>>> legacy_design_filename = "/path/to/exists/design/filename"
>>> output_pathname = "/path/to/design/filename"
>>> legacy_design_filename = "/absolute/path/to/snake_game/docs/system_design.json"
>>> output_pathname = "/absolute/path/to/snake_game/docs/system_design_new.json"
>>> action = WriteDesign()
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, legacy_design_filename=legacy_design_filename, output_pathname=output_pathname)
>>> print(result)
System Design filename: "/path/to/design/filename"
System Design filename: "/absolute/path/to/snake_game/docs/system_design_new.json"
# Write a new system design with the given PRD(Product Requirement Document) and save to the path name.
>>> user_requirement = "Your user requirements"
>>> user_requirement = "Write system design for a snake game based on the PRD at /absolute/path/to/snake_game/docs/prd.json"
>>> extra_info = "Your extra information"
>>> prd_filename = "/path/to/prd/filename"
>>> output_pathname = "/path/to/design/filename"
>>> prd_filename = "/absolute/path/to/snake_game/docs/prd.json"
>>> output_pathname = "/absolute/path/to/snake_game/docs/sytem_design.json"
>>> action = WriteDesign()
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, prd_filename=prd_filename, output_pathname=output_pathname)
>>> print(result)
System Design filename: "/path/to/design/filename"
System Design filename: "/absolute/path/to/snake_game/docs/sytem_design.json"
# Modify an exists system design with the given PRD(Product Requirement Document) and save to the path name.
>>> user_requirement = "Your user requirements"
# Rewrite an existing system design with the given PRD(Product Requirement Document) and save to the path name.
>>> user_requirement = "Write system design for a snake game, include new features such as a web UI"
>>> extra_info = "Your extra information"
>>> prd_filename = "/path/to/prd/filename"
>>> legacy_design_filename = "/path/to/exists/design/filename"
>>> output_pathname = "/path/to/design/filename"
>>> prd_filename = "/absolute/path/to/snake_game/docs/prd.json"
>>> legacy_design_filename = "/absolute/path/to/snake_game/docs/system_design.json"
>>> output_pathname = "/absolute/path/to/snake_game/docs/system_design_new.json"
>>> action = WriteDesign()
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, legacy_design_filename=legacy_design_filename, prd_filename=prd_filename, output_pathname=output_pathname)
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, prd_filename=prd_filename, legacy_design_filename=legacy_design_filename, output_pathname=output_pathname)
>>> print(result)
System Design filename: "/path/to/design/filename"
System Design filename: "/absolute/path/to/snake_game/docs/system_design_new.json"
"""
if not with_messages:
return await self._execute_api(
@ -301,9 +264,10 @@ class WriteDesign(Action):
)
if not output_pathname:
output_path = DEFAULT_WORKSPACE_ROOT
output_path.mkdir(parents=True, exist_ok=True)
output_pathname = Path(output_path) / f"{uuid.uuid4().hex}.json"
output_pathname = Path(output_pathname) / "docs" / "sytem_design.json"
output_pathname.mkdir(parents=True, exist_ok=True)
elif not Path(output_pathname).is_absolute():
output_pathname = DEFAULT_WORKSPACE_ROOT / output_pathname
output_pathname = Path(output_pathname)
await awrite(filename=output_pathname, data=design.content)
output_filename = output_pathname.parent / f"{output_pathname.stem}-class-diagram"

View file

@ -19,11 +19,16 @@ from pydantic import BaseModel, Field
from metagpt.actions.action import Action
from metagpt.actions.project_management_an import PM_NODE, REFINED_PM_NODE
from metagpt.const import PACKAGE_REQUIREMENTS_FILENAME
from metagpt.const import DEFAULT_WORKSPACE_ROOT, PACKAGE_REQUIREMENTS_FILENAME
from metagpt.logs import logger
from metagpt.schema import AIMessage, Document, Documents, Message
from metagpt.tools.tool_registry import register_tool
from metagpt.utils.common import aread, to_markdown_code_block
from metagpt.utils.common import (
aread,
awrite,
save_json_to_markdown,
to_markdown_code_block,
)
from metagpt.utils.project_repo import ProjectRepo
from metagpt.utils.report import DocsReporter
@ -44,7 +49,13 @@ class WriteTasks(Action):
input_args: Optional[BaseModel] = Field(default=None, exclude=True)
async def run(
self, with_messages: List[Message] = None, *, user_requirement: str = "", design_filename: str = "", **kwargs
self,
with_messages: List[Message] = None,
*,
user_requirement: str = "",
design_filename: str = "",
output_pathname: str = "",
**kwargs,
) -> Union[AIMessage, str]:
"""
Write a project schedule given a project system design file.
@ -52,29 +63,33 @@ class WriteTasks(Action):
Args:
user_requirement (str, optional): A string specifying the user's requirements. Defaults to an empty string.
design_filename (str): The filename of the project system design file. Defaults to an empty string.
output_pathname (str, optional): The output path name of file that the project schedule should be saved to.
**kwargs: Additional keyword arguments.
Returns:
AIMessage: The generated project schedule.
str: Path to the generated project schedule.
Example:
# Write a new project schedule.
>>> design_filename = "/path/to/design/filename"
# Write a project schedule with a given system design.
>>> design_filename = "/absolute/path/to/snake_game/docs/system_design.json"
>>> output_pathname = "/absolute/path/to/snake_game/docs/project_schedule.json"
>>> action = WriteTasks()
>>> result = await action.run(design_filename=design_filename)
>>> result = await action.run(design_filename=design_filename, output_pathname=output_pathname)
>>> print(result)
The project schedule is balabala...
The project schedule is at /absolute/path/to/snake_game/docs/project_schedule.json
# Write a new project schedule with the user requirement.
>>> design_filename = "/path/to/design/filename"
>>> user_requirement = "Your user requirements"
# Write a project schedule with a user requirement.
>>> user_requirement = "Write project schedule for a snake game following these requirements: ..."
>>> output_pathname = "/absolute/path/to/snake_game/docs/project_schedule.json"
>>> action = WriteTasks()
>>> result = await action.run(design_filename=design_filename, user_requirement=user_requirement)
>>> result = await action.run(user_requirement=user_requirement, output_pathname=output_pathname)
>>> print(result)
The project schedule is balabala...
The project schedule is at /absolute/path/to/snake_game/docs/project_schedule.json
"""
if not with_messages:
return await self._execute_api(user_requirement=user_requirement, design_filename=design_filename)
return await self._execute_api(
user_requirement=user_requirement, design_filename=design_filename, output_pathname=output_pathname
)
self.input_args = with_messages[-1].instruct_content
self.repo = ProjectRepo(self.input_args.project_path)
@ -158,10 +173,23 @@ class WriteTasks(Action):
packages.add(pkg)
await self.repo.save(filename=PACKAGE_REQUIREMENTS_FILENAME, content="\n".join(packages))
async def _execute_api(self, user_requirement: str = "", design_filename: str = "") -> str:
async def _execute_api(
self, user_requirement: str = "", design_filename: str = "", output_pathname: str = ""
) -> str:
context = to_markdown_code_block(user_requirement)
if not design_filename:
if design_filename:
content = await aread(filename=design_filename)
context += to_markdown_code_block(content)
node = await self._run_new_tasks(context)
return node.instruct_content.model_dump_json()
file_content = node.instruct_content.model_dump_json()
if not output_pathname:
output_pathname = Path(output_pathname) / "docs" / "project_schedule.json"
output_pathname.mkdir(parents=True, exist_ok=True)
elif not Path(output_pathname).is_absolute():
output_pathname = DEFAULT_WORKSPACE_ROOT / output_pathname
output_pathname = Path(output_pathname)
await awrite(filename=output_pathname, data=file_content)
await save_json_to_markdown(content=file_content, output_filename=output_pathname.with_suffix(".md"))
return f'Project Schedule filename: "{str(output_pathname)}"'

View file

@ -15,7 +15,6 @@
from __future__ import annotations
import json
import uuid
from pathlib import Path
from typing import List, Optional, Union
@ -105,45 +104,27 @@ class WritePRD(Action):
**kwargs: Additional keyword arguments.
Returns:
str: The resulting message after generating the Product Requirement Document.
str: The file path of the generated Product Requirement Document.
Example:
# Write a new PRD(Product Requirement Document)
>>> user_requirement = "YOUR REQUIREMENTS"
>>> extra_info = "YOUR EXTRA INFO"
# Write a new PRD (Product Requirement Document)
>>> user_requirement = "Write PRD for a snake game"
>>> output_pathname = "snake_game/docs/prd.json"
>>> extra_info = "YOUR EXTRA INFO, if any"
>>> write_prd = WritePRD()
>>> result = await write_prd.run(user_requirement=user_requirement, extra_info=extra_info)
>>> result = await write_prd.run(user_requirement=user_requirement, output_pathname=output_pathname, extra_info=extra_info)
>>> print(result)
PRD filename: "/path/to/prd/directory/213434ad.json"
PRD filename: "/absolute/path/to/snake_game/docs/prd.json"
# Modify a exists PRD(Product Requirement Document)
>>> user_requirement = "YOUR REQUIREMENTS"
>>> extra_info = "YOUR EXTRA INFO"
>>> legacy_prd_filename = "/path/to/exists/prd_filename"
# Rewrite an existing PRD (Product Requirement Document) and save to a new path.
>>> user_requirement = "Write PRD for a snake game, include new features such as a web UI"
>>> legacy_prd_filename = "/absolute/path/to/snake_game/docs/prd.json"
>>> output_pathname = "/absolute/path/to/snake_game/docs/prd_new.json"
>>> extra_info = "YOUR EXTRA INFO, if any"
>>> write_prd = WritePRD()
>>> result = await write_prd.run(user_requirement=user_requirement, extra_info=extra_info, legacy_prd_filename=legacy_prd_filename)
>>> result = await write_prd.run(user_requirement=user_requirement, legacy_prd_filename=legacy_prd_filename, extra_info=extra_info)
>>> print(result)
PRD filename: "/path/to/prd/directory/213434ad.json"
# Write and save a new PRD(Product Requirement Document) to the path name.
>>> user_requirement = "YOUR REQUIREMENTS"
>>> extra_info = "YOUR EXTRA INFO"
>>> output_pathname = "/path/to/prd/directory/213434ad.json"
>>> write_prd = WritePRD()
>>> result = await write_prd.run(user_requirement=user_requirement, extra_info=extra_info, output_pathname=output_pathname)
>>> print(result)
PRD filename: "/path/to/prd/directory/213434ad.json"
# Modify a exists PRD(Product Requirement Document) and save to the path name.
>>> user_requirement = "YOUR REQUIREMENTS"
>>> extra_info = "YOUR EXTRA INFO"
>>> legacy_prd_filename = "/path/to/exists/prd_filename"
>>> output_pathname = "/path/to/prd/directory/213434ad.json"
>>> write_prd = WritePRD()
>>> result = await write_prd.run(user_requirement=user_requirement, extra_info=extra_info, legacy_prd_filename=legacy_prd_filename, output_pathname=output_pathname)
>>> print(result)
PRD filename: "/path/to/prd/directory/213434ad.json"
PRD filename: "/absolute/path/to/snake_game/docs/prd_new.json"
"""
if not with_messages:
return await self._execute_api(
@ -329,9 +310,10 @@ class WritePRD(Action):
new_prd = await self._merge(req=req, related_doc=old_prd)
if not output_pathname:
output_path = DEFAULT_WORKSPACE_ROOT
output_path.mkdir(parents=True, exist_ok=True)
output_pathname = Path(output_path) / f"{uuid.uuid4().hex}.json"
output_pathname = DEFAULT_WORKSPACE_ROOT / "docs" / "prd.json"
output_pathname.mkdir(parents=True, exist_ok=True)
elif not Path(output_pathname).is_absolute():
output_pathname = DEFAULT_WORKSPACE_ROOT / output_pathname
output_pathname = Path(output_pathname)
await awrite(filename=output_pathname, data=new_prd.content)
competitive_analysis_filename = output_pathname.parent / f"{output_pathname.stem}-competitive-analysis"

View file

@ -1,6 +1,9 @@
from metagpt.prompts.di.role_zero import ROLE_INSTRUCTION
ENGINEER2_INSTRUCTION = (
ROLE_INSTRUCTION
+ "4. Each time you write a code in your response, write with the Editor directly without preparing a repetitive code block beforehand."
)
EXTRA_INSTRUCTION = """
4. Each time you write a code in your response, write with the Editor directly without preparing a repetitive code block beforehand.
5. Take on ONE task and write ONE code file in each response. DON'T attempt all tasks in one response.
6. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path.
7. When provided system design or project schedule, read them first, then adhere to them in your implementation.
"""
ENGINEER2_INSTRUCTION = ROLE_INSTRUCTION + EXTRA_INSTRUCTION.strip()

View file

@ -11,13 +11,14 @@ Your team member:
You should NOT assign consecutive tasks to the same team member, instead, assign an aggregated task (or the complete requirement) and let the team member to decompose it.
When creating a new plan involving multiple members, create all tasks at once.
If plan is created, you should track the progress based on team member feedback message, and update plan accordingly, such as Plan.finish_current_task, Plan.reset_task, Plan.replace_task, etc.
You should use TeamLeader.publish_team_message to team members, asking them to start their task.
You should use TeamLeader.publish_team_message to team members, asking them to start their task. DONT omit any necessary info such as path, link, environment, programming language, framework, requirement, constraint from original content to team members because you are their sole info source.
Pay close attention to new user message, review the conversation history, use RoleZero.reply_to_human to respond to the user directly, DON'T ask your team members.
Note:
1. If the requirement is a pure DATA-RELATED requirement, such as bug fixes, issue reporting, environment setup, terminal operations, pip install, web browsing, web scraping, web searching, web imitation, data science, data analysis, machine learning, deep learning, text-to-image etc. DON'T decompose it, assign a single task with the original user requirement as instruction directly to Data Analyst.
2. If the requirement is developing a software, game, app, or website, excluding the above data-related tasks, you should decompose the requirement into multiple tasks and assign them to different team members based on their expertise, usually the sequence of Product Manager -> Architect -> Project Manager -> Engineer -> (optional: QaEngine if present) -> (optional: DataAnalyst if user requests deployment), each assigned ONE task. When publishing message to Product Manager, you should directly copy the full original user requirement.
3. If the requirement contains both DATA-RELATED part mentioned in 1 and software development part mentioned in 2, you should decompose the software development part and assign them to different team members based on their expertise, and assign the DATA-RELATED part to Data Analyst David directly.
4. It it helpful for Engineer to have both the system design and the project schedule for writing the code, so include paths of both files (if available) when publishing message to Engineer.
"""
FINISH_CURRENT_TASK_CMD = """

View file

@ -54,7 +54,6 @@ class DataAnalyst(DataInterpreter):
if self.tools and not self.tool_recommender:
self.tool_recommender = BM25ToolRecommender(tools=self.tools)
self.set_actions([WriteAnalysisCode])
self._set_state(0)
# HACK: Init Planner, control it through dynamic thinking; Consider formalizing as a react mode
self.planner = Planner(goal="", working_memory=self.rc.working_memory, auto_run=True)
@ -112,11 +111,15 @@ class DataAnalyst(DataInterpreter):
return Message(content="Task completed", role="assistant", sent_from=self._setting, cause_by=WriteAnalysisCode)
async def _react(self) -> Message:
# NOTE: Diff 1: Each time landing here means observing news, set todo to allow news processing in _think
self._set_state(0)
actions_taken = 0
rsp = Message(content="No actions taken yet", cause_by=Action) # will be overwritten after Role _act
while actions_taken < self.rc.max_react_loop:
# NOTE: difference here, keep observing within react
# NOTE: Diff 2: Keep observing within _react, news will go into memory, allowing adapting to new info
await self._observe()
# think
has_todo = await self._think()
if not has_todo:

View file

@ -195,7 +195,7 @@ class RoleZero(Role):
outputs.append(output)
except Exception as e:
tb = traceback.format_exc()
logger.exception(e + tb)
logger.exception(str(e) + tb)
outputs.append(output + f": {tb}")
break # Stop executing if any command fails
else:

View file

@ -2,6 +2,7 @@ import asyncio
import os
from metagpt.roles.architect import Architect
from metagpt.schema import Message
DESIGN_DOC_SNAKE = """
{
@ -30,9 +31,9 @@ async def main(requirement):
with open("temp_design.json", "w") as f:
f.write(DESIGN_DOC_SNAKE)
architect = Architect()
await architect.run(requirement)
await architect.run(Message(content=requirement, send_to="Bob"))
os.remove("temp_design.json")
if __name__ == "__main__":
asyncio.run(main(WRITE_SNAKE))
asyncio.run(main(REWRITE_SNAKE))

View file

@ -0,0 +1,18 @@
import asyncio
from metagpt.roles.product_manager import ProductManager
WRITE_2048 = """Write a PRD for a cli 2048 game"""
REWRITE_2048 = """Rewrite the prd at /Users/gary/Files/temp/workspace/2048_game/docs/prd.json, add a web UI"""
CASUAL_CHAT = """What's your name?"""
async def main(requirement):
product_manager = ProductManager()
await product_manager.run(requirement)
if __name__ == "__main__":
asyncio.run(main(WRITE_2048))

View file

@ -0,0 +1,36 @@
import asyncio
import os
from metagpt.roles.project_manager import ProjectManager
from metagpt.schema import Message
DESIGN_DOC_2048 = '{"Implementation approach":"We will use the Pygame library to implement the 2048 game logic and user interface. Pygame is a set of Python modules designed for writing video games, which will help us create a responsive and visually appealing UI. For the mobile responsiveness, we will ensure that the game scales appropriately on different screen sizes. We will also use the Pygame GUI library to create buttons for restarting the game and choosing difficulty levels.","File list":["main.py","game.py","ui.py"],"Data structures and interfaces":"\\nclassDiagram\\n class Game {\\n -grid: list[list[int]]\\n -score: int\\n +__init__()\\n +move(direction: str) bool\\n +merge() bool\\n +spawn_tile() None\\n +is_game_over() bool\\n +reset() None\\n }\\n class UI {\\n -game: Game\\n +__init__(game: Game)\\n +draw_grid() None\\n +draw_score() None\\n +draw_buttons() None\\n +handle_input() None\\n }\\n class Main {\\n -ui: UI\\n +main() None\\n }\\n Main --> UI\\n UI --> Game\\n","Program call flow":"\\nsequenceDiagram\\n participant M as Main\\n participant U as UI\\n participant G as Game\\n M->>U: __init__(game)\\n U->>G: __init__()\\n M->>U: draw_grid()\\n U->>G: move(direction)\\n G-->>U: return bool\\n U->>G: merge()\\n G-->>U: return bool\\n U->>G: spawn_tile()\\n G-->>U: return None\\n U->>G: is_game_over()\\n G-->>U: return bool\\n U->>G: reset()\\n G-->>U: return None\\n M->>U: draw_score()\\n M->>U: draw_buttons()\\n M->>U: handle_input()\\n","Anything UNCLEAR":"Clarification needed on the specific design elements for the UI to ensure it meets the \'beautiful\' requirement. Additionally, we need to confirm the exact difficulty levels and how they should affect the game mechanics."}'
DESIGN_DOC_SNAKE = """
{
"Implementation approach": "We will use the Pygame library to create the CLI-based snake game. Pygame is a set of Python modules designed for writing video games, which will help us handle graphics, sound, and input. The game will be structured into different modules to handle the main game loop, snake movement, food generation, collision detection, and user interface. We will ensure the game is engaging and responsive by optimizing the game loop and input handling. The score display and different speed levels will be implemented to enhance the user experience.",
"File list": [
"main.py",
"game.py",
"snake.py",
"food.py",
"ui.py"
],
"Data structures and interfaces": "\nclassDiagram\n class Main {\n +main() void\n }\n class Game {\n -Snake snake\n -Food food\n -int score\n -int speed\n +__init__(speed: int)\n +run() void\n +restart() void\n +update_score() void\n }\n class Snake {\n -list body\n -str direction\n +__init__()\n +move() void\n +change_direction(new_direction: str) void\n +check_collision() bool\n +grow() void\n }\n class Food {\n -tuple position\n +__init__()\n +generate_new_position() void\n }\n class UI {\n +display_score(score: int) void\n +display_game_over() void\n +display_game(snake: Snake, food: Food) void\n }\n Main --> Game\n Game --> Snake\n Game --> Food\n Game --> UI\n",
"Program call flow": "\nsequenceDiagram\n participant M as Main\n participant G as Game\n participant S as Snake\n participant F as Food\n participant U as UI\n M->>G: __init__(speed)\n M->>G: run()\n G->>S: __init__()\n G->>F: __init__()\n loop Game Loop\n G->>S: move()\n G->>S: check_collision()\n alt Collision Detected\n G->>G: restart()\n G->>U: display_game_over()\n else No Collision\n G->>F: generate_new_position()\n G->>S: grow()\n G->>G: update_score()\n G->>U: display_score(score)\n end\n G->>U: display_game(snake, food)\n end\n",
"Anything UNCLEAR": "Currently, all aspects of the project are clear."
}
"""
REQ = """Write a project schedule based on the design at temp_design.json"""
CASUAL_CHAT = """what's your name?"""
async def main(requirement):
with open("temp_design.json", "w") as f:
f.write(DESIGN_DOC_2048)
project_manager = ProjectManager()
await project_manager.run(Message(content=requirement, send_to="Eve"))
os.remove("temp_design.json")
if __name__ == "__main__":
asyncio.run(main(REQ))