diff --git a/metagpt/actions/debug_error.py b/metagpt/actions/debug_error.py index 6b9fc744b..114c414e4 100644 --- a/metagpt/actions/debug_error.py +++ b/metagpt/actions/debug_error.py @@ -42,5 +42,24 @@ class DebugError(Action): code = CodeParser.parse_code(block="", text=rsp) - return file_name, code + # async def run(self, code, error): + # prompt = f"Here is a piece of Python code:\n\n{code}\n\nThe following error occurred during execution:" \ + # f"\n\n{error}\n\nPlease try to fix the error in this code." + # fixed_code = await self._aask(prompt) + # return fixed_code + async def run(self, context): + if "PASS" in context: + return "", "the original code works fine, no need to debug" + + file_name = re.search("## File To Rewrite:\s*(.+\\.py)", context).group(1) + + logger.info(f"Debug and rewrite {file_name}") + + prompt = PROMPT_TEMPLATE.format(context=context) + + rsp = await self._aask(prompt) + + code = CodeParser.parse_code(block="", text=rsp) + + return file_name, code diff --git a/metagpt/actions/write_test.py b/metagpt/actions/write_test.py index 60fc92154..944d02192 100644 --- a/metagpt/actions/write_test.py +++ b/metagpt/actions/write_test.py @@ -5,60 +5,45 @@ @Author : alexanderwu @File : environment.py """ -import asyncio -from typing import Iterable +from metagpt.logs import logger +from metagpt.actions.action import Action +from metagpt.utils.common import CodeParser -from pydantic import BaseModel, Field +PROMPT_TEMPLATE = """ +NOTICE +1. Role: You are a QA engineer; the main goal is to design, develop, and execute PEP8 compliant, well-structured, maintainable test cases and scripts for Python 3.9. Your focus should be on ensuring the product quality of the entire project through systematic testing. +2. Requirement: Based on the context, develop a comprehensive test suite that adequately covers all relevant aspects of the code file under review. Your test suite will be part of the overall project QA, so please develop complete, robust, and reusable test cases. +3. Attention1: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the test case or script. +4. Attention2: If there are any settings in your tests, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. +5. Attention3: YOU MUST FOLLOW "Data structures and interface definitions". DO NOT CHANGE ANY DESIGN. Make sure your tests respect the existing design and ensure its validity. +6. Think before writing: What should be tested and validated in this document? What edge cases could exist? What might fail? +7. CAREFULLY CHECK THAT YOU DON'T MISS ANY NECESSARY TEST CASES/SCRIPTS IN THIS FILE. +Attention: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the test case or script and triple quotes. +----- +## Given the following code, please write appropriate test cases using Python's unittest framework to verify the correctness and robustness of this code: +```python +{code_to_test} +``` +Note that the code to test is at {source_file_path}, we will put your test code at {workspace}/tests/{test_file_name}, and run your test code from {workspace}, +you should correctly import the necessary classes based on these file locations! +## {test_file_name}: Write test code with triple quoto. Do your best to implement THIS ONLY ONE FILE. +""" -from metagpt.memory import Memory -from metagpt.roles import Role -from metagpt.schema import Message +class WriteTest(Action): + def __init__(self, name="WriteTest", context=None, llm=None): + super().__init__(name, context, llm) + async def write_code(self, prompt): + code_rsp = await self._aask(prompt) + code = CodeParser.parse_code(block="", text=code_rsp) + return code -class Environment(BaseModel): - """Environment that carries a set of roles. Roles can publish messages to the environment, which can be observed by other roles.""" - - roles: dict[str, Role] = Field(default_factory=dict) - memory: Memory = Field(default_factory=Memory) - history: str = Field(default='') - - class Config: - arbitrary_types_allowed = True - - def add_role(self, role: Role): - """Add a Role to the current environment.""" - role.set_env(self) - self.roles[role.profile] = role - - def add_roles(self, roles: Iterable[Role]): - """Add a batch of Roles to the current environment.""" - for role in roles: - self.add_role(role) - - def publish_message(self, message: Message): - """Publish a message to the current environment.""" - # self.message_queue.put(message) - self.memory.add(message) - self.history += f"\n{message}" - - async def run(self, k=1): - """Process the run of all Roles once.""" - # while not self.message_queue.empty(): - # message = self.message_queue.get() - # rsp = await self.manager.handle(message, self) - # self.message_queue.put(rsp) - for _ in range(k): - futures = [] - for role in self.roles.values(): - future = role.run() - futures.append(future) - - await asyncio.gather(*futures) - - def get_roles(self) -> dict[str, Role]: - """Get all Roles within the environment.""" - return self.roles - - def get_role(self, name: str) -> Role: - """Get a specified Role within the environment.""" - return self.roles.get(name, None) + async def run(self, code_to_test, test_file_name, source_file_path, workspace): + prompt = PROMPT_TEMPLATE.format( + code_to_test=code_to_test, + test_file_name=test_file_name, + source_file_path=source_file_path, + workspace=workspace + ) + code = await self.write_code(prompt) + return code diff --git a/metagpt/environment.py b/metagpt/environment.py index 60fc92154..24e6ada2f 100644 --- a/metagpt/environment.py +++ b/metagpt/environment.py @@ -16,7 +16,10 @@ from metagpt.schema import Message class Environment(BaseModel): - """Environment that carries a set of roles. Roles can publish messages to the environment, which can be observed by other roles.""" + """环境,承载一批角色,角色可以向环境发布消息,可以被其他角色观察到 + Environment, hosting a batch of roles, roles can publish messages to the environment, and can be observed by other roles + + """ roles: dict[str, Role] = Field(default_factory=dict) memory: Memory = Field(default_factory=Memory) @@ -26,23 +29,31 @@ class Environment(BaseModel): arbitrary_types_allowed = True def add_role(self, role: Role): - """Add a Role to the current environment.""" + """增加一个在当前环境的角色 + Add a role in the current environment + """ role.set_env(self) self.roles[role.profile] = role def add_roles(self, roles: Iterable[Role]): - """Add a batch of Roles to the current environment.""" + """增加一批在当前环境的角色 + Add a batch of characters in the current environment + """ for role in roles: self.add_role(role) def publish_message(self, message: Message): - """Publish a message to the current environment.""" - # self.message_queue.put(message) + """向当前环境发布信息 + Post information to the current environment + """ + # self.message_queue.put(message) self.memory.add(message) self.history += f"\n{message}" async def run(self, k=1): - """Process the run of all Roles once.""" + """处理一次所有信息的运行 + Process all Role runs at once + """ # while not self.message_queue.empty(): # message = self.message_queue.get() # rsp = await self.manager.handle(message, self) @@ -56,9 +67,13 @@ class Environment(BaseModel): await asyncio.gather(*futures) def get_roles(self) -> dict[str, Role]: - """Get all Roles within the environment.""" + """获得环境内的所有角色 + Process all Role runs at once + """ return self.roles def get_role(self, name: str) -> Role: - """Get a specified Role within the environment.""" + """获得环境内的指定角色 + get all the environment roles + """ return self.roles.get(name, None) diff --git a/metagpt/llm.py b/metagpt/llm.py index b8aefec61..e6f815950 100644 --- a/metagpt/llm.py +++ b/metagpt/llm.py @@ -13,5 +13,7 @@ DEFAULT_LLM = LLM() CLAUDE_LLM = Claude() async def ai_func(prompt): - """Use LLM for Q&A.""" + """使用LLM进行QA + QA with LLMs + """ return await DEFAULT_LLM.aask(prompt) diff --git a/metagpt/logs.py b/metagpt/logs.py index c6755919d..b2052e9b8 100644 --- a/metagpt/logs.py +++ b/metagpt/logs.py @@ -13,7 +13,9 @@ from loguru import logger as _logger from metagpt.const import PROJECT_ROOT def define_log_level(print_level="INFO", logfile_level="DEBUG"): - """Adjust log level to above the specified level.""" + """调整日志级别到level之上 + Adjust the log level to above level + """ _logger.remove() _logger.add(sys.stderr, level=print_level) _logger.add(PROJECT_ROOT / 'logs/log.txt', level=logfile_level) diff --git a/metagpt/manager.py b/metagpt/manager.py index 5e5b256c0..9d238c621 100644 --- a/metagpt/manager.py +++ b/metagpt/manager.py @@ -32,7 +32,8 @@ class Manager: async def handle(self, message: Message, environment): """ - Manager handles the message, currently simply passes the message to the next person. + 管理员处理信息,现在简单的将信息递交给下一个人 + The administrator processes the information, now simply passes the information on to the next person :param message: :param environment: :return: @@ -49,7 +50,8 @@ class Manager: # Ask the LLM to decide which role should handle the message # chosen_role_name = self.llm.ask(self.prompt_template.format(context)) - # FIXME: Currently deciding the direction using a simple dictionary, but in the future, a thought process should be involved. + # FIXME: 现在通过简单的字典决定流向,但之后还是应该有思考过程 + #The direction of flow is now determined by a simple dictionary, but there should still be a thought process afterwards next_role_profile = self.role_directions[message.role] # logger.debug(f"{next_role_profile}") for _, role in roles.items(): diff --git a/metagpt/schema.py b/metagpt/schema.py index 8db2c4ac1..27f5dd10c 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -46,21 +46,27 @@ class Message: @dataclass class UserMessage(Message): - """Facilitates support for OpenAI messages""" + """便于支持OpenAI的消息 + Facilitate support for OpenAI messages + """ def __init__(self, content: str): super().__init__(content, 'user') @dataclass class SystemMessage(Message): - """Facilitates support for OpenAI messages""" + """便于支持OpenAI的消息 + Facilitate support for OpenAI messages + """ def __init__(self, content: str): super().__init__(content, 'system') @dataclass class AIMessage(Message): - """Facilitates support for OpenAI messages""" + """便于支持OpenAI的消息 + Facilitate support for OpenAI messages + """ def __init__(self, content: str): super().__init__(content, 'assistant')