mgx_write_project_framework.py
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 """ 4 @Time : 2024/6/13 5 @Author : mashenquan 6 @File : write_project_framework.py 7 @Desc : The implementation of RFC243. https://deepwisdom.feishu.cn/wiki/QobGwPkImijoyukBUKHcrYetnBb 8 """ 9 import asyncio 10 import json 11 import uuid 12 from json import JSONDecodeError 13 from pathlib import Path 14 from typing import Dict, List 15 16 import typer 17 from pydantic import BaseModel 18 19 from metagpt.config2 import Config 20 from metagpt.const import DEFAULT_WORKSPACE_ROOT 21 from metagpt.context import Context 22 from metagpt.environment import Environment 23 from metagpt.environment.mgx.mgx_env import MGXEnv 24 from metagpt.logs import logger 25 from metagpt.roles import Architect 26 from metagpt.roles.di.team_leader import TeamLeader 27 from metagpt.schema import AIMessage, UserMessage 28 from metagpt.strategy.experience_retriever import TRDToolExpRetriever 29 from metagpt.utils.common import aread 30 31 app = typer.Typer(add_completion=False) 32 33 34 class EnvBuilder(BaseModel): 35 context: Context 36 user_requirements: List[str] 37 actors: Dict[str, str] 38 technical_constraint: str 39 output_dir: Path 40 41 def build(self) -> Environment: 42 env = MGXEnv(context=self.context) 43 team_leader = TeamLeader() 44 architect = Architect(experience_retriever=TRDToolExpRetriever()) 45 46 # Prepare context 47 use_case_actors = "".join([f"- {v}: {k}\n" for k, v in self.actors.items()]) 48 msg = """ 49 The content of "Actor, System, External System" provides an explanation of actors and systems that appear in UML Use Case diagram. 50 ## Actor, System, External System 51 {use_case_actors} 52 """ 53 architect.rc.memory.add(AIMessage(content=msg.format(use_case_actors=use_case_actors))) 54 55 # Prepare technical requirements 56 msg = """ 57 "Additional Technical Requirements" specifies the additional technical requirements that the generated software framework code must meet. 58 ## Additional Technical Requirements 59 {technical_requirements} 60 """ 61 architect.rc.memory.add(AIMessage(content=msg.format(technical_requirements=self.technical_constraint))) 62 63 env.add_roles([team_leader, architect]) 64 return env 65 66 67 async def develop( 68 context: Context, 69 user_requirement_filename: str, 70 actors_filename: str, 71 constraint_filename: str, 72 output_dir: str, 73 ): 74 output_dir = Path(output_dir) if output_dir else DEFAULT_WORKSPACE_ROOT / uuid.uuid4().hex 75 76 v = await aread(filename=user_requirement_filename) 77 try: 78 user_requirements = json.loads(v) 79 except JSONDecodeError: 80 user_requirements = [v] 81 v = await aread(filename=actors_filename) 82 actors = json.loads(v) 83 technical_constraint = await aread(filename=constraint_filename) 84 env_builder = EnvBuilder( 85 context=context, 86 user_requirements=user_requirements, 87 actors=actors, 88 technical_constraint=technical_constraint, 89 output_dir=output_dir, 90 ) 91 env = env_builder.build() 92 msg = """ 93 Given the user requirement of "User Requirements", write out the software framework. 94 ## User Requirements 95 {user_requirements} 96 """ 97 env.publish_message( 98 UserMessage(content=msg.format(user_requirements="\n".join(user_requirements)), send_to="Bob"), 99 user_defined_recipient="Bob", 100 ) 101 102 while not env.is_idle: 103 await env.run() 104 105 106 @app.command() 107 def startup( 108 user_requirement_filename: str = typer.Argument(..., help="The filename of the user requirements."), 109 actors_filename: str = typer.Argument(..., help="The filename of UML use case actors description."), 110 llm_config: str = typer.Option(default="", help="Low-cost LLM config"), 111 constraint_filename: str = typer.Option(default="", help="What technical dependency constraints are."), 112 output_dir: str = typer.Option(default="", help="Output directory."), 113 ): 114 if llm_config and Path(llm_config).exists(): 115 config = Config.from_yaml_file(Path(llm_config)) 116 else: 117 logger.info("GPT 4 turbo is recommended") 118 config = Config.default() 119 ctx = Context(config=config) 120 121 asyncio.run(develop(ctx, user_requirement_filename, actors_filename, constraint_filename, output_dir)) 122 123 124 if __name__ == "__main__": 125 app()