diff --git a/metagpt/actions/design_api.py b/metagpt/actions/design_api.py index 51472b960..64a4a3c14 100644 --- a/metagpt/actions/design_api.py +++ b/metagpt/actions/design_api.py @@ -12,6 +12,7 @@ from metagpt.actions import Action from metagpt.const import WORKSPACE_ROOT from metagpt.utils.common import CodeParser from metagpt.schema import Message +from metagpt.logs import logger from metagpt.utils.mermaid import mermaid_to_file PROMPT_TEMPLATE = """ @@ -56,6 +57,7 @@ class WriteDesign(Action): prd_file = docs_path / 'prd.md' quadrant_chart = CodeParser.parse_code(block="Competitive Quadrant Chart", text=prd) mermaid_to_file(quadrant_chart, resources_path / 'competitive_analysis') + logger.info(f"Saving PRD to {prd_file}") prd_file.write_text(prd) def _save_system_design(self, docs_path, resources_path, system_design): @@ -64,6 +66,7 @@ class WriteDesign(Action): mermaid_to_file(data_api_design, resources_path / 'data_api_design') mermaid_to_file(seq_flow, resources_path / 'seq_flow') system_design_file = docs_path / 'system_design.md' + logger.info(f"Saving System Designs to {system_design_file}") system_design_file.write_text(system_design) def _save(self, context, system_design): diff --git a/metagpt/actions/write_code.py b/metagpt/actions/write_code.py index af688dacd..3f413d5fb 100644 --- a/metagpt/actions/write_code.py +++ b/metagpt/actions/write_code.py @@ -41,8 +41,8 @@ class WriteCode(Action): return any(i in filename for i in ["mp3", "wav"]) def _save(self, context, filename, code_rsp): - logger.info(filename) - logger.info(code_rsp) + # logger.info(filename) + # logger.info(code_rsp) if self._is_invalid(filename): return @@ -55,9 +55,13 @@ class WriteCode(Action): code_path.parent.mkdir(parents=True, exist_ok=True) code = CodeParser.parse_code(block="", text=code_rsp) code_path.write_text(code) + logger.info(f"Saving Code to {code_path}") async def run(self, **kwargs): prompt = PROMPT_TEMPLATE.format(**kwargs) + filename = kwargs['filename'] + context = kwargs['context'] + logger.info(f'Writing {filename}..') code_rsp = await self._aask(prompt) - self._save(kwargs['context'], kwargs['filename'], code_rsp) + self._save(context, filename, code_rsp) return code_rsp diff --git a/metagpt/actions/write_prd.py b/metagpt/actions/write_prd.py index e930d9110..4093ab018 100644 --- a/metagpt/actions/write_prd.py +++ b/metagpt/actions/write_prd.py @@ -68,8 +68,9 @@ class WritePRD(Action): sas = SearchAndSummarize() rsp = await sas.run(context=requirements, system_text=SEARCH_AND_SUMMARIZE_SYSTEM_EN_US) info = f"### Search Results\n{sas.result}\n\n### Search Summary\n{rsp}" - logger.info(sas.result) - logger.info(rsp) + if sas.result: + logger.info(sas.result) + logger.info(rsp) prompt = PROMPT_TEMPLATE.format(requirements=requirements, search_information=info) prd = await self._aask(prompt) diff --git a/metagpt/provider/base_gpt_api.py b/metagpt/provider/base_gpt_api.py index 20cea8982..4046c08f0 100644 --- a/metagpt/provider/base_gpt_api.py +++ b/metagpt/provider/base_gpt_api.py @@ -41,10 +41,10 @@ class BaseGPTAPI(BaseChatbot): message = self._system_msgs(system_msgs) + [self._user_msg(msg)] else: message = [self._default_system_msg(), self._user_msg(msg)] - rsp = await self.acompletion(message) + rsp = await self.acompletion_text(message, stream=True) logger.debug(message) # logger.debug(rsp) - return self.get_choice_text(rsp) + return rsp def _extract_assistant_rsp(self, context): return "\n".join([i["content"] for i in context if i["role"] == "assistant"]) @@ -58,14 +58,14 @@ class BaseGPTAPI(BaseChatbot): rsp_text = self.get_choice_text(rsp) context.append(self._assistant_msg(rsp_text)) return self._extract_assistant_rsp(context) + async def aask_batch(self, msgs: list) -> str: """Sequential questioning""" context = [] for msg in msgs: umsg = self._user_msg(msg) context.append(umsg) - rsp = await self.acompletion(context) - rsp_text = self.get_choice_text(rsp) + rsp_text = await self.acompletion_text(context) context.append(self._assistant_msg(rsp_text)) return self._extract_assistant_rsp(context) @@ -100,6 +100,10 @@ class BaseGPTAPI(BaseChatbot): ] """ + @abstractmethod + async def acompletion_text(self, messages: list[dict], stream=False) -> str: + """Asynchronous version of completion. Return str. Support stream-print""" + def get_choice_text(self, rsp: dict) -> str: """Required to provide the first text of choice""" return rsp.get("choices")[0]["message"]["content"] diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index a19d0cac8..c8a6c4489 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -166,6 +166,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): collected_messages.append(chunk_message) # save the message if "content" in chunk_message: print(chunk_message["content"], end="") + print() full_reply_content = ''.join([m.get('content', '') for m in collected_messages]) return full_reply_content @@ -206,6 +207,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): return await self._achat_completion(messages) async def acompletion_text(self, messages: list[dict], stream=False) -> str: + """when streaming, print each token in place.""" if stream: return await self._achat_completion_stream(messages) rsp = await self._achat_completion(messages) diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index 42e6cfb33..7777ba79c 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -150,8 +150,9 @@ class Role: # prompt += ROLE_TEMPLATE.format(name=self.profile, state=self.states[self.state], result=response, # history=self.history) + logger.info(f"{self._setting}: ready to {self._rc.todo}") response = await self._rc.todo.run(self._rc.important_memory) - logger.info(response) + # logger.info(response) msg = Message(content=response, role=self.profile, cause_by=type(self._rc.todo)) self._rc.memory.add(msg) # logger.debug(f"{response}") diff --git a/metagpt/utils/mermaid.py b/metagpt/utils/mermaid.py index 75978673e..35aceb2b9 100644 --- a/metagpt/utils/mermaid.py +++ b/metagpt/utils/mermaid.py @@ -24,8 +24,6 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height """ # Write the Mermaid code to a temporary file tmp = Path(f'{output_file_without_suffix}.mmd') - logger.info(tmp) - logger.info(str(tmp)) tmp.write_text(mermaid_code, encoding='utf-8') if check_cmd_exists('mmdc') != 0: @@ -35,6 +33,7 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height for suffix in ['pdf', 'svg', 'png']: output_file = f'{output_file_without_suffix}.{suffix}' # Call the `mmdc` command to convert the Mermaid code to a PNG + logger.info(f"Generating {output_file}..") subprocess.run(['mmdc', '-i', str(tmp), '-o', output_file, '-w', str(width), '-H', str(height)]) return 0 diff --git a/startup.py b/startup.py index 7463444ec..fca731541 100644 --- a/startup.py +++ b/startup.py @@ -15,14 +15,14 @@ async def startup(idea: str, investment: float = 3.0, n_round: int = 5): await company.run(n_round=n_round) -def main(idea: str, investment: float = 3.0): +def main(idea: str, investment: float = 3.0, n_round: int = 5): """ We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities. :param idea: Your innovative idea, such as "Creating a snake game." :param investment: As an investor, you have the opportunity to contribute a certain dollar amount to this AI company. :return: """ - asyncio.run(startup(idea, investment)) + asyncio.run(startup(idea, investment, n_round)) if __name__ == '__main__':