Spaces:
Runtime error
Runtime error
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
""" | |
@Time : 2023/5/11 14:43 | |
@Author : alexanderwu | |
@From : https://github.com/geekan/MetaGPT/blob/main/metagpt/roles/engineer.py | |
""" | |
import asyncio | |
import shutil | |
from collections import OrderedDict | |
from pathlib import Path | |
from autoagents.system.const import WORKSPACE_ROOT | |
from autoagents.system.logs import logger | |
from autoagents.system.schema import Message | |
from autoagents.system.utils.common import CodeParser | |
from autoagents.system.utils.special_tokens import MSG_SEP, FILENAME_CODE_SEP | |
from autoagents.roles import Role | |
from autoagents.actions import WriteCode, WriteCodeReview, WriteTasks, WriteDesign | |
async def gather_ordered_k(coros, k) -> list: | |
tasks = OrderedDict() | |
results = [None] * len(coros) | |
done_queue = asyncio.Queue() | |
for i, coro in enumerate(coros): | |
if len(tasks) >= k: | |
done, _ = await asyncio.wait(tasks.keys(), return_when=asyncio.FIRST_COMPLETED) | |
for task in done: | |
index = tasks.pop(task) | |
await done_queue.put((index, task.result())) | |
task = asyncio.create_task(coro) | |
tasks[task] = i | |
if tasks: | |
done, _ = await asyncio.wait(tasks.keys()) | |
for task in done: | |
index = tasks[task] | |
await done_queue.put((index, task.result())) | |
while not done_queue.empty(): | |
index, result = await done_queue.get() | |
results[index] = result | |
return results | |
class Engineer(Role): | |
def __init__(self, name="Alex", profile="Engineer", goal="Write elegant, readable, extensible, efficient code", | |
constraints="The code you write should conform to code standard like PEP8, be modular, easy to read and maintain", | |
n_borg=1, use_code_review=False, **kwargs): | |
super().__init__(name, profile, goal, constraints, **kwargs) | |
self._init_actions([WriteCode]) | |
self.use_code_review = use_code_review | |
if self.use_code_review: | |
self._init_actions([WriteCode, WriteCodeReview]) | |
self._watch([WriteTasks]) | |
self.todos = [] | |
self.n_borg = n_borg | |
def parse_tasks(self, task_msg: Message) -> list[str]: | |
if task_msg.instruct_content: | |
return task_msg.instruct_content.dict().get("Task list") | |
return CodeParser.parse_file_list(block="Task list", text=task_msg.content) | |
def parse_code(self, code_text: str) -> str: | |
return CodeParser.parse_code(block="", text=code_text) | |
def parse_workspace(cls, system_design_msg: Message) -> str: | |
if system_design_msg.instruct_content: | |
return system_design_msg.instruct_content.dict().get("Python package name").strip().strip("'").strip("\"") | |
return CodeParser.parse_str(block="Python package name", text=system_design_msg.content) | |
def get_workspace(self) -> Path: | |
msg = self._rc.memory.get_by_action(WriteDesign)[-1] | |
if not msg: | |
return WORKSPACE_ROOT / 'src' | |
workspace = self.parse_workspace(msg) | |
# Codes are written in workspace/{package_name}/{package_name} | |
return WORKSPACE_ROOT / workspace / workspace | |
def recreate_workspace(self): | |
workspace = self.get_workspace() | |
try: | |
shutil.rmtree(workspace) | |
except FileNotFoundError: | |
pass # 文件夹不存在,但我们不在意 | |
workspace.mkdir(parents=True, exist_ok=True) | |
def write_file(self, filename: str, code: str): | |
workspace = self.get_workspace() | |
filename = filename.replace('"', '').replace('\n', '') | |
file = workspace / filename | |
file.parent.mkdir(parents=True, exist_ok=True) | |
file.write_text(code) | |
return file | |
def recv(self, message: Message) -> None: | |
self._rc.memory.add(message) | |
if message in self._rc.important_memory: | |
self.todos = self.parse_tasks(message) | |
async def _act_mp(self) -> Message: | |
# self.recreate_workspace() | |
todo_coros = [] | |
for todo in self.todos: | |
todo_coro = WriteCode(llm=self._llm).run( | |
context=self._rc.memory.get_by_actions([WriteTasks, WriteDesign]), | |
filename=todo | |
) | |
todo_coros.append(todo_coro) | |
rsps = await gather_ordered_k(todo_coros, self.n_borg) | |
for todo, code_rsp in zip(self.todos, rsps): | |
_ = self.parse_code(code_rsp) | |
logger.info(todo) | |
logger.info(code_rsp) | |
# self.write_file(todo, code) | |
msg = Message(content=code_rsp, role=self.profile, cause_by=type(self._rc.todo)) | |
self._rc.memory.add(msg) | |
del self.todos[0] | |
logger.info(f'Done {self.get_workspace()} generating.') | |
msg = Message(content="all done.", role=self.profile, cause_by=type(self._rc.todo)) | |
return msg | |
async def _act_sp(self) -> Message: | |
code_msg_all = [] # gather all code info, will pass to qa_engineer for tests later | |
for todo in self.todos: | |
code = await WriteCode(llm=self._llm).run( | |
context=self._rc.history, | |
filename=todo | |
) | |
# logger.info(todo) | |
# logger.info(code_rsp) | |
# code = self.parse_code(code_rsp) | |
file_path = self.write_file(todo, code) | |
msg = Message(content=code, role=self.profile, cause_by=type(self._rc.todo)) | |
self._rc.memory.add(msg) | |
code_msg = todo + FILENAME_CODE_SEP + str(file_path) | |
code_msg_all.append(code_msg) | |
logger.info(f'Done {self.get_workspace()} generating.') | |
msg = Message( | |
content=MSG_SEP.join(code_msg_all), | |
role=self.profile, | |
cause_by=type(self._rc.todo), | |
send_to="ActionObserver" | |
) | |
return msg | |
async def _act_sp_precision(self) -> Message: | |
code_msg_all = [] # gather all code info, will pass to qa_engineer for tests later | |
for todo in self.todos: | |
""" | |
# 从历史信息中挑选必须的信息,以减少prompt长度(人工经验总结) | |
1. Architect全部 | |
2. ProjectManager全部 | |
3. 是否需要其他代码(暂时需要)? | |
TODO:目标是不需要。在任务拆分清楚后,根据设计思路,不需要其他代码也能够写清楚单个文件,如果不能则表示还需要在定义的更清晰,这个是代码能够写长的关键 | |
""" | |
context = [] | |
msg = self._rc.memory.get_by_actions([WriteDesign, WriteTasks, WriteCode]) | |
for m in msg: | |
context.append(m.content) | |
context_str = "\n".join(context) | |
# 编写code | |
code = await WriteCode(llm=self._llm).run( | |
context=context_str, | |
filename=todo | |
) | |
# code review | |
if self.use_code_review: | |
try: | |
rewrite_code = await WriteCodeReview(llm=self._llm).run( | |
context=context_str, | |
code=code, | |
filename=todo | |
) | |
code = rewrite_code | |
except Exception as e: | |
logger.error("code review failed!", e) | |
pass | |
file_path = self.write_file(todo, code) | |
msg = Message(content=code, role=self.profile, cause_by=WriteCode) | |
self._rc.memory.add(msg) | |
code_msg = todo + FILENAME_CODE_SEP + str(file_path) | |
code_msg_all.append(code_msg) | |
logger.info(f'Done {self.get_workspace()} generating.') | |
msg = Message( | |
content=MSG_SEP.join(code_msg_all), | |
role=self.profile, | |
cause_by=type(self._rc.todo), | |
send_to="ActionObserver" | |
) | |
return msg | |
async def _act(self) -> Message: | |
if self.use_code_review: | |
return await self._act_sp_precision() | |
return await self._act_sp() |