From b81c00a44dede104651f74c5ce4d7777bb504adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 11 Oct 2024 18:31:33 +0800 Subject: [PATCH] add Deployer tool --- metagpt/prompts/di/engineer2.py | 4 +-- metagpt/roles/di/engineer2.py | 6 +++- metagpt/roles/di/role_zero.py | 7 +++-- metagpt/strategy/experience_retriever.py | 39 +++++++++++++++++++++--- metagpt/tools/libs/deployer.py | 30 +++++++++++++++--- 5 files changed, 73 insertions(+), 13 deletions(-) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 96392e334..66ed47932 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -83,8 +83,8 @@ Note: 25. When writing Vue/React project: The Vue template is in the {vue_template_path}, the React template is in the {react_template_path}. 25.1. Create the project folder first. Use cmd " mkdir -p {{project_name}} " 25.2. Copy a Vue/React template to your project and view all files. This must be a single respond. Use cmd "cp -r {{template_folder}}/* {{workspace}}/{{project_name}}/ && cd {{workspace}}/{{project_name}} && pwd && tree -f". -25.3. Read the content of each file and use the write_new_code command to rewrite the code. Be sure you are in the {{project_name}}. Reorganize the file structure to match the project template if it differs from the system design. -25.4. After finish the project. use "npm install" and "npm run build" to build the project. +25.3. Read the content of each file and use the write_new_code command to rewrite the code. Be sure you are in the {{project_name}}. +25.4. After finish the project. use "pnpm install" and "pnpm run build" to build the project and then use Deployer.deploy_to_public to deploy the project to the public. 26. Engineer2.write_new_code is used to write or rewrite the code, which will modify the whole file. Editor.edit_file_by_replace is used to edit a small part of the file. """.format( vue_template_path=VUE_TEMPLATE_PATH.absolute(), diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 799361df6..2fb9d05ee 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -18,6 +18,7 @@ from metagpt.roles.di.role_zero import RoleZero from metagpt.schema import UserMessage from metagpt.strategy.experience_retriever import ENGINEER_EXAMPLE from metagpt.tools.libs.cr import CodeReview +from metagpt.tools.libs.deployer import Deployer from metagpt.tools.libs.git import git_create_pull from metagpt.tools.libs.image_getter import ImageGetter from metagpt.tools.libs.terminal import Terminal @@ -33,7 +34,7 @@ class Engineer2(RoleZero): goal: str = "Take on game, app, and web development." instruction: str = ENGINEER2_INSTRUCTION terminal: Terminal = Field(default_factory=Terminal, exclude=True) - + deployer: Deployer = Field(default_factory=Deployer) tools: list[str] = [ "Plan", "Editor", @@ -45,6 +46,7 @@ class Engineer2(RoleZero): "Engineer2", "CodeReview", "ImageGetter", + "Deployer", ] # SWE Agent parameter run_eval: bool = False @@ -86,6 +88,7 @@ class Engineer2(RoleZero): "Terminal.run_command": self._eval_terminal_run, "RoleZero.ask_human": self._end, "RoleZero.reply_to_human": self._end, + "Deployer.deploy_to_public": self.deployer.deploy_to_public, } ) else: @@ -98,6 +101,7 @@ class Engineer2(RoleZero): "CodeReview.review": cr.review, "CodeReview.fix": cr.fix, "Terminal.run_command": self.terminal.run_command, + "Deployer.deploy_to_public": self.deployer.deploy_to_public, } ) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index fda29add9..d4c77630c 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -76,7 +76,7 @@ class RoleZero(Role): tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools tool_recommender: Optional[ToolRecommender] = None tool_execution_map: Annotated[dict[str, Callable], Field(exclude=True)] = {} - special_tool_commands: list[str] = ["Plan.finish_current_task", "end", "Bash.run", "RoleZero.ask_human"] + special_tool_commands: list[str] = ["Plan.finish_current_task", "end", "Terminal.run_command", "RoleZero.ask_human"] # List of exclusive tool commands. # If multiple instances of these commands appear, only the first occurrence will be retained. exclusive_tool_commands: list[str] = [ @@ -543,7 +543,10 @@ class RoleZero(Role): return end_output return human_response # output from bash.run may be empty, add decorations to the output to ensure visibility. - elif cmd["command_name"] == "Bash.run": + elif cmd["command_name"] == "Terminal.run_command": + if "npm run dev" in cmd["args"]: + command_output = "command run failed! Pleae use Delopyer to deploy your project after build." + tool_obj = self.tool_execution_map[cmd["command_name"]] tool_output = await tool_obj(**cmd["args"]) if len(tool_output) <= 10: diff --git a/metagpt/strategy/experience_retriever.py b/metagpt/strategy/experience_retriever.py index da66f4796..d1772dc22 100644 --- a/metagpt/strategy/experience_retriever.py +++ b/metagpt/strategy/experience_retriever.py @@ -845,13 +845,21 @@ Explanation: This is a project that needs to be implemented using Vue.js. Theref ## example 2.2 User Requirement: Writing code. +The template is : +├── public +├── src +│ ├── App.jsx +│ ├── index.css +│ └── main.jsx +└── vite.config.js + Here's the plan: [Optional] Read the original file in the template if they exist. [Optional] Obtain images before coding. -1. **Task 1**: Rewrite `App.vue` - This file will contain the Vue structure necessary for the game's UI, the game logic and UI interactions. +1. **Task 1**: Rewrite `App.jsx` - This file will contain the Vue structure necessary for the game's UI, the game logic and UI interactions. 2. **Task 2**: Rewrite `style.css` - This file will define the CSS styles to make the game visually appealing and responsive. Default use tailwindcss. 3. **Task 3**: Rewrite `main.js` - This file is the main entry of Vue project, including the main Vue instance, global styles, and the router. -[Optional] Install the dependencies after finishing project. +If the project is a Vue or React Project, install the dependencies after finishing project. And then deploy the project to the public. ```json [ { @@ -894,8 +902,8 @@ Here's the plan: "command_name": "Plan.append_task", "args": { "task_id": "5", - "dependent_task_ids": ["2","3","4"], - "instruction": "Install the necessary dependencies and configure the project structure.", + "dependent_task_ids": [], + "instruction": "Install the necessary dependencies, configure the project structure and deploy it to the public", "assignee": "Alex" } } @@ -919,6 +927,29 @@ Explanation: Take on one task, such as writing or rewriting a file. Upon complet } ] ``` +## example 3.2 +Explanation: The project have been completed. And this project is a Vue/React Project,so i will deploy the project to the public. + +```json +[ + { + "command_name": "Terminal.run_command", + "args": { + "cmd": "pnpm install && pnpm run build" + } + } +] +## example 3.3 +Explanation: After install the project. I will deploy the project to the public. +```json +[ + { + "command_name": "Deployer.deploy_to_public, + "args": { + "dist_dir": "{{project_path}}/dist" + } + } +] ## example 4 I have received a GitHub issue URL. diff --git a/metagpt/tools/libs/deployer.py b/metagpt/tools/libs/deployer.py index c30ad0176..85f06695b 100644 --- a/metagpt/tools/libs/deployer.py +++ b/metagpt/tools/libs/deployer.py @@ -1,11 +1,33 @@ from metagpt.tools.tool_registry import register_tool -from metagpt.utils.report import ServerReporter # An un-implemented tool reserved for deploying a local service to public -@register_tool() +@register_tool( + include_functions=[ + "deploy_to_public", + ] +) class Deployer: """Deploy a local service to public. Used only for final deployment, you should NOT use it for development and testing.""" - def deploy_to_public(self, local_url: str): - ServerReporter().report(local_url, "local_url") + async def static_server(self, src_path: str) -> str: + """This function will be implemented in the remote service.""" + # app = aiohttp.web.Application() + # app.router.add_static('/', src_path, show_index=True) + # runner = aiohttp.web.AppRunner(app) + # await runner.setup() + # site = aiohttp.web.TCPSite(runner, "127.0.0.1", 0) + # await site.start() + # port = site._server.sockets[0].getsockname()[1] + return f"http://127.0.0.1:{8000}/index.html" + + async def deploy_to_public(self, dist_dir: str): + """ + Deploy a web project to public. + Args: + dist_dir (str): The dist directory of the web project after run build. + >>> + deployer = Deployer("2048_game/dist") + """ + url = await self.static_server(dist_dir) + return "The Project is deployed to: " + url + "\n Deployment successed!"