From e6c33f199c315518a6e46496dbc177275a97df78 Mon Sep 17 00:00:00 2001 From: AK Date: Sun, 6 Aug 2023 22:02:15 +0800 Subject: [PATCH 01/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2f95437d..72b8cc92c 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ ## Installation ### Traditional Installation ```bash -# Step 1: Ensure that NPM is installed on your system. Then install mermaid-js. +# Step 1: Ensure that NPM is installed on your system.(If you don't have npm in your computer, please go to the offical website to install Node.js https://nodejs.org/ and then you will have npm tool in your computer.) Then install mermaid-js. npm --version sudo npm install -g @mermaid-js/mermaid-cli From e10bbf78b05c1a22b90beb19d8f737088615812d Mon Sep 17 00:00:00 2001 From: AK Date: Sun, 6 Aug 2023 22:02:41 +0800 Subject: [PATCH 02/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 72b8cc92c..143bdf0f9 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ ## Installation ### Traditional Installation ```bash -# Step 1: Ensure that NPM is installed on your system.(If you don't have npm in your computer, please go to the offical website to install Node.js https://nodejs.org/ and then you will have npm tool in your computer.) Then install mermaid-js. +# Step 1: Ensure that NPM is installed on your system.(If you don't have npm in your computer, please go to the Node.js offical website to install Node.js https://nodejs.org/ and then you will have npm tool in your computer.) Then install mermaid-js. npm --version sudo npm install -g @mermaid-js/mermaid-cli From 102ad7d807492bd8e86fd3fe89cdfcce66ca887a Mon Sep 17 00:00:00 2001 From: AK Date: Sun, 6 Aug 2023 23:07:54 +0800 Subject: [PATCH 03/76] Update README.md (If you don't have npm in your computer, please go to the Node.js offical website to install Node.js https://nodejs.org/ and then you will have npm tool in your computer.) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 143bdf0f9..b6b1f862b 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ ## Installation ### Traditional Installation ```bash -# Step 1: Ensure that NPM is installed on your system.(If you don't have npm in your computer, please go to the Node.js offical website to install Node.js https://nodejs.org/ and then you will have npm tool in your computer.) Then install mermaid-js. +# Step 1: Ensure that NPM is installed on your system. Then install mermaid-js. (If you don't have npm in your computer, please go to the Node.js offical website to install Node.js https://nodejs.org/ and then you will have npm tool in your computer.) npm --version sudo npm install -g @mermaid-js/mermaid-cli From 949a5074ce6240fb34b1f2bf00771ab7375fc9c6 Mon Sep 17 00:00:00 2001 From: rainyrfeng Date: Thu, 31 Aug 2023 11:05:59 +0800 Subject: [PATCH 04/76] fix feature --- config/config.yaml | 3 ++- metagpt/config.py | 1 + metagpt/provider/openai_api.py | 11 +++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index 274cdf469..428f8dae4 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -16,11 +16,12 @@ RPM: 10 #Anthropic_API_KEY: "YOUR_API_KEY" #### if AZURE, check https://github.com/openai/openai-cookbook/blob/main/examples/azure/chat.ipynb - +#### You can use ENGINE or DEPLOYMENT mode #OPENAI_API_TYPE: "azure" #OPENAI_API_BASE: "YOUR_AZURE_ENDPOINT" #OPENAI_API_KEY: "YOUR_AZURE_API_KEY" #OPENAI_API_VERSION: "YOUR_AZURE_API_VERSION" +#OPENAI_API_ENGINE: "YOUR_OPENAI_API_ENGINE" #DEPLOYMENT_ID: "YOUR_DEPLOYMENT_ID" #### for Search diff --git a/metagpt/config.py b/metagpt/config.py index 2c1096877..16c4117cd 100644 --- a/metagpt/config.py +++ b/metagpt/config.py @@ -56,6 +56,7 @@ class Config(metaclass=Singleton): openai.api_base = self.openai_api_base self.openai_api_type = self._get("OPENAI_API_TYPE") self.openai_api_version = self._get("OPENAI_API_VERSION") + self.openai_api_engine = self._get('OPENAI_API_ENGINE') self.openai_api_rpm = self._get("RPM", 3) self.openai_api_model = self._get("OPENAI_API_MODEL", "gpt-4") self.max_tokens_rsp = self._get("MAX_TOKENS", 2048) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 79121c8de..7ef694a98 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -156,10 +156,12 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): # iterate through the stream of events async for chunk in response: collected_chunks.append(chunk) # save the event response - chunk_message = chunk["choices"][0]["delta"] # extract the message - collected_messages.append(chunk_message) # save the message - if "content" in chunk_message: - print(chunk_message["content"], end="") + choices = chunk["choices"] + if len(choices) > 0: + chunk_message = chunk["choices"][0].get("delta", {}) # extract the message + 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]) @@ -170,6 +172,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): def _cons_kwargs(self, messages: list[dict]) -> dict: if CONFIG.openai_api_type == "azure": kwargs = { + "engine": CONFIG.openai_api_engine, "deployment_id": CONFIG.deployment_id, "messages": messages, "max_tokens": self.get_max_tokens(messages), From 689aa935eb87861fb9c2d647e180ddd2ee00618c Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:19:37 +0800 Subject: [PATCH 05/76] Update startup.py set default params --- startup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/startup.py b/startup.py index 611317fd4..e6d5fc4e9 100644 --- a/startup.py +++ b/startup.py @@ -44,9 +44,9 @@ def main( idea: str, investment: float = 3.0, n_round: int = 5, - code_review: bool = False, + code_review: bool = True, run_tests: bool = False, - implement: bool = False + implement: bool = True ): """ We are a software startup comprised of AI. By investing in us, @@ -66,4 +66,4 @@ def main( if __name__ == '__main__': fire.Fire(main) - \ No newline at end of file + From 26fbbfc83c83f9f5c5515bcc3dd1782688de3d90 Mon Sep 17 00:00:00 2001 From: stellahsr Date: Mon, 4 Sep 2023 19:51:28 +0800 Subject: [PATCH 06/76] rm useless files --- Message | 0 None | 0 int | 0 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Message delete mode 100644 None delete mode 100644 int diff --git a/Message b/Message deleted file mode 100644 index e69de29bb..000000000 diff --git a/None b/None deleted file mode 100644 index e69de29bb..000000000 diff --git a/int b/int deleted file mode 100644 index e69de29bb..000000000 From a386c7e974cc35e9bfd80d9fe680a1c4c313d43b Mon Sep 17 00:00:00 2001 From: "hy.li" Date: Mon, 4 Sep 2023 23:30:27 +0800 Subject: [PATCH 07/76] playwright version mmdc --- README.md | 24 + config/config.yaml | 4 + metagpt/config.py | 1 + metagpt/utils/common.py | 2 +- metagpt/utils/index.html | 2212 +++++++++++++++++++++++++++ metagpt/utils/mermaid.py | 3 + metagpt/utils/mermaid_playwright.py | 199 +++ 7 files changed, 2444 insertions(+), 1 deletion(-) create mode 100644 metagpt/utils/index.html create mode 100644 metagpt/utils/mermaid_playwright.py diff --git a/README.md b/README.md index 84dafa46b..adc9d8cea 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,30 @@ # Step 3: Clone the repository to your local machine, and install it. - if `python setup.py install` fails with error `[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'`, try instead running `python setup.py install --user` +- To convert Mermaid charts to SVG, PNG, and PDF formats. In addition to the Node.js version of Mermaid-CLI, you now have the option to use Python version Playwright for this task. + +- **Install Playwright** + +```bash +pip install playwright +``` + +- **Install the Required Browsers** + +to support PDF conversion, had better install Chrominum. + +```bash +playwright install --with-deps chromium +``` + +- **modify `config.yaml`** + +uncomment MERMAID_ENGINE from config.yaml and change it to `playwright` + +```yaml +MERMAID_ENGINE: playwright +``` + ### Installation by Docker ```bash diff --git a/config/config.yaml b/config/config.yaml index 274cdf469..ec89a9932 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -75,3 +75,7 @@ SD_T2I_API: "/sdapi/v1/txt2img" ### for Research MODEL_FOR_RESEARCHER_SUMMARY: gpt-3.5-turbo MODEL_FOR_RESEARCHER_REPORT: gpt-3.5-turbo-16k + +### choose the engine for mermaid conversion, +# default is nodejs, you can change it to playwright +# MERMAID_ENGINE: nodejs \ No newline at end of file diff --git a/metagpt/config.py b/metagpt/config.py index 76c6563cb..b51c81862 100644 --- a/metagpt/config.py +++ b/metagpt/config.py @@ -82,6 +82,7 @@ class Config(metaclass=Singleton): self.calc_usage = self._get("CALC_USAGE", True) self.model_for_researcher_summary = self._get("MODEL_FOR_RESEARCHER_SUMMARY") self.model_for_researcher_report = self._get("MODEL_FOR_RESEARCHER_REPORT") + self.mermaid_engine = self._get("MERMAID_ENGINE", 'nodejs') def _init_with_config_files_and_env(self, configs: dict, yaml_file): """Load from config/key.yaml, config/config.yaml, and env in decreasing order of priority""" diff --git a/metagpt/utils/common.py b/metagpt/utils/common.py index 7f090cf63..2e214685c 100644 --- a/metagpt/utils/common.py +++ b/metagpt/utils/common.py @@ -187,7 +187,7 @@ class CodeParser: else: logger.error(f"{pattern} not match following text:") logger.error(text) - raise Exception + # raise Exception return code @classmethod diff --git a/metagpt/utils/index.html b/metagpt/utils/index.html new file mode 100644 index 000000000..0ac6d9a74 --- /dev/null +++ b/metagpt/utils/index.html @@ -0,0 +1,2212 @@ + + + + + + + +
+ + + diff --git a/metagpt/utils/mermaid.py b/metagpt/utils/mermaid.py index 24aabe8ae..f395b43b2 100644 --- a/metagpt/utils/mermaid.py +++ b/metagpt/utils/mermaid.py @@ -14,6 +14,7 @@ from metagpt.logs import logger from metagpt.utils.common import check_cmd_exists + def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048) -> int: """suffix: png/svg/pdf @@ -56,6 +57,8 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height subprocess.run([CONFIG.mmdc, "-i", str(tmp), "-o", output_file, "-w", str(width), "-H", str(height)]) return 0 +if CONFIG.mermaid_engine.lower() == "playwright": + from metagpt.utils.mermaid_playwright import mermaid_to_file MMC1 = """classDiagram class Main { diff --git a/metagpt/utils/mermaid_playwright.py b/metagpt/utils/mermaid_playwright.py new file mode 100644 index 000000000..aa04e70eb --- /dev/null +++ b/metagpt/utils/mermaid_playwright.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/4 16:12 +@Author : Steven Lee +@File : mermaid_playwright.py +""" +import os +import asyncio +from metagpt.config import CONFIG +from metagpt.const import PROJECT_ROOT +from metagpt.logs import logger + +from urllib.parse import urljoin +from playwright.async_api import async_playwright +import nest_asyncio + +__dirname = os.path.dirname(os.path.abspath(__file__)) + + +def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048, output_formats=['png', 'svg', 'pdf']) -> int: + """ + Converts the given Mermaid code to various output formats and saves them to files. + + Args: + mermaid_code (str): The Mermaid code to convert. + output_file_without_suffix (str): The output file name without the file extension. + width (int, optional): The width of the output image in pixels. Defaults to 2048. + height (int, optional): The height of the output image in pixels. Defaults to 2048. + output_formats (list[str], optional): The list of output formats to generate. Defaults to ['png', 'svg', 'pdf']. + + Returns: + int: Returns 1 if the conversion and saving were successful, -1 otherwise. + """ + + async def mermaid_to_file0(mermaid_code, output_file_without_suffix, width=2048, height=2048, output_formats=['png', 'svg', 'pdf'])-> int: + + async with async_playwright() as p: + browser = await p.chromium.launch() + device_scale_factor = 1.0 + + context = await browser.new_context( + viewport={'width': width, 'height': height}, + device_scale_factor=device_scale_factor, + ) + page = await context.new_page() + + async def console_message(msg): + print(msg.text) + page.on('console', console_message) + + try: + await page.set_viewport_size({'width': width, 'height': height}) + + mermaid_html_path = os.path.abspath( + os.path.join(__dirname, 'index.html')) + mermaid_html_url = urljoin('file:', mermaid_html_path) + await page.goto(mermaid_html_url) + await page.wait_for_load_state("networkidle") + + await page.wait_for_selector("div#container", state="attached") + mermaid_config = {} + background_color = "#ffffff" + my_css = "" + await page.evaluate(f'document.body.style.background = "{background_color}";') + + metadata = await page.evaluate('''async ([definition, mermaidConfig, myCSS, backgroundColor]) => { + const { mermaid, zenuml } = globalThis; + await mermaid.registerExternalDiagrams([zenuml]); + mermaid.initialize({ startOnLoad: false, ...mermaidConfig }); + const { svg } = await mermaid.render('my-svg', definition, document.getElementById('container')); + document.getElementById('container').innerHTML = svg; + const svgElement = document.querySelector('svg'); + svgElement.style.backgroundColor = backgroundColor; + + if (myCSS) { + const style = document.createElementNS('http://www.w3.org/2000/svg', 'style'); + style.appendChild(document.createTextNode(myCSS)); + svgElement.appendChild(style); + } + + let title = null; + let desc = null; + + if (svgElement.firstChild instanceof SVGTitleElement) { + title = svgElement.firstChild.textContent; + } + + for (const svgNode of svgElement.children) { + if (svgNode instanceof SVGDescElement) { + desc = svgNode.textContent; + break; + } + } + + return { + title, + desc + }; + }''', [mermaid_code, mermaid_config, my_css, background_color]) + + if 'svg' in output_formats : + svg_xml = await page.evaluate('''() => { + const svg = document.querySelector('svg'); + const xmlSerializer = new XMLSerializer(); + return xmlSerializer.serializeToString(svg); + }''') + # result[f'{output_file_without_suffix}.svg'] = svg_xml + with open(f'{output_file_without_suffix}.svg', 'wb') as f: + f.write(svg_xml.encode('utf-8')) + + if 'png' in output_formats: + clip = await page.evaluate('''() => { + const svg = document.querySelector('svg'); + const rect = svg.getBoundingClientRect(); + return { + x: Math.floor(rect.left), + y: Math.floor(rect.top), + width: Math.ceil(rect.width), + height: Math.ceil(rect.height) + }; + }''') + await page.set_viewport_size({'width': clip['x'] + clip['width'], 'height': clip['y'] + clip['height']}) + screenshot = await page.screenshot(clip=clip, omit_background=True, scale='device') + with open(f'{output_file_without_suffix}.png', 'wb') as f: + f.write(screenshot) + if 'pdf' in output_formats: + pdf_data = await page.pdf(scale=device_scale_factor) + with open(f'{output_file_without_suffix}.pdf', 'wb') as f: + f.write(pdf_data) + return 1 + except Exception as e: + logger.error(e) + return -1 + finally: + await browser.close() + with open(f"{output_file_without_suffix}.mmd", "w", encoding="utf-8") as f: + f.write(mermaid_code) + nest_asyncio.apply() + loop = asyncio.new_event_loop() + result = loop.run_until_complete(mermaid_to_file0(mermaid_code, output_file_without_suffix, width, height, output_formats)) + loop.close() + return result + +MMC1 = """classDiagram + class Main { + -SearchEngine search_engine + +main() str + } + class SearchEngine { + -Index index + -Ranking ranking + -Summary summary + +search(query: str) str + } + class Index { + -KnowledgeBase knowledge_base + +create_index(data: dict) + +query_index(query: str) list + } + class Ranking { + +rank_results(results: list) list + } + class Summary { + +summarize_results(results: list) str + } + class KnowledgeBase { + +update(data: dict) + +fetch_data(query: str) dict + } + Main --> SearchEngine + SearchEngine --> Index + SearchEngine --> Ranking + SearchEngine --> Summary + Index --> KnowledgeBase""" + +MMC2 = """sequenceDiagram + participant M as Main + participant SE as SearchEngine + participant I as Index + participant R as Ranking + participant S as Summary + participant KB as KnowledgeBase + M->>SE: search(query) + SE->>I: query_index(query) + I->>KB: fetch_data(query) + KB-->>I: return data + I-->>SE: return results + SE->>R: rank_results(results) + R-->>SE: return ranked_results + SE->>S: summarize_results(ranked_results) + S-->>SE: return summary + SE-->>M: return summary""" + + +if __name__ == "__main__": + # logger.info(print_members(print_members)) + mermaid_to_file(MMC1, PROJECT_ROOT / "MMC1") + mermaid_to_file(MMC2, PROJECT_ROOT / "MMC2") From a0be280879024fb7e422ca7dafa95491211721df Mon Sep 17 00:00:00 2001 From: rainyrfeng Date: Tue, 5 Sep 2023 11:26:16 +0800 Subject: [PATCH 08/76] fix feature --- metagpt/provider/openai_api.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 752fbb4dd..11e35b114 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -176,25 +176,23 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): return full_reply_content def _cons_kwargs(self, messages: list[dict]) -> dict: + kwargs = { + "messages": messages, + "max_tokens": self.get_max_tokens(messages), + "n": 1, + "stop": None, + "temperature": 0.3, + } if CONFIG.openai_api_type == "azure": - kwargs = { - "engine": CONFIG.openai_api_engine, - "deployment_id": CONFIG.deployment_id, - "messages": messages, - "max_tokens": self.get_max_tokens(messages), - "n": 1, - "stop": None, - "temperature": 0.3, - } + if CONFIG.openai_api_engine and CONFIG.deployment_id: + raise ValueError("You can only use one of the `deployment_id` or `engine` model") + elif not CONFIG.openai_api_engine and not CONFIG.deployment_id: + raise ValueError("You must specify `OPENAI_API_ENGINE` or `DEPLOYMENT_ID` parameter") + kwargs_mode = {"engine": CONFIG.openai_api_engine} if CONFIG.openai_api_engine \ + else {"deployment_id": CONFIG.deployment_id} else: - kwargs = { - "model": self.model, - "messages": messages, - "max_tokens": self.get_max_tokens(messages), - "n": 1, - "stop": None, - "temperature": 0.3, - } + kwargs_mode = {"model": self.model} + kwargs.update(kwargs_mode) kwargs["timeout"] = 3 return kwargs From 864a66c7024d89994fcfe677703173f3db8a567d Mon Sep 17 00:00:00 2001 From: rainyrfeng Date: Tue, 5 Sep 2023 20:36:32 +0800 Subject: [PATCH 09/76] fix feature --- config/config.yaml | 2 +- metagpt/config.py | 2 +- metagpt/provider/openai_api.py | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index 428f8dae4..4519288d3 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -21,7 +21,7 @@ RPM: 10 #OPENAI_API_BASE: "YOUR_AZURE_ENDPOINT" #OPENAI_API_KEY: "YOUR_AZURE_API_KEY" #OPENAI_API_VERSION: "YOUR_AZURE_API_VERSION" -#OPENAI_API_ENGINE: "YOUR_OPENAI_API_ENGINE" +#DEPLOYMENT_NAME: "YOUR_DEPLOYMENT_NAME" #DEPLOYMENT_ID: "YOUR_DEPLOYMENT_ID" #### for Search diff --git a/metagpt/config.py b/metagpt/config.py index 7ac32f4d0..96f402b38 100644 --- a/metagpt/config.py +++ b/metagpt/config.py @@ -56,10 +56,10 @@ class Config(metaclass=Singleton): openai.api_base = self.openai_api_base self.openai_api_type = self._get("OPENAI_API_TYPE") self.openai_api_version = self._get("OPENAI_API_VERSION") - self.openai_api_engine = self._get('OPENAI_API_ENGINE') self.openai_api_rpm = self._get("RPM", 3) self.openai_api_model = self._get("OPENAI_API_MODEL", "gpt-4") self.max_tokens_rsp = self._get("MAX_TOKENS", 2048) + self.deployment_name = self._get('DEPLOYMENT_NAME') self.deployment_id = self._get("DEPLOYMENT_ID") self.claude_api_key = self._get("Anthropic_API_KEY") diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 11e35b114..ad9df0396 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -182,18 +182,18 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): "n": 1, "stop": None, "temperature": 0.3, + "timeout": 3 } if CONFIG.openai_api_type == "azure": - if CONFIG.openai_api_engine and CONFIG.deployment_id: - raise ValueError("You can only use one of the `deployment_id` or `engine` model") - elif not CONFIG.openai_api_engine and not CONFIG.deployment_id: - raise ValueError("You must specify `OPENAI_API_ENGINE` or `DEPLOYMENT_ID` parameter") - kwargs_mode = {"engine": CONFIG.openai_api_engine} if CONFIG.openai_api_engine \ + if CONFIG.deployment_name and CONFIG.deployment_id: + raise ValueError("You can only use one of the `deployment_id` or `deployment_name` model") + elif not CONFIG.deployment_name and not CONFIG.deployment_id: + raise ValueError("You must specify `DEPLOYMENT_NAME` or `DEPLOYMENT_ID` parameter") + kwargs_mode = {"engine": CONFIG.deployment_name} if CONFIG.deployment_name \ else {"deployment_id": CONFIG.deployment_id} else: kwargs_mode = {"model": self.model} kwargs.update(kwargs_mode) - kwargs["timeout"] = 3 return kwargs async def _achat_completion(self, messages: list[dict]) -> dict: From 414dea3ea695c174c64d9c0778f31ca840ba94fa Mon Sep 17 00:00:00 2001 From: Stitch-z <284618289@qq.com> Date: Wed, 6 Sep 2023 14:43:24 +0800 Subject: [PATCH 10/76] feature: add a new role tutorial assistant --- examples/write_tutorial.py | 20 +++++ metagpt/actions/write_tutorial.py | 102 +++++++++++++++++++++++ metagpt/const.py | 1 + metagpt/prompts/tutorial_assistant.py | 39 +++++++++ metagpt/roles/tutorial_assistant.py | 113 ++++++++++++++++++++++++++ 5 files changed, 275 insertions(+) create mode 100644 examples/write_tutorial.py create mode 100644 metagpt/actions/write_tutorial.py create mode 100644 metagpt/prompts/tutorial_assistant.py create mode 100644 metagpt/roles/tutorial_assistant.py diff --git a/examples/write_tutorial.py b/examples/write_tutorial.py new file mode 100644 index 000000000..167f3eb7c --- /dev/null +++ b/examples/write_tutorial.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +""" +@Time : 2023/9/4 21:40:57 +@Author : Stitch-z +@File : tutorial_assistant.py +""" +import asyncio + +from metagpt.roles.tutorial_assistant import TutorialAssistant + + +async def main(): + topic = "Write a tutorial about MySQL" + role = TutorialAssistant(language="Chinese") + await role.run(topic) + + +if __name__ == '__main__': + asyncio.run(main()) \ No newline at end of file diff --git a/metagpt/actions/write_tutorial.py b/metagpt/actions/write_tutorial.py new file mode 100644 index 000000000..38a45c4c3 --- /dev/null +++ b/metagpt/actions/write_tutorial.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +""" +@Time : 2023/9/4 15:40:40 +@Author : Stitch-z +@File : tutorial_assistant.py +@Describe : Actions of the tutorial assistant, including writing directories and document content. +""" +import json +from datetime import datetime +from typing import Dict + +import aiofiles + +from metagpt.actions import Action +from metagpt.const import TUTORIAL_PATH +from metagpt.logs import logger +from metagpt.prompts.tutorial_assistant import DIRECTORY_PROMPT, CONTENT_PROMPT + + +class WriteDirectory(Action): + """Action class for writing tutorial directories. + + Args: + name: The name of the action. + language: The language to output, default is "Chinese". + """ + + def __init__(self, name: str = "", language: str = "Chinese", *args, **kwargs): + super().__init__(name, *args, **kwargs) + self.language = language + + async def run(self, topic: str, *args, **kwargs) -> Dict: + """Execute the action to generate a tutorial directory according to the topic. + + Args: + topic: The tutorial topic. + + Returns: + the tutorial directory information, such as {"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]} + """ + prompt = DIRECTORY_PROMPT.format(topic=topic, language=self.language) + directory = await self._aask(prompt=prompt) + return json.loads(directory) + + +class WriteContent(Action): + """Action class for writing tutorial content. + + Args: + name: The name of the action. + directory: The content to write. + language: The language to output, default is "Chinese". + """ + + def __init__(self, name: str = "", directory: str = "", language: str = "Chinese", *args, **kwargs): + super().__init__(name, *args, **kwargs) + self.language = language + self.directory = directory + + async def run(self, topic: str, *args, **kwargs) -> str: + """Execute the action to write document content according to the directory and topic. + + Args: + topic: The tutorial topic. + + Returns: + The written tutorial content. + """ + prompt = CONTENT_PROMPT.format(topic=topic, language=self.language, directory=self.directory) + return await self._aask(prompt=prompt) + + +class SaveDocx(Action): + """Action class for saving tutorial docx. + + Args: + name: The name of the action. + """ + + def __init__(self, name: str = "", *args, **kwargs): + super().__init__(name, *args, **kwargs) + + async def run(self, title: str, content: str, *args, **kwargs) -> str: + """Execute the action to save the generated tutorial document to a Markdown file. + + Args: + title: The title of tutorial. + content: The total content of tutorial. + + Returns: + The full filename of tutorial content. + + """ + current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + pathname = TUTORIAL_PATH / current_time + pathname.mkdir(parents=True, exist_ok=True) + filename = f"{pathname}/{title}.md" + async with aiofiles.open(filename, mode="w", encoding="utf-8") as writer: + await writer.write(content) + logger.info(f"Successfully write docx: {filename}") + return filename \ No newline at end of file diff --git a/metagpt/const.py b/metagpt/const.py index 16f652186..35b4c9fa7 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -33,5 +33,6 @@ API_QUESTIONS_PATH = UT_PATH / "files/question/" YAPI_URL = "http://yapi.deepwisdomai.com/" TMP = PROJECT_ROOT / 'tmp' RESEARCH_PATH = DATA_PATH / "research" +TUTORIAL_PATH = DATA_PATH / "tutorial_docx" MEM_TTL = 24 * 30 * 3600 diff --git a/metagpt/prompts/tutorial_assistant.py b/metagpt/prompts/tutorial_assistant.py new file mode 100644 index 000000000..aaf9ca215 --- /dev/null +++ b/metagpt/prompts/tutorial_assistant.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +""" +@Time : 2023/9/4 15:40:40 +@Author : Stitch-z +@File : tutorial_assistant.py +@Describe : Tutorial Assistant's prompt templates. +""" + + +DIRECTORY_PROMPT = """ +You are now a seasoned technical professional in the field of the internet. +We need you to write a technical tutorial with the topic "{topic}". +Please provide the specific table of contents for this tutorial, strictly following the following requirements: +1. The output must be strictly in the specified language, {language}. +2. Answer in the dictionary format like {{"title": "xxx", "directory": [{{"dir 1": ["sub dir 1", "sub dir 2"]}}]}}. +3. The directory should be as specific and sufficient as possible, with a primary and secondary directory.The secondary directory is in the array. +4. Do not have extra spaces or line breaks. +5. Each directory title has practical significance. +""" + +CONTENT_PROMPT = """ +You are now a seasoned technical professional in the field of the internet. +We need you to write a technical tutorial with the topic "{topic}". +Now I will give you the module directory titles for the topic. +Please output the detailed principle content of this title in detail. +If there are code examples, please provide them according to standard code specifications. +Without a code example, it is not necessary. + +The module directory titles for the topic is as follows: +{directory} + +Strictly limit output according to the following requirements: +1. Follow the Markdown syntax format for layout. +2. If there are code examples, they must follow standard syntax specifications, have document annotations, and be displayed in code blocks. +3. The output must be strictly in the specified language, {language}. +4. Do not have redundant output, including concluding remarks. +5. Don't return the topic "{topic}". +""" \ No newline at end of file diff --git a/metagpt/roles/tutorial_assistant.py b/metagpt/roles/tutorial_assistant.py new file mode 100644 index 000000000..daf4daf40 --- /dev/null +++ b/metagpt/roles/tutorial_assistant.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +""" +@Time : 2023/9/4 15:40:40 +@Author : Stitch-z +@File : tutorial_assistant.py +""" + +from typing import Dict + +from metagpt.actions.write_tutorial import WriteDirectory, WriteContent, SaveDocx +from metagpt.logs import logger +from metagpt.roles import Role +from metagpt.schema import Message + + +class TutorialAssistant(Role): + """Tutorial assistant, input one sentence to generate a tutorial document in markup format. + + Args: + name: The name of the role. + profile: The role profile description. + goal: The goal of the role. + constraints: Constraints or requirements for the role. + language: The language in which the tutorial documents will be generated. + """ + + def __init__( + self, + name: str = "Stitch", + profile: str = "Tutorial Assistant", + goal: str = "Generate tutorial documents", + constraints: str = "Strictly follow Markdown's syntax, with neat and standardized layout", + language: str = "Chinese", + ): + super().__init__(name, profile, goal, constraints) + self._init_actions([WriteDirectory(language=language)]) + self.topic = "" + self.main_title = "" + self.total_content = "" + self.language = language + + async def _think(self) -> None: + """Determine the next action to be taken by the role.""" + if self._rc.todo is None: + self._set_state(0) + return + + if self._rc.state + 1 < len(self._states): + self._set_state(self._rc.state + 1) + else: + self._rc.todo = None + + async def _handle_directory(self, titles: Dict) -> Message: + """Handle the directories for the tutorial document. + + Args: + titles: A dictionary containing the titles and directory structure, + such as {"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]} + + Returns: + A message containing information about the directory. + """ + self.main_title = titles.get("title") + directory = f"{self.main_title}\n" + self.total_content += f"# {self.main_title}" + actions = list() + for first_dir in titles.get("directory"): + actions.append(WriteContent(language=self.language, directory=first_dir)) + key = list(first_dir.keys())[0] + directory += f"- {key}\n" + for second_dir in first_dir[key]: + directory += f" - {second_dir}\n" + actions.append(SaveDocx()) + self._init_actions(actions) + self._rc.todo = None + return Message(content=directory) + + async def _act(self) -> Message: + """Perform an action as determined by the role. + + Returns: + A message containing the result of the action. + """ + todo = self._rc.todo + if type(todo) is WriteDirectory: + msg = self._rc.memory.get(k=1)[0] + self.topic = msg.content + resp = await todo.run(topic=self.topic) + logger.info(resp) + return await self._handle_directory(resp) + elif type(todo) is SaveDocx: + filename = await todo.run(title=self.main_title, content=self.total_content) + return Message(content=filename, role=self.profile) + resp = await todo.run(topic=self.topic) + logger.info(resp) + if self.total_content != "": + self.total_content += "\n\n\n" + self.total_content += resp + return Message(content=resp, role=self.profile) + + async def _react(self) -> Message: + """Execute the assistant's think and actions. + + Returns: + A message containing the final result of the assistant's actions. + """ + while True: + await self._think() + if self._rc.todo is None: + break + msg = await self._act() + return msg From c0aab24b291eb7e2f5e7dd71e3ef8aa01c044706 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Wed, 6 Sep 2023 17:47:35 +0800 Subject: [PATCH 11/76] fix: windows compatibility --- metagpt/utils/common.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/metagpt/utils/common.py b/metagpt/utils/common.py index 7f090cf63..a0dabd77e 100644 --- a/metagpt/utils/common.py +++ b/metagpt/utils/common.py @@ -9,6 +9,7 @@ import ast import contextlib import inspect import os +import platform import re from typing import List, Tuple @@ -20,7 +21,10 @@ def check_cmd_exists(command) -> int: :param command: 待检查的命令 :return: 如果命令存在,返回0,如果不存在,返回非0 """ - check_command = 'command -v ' + command + ' >/dev/null 2>&1 || { echo >&2 "no mermaid"; exit 1; }' + if platform.system().lower() == 'windows': + check_command = 'where ' + command + else: + check_command = 'command -v ' + command + ' >/dev/null 2>&1 || { echo >&2 "no mermaid"; exit 1; }' result = os.system(check_command) return result From bc332a5f56d8e4f3e4697f8f30c73b56d504abeb Mon Sep 17 00:00:00 2001 From: Stitch-z <284618289@qq.com> Date: Wed, 6 Sep 2023 18:15:07 +0800 Subject: [PATCH 12/76] update: optimize prompts --- metagpt/prompts/tutorial_assistant.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/metagpt/prompts/tutorial_assistant.py b/metagpt/prompts/tutorial_assistant.py index aaf9ca215..c9039fd41 100644 --- a/metagpt/prompts/tutorial_assistant.py +++ b/metagpt/prompts/tutorial_assistant.py @@ -7,10 +7,12 @@ @Describe : Tutorial Assistant's prompt templates. """ - -DIRECTORY_PROMPT = """ +COMMON_PROMPT = """ You are now a seasoned technical professional in the field of the internet. -We need you to write a technical tutorial with the topic "{topic}". +We need you to write a technical tutorial with the topic "{topic}". +""" + +DIRECTORY_PROMPT = COMMON_PROMPT + """ Please provide the specific table of contents for this tutorial, strictly following the following requirements: 1. The output must be strictly in the specified language, {language}. 2. Answer in the dictionary format like {{"title": "xxx", "directory": [{{"dir 1": ["sub dir 1", "sub dir 2"]}}]}}. @@ -19,9 +21,7 @@ Please provide the specific table of contents for this tutorial, strictly follow 5. Each directory title has practical significance. """ -CONTENT_PROMPT = """ -You are now a seasoned technical professional in the field of the internet. -We need you to write a technical tutorial with the topic "{topic}". +CONTENT_PROMPT = COMMON_PROMPT + """ Now I will give you the module directory titles for the topic. Please output the detailed principle content of this title in detail. If there are code examples, please provide them according to standard code specifications. From 23686efe031af165be9ea550700ec4b5b4369357 Mon Sep 17 00:00:00 2001 From: gallonyin Date: Wed, 6 Sep 2023 20:19:23 +0800 Subject: [PATCH 13/76] =?UTF-8?q?Fix=20README=5FCN.md=20=E2=80=9C=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E9=94=99=E8=AF=AF=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README_CN.md b/docs/README_CN.md index 2180eb518..4a1f0c44e 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -115,7 +115,7 @@ ## 示例:启动一个创业公司 ```shell python startup.py "写一个命令行贪吃蛇" -# 开启code review模式会会花费更多的money, 但是会提升代码质量和成功率 +# 开启code review模式会花费更多的money, 但是会提升代码质量和成功率 python startup.py "写一个命令行贪吃蛇" --code_review True ``` From 9f5ff2fb7d802adc0c29bf40119f943d732ceabb Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 21:36:44 +0800 Subject: [PATCH 14/76] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 84dafa46b..5998adbb1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,6 @@ # MetaGPT: The Multi-Agent Framework Discord Follow License: MIT roadmap -roadmap Twitter Follow

From c1afa18882202bb720a1f48ff09edcef1496837c Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 21:41:47 +0800 Subject: [PATCH 15/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5998adbb1..b35291e56 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ # MetaGPT: The Multi-Agent Framework CN doc EN doc JA doc -Discord Follow +Discord License: MIT roadmap Twitter Follow From 813366f3e35e635b25e1d579cbcbe668322b6e52 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 21:42:06 +0800 Subject: [PATCH 16/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b35291e56..5998adbb1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ # MetaGPT: The Multi-Agent Framework CN doc EN doc JA doc -Discord +Discord Follow License: MIT roadmap Twitter Follow From 68b7118aed372879e44c304a29ddbe8b22202077 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:07:22 +0800 Subject: [PATCH 17/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5998adbb1..dfb04cedf 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ # MetaGPT: The Multi-Agent Framework CN doc EN doc JA doc -Discord Follow +Discord Follow License: MIT roadmap Twitter Follow From 3cc5503ab46241eba57a346861ffebb87c1a949c Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:27:10 +0800 Subject: [PATCH 18/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dfb04cedf..7623fde3f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ # MetaGPT: The Multi-Agent Framework CN doc EN doc JA doc -Discord Follow +Discord Follow License: MIT roadmap Twitter Follow From c5903eff8a77af362f18f8f3d723fb6045d7e871 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:28:46 +0800 Subject: [PATCH 19/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7623fde3f..a3f669d7b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ # MetaGPT: The Multi-Agent Framework CN doc EN doc JA doc -Discord Follow +Discord Follow License: MIT roadmap Twitter Follow From 8e0b9308a8b5c6a528911609ea3f24b5875b385d Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:30:59 +0800 Subject: [PATCH 20/76] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a3f669d7b..2c0c1757a 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ # MetaGPT: The Multi-Agent Framework

+ ![Python 3.9+](https://img.shields.io/badge/Python-3.9%2B-brightgreen.svg) Open in Dev Containers Open in GitHub Codespaces

From 5158ede41f618d8ef44d0b6c10722cc86b8888d7 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:31:49 +0800 Subject: [PATCH 21/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c0c1757a..8934f48f8 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ # MetaGPT: The Multi-Agent Framework

- ![Python 3.9+](https://img.shields.io/badge/Python-3.9%2B-brightgreen.svg) + Open in Dev Containers Open in GitHub Codespaces

From 034a133817a9a4d23cd9a119665588ac469f0d49 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:34:44 +0800 Subject: [PATCH 22/76] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8934f48f8..a3f669d7b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ # MetaGPT: The Multi-Agent Framework

- Open in Dev Containers Open in GitHub Codespaces

From b90e16e74565906699e08b8dac2be1e5db20b5e4 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:34:57 +0800 Subject: [PATCH 23/76] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a3f669d7b..8934f48f8 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ # MetaGPT: The Multi-Agent Framework

+ Open in Dev Containers Open in GitHub Codespaces

From 76fe10ca0a2c047e38f84cb0729aaf379e1cbaab Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:37:37 +0800 Subject: [PATCH 24/76] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8934f48f8..a3f669d7b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ # MetaGPT: The Multi-Agent Framework

- Open in Dev Containers Open in GitHub Codespaces

From 473baf193c4dd8f689e3d9295b0ca8731cd2df3a Mon Sep 17 00:00:00 2001 From: Stitch-z <284618289@qq.com> Date: Wed, 6 Sep 2023 23:38:46 +0800 Subject: [PATCH 25/76] update: add unit test for the role tutorial assistant --- tests/metagpt/actions/test_write_tutorial.py | 53 +++++++++++++++++++ .../metagpt/roles/test_tutorial_assistant.py | 27 ++++++++++ 2 files changed, 80 insertions(+) create mode 100644 tests/metagpt/actions/test_write_tutorial.py create mode 100644 tests/metagpt/roles/test_tutorial_assistant.py diff --git a/tests/metagpt/actions/test_write_tutorial.py b/tests/metagpt/actions/test_write_tutorial.py new file mode 100644 index 000000000..6460aa08b --- /dev/null +++ b/tests/metagpt/actions/test_write_tutorial.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +""" +@Time : 2023/9/6 21:41:34 +@Author : Stitch-z +@File : test_write_tutorial.py +""" +from typing import Dict + +import aiofiles +import pytest + +from metagpt.actions.write_tutorial import WriteDirectory, WriteContent, SaveDocx + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("language", "topic"), + [("English", "Write a tutorial about Python")] +) +async def test_write_directory(language: str, topic: str): + ret = await WriteDirectory(language=language).run(topic=topic) + assert isinstance(ret, dict) + assert "title" in ret + assert "directory" in ret + assert isinstance(ret["directory"], list) + assert len(ret["directory"]) + assert isinstance(ret["directory"][0], dict) + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("language", "topic", "directory"), + [("English", "Write a tutorial about Python", {"Introduction": ["What is Python?", "Why learn Python?"]})] +) +async def test_write_content(language: str, topic: str, directory: Dict): + ret = await WriteContent(language=language, directory=directory).run(topic=topic) + assert isinstance(ret, str) + assert list(directory.keys())[0] in ret + for value in list(directory.values())[0]: + assert value in ret + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("title", "content"), + [("Python", "Write a tutorial about Python")] +) +async def test_save_docx(title: str, content: str): + ret = await SaveDocx().run(title=title, content=content) + assert isinstance(ret, str) + assert title in ret + async with aiofiles.open(ret, mode="r") as reader: + body = await reader.read() + assert body == content \ No newline at end of file diff --git a/tests/metagpt/roles/test_tutorial_assistant.py b/tests/metagpt/roles/test_tutorial_assistant.py new file mode 100644 index 000000000..945620cfc --- /dev/null +++ b/tests/metagpt/roles/test_tutorial_assistant.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +""" +@Time : 2023/9/6 23:11:27 +@Author : Stitch-z +@File : test_tutorial_assistant.py +""" +import aiofiles +import pytest + +from metagpt.roles.tutorial_assistant import TutorialAssistant + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("language", "topic"), + [("Chinese", "Write a tutorial about Python")] +) +async def test_tutorial_assistant(language: str, topic: str): + topic = "Write a tutorial about MySQL" + role = TutorialAssistant(language=language) + msg = await role.run(topic) + filename = msg.content + title = filename.split("/")[-1].split(".")[0] + async with aiofiles.open(filename, mode="r") as reader: + content = await reader.read() + assert content.startswith(f"# {title}") \ No newline at end of file From eb7a8f5287992b0d15bdf2d76a58a91e5fd59e63 Mon Sep 17 00:00:00 2001 From: ushio0107 Date: Wed, 6 Sep 2023 23:57:46 +0800 Subject: [PATCH 26/76] Update README_CN.md --- docs/README_CN.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/README_CN.md b/docs/README_CN.md index 2180eb518..dda5f15eb 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -53,6 +53,25 @@ # 第 3 步:克隆仓库到您的本地机器,并进行安装。 python setup.py install ``` +**注意:** + +- 如果已经安装了Chrome、Chromium或MS Edge,可以通过将环境变量`PUPPETEER_SKIP_CHROMIUM_DOWNLOAD`设置为`true`来跳过下载Chromium。 + +- 一些人在全局安装此工具时遇到问题。在本地安装是替代解决方案, + + ```bash + npm install @mermaid-js/mermaid-cli + ``` + +- 不要忘记在config.yml中为mmdc配置配置, + + ```yml + PUPPETEER_CONFIG: "./config/puppeteer-config.json" + MMDC: "./node_modules/.bin/mmdc" + ``` + +- 如果`python setup.py install`失败并显示错误`[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'`,请尝试使用`python setup.py install --user`运行。 + ### Docker安装 ```bash @@ -115,7 +134,7 @@ ## 示例:启动一个创业公司 ```shell python startup.py "写一个命令行贪吃蛇" -# 开启code review模式会会花费更多的money, 但是会提升代码质量和成功率 +# 开启code review模式会会花费更多的金钱, 但是会提升代码质量和成功率 python startup.py "写一个命令行贪吃蛇" --code_review True ``` From 207485e24a9d068fc301741558c55a5661861406 Mon Sep 17 00:00:00 2001 From: ushio0107 Date: Thu, 7 Sep 2023 00:09:40 +0800 Subject: [PATCH 27/76] Update README_CN.md --- docs/README_CN.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/README_CN.md b/docs/README_CN.md index dda5f15eb..8e994e664 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -142,7 +142,6 @@ # 开启code review模式会会花费更多的金钱, 但是会提升代码质 ### 平台或工具的倾向性 可以在阐述需求时说明想要使用的平台或工具。 例如: - ```shell python startup.py "写一个基于pygame的命令行贪吃蛇" ``` From 4973364556da056476c56874f7cfea0e9f190a4e Mon Sep 17 00:00:00 2001 From: ushio0107 Date: Thu, 7 Sep 2023 13:55:07 +0800 Subject: [PATCH 28/76] Update README_CN.md --- docs/README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README_CN.md b/docs/README_CN.md index 8e994e664..ae5d954e4 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -134,7 +134,7 @@ ## 示例:启动一个创业公司 ```shell python startup.py "写一个命令行贪吃蛇" -# 开启code review模式会会花费更多的金钱, 但是会提升代码质量和成功率 +# 开启code review模式会花费更多的金钱, 但是会提升代码质量和成功率 python startup.py "写一个命令行贪吃蛇" --code_review True ``` From d83d1e105c28b558f9da5e56967834de6b8fe3c6 Mon Sep 17 00:00:00 2001 From: Stitch-z <284618289@qq.com> Date: Thu, 7 Sep 2023 19:55:50 +0800 Subject: [PATCH 29/76] update: format directory structure and extract universal file operation class --- metagpt/actions/write_tutorial.py | 67 +++++++++----------- metagpt/prompts/tutorial_assistant.py | 4 +- metagpt/roles/tutorial_assistant.py | 11 ++-- metagpt/utils/file.py | 35 ++++++++++ tests/metagpt/actions/test_write_tutorial.py | 17 +---- tests/metagpt/utils/test_file.py | 27 ++++++++ 6 files changed, 102 insertions(+), 59 deletions(-) create mode 100644 metagpt/utils/file.py create mode 100644 tests/metagpt/utils/test_file.py diff --git a/metagpt/actions/write_tutorial.py b/metagpt/actions/write_tutorial.py index 38a45c4c3..b23fc2ad4 100644 --- a/metagpt/actions/write_tutorial.py +++ b/metagpt/actions/write_tutorial.py @@ -7,13 +7,9 @@ @Describe : Actions of the tutorial assistant, including writing directories and document content. """ import json -from datetime import datetime from typing import Dict -import aiofiles - from metagpt.actions import Action -from metagpt.const import TUTORIAL_PATH from metagpt.logs import logger from metagpt.prompts.tutorial_assistant import DIRECTORY_PROMPT, CONTENT_PROMPT @@ -30,6 +26,33 @@ class WriteDirectory(Action): super().__init__(name, *args, **kwargs) self.language = language + @staticmethod + async def _handle_resp(resp: str) -> Dict: + """Process string results and convert them to JSON format. + + Args: + resp: The directory results returned by gpt. + + Returns: + The parsed dictionary, such as {"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]}. + + Raises: + Exception: If no matching dictionary section is found. + json.JSONDecodeError: If the dictionary part cannot be parsed as JSON. + """ + start = resp.find('{') + end = resp.rfind('}') + if start != -1 and end != -1 and end > start: + directory_str = resp[start:end + 1] + logger.info(f"Successfully parsed json: {str(directory_str)}") + try: + return json.loads(directory_str) + except json.JSONDecodeError as e: + logger.error(f"Json parsing error: {e}") + raise e + else: + raise Exception("No matching dictionary section found.") + async def run(self, topic: str, *args, **kwargs) -> Dict: """Execute the action to generate a tutorial directory according to the topic. @@ -37,11 +60,11 @@ class WriteDirectory(Action): topic: The tutorial topic. Returns: - the tutorial directory information, such as {"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]} + the tutorial directory information, including {"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]}. """ prompt = DIRECTORY_PROMPT.format(topic=topic, language=self.language) - directory = await self._aask(prompt=prompt) - return json.loads(directory) + resp = await self._aask(prompt=prompt) + return await self._handle_resp(resp) class WriteContent(Action): @@ -70,33 +93,3 @@ class WriteContent(Action): prompt = CONTENT_PROMPT.format(topic=topic, language=self.language, directory=self.directory) return await self._aask(prompt=prompt) - -class SaveDocx(Action): - """Action class for saving tutorial docx. - - Args: - name: The name of the action. - """ - - def __init__(self, name: str = "", *args, **kwargs): - super().__init__(name, *args, **kwargs) - - async def run(self, title: str, content: str, *args, **kwargs) -> str: - """Execute the action to save the generated tutorial document to a Markdown file. - - Args: - title: The title of tutorial. - content: The total content of tutorial. - - Returns: - The full filename of tutorial content. - - """ - current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") - pathname = TUTORIAL_PATH / current_time - pathname.mkdir(parents=True, exist_ok=True) - filename = f"{pathname}/{title}.md" - async with aiofiles.open(filename, mode="w", encoding="utf-8") as writer: - await writer.write(content) - logger.info(f"Successfully write docx: {filename}") - return filename \ No newline at end of file diff --git a/metagpt/prompts/tutorial_assistant.py b/metagpt/prompts/tutorial_assistant.py index c9039fd41..d690aad83 100644 --- a/metagpt/prompts/tutorial_assistant.py +++ b/metagpt/prompts/tutorial_assistant.py @@ -15,7 +15,7 @@ We need you to write a technical tutorial with the topic "{topic}". DIRECTORY_PROMPT = COMMON_PROMPT + """ Please provide the specific table of contents for this tutorial, strictly following the following requirements: 1. The output must be strictly in the specified language, {language}. -2. Answer in the dictionary format like {{"title": "xxx", "directory": [{{"dir 1": ["sub dir 1", "sub dir 2"]}}]}}. +2. Answer strictly in the dictionary format like {{"title": "xxx", "directory": [{{"dir 1": ["sub dir 1", "sub dir 2"]}}, {{"dir 2": ["sub dir 3", "sub dir 4"]}}]}}. 3. The directory should be as specific and sufficient as possible, with a primary and secondary directory.The secondary directory is in the array. 4. Do not have extra spaces or line breaks. 5. Each directory title has practical significance. @@ -35,5 +35,5 @@ Strictly limit output according to the following requirements: 2. If there are code examples, they must follow standard syntax specifications, have document annotations, and be displayed in code blocks. 3. The output must be strictly in the specified language, {language}. 4. Do not have redundant output, including concluding remarks. -5. Don't return the topic "{topic}". +5. Strict requirement not to output the topic "{topic}". """ \ No newline at end of file diff --git a/metagpt/roles/tutorial_assistant.py b/metagpt/roles/tutorial_assistant.py index daf4daf40..9a7df4f4d 100644 --- a/metagpt/roles/tutorial_assistant.py +++ b/metagpt/roles/tutorial_assistant.py @@ -6,12 +6,15 @@ @File : tutorial_assistant.py """ +from datetime import datetime from typing import Dict -from metagpt.actions.write_tutorial import WriteDirectory, WriteContent, SaveDocx +from metagpt.actions.write_tutorial import WriteDirectory, WriteContent +from metagpt.const import TUTORIAL_PATH from metagpt.logs import logger from metagpt.roles import Role from metagpt.schema import Message +from metagpt.utils.file import File class TutorialAssistant(Role): @@ -71,7 +74,6 @@ class TutorialAssistant(Role): directory += f"- {key}\n" for second_dir in first_dir[key]: directory += f" - {second_dir}\n" - actions.append(SaveDocx()) self._init_actions(actions) self._rc.todo = None return Message(content=directory) @@ -89,9 +91,6 @@ class TutorialAssistant(Role): resp = await todo.run(topic=self.topic) logger.info(resp) return await self._handle_directory(resp) - elif type(todo) is SaveDocx: - filename = await todo.run(title=self.main_title, content=self.total_content) - return Message(content=filename, role=self.profile) resp = await todo.run(topic=self.topic) logger.info(resp) if self.total_content != "": @@ -110,4 +109,6 @@ class TutorialAssistant(Role): if self._rc.todo is None: break msg = await self._act() + root_path = TUTORIAL_PATH / datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + await File.write(root_path, f"{self.main_title}.md", self.total_content.encode('utf-8')) return msg diff --git a/metagpt/utils/file.py b/metagpt/utils/file.py new file mode 100644 index 000000000..93e1ad6c7 --- /dev/null +++ b/metagpt/utils/file.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +""" +@Time : 2023/9/4 15:40:40 +@Author : Stitch-z +@File : file.py +@Describe : General file operations. +""" +import aiofiles +from pathlib import Path + +from metagpt.logs import logger + + +class File: + """A general util for file operations.""" + + @classmethod + async def write(cls, root_path: Path, filename: str, content: bytes) -> Path: + """Write the file content to the local specified path. + + Args: + root_path: The root path of file, such as "/data". + filename: The name of file, such as "test.txt". + content: The binary content of file. + + Returns: + The full filename of file, such as "/data/test.txt". + """ + root_path.mkdir(parents=True, exist_ok=True) + full_path = root_path / filename + async with aiofiles.open(full_path, mode="wb") as writer: + await writer.write(content) + logger.info(f"Successfully write docx: {full_path}") + return full_path \ No newline at end of file diff --git a/tests/metagpt/actions/test_write_tutorial.py b/tests/metagpt/actions/test_write_tutorial.py index 6460aa08b..683fee082 100644 --- a/tests/metagpt/actions/test_write_tutorial.py +++ b/tests/metagpt/actions/test_write_tutorial.py @@ -7,10 +7,9 @@ """ from typing import Dict -import aiofiles import pytest -from metagpt.actions.write_tutorial import WriteDirectory, WriteContent, SaveDocx +from metagpt.actions.write_tutorial import WriteDirectory, WriteContent @pytest.mark.asyncio @@ -27,6 +26,7 @@ async def test_write_directory(language: str, topic: str): assert len(ret["directory"]) assert isinstance(ret["directory"][0], dict) + @pytest.mark.asyncio @pytest.mark.parametrize( ("language", "topic", "directory"), @@ -38,16 +38,3 @@ async def test_write_content(language: str, topic: str, directory: Dict): assert list(directory.keys())[0] in ret for value in list(directory.values())[0]: assert value in ret - -@pytest.mark.asyncio -@pytest.mark.parametrize( - ("title", "content"), - [("Python", "Write a tutorial about Python")] -) -async def test_save_docx(title: str, content: str): - ret = await SaveDocx().run(title=title, content=content) - assert isinstance(ret, str) - assert title in ret - async with aiofiles.open(ret, mode="r") as reader: - body = await reader.read() - assert body == content \ No newline at end of file diff --git a/tests/metagpt/utils/test_file.py b/tests/metagpt/utils/test_file.py new file mode 100644 index 000000000..a9f1a353d --- /dev/null +++ b/tests/metagpt/utils/test_file.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +""" +@Time : 2023/9/4 15:40:40 +@Author : Stitch-z +@File : test_file.py +""" +from pathlib import Path + +import aiofiles +import pytest + +from metagpt.utils.file import File + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("root_path", "filename", "content"), + [(Path("/code/MetaGPT/data/tutorial_docx/2023-09-07_17-05-20"), "test.md", "Hello World!")] +) +async def test_write_file(root_path: Path, filename: str, content: bytes): + full_file_name = await File.write(root_path=root_path, filename=filename, content=content.encode('utf-8')) + assert isinstance(full_file_name, Path) + assert root_path / filename == full_file_name + async with aiofiles.open(full_file_name, mode="r") as reader: + body = await reader.read() + assert body == content \ No newline at end of file From b2b227391a8a30bc70ae6f40214d6e697ccb51cd Mon Sep 17 00:00:00 2001 From: Stitch-z <284618289@qq.com> Date: Thu, 7 Sep 2023 20:08:02 +0800 Subject: [PATCH 30/76] update: Add exception handling for write file operation. --- metagpt/utils/file.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/metagpt/utils/file.py b/metagpt/utils/file.py index 93e1ad6c7..3e2adcf7b 100644 --- a/metagpt/utils/file.py +++ b/metagpt/utils/file.py @@ -27,9 +27,13 @@ class File: Returns: The full filename of file, such as "/data/test.txt". """ - root_path.mkdir(parents=True, exist_ok=True) - full_path = root_path / filename - async with aiofiles.open(full_path, mode="wb") as writer: - await writer.write(content) - logger.info(f"Successfully write docx: {full_path}") - return full_path \ No newline at end of file + try: + root_path.mkdir(parents=True, exist_ok=True) + full_path = root_path / filename + async with aiofiles.open(full_path, mode="wb") as writer: + await writer.write(content) + logger.info(f"Successfully write docx: {full_path}") + return full_path + except Exception as e: + logger.error(f"Error writing file: {e}") + raise e \ No newline at end of file From 5405d9cf32e79f3408a25d481940fe39e8c6316e Mon Sep 17 00:00:00 2001 From: Stitch-z <284618289@qq.com> Date: Thu, 7 Sep 2023 20:09:04 +0800 Subject: [PATCH 31/76] update: Add exception handling for write file operation. --- metagpt/utils/file.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/metagpt/utils/file.py b/metagpt/utils/file.py index 3e2adcf7b..84b2f8aeb 100644 --- a/metagpt/utils/file.py +++ b/metagpt/utils/file.py @@ -26,6 +26,9 @@ class File: Returns: The full filename of file, such as "/data/test.txt". + + Raises: + Exception: If an unexpected error occurs during the file writing process. """ try: root_path.mkdir(parents=True, exist_ok=True) From a90c4309a0dba269e72192521c111c8d316adea6 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Sun, 10 Sep 2023 23:17:58 +0800 Subject: [PATCH 32/76] Update prompt.py fixquotes error --- metagpt/roles/prompt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/prompt.py b/metagpt/roles/prompt.py index d13551203..c22e0226b 100644 --- a/metagpt/roles/prompt.py +++ b/metagpt/roles/prompt.py @@ -32,7 +32,7 @@ class PromptString(Enum): RECENT_ACTIVITY = "Based on the following memory, produce a brief summary of what {full_name} has been up to recently. Do not invent details not explicitly stated in the memory. For any conversation, be sure to mention whether the conversation has concluded or is still ongoing.\n\nMemory: {memory_descriptions}" - MAKE_PLANS = 'You are a plan-generating AI. Your job is to assist the character in formulating new plans based on new information. Given the character's information (profile, objectives, recent activities, current plans, and location context) and their current thought process, produce a new set of plans for them. The final plan should comprise at least {time_window} of activities and no more than 5 individual plans. List the plans in the order they should be executed, with each plan detailing its description, location, start time, stop criteria, and maximum duration.\n\nSample plan: \'{{"index": 1, "description": "Cook dinner", "location_id": "0a3bc22b-36aa-48ab-adb0-18616004caed","start_time": "2022-12-12T20:00:00+00:00","max_duration_hrs": 1.5, "stop_condition": "Dinner is fully prepared"}}\'\n\nFor each plan, choose the most appropriate location name from this list: {allowed_location_descriptions}\n\n{format_instructions}\n\nAlways prioritize completing any unfinished conversations.\n\nLet's begin!\n\nName: {full_name}\nProfile: {private_bio}\nObjectives: {directives}\nLocation Context: {location_context}\nCurrent Plans: {current_plans}\nRecent Activities: {recent_activity}\nThought Process: {thought_process}\nIt's essential to encourage the character to collaborate with other characters in their plans.\n\n' + MAKE_PLANS = "You are a plan-generating AI. Your job is to assist the character in formulating new plans based on new information. Given the character's information (profile, objectives, recent activities, current plans, and location context) and their current thought process, produce a new set of plans for them. The final plan should comprise at least {time_window} of activities and no more than 5 individual plans. List the plans in the order they should be executed, with each plan detailing its description, location, start time, stop criteria, and maximum duration.\n\nSample plan: {{\"index\": 1, \"description\": \"Cook dinner\", \"location_id\": \"0a3bc22b-36aa-48ab-adb0-18616004caed\",\"start_time\": \"2022-12-12T20:00:00+00:00\",\"max_duration_hrs\": 1.5, \"stop_condition\": \"Dinner is fully prepared\"}}\'\n\nFor each plan, choose the most appropriate location name from this list: {allowed_location_descriptions}\n\n{format_instructions}\n\nAlways prioritize completing any unfinished conversations.\n\nLet's begin!\n\nName: {full_name}\nProfile: {private_bio}\nObjectives: {directives}\nLocation Context: {location_context}\nCurrent Plans: {current_plans}\nRecent Activities: {recent_activity}\nThought Process: {thought_process}\nIt's essential to encourage the character to collaborate with other characters in their plans.\n\n" EXECUTE_PLAN = "You are a role-playing AI, playing the role of {your_name}, in front of a live audience. Every word you say can be observed by the audience, so make sure you speak often and make it interesting. You cannot interact directly with the audience.\n\nBased on the following context and tools, proceed as if you are {your_name}. Your primary task is to complete the task given below, however, if you are currently in a conversation with another character, you should always complete the conversation first and then proceed with the task. Do not start working while you are engaged in an unfinished conversation. Use your best judgment to determine whether a conversation involves you and whether it is unfinished. You don't need to respond to every message you receive.\n\nThis is a personal profile of your character:\n{your_private_bio}\n\nThis is a description of your location and other nearby characters you can converse with:\n\n{location_context}\n\nThese are some relevant memories:\n```\n{relevant_memories}\n```\n\nThis is some relevant conversation history:\n```\n{conversation_history}\n```\n\nThese are the tools you can use:\n{tools}\n\nYour response should follow the format below:\n\nTask: The task you must complete\nThoughts: What you should think about doing\nAction: The action to take, must be one of these words: [{tool_names}]\nAction Input: Input for the action\nObservation: The result of the action\n... (This Thoughts/Action/Action Input/Observation can be repeated N times)\nThoughts: 'I have completed the task'\nFinal Response: The final response to the task\n\nIf you are not ready with a final response, then you must take an action.\n\nIf you are certain that you cannot complete the task with the tools provided, return 'Final Response: Need Help', however, if you are in conversation with another character, responses like 'I don't know' are valid. During conversations, you should never break character or admit you are an AI.\nIf the task is completed and no specific response is required, return 'Final Response: Done'\nLet's begin!\n\nTask: {input}\n\n{agent_scratchpad}" From 90b045b11a87c1f6801c012f9c78a1f4b3277a13 Mon Sep 17 00:00:00 2001 From: Stitch-z <284618289@qq.com> Date: Mon, 11 Sep 2023 10:20:05 +0800 Subject: [PATCH 33/76] update file tool log --- metagpt/utils/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/utils/file.py b/metagpt/utils/file.py index 84b2f8aeb..5aca2a0e5 100644 --- a/metagpt/utils/file.py +++ b/metagpt/utils/file.py @@ -35,7 +35,7 @@ class File: full_path = root_path / filename async with aiofiles.open(full_path, mode="wb") as writer: await writer.write(content) - logger.info(f"Successfully write docx: {full_path}") + logger.info(f"Successfully write file: {full_path}") return full_path except Exception as e: logger.error(f"Error writing file: {e}") From 3b97adcad1b0badd5731826af1fa4d13eb6e5314 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:40:02 +0800 Subject: [PATCH 34/76] Update README.md add HF space link --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a3f669d7b..db4d394ea 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ # MetaGPT: The Multi-Agent Framework EN doc JA doc Discord Follow +Hugging Face License: MIT roadmap Twitter Follow @@ -213,6 +214,9 @@ ## QuickStart - [MetaGPT quickstart](https://deepwisdom.feishu.cn/wiki/CyY9wdJc4iNqArku3Lncl4v8n2b) +Try it on Huggingface Space +- https://huggingface.co/spaces/deepwisdom/MetaGPT + ## Citation For now, cite the [Arxiv paper](https://arxiv.org/abs/2308.00352): From 222a3236b5e9e6b794a62f6a99ff82059c0c5454 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:42:30 +0800 Subject: [PATCH 35/76] Update README_CN.md add HF space link --- docs/README_CN.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/README_CN.md b/docs/README_CN.md index ae5d954e4..c3a0e92df 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -13,6 +13,7 @@ # MetaGPT: 多智能体框架 EN doc JA doc Discord Follow +Hugging Face License: MIT roadmap roadmap @@ -198,6 +199,10 @@ ## 快速体验 - [MetaGPT快速体验](https://deepwisdom.feishu.cn/wiki/Q8ycw6J9tiNXdHk66MRcIN8Pnlg) +可直接在Huggingface Space体验 + +- https://huggingface.co/spaces/deepwisdom/MetaGPT + ## 联系信息 如果您对这个项目有任何问题或反馈,欢迎联系我们。我们非常欢迎您的建议! From 8d74c9954c16cf76ab0844002a0e16cbefa4daaf Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:45:27 +0800 Subject: [PATCH 36/76] Update README_JA.md add HF space link --- docs/README_JA.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/README_JA.md b/docs/README_JA.md index 4be862b6f..9bda95556 100644 --- a/docs/README_JA.md +++ b/docs/README_JA.md @@ -13,6 +13,7 @@ # MetaGPT: マルチエージェントフレームワーク EN doc JA doc Discord Follow +Hugging Face License: MIT roadmap roadmap @@ -198,6 +199,9 @@ ## クイックスタート - [MetaGPT クイックスタート](https://deepwisdom.feishu.cn/wiki/CyY9wdJc4iNqArku3Lncl4v8n2b) +試着する Huggingface Space +- https://huggingface.co/spaces/deepwisdom/MetaGPT + ## 引用 現時点では、[Arxiv 論文](https://arxiv.org/abs/2308.00352)を引用してください: From c043ba8e05b2f19d6bb96fd0c158d049a55bffbb Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:39:06 +0800 Subject: [PATCH 37/76] Update README.md update logo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db4d394ea..241d12ebf 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ # MetaGPT: The Multi-Agent Framework CN doc EN doc JA doc -Discord Follow -Hugging Face +Discord Follow +Hugging Face License: MIT roadmap Twitter Follow From 12117f49bd38a583154a2a9849a577a5f30d06ac Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:42:53 +0800 Subject: [PATCH 38/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 241d12ebf..b2a4f18c1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ # MetaGPT: The Multi-Agent Framework EN doc JA doc Discord Follow -Hugging Face License: MIT roadmap Twitter Follow @@ -22,6 +21,7 @@ # MetaGPT: The Multi-Agent Framework

Open in Dev Containers Open in GitHub Codespaces + Hugging Face

1. MetaGPT takes a **one line requirement** as input and outputs **user stories / competitive analysis / requirements / data structures / APIs / documents, etc.** From 7e9eb2b1017a428c8221e854083e19f6b2cc5cd5 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:46:18 +0800 Subject: [PATCH 39/76] Update README_CN.md update logo --- docs/README_CN.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/README_CN.md b/docs/README_CN.md index c3a0e92df..a5e4c6879 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -12,13 +12,17 @@ # MetaGPT: 多智能体框架 CN doc EN doc JA doc -Discord Follow -Hugging Face +Discord Follow License: MIT roadmap roadmap Twitter Follow

+

+ Open in Dev Containers + Open in GitHub Codespaces + Hugging Face +

1. MetaGPT输入**一句话的老板需求**,输出**用户故事 / 竞品分析 / 需求 / 数据结构 / APIs / 文件等** 2. MetaGPT内部包括**产品经理 / 架构师 / 项目经理 / 工程师**,它提供了一个**软件公司**的全过程与精心调配的SOP From 4b4a6613d0c8b0d696ca45bce3b1967ca0c72f7f Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:47:28 +0800 Subject: [PATCH 40/76] Update README_JA.md update logo --- docs/README_JA.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/README_JA.md b/docs/README_JA.md index 9bda95556..f930c0cc2 100644 --- a/docs/README_JA.md +++ b/docs/README_JA.md @@ -12,13 +12,17 @@ # MetaGPT: マルチエージェントフレームワーク CN doc EN doc JA doc -Discord Follow -Hugging Face +Discord Follow License: MIT roadmap roadmap Twitter Follow

+

+ Open in Dev Containers + Open in GitHub Codespaces + Hugging Face +

1. MetaGPT は、**1 行の要件** を入力とし、**ユーザーストーリー / 競合分析 / 要件 / データ構造 / API / 文書など** を出力します。 2. MetaGPT には、**プロダクト マネージャー、アーキテクト、プロジェクト マネージャー、エンジニア** が含まれています。MetaGPT は、**ソフトウェア会社のプロセス全体を、慎重に調整された SOP とともに提供します。** From 10ce3ed70245462f50fcb23eeee7708b637d65ad Mon Sep 17 00:00:00 2001 From: "hy.li" Date: Mon, 11 Sep 2023 16:59:41 +0800 Subject: [PATCH 41/76] more options to convert mermaid --- README.md | 69 ++++++++-- config/config.yaml | 2 +- metagpt/utils/mermaid.py | 73 ++++++---- metagpt/utils/mmdc_ink.py | 51 +++++++ ...rmaid_playwright.py => mmdc_playwright.py} | 119 ++++------------ metagpt/utils/mmdc_pyppeteer.py | 129 ++++++++++++++++++ 6 files changed, 312 insertions(+), 131 deletions(-) create mode 100644 metagpt/utils/mmdc_ink.py rename metagpt/utils/{mermaid_playwright.py => mmdc_playwright.py} (63%) create mode 100644 metagpt/utils/mmdc_pyppeteer.py diff --git a/README.md b/README.md index 864d56c53..a380ce1a8 100644 --- a/README.md +++ b/README.md @@ -81,29 +81,68 @@ # Step 3: Clone the repository to your local machine, and install it. - if `python setup.py install` fails with error `[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'`, try instead running `python setup.py install --user` -- To convert Mermaid charts to SVG, PNG, and PDF formats. In addition to the Node.js version of Mermaid-CLI, you now have the option to use Python version Playwright for this task. +- To convert Mermaid charts to SVG, PNG, and PDF formats. In addition to the Node.js version of Mermaid-CLI, you now have the option to use Python version Playwright, pyppeteer or mermaid.ink for this task. -- **Install Playwright** + - Playwright + - **Install Playwright** -```bash -pip install playwright -``` + ```bash + pip install playwright + ``` -- **Install the Required Browsers** + - **Install the Required Browsers** -to support PDF conversion, had better install Chrominum. + to support PDF conversion, had better install Chrominum. -```bash -playwright install --with-deps chromium -``` + ```bash + playwright install --with-deps chromium + ``` -- **modify `config.yaml`** + - **modify `config.yaml`** -uncomment MERMAID_ENGINE from config.yaml and change it to `playwright` + uncomment MERMAID_ENGINE from config.yaml and change it to `playwright` -```yaml -MERMAID_ENGINE: playwright -``` + ```yaml + MERMAID_ENGINE: playwright + ``` + + - pyppeteer + - **Install pyppeteer** + + ```bash + pip install pyppeteer + ``` + + - **Install the Required Browsers** + + ```bash + pyppeteer-install + ``` + + pyppeteer alow you use already installed browsers, if you do not want to run the above command, please set the following envirment + + ```bash + export PUPPETEER_EXECUTABLE_PATH = /path/to/your/chromium or edge or chrome + ``` + + - **modify `config.yaml`** + + uncomment MERMAID_ENGINE from config.yaml and change it to `pyppeteer` + + ```yaml + MERMAID_ENGINE: pyppeteer + ``` + + - mermaid.ink + - **modify `config.yaml`** + + uncomment MERMAID_ENGINE from config.yaml and change it to `ink` + + ```yaml + MERMAID_ENGINE: ink + ``` + + Note: this method does not support pdf export. ### Installation by Docker diff --git a/config/config.yaml b/config/config.yaml index 40d37451a..179985a6f 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -78,5 +78,5 @@ MODEL_FOR_RESEARCHER_SUMMARY: gpt-3.5-turbo MODEL_FOR_RESEARCHER_REPORT: gpt-3.5-turbo-16k ### choose the engine for mermaid conversion, -# default is nodejs, you can change it to playwright +# default is nodejs, you can change it to playwright,pyppeteer or ink # MERMAID_ENGINE: nodejs \ No newline at end of file diff --git a/metagpt/utils/mermaid.py b/metagpt/utils/mermaid.py index f395b43b2..713f49601 100644 --- a/metagpt/utils/mermaid.py +++ b/metagpt/utils/mermaid.py @@ -12,6 +12,7 @@ from metagpt.config import CONFIG from metagpt.const import PROJECT_ROOT from metagpt.logs import logger from metagpt.utils.common import check_cmd_exists +import os @@ -31,34 +32,58 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height if check_cmd_exists("mmdc") != 0: logger.warning("RUN `npm install -g @mermaid-js/mermaid-cli` to install mmdc") return -1 + engine = CONFIG.mermaid_engine.lower() - 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}..") + if engine == "nodejs": + 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}..") - if CONFIG.puppeteer_config: - subprocess.run( - [ - CONFIG.mmdc, - "-p", - CONFIG.puppeteer_config, - "-i", - str(tmp), - "-o", - output_file, - "-w", - str(width), - "-H", - str(height), - ] - ) - else: - subprocess.run([CONFIG.mmdc, "-i", str(tmp), "-o", output_file, "-w", str(width), "-H", str(height)]) + if CONFIG.puppeteer_config: + subprocess.run( + [ + CONFIG.mmdc, + "-p", + CONFIG.puppeteer_config, + "-i", + str(tmp), + "-o", + output_file, + "-w", + str(width), + "-H", + str(height), + ] + ) + else: + subprocess.run([CONFIG.mmdc, "-i", str(tmp), "-o", output_file, "-w", str(width), "-H", str(height)]) + else: + if engine not in ['playwright', 'puppeteer', 'ink']: + logger.warning(f"Unsupported mermaid engine: {engine}") + return -1 + __dirname = os.path.dirname(os.path.abspath(__file__)) + module_path = os.path.join(__dirname, f'mmdc_{engine}.py') + import sys + # 构建命令行参数 + command = [ + sys.executable, + module_path, + "-i",mermaid_code, + "-o",output_file_without_suffix + ] + + # 执行命令 + try: + result = subprocess.run(command, text=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + logger.info(result.stdout) + if result.stderr: + logger.error(result.stderr) + except subprocess.CalledProcessError as e: + logger.error(f"Command execution failed with return code {e.returncode}") + logger.error(e.output) return 0 -if CONFIG.mermaid_engine.lower() == "playwright": - from metagpt.utils.mermaid_playwright import mermaid_to_file MMC1 = """classDiagram class Main { diff --git a/metagpt/utils/mmdc_ink.py b/metagpt/utils/mmdc_ink.py new file mode 100644 index 000000000..ce50b11cd --- /dev/null +++ b/metagpt/utils/mmdc_ink.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/7/4 10:53 +@Author : alexanderwu, imjohndoe +@File : mermaid.py +""" + +import requests +import base64 +import os + +import click +@click.command() +@click.version_option() +@click.option("-i","--mermaid_code", type=str, help="mermaid code") +@click.option("-o","--output_file_without_suffix", type=str, help="output filename without suffix") +def mermaid_to_file(mermaid_code, output_file_without_suffix): + """suffix: png/svg + :param mermaid_code: mermaid code + :param output_file_without_suffix: output filename without suffix + :return: 0 if succeed, -1 if failed + """ + print('Starting mermaid_to_file command of mermaid.ink...') + + encoded_string = base64.b64encode(mermaid_code.encode()).decode() + + dir_name = os.path.dirname(output_file_without_suffix) + if dir_name and not os.path.exists(dir_name): + os.makedirs(dir_name) + with open(f"{output_file_without_suffix}.mmd", "w", encoding="utf-8") as f: + f.write(mermaid_code) + + for suffix in ["svg", "png"]: + output_file = f"{output_file_without_suffix}.{suffix}" + path_type = "svg" if suffix == "svg" else "img" + url = f"https://mermaid.ink/{path_type}/{encoded_string}" + response = requests.get(url) + + if response.status_code == 200: + with open(output_file, 'wb') as f: + f.write(response.content) + print(f"Generating {output_file}..") + else: + print(f"Failed to retrieve {suffix}") + return -1 + + return 0 + +if __name__ == "__main__": + mermaid_to_file() \ No newline at end of file diff --git a/metagpt/utils/mermaid_playwright.py b/metagpt/utils/mmdc_playwright.py similarity index 63% rename from metagpt/utils/mermaid_playwright.py rename to metagpt/utils/mmdc_playwright.py index aa04e70eb..d5d6b898e 100644 --- a/metagpt/utils/mermaid_playwright.py +++ b/metagpt/utils/mmdc_playwright.py @@ -3,22 +3,24 @@ """ @Time : 2023/9/4 16:12 @Author : Steven Lee -@File : mermaid_playwright.py +@File : mmdc_playwright.py """ import os import asyncio -from metagpt.config import CONFIG -from metagpt.const import PROJECT_ROOT -from metagpt.logs import logger - +import click from urllib.parse import urljoin + from playwright.async_api import async_playwright -import nest_asyncio -__dirname = os.path.dirname(os.path.abspath(__file__)) +@click.command() +@click.version_option() +@click.option("-i","--mermaid_code", type=str, help="mermaid code") +@click.option("-o","--output_file_without_suffix", type=str, help="output filename without suffix") +@click.option("--width",type=int,help="width",default=2048) +@click.option("--height",type=int,help="height",default=2048) +def mermaid_to_file(mermaid_code, output_file_without_suffix, width, height): - -def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048, output_formats=['png', 'svg', 'pdf']) -> int: + """ Converts the given Mermaid code to various output formats and saves them to files. @@ -27,18 +29,18 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height output_file_without_suffix (str): The output file name without the file extension. width (int, optional): The width of the output image in pixels. Defaults to 2048. height (int, optional): The height of the output image in pixels. Defaults to 2048. - output_formats (list[str], optional): The list of output formats to generate. Defaults to ['png', 'svg', 'pdf']. Returns: int: Returns 1 if the conversion and saving were successful, -1 otherwise. """ - async def mermaid_to_file0(mermaid_code, output_file_without_suffix, width=2048, height=2048, output_formats=['png', 'svg', 'pdf'])-> int: + __dirname = os.path.dirname(os.path.abspath(__file__)) + + async def mermaid_to_file0(mermaid_code, output_file_without_suffix, width=2048, height=2048, suffixes=['png', 'svg', 'pdf'])-> int: async with async_playwright() as p: browser = await p.chromium.launch() device_scale_factor = 1.0 - context = await browser.new_context( viewport={'width': width, 'height': height}, device_scale_factor=device_scale_factor, @@ -79,37 +81,19 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height svgElement.appendChild(style); } - let title = null; - let desc = null; - - if (svgElement.firstChild instanceof SVGTitleElement) { - title = svgElement.firstChild.textContent; - } - - for (const svgNode of svgElement.children) { - if (svgNode instanceof SVGDescElement) { - desc = svgNode.textContent; - break; - } - } - - return { - title, - desc - }; }''', [mermaid_code, mermaid_config, my_css, background_color]) - if 'svg' in output_formats : + if 'svg' in suffixes : svg_xml = await page.evaluate('''() => { const svg = document.querySelector('svg'); const xmlSerializer = new XMLSerializer(); return xmlSerializer.serializeToString(svg); }''') - # result[f'{output_file_without_suffix}.svg'] = svg_xml + print(f"Generating {output_file_without_suffix}.svg..") with open(f'{output_file_without_suffix}.svg', 'wb') as f: f.write(svg_xml.encode('utf-8')) - if 'png' in output_formats: + if 'png' in suffixes: clip = await page.evaluate('''() => { const svg = document.querySelector('svg'); const rect = svg.getBoundingClientRect(); @@ -122,78 +106,31 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height }''') await page.set_viewport_size({'width': clip['x'] + clip['width'], 'height': clip['y'] + clip['height']}) screenshot = await page.screenshot(clip=clip, omit_background=True, scale='device') + print(f"Generating {output_file_without_suffix}.png..") with open(f'{output_file_without_suffix}.png', 'wb') as f: f.write(screenshot) - if 'pdf' in output_formats: + if 'pdf' in suffixes: pdf_data = await page.pdf(scale=device_scale_factor) + print(f"Generating {output_file_without_suffix}.pdf..") with open(f'{output_file_without_suffix}.pdf', 'wb') as f: f.write(pdf_data) return 1 except Exception as e: - logger.error(e) + print(e) return -1 finally: await browser.close() + dir_name = os.path.dirname(output_file_without_suffix) + if dir_name and not os.path.exists(dir_name): + os.makedirs(dir_name) with open(f"{output_file_without_suffix}.mmd", "w", encoding="utf-8") as f: f.write(mermaid_code) - nest_asyncio.apply() + suffixes = ['png', 'svg', 'pdf'] loop = asyncio.new_event_loop() - result = loop.run_until_complete(mermaid_to_file0(mermaid_code, output_file_without_suffix, width, height, output_formats)) + result = loop.run_until_complete(mermaid_to_file0(mermaid_code, output_file_without_suffix, width, height, suffixes)) loop.close() return result -MMC1 = """classDiagram - class Main { - -SearchEngine search_engine - +main() str - } - class SearchEngine { - -Index index - -Ranking ranking - -Summary summary - +search(query: str) str - } - class Index { - -KnowledgeBase knowledge_base - +create_index(data: dict) - +query_index(query: str) list - } - class Ranking { - +rank_results(results: list) list - } - class Summary { - +summarize_results(results: list) str - } - class KnowledgeBase { - +update(data: dict) - +fetch_data(query: str) dict - } - Main --> SearchEngine - SearchEngine --> Index - SearchEngine --> Ranking - SearchEngine --> Summary - Index --> KnowledgeBase""" - -MMC2 = """sequenceDiagram - participant M as Main - participant SE as SearchEngine - participant I as Index - participant R as Ranking - participant S as Summary - participant KB as KnowledgeBase - M->>SE: search(query) - SE->>I: query_index(query) - I->>KB: fetch_data(query) - KB-->>I: return data - I-->>SE: return results - SE->>R: rank_results(results) - R-->>SE: return ranked_results - SE->>S: summarize_results(ranked_results) - S-->>SE: return summary - SE-->>M: return summary""" - - if __name__ == "__main__": - # logger.info(print_members(print_members)) - mermaid_to_file(MMC1, PROJECT_ROOT / "MMC1") - mermaid_to_file(MMC2, PROJECT_ROOT / "MMC2") + mermaid_to_file() + diff --git a/metagpt/utils/mmdc_pyppeteer.py b/metagpt/utils/mmdc_pyppeteer.py new file mode 100644 index 000000000..e6986bc76 --- /dev/null +++ b/metagpt/utils/mmdc_pyppeteer.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/4 16:12 +@Author : Steven Lee +@File : mmdc_pyppeteer.py +""" +import asyncio +import click +import os +from urllib.parse import urljoin +import sys +from pyppeteer import launch + +@click.command() +@click.version_option() +@click.option("-i","--mermaid_code", type=str, help="mermaid code") +@click.option("-o","--output_file_without_suffix", type=str, help="output filename without suffix") +@click.option("--width",type=int,help="width",default=2048) +@click.option("--height",type=int,help="height",default=2048) +def mermaid_to_file(mermaid_code, output_file_without_suffix, width, height): + """ + Converts the given Mermaid code to various output formats and saves them to files. + + Args: + mermaid_code (str): The Mermaid code to convert. + output_file_without_suffix (str): The output file name without the file extension. + width (int, optional): The width of the output image in pixels. Defaults to 2048. + height (int, optional): The height of the output image in pixels. Defaults to 2048. + + Returns: + int: Returns 1 if the conversion and saving were successful, -1 otherwise. + """ + + async def mermaid_to_file0(mermaid_code, output_file_without_suffix, width=2048, height=2048, suffixes=['png', 'svg', 'pdf'])-> int: + __dirname = os.path.dirname(os.path.abspath(__file__)) + browser = await launch(headless=True, + executablePath=os.getenv('PUPPETEER_EXECUTABLE_PATH',"/opt/homebrew/bin/chromium"), + args=['--disable-extensions',"--no-sandbox"] + ) + page = await browser.newPage() + device_scale_factor = 1.0 + + async def console_message(msg): + print(msg.text) + page.on('console', console_message) + + try: + await page.setViewport(viewport={'width': width, 'height': height, 'deviceScaleFactor': device_scale_factor}) + + mermaid_html_path = os.path.abspath( + os.path.join(__dirname, 'index.html')) + mermaid_html_url = urljoin('file:', mermaid_html_path) + await page.goto(mermaid_html_url) + + await page.querySelector("div#container") + mermaid_config = {} + background_color = "#ffffff" + my_css = "" + await page.evaluate(f'document.body.style.background = "{background_color}";') + + metadata = await page.evaluate('''async ([definition, mermaidConfig, myCSS, backgroundColor]) => { + const { mermaid, zenuml } = globalThis; + await mermaid.registerExternalDiagrams([zenuml]); + mermaid.initialize({ startOnLoad: false, ...mermaidConfig }); + const { svg } = await mermaid.render('my-svg', definition, document.getElementById('container')); + document.getElementById('container').innerHTML = svg; + const svgElement = document.querySelector('svg'); + svgElement.style.backgroundColor = backgroundColor; + + if (myCSS) { + const style = document.createElementNS('http://www.w3.org/2000/svg', 'style'); + style.appendChild(document.createTextNode(myCSS)); + svgElement.appendChild(style); + } + }''', [mermaid_code, mermaid_config, my_css, background_color]) + + if 'svg' in suffixes : + svg_xml = await page.evaluate('''() => { + const svg = document.querySelector('svg'); + const xmlSerializer = new XMLSerializer(); + return xmlSerializer.serializeToString(svg); + }''') + print(f"Generating {output_file_without_suffix}.svg..") + with open(f'{output_file_without_suffix}.svg', 'wb') as f: + f.write(svg_xml.encode('utf-8')) + + if 'png' in suffixes: + clip = await page.evaluate('''() => { + const svg = document.querySelector('svg'); + const rect = svg.getBoundingClientRect(); + return { + x: Math.floor(rect.left), + y: Math.floor(rect.top), + width: Math.ceil(rect.width), + height: Math.ceil(rect.height) + }; + }''') + await page.setViewport({'width': clip['x'] + clip['width'], 'height': clip['y'] + clip['height'], 'deviceScaleFactor': device_scale_factor}) + screenshot = await page.screenshot(clip=clip, omit_background=True, scale='device') + print(f"Generating {output_file_without_suffix}.png..") + with open(f'{output_file_without_suffix}.png', 'wb') as f: + f.write(screenshot) + if 'pdf' in suffixes: + pdf_data = await page.pdf(scale=device_scale_factor) + print(f"Generating {output_file_without_suffix}.pdf..") + with open(f'{output_file_without_suffix}.pdf', 'wb') as f: + f.write(pdf_data) + return 1 + except Exception as e: + print(e) + return -1 + finally: + await browser.close() + + + suffixes = ['png', 'svg', 'pdf'] + dir_name = os.path.dirname(output_file_without_suffix) + if dir_name and not os.path.exists(dir_name): + os.makedirs(dir_name) + with open(f"{output_file_without_suffix}.mmd", "w", encoding="utf-8") as f: + f.write(mermaid_code) + loop = asyncio.new_event_loop() + result = loop.run_until_complete(mermaid_to_file0(mermaid_code, output_file_without_suffix, width, height,suffixes)) + loop.close() + return result + +if __name__ == "__main__": + mermaid_to_file() From b2a6cacd1e295caab71d535381e3be9bb182a48c Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:28:52 +0800 Subject: [PATCH 42/76] Delete docs/resources/20230811-214014.jpg rm wechat img --- docs/resources/20230811-214014.jpg | Bin 59081 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/resources/20230811-214014.jpg diff --git a/docs/resources/20230811-214014.jpg b/docs/resources/20230811-214014.jpg deleted file mode 100644 index 2006f2646d960305e5b41ec53404ac5d235d411c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59081 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 Db^17V From c4ba490b268fab969008275ccdc3c1432fbc0eb5 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:29:36 +0800 Subject: [PATCH 43/76] Delete docs/resources/MetaGPT-WeChat-Personal.jpeg rm outdated wechat img --- docs/resources/MetaGPT-WeChat-Personal.jpeg | Bin 82272 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/resources/MetaGPT-WeChat-Personal.jpeg diff --git a/docs/resources/MetaGPT-WeChat-Personal.jpeg b/docs/resources/MetaGPT-WeChat-Personal.jpeg deleted file mode 100644 index f6b48577d132d0f30353585f54d6aeebaee98e01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82272 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

+ AgentStore Waitlist Open in Dev Containers Open in GitHub Codespaces - Hugging Face + Hugging Face

1. MetaGPT takes a **one line requirement** as input and outputs **user stories / competitive analysis / requirements / data structures / APIs / documents, etc.** From fe4bb73379be7cfa220b5504442779c5fc9717a0 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:10:08 +0800 Subject: [PATCH 48/76] Update README_CN.md update info and add agentstore waitlist --- docs/README_CN.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/README_CN.md b/docs/README_CN.md index a5e4c6879..4ee4c7408 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -9,19 +9,20 @@ # MetaGPT: 多智能体框架

-CN doc -EN doc -JA doc -Discord Follow -License: MIT -roadmap -roadmap +CN doc +EN doc +JA doc +Discord Follow +License: MIT +roadmap Twitter Follow

+

+ AgentStore Waitlist Open in Dev Containers Open in GitHub Codespaces - Hugging Face + Hugging Face

1. MetaGPT输入**一句话的老板需求**,输出**用户故事 / 竞品分析 / 需求 / 数据结构 / APIs / 文件等** From c28034ccbc90ca7f8ae7a68e23a6a7c34f32d8e1 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:10:42 +0800 Subject: [PATCH 49/76] Update README_JA.md update info and add agentstore waitlist --- docs/README_JA.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/README_JA.md b/docs/README_JA.md index f930c0cc2..158ad8ceb 100644 --- a/docs/README_JA.md +++ b/docs/README_JA.md @@ -9,19 +9,20 @@ # MetaGPT: マルチエージェントフレームワーク

-CN doc -EN doc -JA doc -Discord Follow -License: MIT +CN doc +EN doc +JA doc +Discord Follow +License: MIT roadmap -roadmap Twitter Follow

+

+ AgentStore Waitlist Open in Dev Containers Open in GitHub Codespaces - Hugging Face + Hugging Face

1. MetaGPT は、**1 行の要件** を入力とし、**ユーザーストーリー / 競合分析 / 要件 / データ構造 / API / 文書など** を出力します。 From 4b438fc8449b3b94136a26cfbccd00b8794b05a1 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Wed, 13 Sep 2023 12:28:07 +0900 Subject: [PATCH 50/76] Update README_JA.md --- docs/README_JA.md | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/docs/README_JA.md b/docs/README_JA.md index 158ad8ceb..d5d70eef4 100644 --- a/docs/README_JA.md +++ b/docs/README_JA.md @@ -43,6 +43,10 @@ ## 例(GPT-4 で完全生成) ## インストール +### インストールビデオガイド + +- [Matthew Berman: How To Install MetaGPT - Build A Startup With One Prompt!!](https://youtu.be/uT75J_KG_aY) + ### 伝統的なインストール ```bash @@ -66,16 +70,16 @@ # ステップ 3: リポジトリをローカルマシンにクローンし、 - このツールをグローバルにインストールする[問題を抱えている](https://github.com/mermaidjs/mermaid.cli/issues/15)人もいます。ローカルにインストールするのが代替の解決策です、 - ```bash - npm install @mermaid-js/mermaid-cli - ``` + ```bash + npm install @mermaid-js/mermaid-cli + ``` - config.yml に mmdc のコンフィギュレーションを記述するのを忘れないこと - ```yml - PUPPETEER_CONFIG: "./config/puppeteer-config.json" - MMDC: "./node_modules/.bin/mmdc" - ``` + ```yml + PUPPETEER_CONFIG: "./config/puppeteer-config.json" + MMDC: "./node_modules/.bin/mmdc" + ``` - もし `python setup.py install` がエラー `[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'` で失敗したら、代わりに `python setup.py install --user` を実行してみてください @@ -140,18 +144,24 @@ # 設定ファイルをコピーし、必要な修正を加える。 ## チュートリアル: スタートアップの開始 ```shell +# スクリプトの実行 python startup.py "Write a cli snake game" -# コードレビューを利用すれば、コストはかかるが、より良いコード品質を選ぶことができます。 +# プロジェクトの実施にエンジニアを雇わないこと +python startup.py "Write a cli snake game" --implement False +# エンジニアを雇い、コードレビューを行う python startup.py "Write a cli snake game" --code_review True ``` スクリプトを実行すると、`workspace/` ディレクトリに新しいプロジェクトが見つかります。 + ### プラットフォームまたはツールの設定 要件を述べるときに、どのプラットフォームまたはツールを使用するかを指定できます。 + ```shell python startup.py "pygame をベースとした cli ヘビゲームを書く" ``` + ### 使用方法 ``` @@ -200,16 +210,18 @@ ### コードウォークスルー `examples` でシングル・ロール(ナレッジ・ベース付き)と LLM のみの例を詳しく見ることができます。 ## クイックスタート + ローカル環境のインストールや設定は、ユーザーによっては難しいものです。以下のチュートリアルで MetaGPT の魅力をすぐに体験できます。 - [MetaGPT クイックスタート](https://deepwisdom.feishu.cn/wiki/CyY9wdJc4iNqArku3Lncl4v8n2b) -試着する Huggingface Space +Hugging Face Space で試す - https://huggingface.co/spaces/deepwisdom/MetaGPT ## 引用 現時点では、[Arxiv 論文](https://arxiv.org/abs/2308.00352)を引用してください: + ```bibtex @misc{hong2023metagpt, title={MetaGPT: Meta Programming for Multi-Agent Collaborative Framework}, @@ -233,3 +245,10 @@ ## お問い合わせ先 ## デモ https://github.com/geekan/MetaGPT/assets/2707039/5e8c1062-8c35-440f-bb20-2b0320f8d27d + +## 参加する + +📢 Discord チャンネルに参加してください! +https://discord.gg/ZRHeExS6xv + +お会いできることを楽しみにしています! 🎉 From 69ea116d1a265b4b9c0e112832392d5decd7aab1 Mon Sep 17 00:00:00 2001 From: "hy.li" Date: Wed, 13 Sep 2023 12:41:38 +0800 Subject: [PATCH 51/76] change to async --- metagpt/actions/design_api.py | 18 +-- metagpt/utils/mermaid.py | 71 ++++++------ metagpt/utils/mmdc_ink.py | 52 ++++----- metagpt/utils/mmdc_playwright.py | 179 +++++++++++++----------------- metagpt/utils/mmdc_pyppeteer.py | 182 ++++++++++++++----------------- 5 files changed, 225 insertions(+), 277 deletions(-) diff --git a/metagpt/actions/design_api.py b/metagpt/actions/design_api.py index abd1f9d4c..4d17e4f5e 100644 --- a/metagpt/actions/design_api.py +++ b/metagpt/actions/design_api.py @@ -103,23 +103,23 @@ class WriteDesign(Action): pass # Folder does not exist, but we don't care workspace.mkdir(parents=True, exist_ok=True) - def _save_prd(self, docs_path, resources_path, prd): + async def _save_prd(self, docs_path, resources_path, prd): 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') + await 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, content): + async def _save_system_design(self, docs_path, resources_path, content): data_api_design = CodeParser.parse_code(block="Data structures and interface definitions", text=content) seq_flow = CodeParser.parse_code(block="Program call flow", text=content) - mermaid_to_file(data_api_design, resources_path / 'data_api_design') - mermaid_to_file(seq_flow, resources_path / 'seq_flow') + await mermaid_to_file(data_api_design, resources_path / 'data_api_design') + await 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(content) - def _save(self, context, system_design): + async def _save(self, context, system_design): if isinstance(system_design, ActionOutput): content = system_design.content ws_name = CodeParser.parse_str(block="Python package name", text=content) @@ -132,13 +132,13 @@ class WriteDesign(Action): resources_path = workspace / 'resources' docs_path.mkdir(parents=True, exist_ok=True) resources_path.mkdir(parents=True, exist_ok=True) - self._save_prd(docs_path, resources_path, context[-1].content) - self._save_system_design(docs_path, resources_path, content) + await self._save_prd(docs_path, resources_path, context[-1].content) + await self._save_system_design(docs_path, resources_path, content) async def run(self, context): prompt = PROMPT_TEMPLATE.format(context=context, format_example=FORMAT_EXAMPLE) # system_design = await self._aask(prompt) system_design = await self._aask_v1(prompt, "system_design", OUTPUT_MAPPING) - self._save(context, system_design) + await self._save(context, system_design) return system_design \ No newline at end of file diff --git a/metagpt/utils/mermaid.py b/metagpt/utils/mermaid.py index b13199a93..d2cce3965 100644 --- a/metagpt/utils/mermaid.py +++ b/metagpt/utils/mermaid.py @@ -2,9 +2,10 @@ # -*- coding: utf-8 -*- """ @Time : 2023/7/4 10:53 -@Author : alexanderwu +@Author : alexanderwu alitrack @File : mermaid.py """ +import asyncio import subprocess from pathlib import Path @@ -15,18 +16,22 @@ from metagpt.utils.common import check_cmd_exists import os import sys -def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048) -> int: +async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048) -> int: """suffix: png/svg/pdf :param mermaid_code: mermaid code :param output_file_without_suffix: output filename :param width: :param height: - :return: 0 if succed, -1 if failed + :return: 0 if succeed, -1 if failed """ # Write the Mermaid code to a temporary file + dir_name = os.path.dirname(output_file_without_suffix) + if dir_name and not os.path.exists(dir_name): + os.makedirs(dir_name) tmp = Path(f"{output_file_without_suffix}.mmd") tmp.write_text(mermaid_code, encoding="utf-8") + engine = CONFIG.mermaid_engine.lower() if engine == "nodejs": if check_cmd_exists("mmdc") != 0: @@ -39,8 +44,7 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height logger.info(f"Generating {output_file}..") if CONFIG.puppeteer_config: - subprocess.run( - [ + commands =[ CONFIG.mmdc, "-p", CONFIG.puppeteer_config, @@ -53,33 +57,32 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height "-H", str(height), ] - ) else: - subprocess.run([CONFIG.mmdc, "-i", str(tmp), "-o", output_file, "-w", str(width), "-H", str(height)]) - else: - if engine not in ['playwright', 'pyppeteer', 'ink']: - logger.warning(f"Unsupported mermaid engine: {engine}") - return -1 - __dirname = os.path.dirname(os.path.abspath(__file__)) - module_path = os.path.join(__dirname, f'mmdc_{engine}.py') - - # 构建命令行参数 - command = [ - sys.executable, - module_path, - "-i",mermaid_code, - "-o",output_file_without_suffix - ] + commands =[CONFIG.mmdc, "-i", str(tmp), "-o", output_file, "-w", str(width), "-H", str(height)] + process = await asyncio.create_subprocess_exec( + *commands, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) - # 执行命令 - try: - result = subprocess.run(command, text=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - logger.info(result.stdout) - if result.stderr: - logger.error(result.stderr) - except subprocess.CalledProcessError as e: - logger.error(f"Command execution failed with return code {e.returncode}") - logger.error(e.output) + stdout, stderr = await process.communicate() + if stdout: + logger.info(stdout.decode()) + if stderr: + logger.error(stderr.decode()) + else: + + if engine =='playwright': + from metagpt.utils.mmdc_playwright import mermaid_to_file + return await mermaid_to_file(mermaid_code, output_file_without_suffix, width, height) + elif engine =='pyppeteer': + from metagpt.utils.mmdc_pyppeteer import mermaid_to_file + return await mermaid_to_file(mermaid_code, output_file_without_suffix, width, height) + elif engine =='ink': + from metagpt.utils.mmdc_ink import mermaid_to_file + return await mermaid_to_file(mermaid_code, output_file_without_suffix) + else: + logger.warning(f"Unsupported mermaid engine: {engine}") return 0 @@ -134,7 +137,9 @@ MMC2 = """sequenceDiagram SE-->>M: return summary""" + if __name__ == "__main__": - # logger.info(print_members(print_members)) - mermaid_to_file(MMC1, PROJECT_ROOT / "tmp/1.png") - mermaid_to_file(MMC2, PROJECT_ROOT / "tmp/2.png") + loop = asyncio.new_event_loop() + result = loop.run_until_complete(mermaid_to_file(MMC1, PROJECT_ROOT / f"{CONFIG.mermaid_engine}/1")) + result = loop.run_until_complete(mermaid_to_file(MMC2, PROJECT_ROOT / f"{CONFIG.mermaid_engine}/1")) + loop.close() diff --git a/metagpt/utils/mmdc_ink.py b/metagpt/utils/mmdc_ink.py index ce50b11cd..3d91cde9d 100644 --- a/metagpt/utils/mmdc_ink.py +++ b/metagpt/utils/mmdc_ink.py @@ -1,51 +1,41 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ -@Time : 2023/7/4 10:53 -@Author : alexanderwu, imjohndoe +@Time : 2023/9/4 16:12 +@Author : alitrack @File : mermaid.py """ - -import requests import base64 import os -import click -@click.command() -@click.version_option() -@click.option("-i","--mermaid_code", type=str, help="mermaid code") -@click.option("-o","--output_file_without_suffix", type=str, help="output filename without suffix") -def mermaid_to_file(mermaid_code, output_file_without_suffix): +from aiohttp import ClientSession,ClientError +from metagpt.logs import logger + + +async def mermaid_to_file(mermaid_code, output_file_without_suffix): """suffix: png/svg :param mermaid_code: mermaid code :param output_file_without_suffix: output filename without suffix :return: 0 if succeed, -1 if failed """ - print('Starting mermaid_to_file command of mermaid.ink...') - encoded_string = base64.b64encode(mermaid_code.encode()).decode() - dir_name = os.path.dirname(output_file_without_suffix) - if dir_name and not os.path.exists(dir_name): - os.makedirs(dir_name) - with open(f"{output_file_without_suffix}.mmd", "w", encoding="utf-8") as f: - f.write(mermaid_code) - for suffix in ["svg", "png"]: output_file = f"{output_file_without_suffix}.{suffix}" path_type = "svg" if suffix == "svg" else "img" url = f"https://mermaid.ink/{path_type}/{encoded_string}" - response = requests.get(url) - - if response.status_code == 200: - with open(output_file, 'wb') as f: - f.write(response.content) - print(f"Generating {output_file}..") - else: - print(f"Failed to retrieve {suffix}") - return -1 - + async with ClientSession() as session: + try: + async with session.get(url) as response: + if response.status == 200: + text = await response.content.read() + with open(output_file, 'wb') as f: + f.write(text) + logger.info(f"Generating {output_file}..") + else: + logger.error(f"Failed to generate {output_file}") + return -1 + except ClientError as e: + logger.error(f"network error: {e}") + return -1 return 0 - -if __name__ == "__main__": - mermaid_to_file() \ No newline at end of file diff --git a/metagpt/utils/mmdc_playwright.py b/metagpt/utils/mmdc_playwright.py index d5d6b898e..bdbfd82ff 100644 --- a/metagpt/utils/mmdc_playwright.py +++ b/metagpt/utils/mmdc_playwright.py @@ -5,22 +5,13 @@ @Author : Steven Lee @File : mmdc_playwright.py """ + import os -import asyncio -import click from urllib.parse import urljoin - from playwright.async_api import async_playwright +from metagpt.logs import logger -@click.command() -@click.version_option() -@click.option("-i","--mermaid_code", type=str, help="mermaid code") -@click.option("-o","--output_file_without_suffix", type=str, help="output filename without suffix") -@click.option("--width",type=int,help="width",default=2048) -@click.option("--height",type=int,help="height",default=2048) -def mermaid_to_file(mermaid_code, output_file_without_suffix, width, height): - - +async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048)-> int: """ Converts the given Mermaid code to various output formats and saves them to files. @@ -33,104 +24,88 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width, height): Returns: int: Returns 1 if the conversion and saving were successful, -1 otherwise. """ - + suffixes=['png', 'svg', 'pdf'] __dirname = os.path.dirname(os.path.abspath(__file__)) - async def mermaid_to_file0(mermaid_code, output_file_without_suffix, width=2048, height=2048, suffixes=['png', 'svg', 'pdf'])-> int: + async with async_playwright() as p: + browser = await p.chromium.launch() + device_scale_factor = 1.0 + context = await browser.new_context( + viewport={'width': width, 'height': height}, + device_scale_factor=device_scale_factor, + ) + page = await context.new_page() - async with async_playwright() as p: - browser = await p.chromium.launch() - device_scale_factor = 1.0 - context = await browser.new_context( - viewport={'width': width, 'height': height}, - device_scale_factor=device_scale_factor, - ) - page = await context.new_page() + async def console_message(msg): + logger.info(msg.text) + page.on('console', console_message) - async def console_message(msg): - print(msg.text) - page.on('console', console_message) + try: + await page.set_viewport_size({'width': width, 'height': height}) - try: - await page.set_viewport_size({'width': width, 'height': height}) + mermaid_html_path = os.path.abspath( + os.path.join(__dirname, 'index.html')) + mermaid_html_url = urljoin('file:', mermaid_html_path) + await page.goto(mermaid_html_url) + await page.wait_for_load_state("networkidle") - mermaid_html_path = os.path.abspath( - os.path.join(__dirname, 'index.html')) - mermaid_html_url = urljoin('file:', mermaid_html_path) - await page.goto(mermaid_html_url) - await page.wait_for_load_state("networkidle") + await page.wait_for_selector("div#container", state="attached") + mermaid_config = {} + background_color = "#ffffff" + my_css = "" + await page.evaluate(f'document.body.style.background = "{background_color}";') - await page.wait_for_selector("div#container", state="attached") - mermaid_config = {} - background_color = "#ffffff" - my_css = "" - await page.evaluate(f'document.body.style.background = "{background_color}";') + metadata = await page.evaluate('''async ([definition, mermaidConfig, myCSS, backgroundColor]) => { + const { mermaid, zenuml } = globalThis; + await mermaid.registerExternalDiagrams([zenuml]); + mermaid.initialize({ startOnLoad: false, ...mermaidConfig }); + const { svg } = await mermaid.render('my-svg', definition, document.getElementById('container')); + document.getElementById('container').innerHTML = svg; + const svgElement = document.querySelector('svg'); + svgElement.style.backgroundColor = backgroundColor; - metadata = await page.evaluate('''async ([definition, mermaidConfig, myCSS, backgroundColor]) => { - const { mermaid, zenuml } = globalThis; - await mermaid.registerExternalDiagrams([zenuml]); - mermaid.initialize({ startOnLoad: false, ...mermaidConfig }); - const { svg } = await mermaid.render('my-svg', definition, document.getElementById('container')); - document.getElementById('container').innerHTML = svg; - const svgElement = document.querySelector('svg'); - svgElement.style.backgroundColor = backgroundColor; + if (myCSS) { + const style = document.createElementNS('http://www.w3.org/2000/svg', 'style'); + style.appendChild(document.createTextNode(myCSS)); + svgElement.appendChild(style); + } - if (myCSS) { - const style = document.createElementNS('http://www.w3.org/2000/svg', 'style'); - style.appendChild(document.createTextNode(myCSS)); - svgElement.appendChild(style); - } + }''', [mermaid_code, mermaid_config, my_css, background_color]) - }''', [mermaid_code, mermaid_config, my_css, background_color]) - - if 'svg' in suffixes : - svg_xml = await page.evaluate('''() => { - const svg = document.querySelector('svg'); - const xmlSerializer = new XMLSerializer(); - return xmlSerializer.serializeToString(svg); - }''') - print(f"Generating {output_file_without_suffix}.svg..") - with open(f'{output_file_without_suffix}.svg', 'wb') as f: - f.write(svg_xml.encode('utf-8')) - - if 'png' in suffixes: - clip = await page.evaluate('''() => { - const svg = document.querySelector('svg'); - const rect = svg.getBoundingClientRect(); - return { - x: Math.floor(rect.left), - y: Math.floor(rect.top), - width: Math.ceil(rect.width), - height: Math.ceil(rect.height) - }; - }''') - await page.set_viewport_size({'width': clip['x'] + clip['width'], 'height': clip['y'] + clip['height']}) - screenshot = await page.screenshot(clip=clip, omit_background=True, scale='device') - print(f"Generating {output_file_without_suffix}.png..") - with open(f'{output_file_without_suffix}.png', 'wb') as f: - f.write(screenshot) - if 'pdf' in suffixes: - pdf_data = await page.pdf(scale=device_scale_factor) - print(f"Generating {output_file_without_suffix}.pdf..") - with open(f'{output_file_without_suffix}.pdf', 'wb') as f: - f.write(pdf_data) - return 1 - except Exception as e: - print(e) - return -1 - finally: - await browser.close() - dir_name = os.path.dirname(output_file_without_suffix) - if dir_name and not os.path.exists(dir_name): - os.makedirs(dir_name) - with open(f"{output_file_without_suffix}.mmd", "w", encoding="utf-8") as f: - f.write(mermaid_code) - suffixes = ['png', 'svg', 'pdf'] - loop = asyncio.new_event_loop() - result = loop.run_until_complete(mermaid_to_file0(mermaid_code, output_file_without_suffix, width, height, suffixes)) - loop.close() - return result - -if __name__ == "__main__": - mermaid_to_file() + if 'svg' in suffixes : + svg_xml = await page.evaluate('''() => { + const svg = document.querySelector('svg'); + const xmlSerializer = new XMLSerializer(); + return xmlSerializer.serializeToString(svg); + }''') + logger.info(f"Generating {output_file_without_suffix}.svg..") + with open(f'{output_file_without_suffix}.svg', 'wb') as f: + f.write(svg_xml.encode('utf-8')) + if 'png' in suffixes: + clip = await page.evaluate('''() => { + const svg = document.querySelector('svg'); + const rect = svg.getBoundingClientRect(); + return { + x: Math.floor(rect.left), + y: Math.floor(rect.top), + width: Math.ceil(rect.width), + height: Math.ceil(rect.height) + }; + }''') + await page.set_viewport_size({'width': clip['x'] + clip['width'], 'height': clip['y'] + clip['height']}) + screenshot = await page.screenshot(clip=clip, omit_background=True, scale='device') + logger.info(f"Generating {output_file_without_suffix}.png..") + with open(f'{output_file_without_suffix}.png', 'wb') as f: + f.write(screenshot) + if 'pdf' in suffixes: + pdf_data = await page.pdf(scale=device_scale_factor) + logger.info(f"Generating {output_file_without_suffix}.pdf..") + with open(f'{output_file_without_suffix}.pdf', 'wb') as f: + f.write(pdf_data) + return 0 + except Exception as e: + logger.error(e) + return -1 + finally: + await browser.close() diff --git a/metagpt/utils/mmdc_pyppeteer.py b/metagpt/utils/mmdc_pyppeteer.py index f3e00d053..56367236f 100644 --- a/metagpt/utils/mmdc_pyppeteer.py +++ b/metagpt/utils/mmdc_pyppeteer.py @@ -2,23 +2,15 @@ # -*- coding: utf-8 -*- """ @Time : 2023/9/4 16:12 -@Author : Steven Lee +@Author : alitrack @File : mmdc_pyppeteer.py """ -import asyncio -import click import os from urllib.parse import urljoin -import sys from pyppeteer import launch +from metagpt.logs import logger -@click.command() -@click.version_option() -@click.option("-i","--mermaid_code", type=str, help="mermaid code") -@click.option("-o","--output_file_without_suffix", type=str, help="output filename without suffix") -@click.option("--width",type=int,help="width",default=2048) -@click.option("--height",type=int,help="height",default=2048) -def mermaid_to_file(mermaid_code, output_file_without_suffix, width, height): +async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048)-> int: """ Converts the given Mermaid code to various output formats and saves them to files. @@ -31,104 +23,90 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width, height): Returns: int: Returns 1 if the conversion and saving were successful, -1 otherwise. """ + suffixes = ['png', 'svg', 'pdf'] + __dirname = os.path.dirname(os.path.abspath(__file__)) - async def mermaid_to_file0(mermaid_code, output_file_without_suffix, width=2048, height=2048, suffixes=['png', 'svg', 'pdf'])-> int: - __dirname = os.path.dirname(os.path.abspath(__file__)) - executablePath = os.getenv('PUPPETEER_EXECUTABLE_PATH',"") - if executablePath: - browser = await launch(headless=True, - executablePath=executablePath, - args=['--disable-extensions',"--no-sandbox"] - ) - else: - print("Please set the environment variable:PUPPETEER_EXECUTABLE_PATH.") - return -1 - page = await browser.newPage() - device_scale_factor = 1.0 + executablePath = os.getenv('PUPPETEER_EXECUTABLE_PATH',"") + if executablePath: + browser = await launch(headless=True, + executablePath=executablePath, + args=['--disable-extensions',"--no-sandbox"] + ) + else: + logger.error("Please set the environment variable:PUPPETEER_EXECUTABLE_PATH.") + return -1 + page = await browser.newPage() + device_scale_factor = 1.0 - async def console_message(msg): - print(msg.text) - page.on('console', console_message) + async def console_message(msg): + logger.info(msg.text) + page.on('console', console_message) - try: - await page.setViewport(viewport={'width': width, 'height': height, 'deviceScaleFactor': device_scale_factor}) + try: + await page.setViewport(viewport={'width': width, 'height': height, 'deviceScaleFactor': device_scale_factor}) - mermaid_html_path = os.path.abspath( - os.path.join(__dirname, 'index.html')) - mermaid_html_url = urljoin('file:', mermaid_html_path) - await page.goto(mermaid_html_url) + mermaid_html_path = os.path.abspath( + os.path.join(__dirname, 'index.html')) + mermaid_html_url = urljoin('file:', mermaid_html_path) + await page.goto(mermaid_html_url) - await page.querySelector("div#container") - mermaid_config = {} - background_color = "#ffffff" - my_css = "" - await page.evaluate(f'document.body.style.background = "{background_color}";') + await page.querySelector("div#container") + mermaid_config = {} + background_color = "#ffffff" + my_css = "" + await page.evaluate(f'document.body.style.background = "{background_color}";') - metadata = await page.evaluate('''async ([definition, mermaidConfig, myCSS, backgroundColor]) => { - const { mermaid, zenuml } = globalThis; - await mermaid.registerExternalDiagrams([zenuml]); - mermaid.initialize({ startOnLoad: false, ...mermaidConfig }); - const { svg } = await mermaid.render('my-svg', definition, document.getElementById('container')); - document.getElementById('container').innerHTML = svg; - const svgElement = document.querySelector('svg'); - svgElement.style.backgroundColor = backgroundColor; + metadata = await page.evaluate('''async ([definition, mermaidConfig, myCSS, backgroundColor]) => { + const { mermaid, zenuml } = globalThis; + await mermaid.registerExternalDiagrams([zenuml]); + mermaid.initialize({ startOnLoad: false, ...mermaidConfig }); + const { svg } = await mermaid.render('my-svg', definition, document.getElementById('container')); + document.getElementById('container').innerHTML = svg; + const svgElement = document.querySelector('svg'); + svgElement.style.backgroundColor = backgroundColor; - if (myCSS) { - const style = document.createElementNS('http://www.w3.org/2000/svg', 'style'); - style.appendChild(document.createTextNode(myCSS)); - svgElement.appendChild(style); - } - }''', [mermaid_code, mermaid_config, my_css, background_color]) + if (myCSS) { + const style = document.createElementNS('http://www.w3.org/2000/svg', 'style'); + style.appendChild(document.createTextNode(myCSS)); + svgElement.appendChild(style); + } + }''', [mermaid_code, mermaid_config, my_css, background_color]) - if 'svg' in suffixes : - svg_xml = await page.evaluate('''() => { - const svg = document.querySelector('svg'); - const xmlSerializer = new XMLSerializer(); - return xmlSerializer.serializeToString(svg); - }''') - print(f"Generating {output_file_without_suffix}.svg..") - with open(f'{output_file_without_suffix}.svg', 'wb') as f: - f.write(svg_xml.encode('utf-8')) + if 'svg' in suffixes : + svg_xml = await page.evaluate('''() => { + const svg = document.querySelector('svg'); + const xmlSerializer = new XMLSerializer(); + return xmlSerializer.serializeToString(svg); + }''') + logger.info(f"Generating {output_file_without_suffix}.svg..") + with open(f'{output_file_without_suffix}.svg', 'wb') as f: + f.write(svg_xml.encode('utf-8')) - if 'png' in suffixes: - clip = await page.evaluate('''() => { - const svg = document.querySelector('svg'); - const rect = svg.getBoundingClientRect(); - return { - x: Math.floor(rect.left), - y: Math.floor(rect.top), - width: Math.ceil(rect.width), - height: Math.ceil(rect.height) - }; - }''') - await page.setViewport({'width': clip['x'] + clip['width'], 'height': clip['y'] + clip['height'], 'deviceScaleFactor': device_scale_factor}) - screenshot = await page.screenshot(clip=clip, omit_background=True, scale='device') - print(f"Generating {output_file_without_suffix}.png..") - with open(f'{output_file_without_suffix}.png', 'wb') as f: - f.write(screenshot) - if 'pdf' in suffixes: - pdf_data = await page.pdf(scale=device_scale_factor) - print(f"Generating {output_file_without_suffix}.pdf..") - with open(f'{output_file_without_suffix}.pdf', 'wb') as f: - f.write(pdf_data) - return 1 - except Exception as e: - print(e) - return -1 - finally: - await browser.close() - + if 'png' in suffixes: + clip = await page.evaluate('''() => { + const svg = document.querySelector('svg'); + const rect = svg.getBoundingClientRect(); + return { + x: Math.floor(rect.left), + y: Math.floor(rect.top), + width: Math.ceil(rect.width), + height: Math.ceil(rect.height) + }; + }''') + await page.setViewport({'width': clip['x'] + clip['width'], 'height': clip['y'] + clip['height'], 'deviceScaleFactor': device_scale_factor}) + screenshot = await page.screenshot(clip=clip, omit_background=True, scale='device') + logger.info(f"Generating {output_file_without_suffix}.png..") + with open(f'{output_file_without_suffix}.png', 'wb') as f: + f.write(screenshot) + if 'pdf' in suffixes: + pdf_data = await page.pdf(scale=device_scale_factor) + logger.info(f"Generating {output_file_without_suffix}.pdf..") + with open(f'{output_file_without_suffix}.pdf', 'wb') as f: + f.write(pdf_data) + return 0 + except Exception as e: + logger.error(e) + return -1 + finally: + await browser.close() - suffixes = ['png', 'svg', 'pdf'] - dir_name = os.path.dirname(output_file_without_suffix) - if dir_name and not os.path.exists(dir_name): - os.makedirs(dir_name) - with open(f"{output_file_without_suffix}.mmd", "w", encoding="utf-8") as f: - f.write(mermaid_code) - loop = asyncio.new_event_loop() - result = loop.run_until_complete(mermaid_to_file0(mermaid_code, output_file_without_suffix, width, height,suffixes)) - loop.close() - return result - -if __name__ == "__main__": - mermaid_to_file() From e947ce5fea6d543141eb3146ef534609ef124886 Mon Sep 17 00:00:00 2001 From: "hy.li" Date: Wed, 13 Sep 2023 14:39:51 +0800 Subject: [PATCH 52/76] use config for PYPPETEER_EXECUTABLE_PATH --- config/config.yaml | 5 ++++- metagpt/config.py | 1 + metagpt/utils/mmdc_pyppeteer.py | 9 +++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index 179985a6f..93301fcf2 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -79,4 +79,7 @@ MODEL_FOR_RESEARCHER_REPORT: gpt-3.5-turbo-16k ### choose the engine for mermaid conversion, # default is nodejs, you can change it to playwright,pyppeteer or ink -# MERMAID_ENGINE: nodejs \ No newline at end of file +# MERMAID_ENGINE: nodejs + +### browser path for pyppeteer engine, support Chrome, Chromium,MS Edge +#PYPPETEER_EXECUTABLE_PATH: "/usr/bin/google-chrome-stable" \ No newline at end of file diff --git a/metagpt/config.py b/metagpt/config.py index 9260ae605..b4e0fe7fa 100644 --- a/metagpt/config.py +++ b/metagpt/config.py @@ -84,6 +84,7 @@ class Config(metaclass=Singleton): self.model_for_researcher_summary = self._get("MODEL_FOR_RESEARCHER_SUMMARY") self.model_for_researcher_report = self._get("MODEL_FOR_RESEARCHER_REPORT") self.mermaid_engine = self._get("MERMAID_ENGINE", 'nodejs') + self.pyppeteer_executable_path = self._get("PYPPETEER_EXECUTABLE_PATH", '') def _init_with_config_files_and_env(self, configs: dict, yaml_file): """Load from config/key.yaml, config/config.yaml, and env in decreasing order of priority""" diff --git a/metagpt/utils/mmdc_pyppeteer.py b/metagpt/utils/mmdc_pyppeteer.py index 56367236f..7ec30fd12 100644 --- a/metagpt/utils/mmdc_pyppeteer.py +++ b/metagpt/utils/mmdc_pyppeteer.py @@ -9,6 +9,7 @@ import os from urllib.parse import urljoin from pyppeteer import launch from metagpt.logs import logger +from metagpt.config import CONFIG async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048)-> int: """ @@ -26,14 +27,14 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, suffixes = ['png', 'svg', 'pdf'] __dirname = os.path.dirname(os.path.abspath(__file__)) - executablePath = os.getenv('PUPPETEER_EXECUTABLE_PATH',"") - if executablePath: + + if CONFIG.pyppeteer_executable_path: browser = await launch(headless=True, - executablePath=executablePath, + executablePath=CONFIG.pyppeteer_executable_path, args=['--disable-extensions',"--no-sandbox"] ) else: - logger.error("Please set the environment variable:PUPPETEER_EXECUTABLE_PATH.") + logger.error("Please set the environment variable:PYPPETEER_EXECUTABLE_PATH.") return -1 page = await browser.newPage() device_scale_factor = 1.0 From 14dbf27488616fff72dd6a2f366b757aca958124 Mon Sep 17 00:00:00 2001 From: stellahsr Date: Wed, 13 Sep 2023 20:06:42 +0800 Subject: [PATCH 53/76] add .gitattributes --- docs/resources/.gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/resources/.gitattributes diff --git a/docs/resources/.gitattributes b/docs/resources/.gitattributes new file mode 100644 index 000000000..bb940d6a1 --- /dev/null +++ b/docs/resources/.gitattributes @@ -0,0 +1 @@ +tasks.mp4 filter=lfs diff=lfs merge=lfs -text From 694f7197a52d1888baf4a7dee3efd969d457dd30 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 13 Sep 2023 20:13:37 +0800 Subject: [PATCH 54/76] add lfs file --- docs/resources/.gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/resources/.gitattributes diff --git a/docs/resources/.gitattributes b/docs/resources/.gitattributes new file mode 100644 index 000000000..a8c44efcf --- /dev/null +++ b/docs/resources/.gitattributes @@ -0,0 +1 @@ +tasks.mp4 filter=lfs diff=lfs merge=lfs -text From 343f33ebc409fe02fb108f752d2b9061f3bdf6bf Mon Sep 17 00:00:00 2001 From: stellahsr Date: Wed, 13 Sep 2023 20:18:19 +0800 Subject: [PATCH 55/76] Add mp4 file --- docs/resources/tasks.mp4 | Bin 0 -> 133 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/resources/tasks.mp4 diff --git a/docs/resources/tasks.mp4 b/docs/resources/tasks.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..1bfafcdfe7f7a3100ae0daf4755e6072d4fb016b GIT binary patch literal 133 zcmWN_%MHUI3;@u3reJ{vL-;w{fPsS4mZ&Bq<57s_wf;J&VzR;pM5@FmDk&L zrj^E9@=-`$Y8gEXZc$se8!!$rgMqA(XtO!yK(!(|1>TqFAf%ENq49}ugIX&DgLelT PZCSYgitrGWHeN4(S7#^g literal 0 HcmV?d00001 From 97b5191167c6353a11f2dcb1d094a745c06fd019 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 13 Sep 2023 21:28:17 +0800 Subject: [PATCH 56/76] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index b4a272ef0..acfbb2423 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,14 @@ # MetaGPT: The Multi-Agent Framework

Software Company Multi-Role Schematic (Gradually Implementing)

+ + + + + ## Examples (fully generated by GPT-4) For example, if you type `python startup.py "Design a RecSys like Toutiao"`, you would get many outputs, one of them is data & api design From f43208204cf531ef27c44e6d3e19878935ffd2ce Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 13 Sep 2023 21:28:48 +0800 Subject: [PATCH 57/76] Update README.md cancel --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index acfbb2423..2d0487e42 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,6 @@ # MetaGPT: The Multi-Agent Framework

Software Company Multi-Role Schematic (Gradually Implementing)

- - - - ## Examples (fully generated by GPT-4) For example, if you type `python startup.py "Design a RecSys like Toutiao"`, you would get many outputs, one of them is data & api design From d5357fb16267c9442ec80e0033d66cf09a6b5e64 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 13 Sep 2023 23:08:51 +0800 Subject: [PATCH 58/76] Update README.md add demos --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 2d0487e42..148b1ee41 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,12 @@ # MetaGPT: The Multi-Agent Framework

Software Company Multi-Role Schematic (Gradually Implementing)

+## What Can MetaGPT Do? + + +https://github.com/geekan/MetaGPT/assets/34952977/3cb42bb8-6609-4f74-8d22-601dafda95bb + + ## Examples (fully generated by GPT-4) From 26c98a2f302432d7ce7ff6c0b59250bee3d90b88 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 13 Sep 2023 23:35:54 +0800 Subject: [PATCH 59/76] Update README.md add video of tasks --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 148b1ee41..657ce882f 100644 --- a/README.md +++ b/README.md @@ -33,13 +33,11 @@ # MetaGPT: The Multi-Agent Framework

Software Company Multi-Role Schematic (Gradually Implementing)

-## What Can MetaGPT Do? - +## MetaGPT's Abilities https://github.com/geekan/MetaGPT/assets/34952977/3cb42bb8-6609-4f74-8d22-601dafda95bb - ## Examples (fully generated by GPT-4) For example, if you type `python startup.py "Design a RecSys like Toutiao"`, you would get many outputs, one of them is data & api design @@ -48,6 +46,9 @@ ## Examples (fully generated by GPT-4) It costs approximately **$0.2** (in GPT-4 API fees) to generate one example with analysis and design, and around **$2.0** for a full project. + + + ## Installation ### Installation Video Guide From 75c952a06531303af3a777adb40e0a8e79483cee Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 13 Sep 2023 23:39:14 +0800 Subject: [PATCH 60/76] Update README_CN.md add video of tasks --- docs/README_CN.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/README_CN.md b/docs/README_CN.md index 4ee4c7408..cd06af53f 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -33,6 +33,11 @@ # MetaGPT: 多智能体框架

软件公司多角色示意图(正在逐步实现)

+## MetaGPT 的能力 + +https://github.com/geekan/MetaGPT/assets/34952977/3cb42bb8-6609-4f74-8d22-601dafda95bb + + ## 示例(均由 GPT-4 生成) 例如,键入`python startup.py "写个类似今日头条的推荐系统"`并回车,你会获得一系列输出,其一是数据结构与API设计 From 90782d17863486ca928ab70e7270f67c5b9207cf Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Wed, 13 Sep 2023 23:40:37 +0800 Subject: [PATCH 61/76] Update README_JA.md add video of tasks --- docs/README_JA.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/README_JA.md b/docs/README_JA.md index 158ad8ceb..02215c8c6 100644 --- a/docs/README_JA.md +++ b/docs/README_JA.md @@ -33,6 +33,11 @@ # MetaGPT: マルチエージェントフレームワーク

ソフトウェア会社のマルチロール図式(順次導入)

+## MetaGPTの能力 + +https://github.com/geekan/MetaGPT/assets/34952977/3cb42bb8-6609-4f74-8d22-601dafda95bb + + ## 例(GPT-4 で完全生成) 例えば、`python startup.py "Toutiao のような RecSys をデザインする"`と入力すると、多くの出力が得られます From 5a0d18bb8b7d22ccbeb61ecbda4fefc54438974c Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Thu, 14 Sep 2023 10:29:35 +0800 Subject: [PATCH 62/76] Delete docs/resources/tasks.mp4 rm mp4 data --- docs/resources/tasks.mp4 | Bin 133 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/resources/tasks.mp4 diff --git a/docs/resources/tasks.mp4 b/docs/resources/tasks.mp4 deleted file mode 100644 index 1bfafcdfe7f7a3100ae0daf4755e6072d4fb016b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133 zcmWN_%MHUI3;@u3reJ{vL-;w{fPsS4mZ&Bq<57s_wf;J&VzR;pM5@FmDk&L zrj^E9@=-`$Y8gEXZc$se8!!$rgMqA(XtO!yK(!(|1>TqFAf%ENq49}ugIX&DgLelT PZCSYgitrGWHeN4(S7#^g From ab5503215924b5186336b0f74fa1f1e6fe00f614 Mon Sep 17 00:00:00 2001 From: stellahsr Date: Thu, 14 Sep 2023 11:55:10 +0800 Subject: [PATCH 63/76] update locally --- docs/resources/tasks.mp4 | Bin 133 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/resources/tasks.mp4 diff --git a/docs/resources/tasks.mp4 b/docs/resources/tasks.mp4 deleted file mode 100644 index 1bfafcdfe7f7a3100ae0daf4755e6072d4fb016b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133 zcmWN_%MHUI3;@u3reJ{vL-;w{fPsS4mZ&Bq<57s_wf;J&VzR;pM5@FmDk&L zrj^E9@=-`$Y8gEXZc$se8!!$rgMqA(XtO!yK(!(|1>TqFAf%ENq49}ugIX&DgLelT PZCSYgitrGWHeN4(S7#^g From 777c9cb63681edf00390e40440023a721fdb7da5 Mon Sep 17 00:00:00 2001 From: stellahsr Date: Thu, 14 Sep 2023 12:12:17 +0800 Subject: [PATCH 64/76] rm files --- docs/resources/.gitattributes | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/resources/.gitattributes diff --git a/docs/resources/.gitattributes b/docs/resources/.gitattributes deleted file mode 100644 index bb940d6a1..000000000 --- a/docs/resources/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -tasks.mp4 filter=lfs diff=lfs merge=lfs -text From f6153a8d1054ef92695ba65c5b4c1ed31ec9d728 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:01:35 +0800 Subject: [PATCH 65/76] Update README.md update tasks --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 657ce882f..41266ce01 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,9 @@ # MetaGPT: The Multi-Agent Framework ## MetaGPT's Abilities -https://github.com/geekan/MetaGPT/assets/34952977/3cb42bb8-6609-4f74-8d22-601dafda95bb + +https://github.com/geekan/MetaGPT/assets/34952977/34345016-5d13-489d-b9f9-b82ace413419 + ## Examples (fully generated by GPT-4) From 2abaa8b61713356d1a30d565d61086602ceda7f1 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:03:15 +0800 Subject: [PATCH 66/76] Update README_CN.md update tasks --- docs/README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README_CN.md b/docs/README_CN.md index cd06af53f..e96193d47 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -35,7 +35,7 @@ # MetaGPT: 多智能体框架 ## MetaGPT 的能力 -https://github.com/geekan/MetaGPT/assets/34952977/3cb42bb8-6609-4f74-8d22-601dafda95bb +https://github.com/geekan/MetaGPT/assets/34952977/34345016-5d13-489d-b9f9-b82ace413419 ## 示例(均由 GPT-4 生成) From 5429b028c176b1fe464487fa97448e993e0c7013 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:03:39 +0800 Subject: [PATCH 67/76] Update README_JA.md update tasks --- docs/README_JA.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README_JA.md b/docs/README_JA.md index 02215c8c6..9e06134eb 100644 --- a/docs/README_JA.md +++ b/docs/README_JA.md @@ -35,7 +35,7 @@ # MetaGPT: マルチエージェントフレームワーク ## MetaGPTの能力 -https://github.com/geekan/MetaGPT/assets/34952977/3cb42bb8-6609-4f74-8d22-601dafda95bb +https://github.com/geekan/MetaGPT/assets/34952977/34345016-5d13-489d-b9f9-b82ace413419 ## 例(GPT-4 で完全生成) From 9bf1e51d02069d4e57be7938bcf9678d9987850b Mon Sep 17 00:00:00 2001 From: zhanglei Date: Thu, 14 Sep 2023 15:21:34 +0800 Subject: [PATCH 68/76] add: openai moderation --- metagpt/provider/openai_api.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index ad9df0396..30f61cf2d 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -6,7 +6,7 @@ """ import asyncio import time -from typing import NamedTuple +from typing import NamedTuple, Union, List, Optional import openai from openai.error import APIConnectionError @@ -286,3 +286,17 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): if not self.auto_max_tokens: return CONFIG.max_tokens_rsp return get_max_completion_tokens(messages, self.model, CONFIG.max_tokens_rsp) + + def moderation(self,content: Union[str, List[str]],model: Optional[str] = None,api_key: Optional[str] = None,): + try: + if content is None or content == "" or len(content) == 0: + logger.error("content cannot be empty!") + else: + rsp = self._moderation(content=content,model=model,api_key=api_key) + return rsp + except Exception as e: + logger.error("moderating failed!", e) + + def _moderation(self,content: Union[str, List[str]],model: Optional[str] = None,api_key: Optional[str] = None): + rsp = self.llm.Moderation.create(input=content,model=model,api_key=api_key) + return rsp From 0bf0ab29174d593dccd098d44542cca13eedbc19 Mon Sep 17 00:00:00 2001 From: zhanglei Date: Thu, 14 Sep 2023 16:33:00 +0800 Subject: [PATCH 69/76] update:Modify based on comments --- metagpt/provider/openai_api.py | 59 ++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 30f61cf2d..26dcc65c5 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -6,11 +6,17 @@ """ import asyncio import time -from typing import NamedTuple, Union, List, Optional +from typing import List, NamedTuple, Union import openai from openai.error import APIConnectionError -from tenacity import retry, stop_after_attempt, after_log, wait_fixed, retry_if_exception_type +from tenacity import ( + after_log, + retry, + retry_if_exception_type, + stop_after_attempt, + wait_fixed, +) from metagpt.config import CONFIG from metagpt.logs import logger @@ -48,12 +54,14 @@ class RateLimiter: self.last_call_time = time.time() + class Costs(NamedTuple): total_prompt_tokens: int total_completion_tokens: int total_cost: float total_budget: float + class CostManager(metaclass=Singleton): """计算使用接口的开销""" @@ -74,7 +82,9 @@ class CostManager(metaclass=Singleton): """ self.total_prompt_tokens += prompt_tokens self.total_completion_tokens += completion_tokens - cost = (prompt_tokens * TOKEN_COSTS[model]["prompt"] + completion_tokens * TOKEN_COSTS[model]["completion"]) / 1000 + cost = ( + prompt_tokens * TOKEN_COSTS[model]["prompt"] + completion_tokens * TOKEN_COSTS[model]["completion"] + ) / 1000 self.total_cost += cost logger.info( f"Total running cost: ${self.total_cost:.3f} | Max budget: ${CONFIG.max_budget:.3f} | " @@ -100,6 +110,7 @@ class CostManager(metaclass=Singleton): """ return self.total_completion_tokens + def get_total_cost(self): """ Get the total cost of API calls. @@ -109,25 +120,20 @@ def get_total_cost(self): """ return self.total_cost + def get_costs(self) -> Costs: """Get all costs""" return Costs(self.total_prompt_tokens, self.total_completion_tokens, self.total_cost, self.total_budget) -def log_and_reraise(retry_state): - logger.error(f"Retry attempts exhausted. Last exception: {retry_state.outcome.exception()}") - logger.warning(""" -Recommend going to https://deepwisdom.feishu.cn/wiki/MsGnwQBjiif9c3koSJNcYaoSnu4#part-XdatdVlhEojeAfxaaEZcMV3ZniQ -See FAQ 5.8 -""") - raise retry_state.outcome.exception() - def log_and_reraise(retry_state): logger.error(f"Retry attempts exhausted. Last exception: {retry_state.outcome.exception()}") - logger.warning(""" + logger.warning( + """ Recommend going to https://deepwisdom.feishu.cn/wiki/MsGnwQBjiif9c3koSJNcYaoSnu4#part-XdatdVlhEojeAfxaaEZcMV3ZniQ See FAQ 5.8 -""") +""" + ) raise retry_state.outcome.exception() @@ -182,15 +188,18 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): "n": 1, "stop": None, "temperature": 0.3, - "timeout": 3 + "timeout": 3, } if CONFIG.openai_api_type == "azure": if CONFIG.deployment_name and CONFIG.deployment_id: raise ValueError("You can only use one of the `deployment_id` or `deployment_name` model") elif not CONFIG.deployment_name and not CONFIG.deployment_id: raise ValueError("You must specify `DEPLOYMENT_NAME` or `DEPLOYMENT_ID` parameter") - kwargs_mode = {"engine": CONFIG.deployment_name} if CONFIG.deployment_name \ + kwargs_mode = ( + {"engine": CONFIG.deployment_name} + if CONFIG.deployment_name else {"deployment_id": CONFIG.deployment_id} + ) else: kwargs_mode = {"model": self.model} kwargs.update(kwargs_mode) @@ -219,7 +228,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): @retry( stop=stop_after_attempt(3), wait=wait_fixed(1), - after=after_log(logger, logger.level('WARNING').name), + after=after_log(logger, logger.level("WARNING").name), retry=retry_if_exception_type(APIConnectionError), retry_error_callback=log_and_reraise, ) @@ -236,8 +245,8 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): try: prompt_tokens = count_message_tokens(messages, self.model) completion_tokens = count_string_tokens(rsp, self.model) - usage['prompt_tokens'] = prompt_tokens - usage['completion_tokens'] = completion_tokens + usage["prompt_tokens"] = prompt_tokens + usage["completion_tokens"] = completion_tokens return usage except Exception as e: logger.error("usage calculation failed!", e) @@ -273,8 +282,8 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): def _update_costs(self, usage: dict): if CONFIG.calc_usage: try: - prompt_tokens = int(usage['prompt_tokens']) - completion_tokens = int(usage['completion_tokens']) + prompt_tokens = int(usage["prompt_tokens"]) + completion_tokens = int(usage["completion_tokens"]) self._cost_manager.update_cost(prompt_tokens, completion_tokens, self.model) except Exception as e: logger.error("updating costs failed!", e) @@ -287,16 +296,16 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): return CONFIG.max_tokens_rsp return get_max_completion_tokens(messages, self.model, CONFIG.max_tokens_rsp) - def moderation(self,content: Union[str, List[str]],model: Optional[str] = None,api_key: Optional[str] = None,): + def moderation(self, content: Union[str, List[str]]): try: - if content is None or content == "" or len(content) == 0: + if not content: logger.error("content cannot be empty!") else: - rsp = self._moderation(content=content,model=model,api_key=api_key) + rsp = self._moderation(content=content) return rsp except Exception as e: logger.error("moderating failed!", e) - def _moderation(self,content: Union[str, List[str]],model: Optional[str] = None,api_key: Optional[str] = None): - rsp = self.llm.Moderation.create(input=content,model=model,api_key=api_key) + def _moderation(self, content: Union[str]): + rsp = self.llm.Moderation.create(input=content) return rsp From ebb5ec9c0a84f1d85f99db72db78e0b4cbdf5017 Mon Sep 17 00:00:00 2001 From: zhanglei Date: Thu, 14 Sep 2023 20:18:11 +0800 Subject: [PATCH 70/76] update:Modify based on comments --- metagpt/provider/openai_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 26dcc65c5..624388d35 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -304,7 +304,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): rsp = self._moderation(content=content) return rsp except Exception as e: - logger.error("moderating failed!", e) + logger.error(f"moderating failed:{e}") def _moderation(self, content: Union[str]): rsp = self.llm.Moderation.create(input=content) From ac4b550736ae0709528a1a23f1bb65a38c1a1e25 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Thu, 14 Sep 2023 20:41:14 +0800 Subject: [PATCH 71/76] Update ut_writer.py bug fix: fix quotes error --- metagpt/tools/ut_writer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/metagpt/tools/ut_writer.py b/metagpt/tools/ut_writer.py index 263a0269e..43ca72150 100644 --- a/metagpt/tools/ut_writer.py +++ b/metagpt/tools/ut_writer.py @@ -60,6 +60,7 @@ def test_node_tags(project_key, nodes, operations, expected_msg): # 3. If comments are needed, use Chinese. # If you understand, please wait for me to give the interface definition and just answer "Understood" to save tokens. +''' ACT_PROMPT_PREFIX = '''Refer to the test types: such as missing request parameters, field boundary verification, incorrect field type. Please output 10 test cases within one `@pytest.mark.parametrize` scope. @@ -94,7 +95,8 @@ Name Type Required Default Value Remarks code integer Yes message string Yes data object Yes - +``` +''' class UTGenerator: From 1463b7d8a6eef40d8202a9f9b3f708240aa6e0a4 Mon Sep 17 00:00:00 2001 From: zhanglei Date: Fri, 15 Sep 2023 10:02:15 +0800 Subject: [PATCH 72/76] update: moderation add asyncio func impl --- metagpt/provider/openai_api.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 624388d35..7e865f288 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -6,7 +6,7 @@ """ import asyncio import time -from typing import List, NamedTuple, Union +from typing import NamedTuple, Union import openai from openai.error import APIConnectionError @@ -296,7 +296,7 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): return CONFIG.max_tokens_rsp return get_max_completion_tokens(messages, self.model, CONFIG.max_tokens_rsp) - def moderation(self, content: Union[str, List[str]]): + def moderation(self, content: Union[str, list[str]]): try: if not content: logger.error("content cannot be empty!") @@ -306,6 +306,20 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter): except Exception as e: logger.error(f"moderating failed:{e}") - def _moderation(self, content: Union[str]): + def _moderation(self, content: Union[str, list[str]]): rsp = self.llm.Moderation.create(input=content) return rsp + + async def amoderation(self, content: Union[str, list[str]]): + try: + if not content: + logger.error("content cannot be empty!") + else: + rsp = await self._amoderation(content=content) + return rsp + except Exception as e: + logger.error(f"moderating failed:{e}") + + async def _amoderation(self, content: Union[str, list[str]]): + rsp = await self.llm.Moderation.acreate(input=content) + return rsp From 53d5a1e86efa827bedfe45a50b885a6af9798a0e Mon Sep 17 00:00:00 2001 From: "hy.li" Date: Fri, 15 Sep 2023 13:03:21 +0800 Subject: [PATCH 73/76] extras_require: pyppeteer --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index a88f9de92..f9ae768e6 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ setup( "selenium": ["selenium>4", "webdriver_manager", "beautifulsoup4"], "search-google": ["google-api-python-client==2.94.0"], "search-ddg": ["duckduckgo-search==3.8.5"], + "pyppeteer": ["pyppeteer>=1.0.2"], }, cmdclass={ "install_mermaid": InstallMermaidCLI, From 494226b80a3880e67acc5d6250f432eda8fdb151 Mon Sep 17 00:00:00 2001 From: "hy.li" Date: Mon, 18 Sep 2023 17:25:15 +0800 Subject: [PATCH 74/76] compress index.html --- metagpt/utils/index.html | 2213 +------------------------------------- 1 file changed, 1 insertion(+), 2212 deletions(-) diff --git a/metagpt/utils/index.html b/metagpt/utils/index.html index 0ac6d9a74..d750a1b6a 100644 --- a/metagpt/utils/index.html +++ b/metagpt/utils/index.html @@ -1,2212 +1 @@ - - - - - - - -
- - - +
\ No newline at end of file From 5415ea4a8c46faf50cfbd1f8de2640c9d3807d38 Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Mon, 18 Sep 2023 22:16:11 +0800 Subject: [PATCH 75/76] replace running install running bdist_egg running egg_info writing metagpt.egg-info/PKG-INFO writing dependency_links to metagpt.egg-info/dependency_links.txt writing requirements to metagpt.egg-info/requires.txt writing top-level names to metagpt.egg-info/top_level.txt reading manifest file 'metagpt.egg-info/SOURCES.txt' adding license file 'LICENSE' writing manifest file 'metagpt.egg-info/SOURCES.txt' installing library code to build/bdist.linux-x86_64/egg running install_lib running build_py creating build/bdist.linux-x86_64/egg creating build/bdist.linux-x86_64/egg/tests copying build/lib/tests/conftest.py -> build/bdist.linux-x86_64/egg/tests copying build/lib/tests/__init__.py -> build/bdist.linux-x86_64/egg/tests creating build/bdist.linux-x86_64/egg/tests/metagpt copying build/lib/tests/metagpt/test_message.py -> build/bdist.linux-x86_64/egg/tests/metagpt copying build/lib/tests/metagpt/test_manager.py -> build/bdist.linux-x86_64/egg/tests/metagpt creating build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_serialize.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_code_parser.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_custom_aio_session.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_read_docx.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_text.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_config.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_parse_html.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_file.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_output_parser.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/__init__.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_token_counter.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_common.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils copying build/lib/tests/metagpt/utils/test_pycst.py -> build/bdist.linux-x86_64/egg/tests/metagpt/utils creating build/bdist.linux-x86_64/egg/tests/metagpt/document_store copying build/lib/tests/metagpt/document_store/test_lancedb_store.py -> build/bdist.linux-x86_64/egg/tests/metagpt/document_store copying build/lib/tests/metagpt/document_store/test_milvus_store.py -> build/bdist.linux-x86_64/egg/tests/metagpt/document_store copying build/lib/tests/metagpt/document_store/__init__.py -> build/bdist.linux-x86_64/egg/tests/metagpt/document_store copying build/lib/tests/metagpt/document_store/test_faiss_store.py -> build/bdist.linux-x86_64/egg/tests/metagpt/document_store copying build/lib/tests/metagpt/document_store/test_document.py -> build/bdist.linux-x86_64/egg/tests/metagpt/document_store copying build/lib/tests/metagpt/document_store/test_chromadb_store.py -> build/bdist.linux-x86_64/egg/tests/metagpt/document_store copying build/lib/tests/metagpt/document_store/test_qdrant_store.py -> build/bdist.linux-x86_64/egg/tests/metagpt/document_store copying build/lib/tests/metagpt/test_environment.py -> build/bdist.linux-x86_64/egg/tests/metagpt creating build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/test_researcher.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/test_qa_engineer.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/test_ui.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/test_architect.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/__init__.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/mock.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/test_product_manager.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/test_tutorial_assistant.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/test_engineer.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/ui_role.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles copying build/lib/tests/metagpt/roles/test_project_manager.py -> build/bdist.linux-x86_64/egg/tests/metagpt/roles creating build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_action_output.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_write_docstring.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_action.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_run_code.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_write_prd.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_write_test.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_debug_error.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/__init__.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_write_prd_review.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_design_api.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_write_code_review.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_project_management.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/mock.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_write_code.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_ui_design.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_azure_tts.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_write_tutorial.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/actions/test_design_api_review.py -> build/bdist.linux-x86_64/egg/tests/metagpt/actions copying build/lib/tests/metagpt/test_gpt.py -> build/bdist.linux-x86_64/egg/tests/metagpt copying build/lib/tests/metagpt/test_action.py -> build/bdist.linux-x86_64/egg/tests/metagpt creating build/bdist.linux-x86_64/egg/tests/metagpt/provider copying build/lib/tests/metagpt/provider/test_base_gpt_api.py -> build/bdist.linux-x86_64/egg/tests/metagpt/provider copying build/lib/tests/metagpt/provider/__init__.py -> build/bdist.linux-x86_64/egg/tests/metagpt/provider creating build/bdist.linux-x86_64/egg/tests/metagpt/memory copying build/lib/tests/metagpt/memory/__init__.py -> build/bdist.linux-x86_64/egg/tests/metagpt/memory copying build/lib/tests/metagpt/memory/test_longterm_memory.py -> build/bdist.linux-x86_64/egg/tests/metagpt/memory copying build/lib/tests/metagpt/memory/test_memory_storage.py -> build/bdist.linux-x86_64/egg/tests/metagpt/memory creating build/bdist.linux-x86_64/egg/tests/metagpt/management copying build/lib/tests/metagpt/management/test_skill_manager.py -> build/bdist.linux-x86_64/egg/tests/metagpt/management copying build/lib/tests/metagpt/management/__init__.py -> build/bdist.linux-x86_64/egg/tests/metagpt/management copying build/lib/tests/metagpt/__init__.py -> build/bdist.linux-x86_64/egg/tests/metagpt copying build/lib/tests/metagpt/test_llm.py -> build/bdist.linux-x86_64/egg/tests/metagpt copying build/lib/tests/metagpt/test_schema.py -> build/bdist.linux-x86_64/egg/tests/metagpt copying build/lib/tests/metagpt/test_software_company.py -> build/bdist.linux-x86_64/egg/tests/metagpt copying build/lib/tests/metagpt/test_role.py -> build/bdist.linux-x86_64/egg/tests/metagpt creating build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/test_ut_generator.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/test_web_browser_engine.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/test_web_browser_engine_playwright.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/test_web_browser_engine_selenium.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/test_sd_tool.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/__init__.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/test_prompt_generator.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/test_translate.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/test_search_engine.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/test_summarize.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools copying build/lib/tests/metagpt/tools/test_search_engine_meilisearch.py -> build/bdist.linux-x86_64/egg/tests/metagpt/tools creating build/bdist.linux-x86_64/egg/metagpt copying build/lib/metagpt/logs.py -> build/bdist.linux-x86_64/egg/metagpt creating build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/token_counter.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/parse_html.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/mmdc_playwright.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/text.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/file.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/common.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/special_tokens.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/singleton.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/mmdc_pyppeteer.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/__init__.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/pycst.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/serialize.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/read_document.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/mmdc_ink.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/utils/mermaid.py -> build/bdist.linux-x86_64/egg/metagpt/utils copying build/lib/metagpt/const.py -> build/bdist.linux-x86_64/egg/metagpt creating build/bdist.linux-x86_64/egg/metagpt/document_store copying build/lib/metagpt/document_store/document.py -> build/bdist.linux-x86_64/egg/metagpt/document_store copying build/lib/metagpt/document_store/base_store.py -> build/bdist.linux-x86_64/egg/metagpt/document_store copying build/lib/metagpt/document_store/faiss_store.py -> build/bdist.linux-x86_64/egg/metagpt/document_store copying build/lib/metagpt/document_store/chromadb_store.py -> build/bdist.linux-x86_64/egg/metagpt/document_store copying build/lib/metagpt/document_store/qdrant_store.py -> build/bdist.linux-x86_64/egg/metagpt/document_store copying build/lib/metagpt/document_store/milvus_store.py -> build/bdist.linux-x86_64/egg/metagpt/document_store copying build/lib/metagpt/document_store/__init__.py -> build/bdist.linux-x86_64/egg/metagpt/document_store copying build/lib/metagpt/document_store/lancedb_store.py -> build/bdist.linux-x86_64/egg/metagpt/document_store creating build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/customer_service.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/qa_engineer.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/architect.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/engineer.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/role.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/product_manager.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/prompt.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/__init__.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/researcher.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/project_manager.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/seacher.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/sales.py -> build/bdist.linux-x86_64/egg/metagpt/roles copying build/lib/metagpt/roles/tutorial_assistant.py -> build/bdist.linux-x86_64/egg/metagpt/roles creating build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/write_prd_review.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/write_tutorial.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/design_filenames.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/run_code.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/write_test.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/action.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/write_prd.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/azure_tts.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/project_management.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/add_requirement.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/write_code.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/__init__.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/action_output.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/write_code_review.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/debug_error.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/write_docstring.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/analyze_dep_libs.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/design_api_review.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/design_api.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/research.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/actions/search_and_summarize.py -> build/bdist.linux-x86_64/egg/metagpt/actions copying build/lib/metagpt/llm.py -> build/bdist.linux-x86_64/egg/metagpt creating build/bdist.linux-x86_64/egg/metagpt/provider copying build/lib/metagpt/provider/openai_api.py -> build/bdist.linux-x86_64/egg/metagpt/provider copying build/lib/metagpt/provider/anthropic_api.py -> build/bdist.linux-x86_64/egg/metagpt/provider copying build/lib/metagpt/provider/__init__.py -> build/bdist.linux-x86_64/egg/metagpt/provider copying build/lib/metagpt/provider/base_chatbot.py -> build/bdist.linux-x86_64/egg/metagpt/provider copying build/lib/metagpt/provider/base_gpt_api.py -> build/bdist.linux-x86_64/egg/metagpt/provider creating build/bdist.linux-x86_64/egg/metagpt/memory copying build/lib/metagpt/memory/memory.py -> build/bdist.linux-x86_64/egg/metagpt/memory copying build/lib/metagpt/memory/__init__.py -> build/bdist.linux-x86_64/egg/metagpt/memory copying build/lib/metagpt/memory/longterm_memory.py -> build/bdist.linux-x86_64/egg/metagpt/memory copying build/lib/metagpt/memory/memory_storage.py -> build/bdist.linux-x86_64/egg/metagpt/memory copying build/lib/metagpt/config.py -> build/bdist.linux-x86_64/egg/metagpt creating build/bdist.linux-x86_64/egg/metagpt/management copying build/lib/metagpt/management/skill_manager.py -> build/bdist.linux-x86_64/egg/metagpt/management copying build/lib/metagpt/management/__init__.py -> build/bdist.linux-x86_64/egg/metagpt/management copying build/lib/metagpt/schema.py -> build/bdist.linux-x86_64/egg/metagpt copying build/lib/metagpt/__init__.py -> build/bdist.linux-x86_64/egg/metagpt copying build/lib/metagpt/software_company.py -> build/bdist.linux-x86_64/egg/metagpt copying build/lib/metagpt/inspect_module.py -> build/bdist.linux-x86_64/egg/metagpt copying build/lib/metagpt/environment.py -> build/bdist.linux-x86_64/egg/metagpt creating build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/web_browser_engine.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/ut_writer.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/web_browser_engine_selenium.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/web_browser_engine_playwright.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/search_engine_googleapi.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/search_engine.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/sd_engine.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/prompt_writer.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/translator.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/search_engine_serper.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/search_engine_meilisearch.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/__init__.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/search_engine_ddg.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/tools/search_engine_serpapi.py -> build/bdist.linux-x86_64/egg/metagpt/tools copying build/lib/metagpt/manager.py -> build/bdist.linux-x86_64/egg/metagpt creating build/bdist.linux-x86_64/egg/metagpt/prompts copying build/lib/metagpt/prompts/metagpt_sample.py -> build/bdist.linux-x86_64/egg/metagpt/prompts copying build/lib/metagpt/prompts/decompose.py -> build/bdist.linux-x86_64/egg/metagpt/prompts copying build/lib/metagpt/prompts/summarize.py -> build/bdist.linux-x86_64/egg/metagpt/prompts copying build/lib/metagpt/prompts/structure_action.py -> build/bdist.linux-x86_64/egg/metagpt/prompts copying build/lib/metagpt/prompts/structure_goal.py -> build/bdist.linux-x86_64/egg/metagpt/prompts copying build/lib/metagpt/prompts/__init__.py -> build/bdist.linux-x86_64/egg/metagpt/prompts copying build/lib/metagpt/prompts/use_lib_sop.py -> build/bdist.linux-x86_64/egg/metagpt/prompts copying build/lib/metagpt/prompts/sales.py -> build/bdist.linux-x86_64/egg/metagpt/prompts copying build/lib/metagpt/prompts/tutorial_assistant.py -> build/bdist.linux-x86_64/egg/metagpt/prompts creating build/bdist.linux-x86_64/egg/metagpt/learn copying build/lib/metagpt/learn/__init__.py -> build/bdist.linux-x86_64/egg/metagpt/learn byte-compiling build/bdist.linux-x86_64/egg/tests/conftest.py to conftest.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/test_message.py to test_message.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/test_manager.py to test_manager.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_serialize.py to test_serialize.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_code_parser.py to test_code_parser.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_custom_aio_session.py to test_custom_aio_session.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_read_docx.py to test_read_docx.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_text.py to test_text.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_config.py to test_config.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_parse_html.py to test_parse_html.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_file.py to test_file.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_output_parser.py to test_output_parser.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_token_counter.py to test_token_counter.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_common.py to test_common.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/utils/test_pycst.py to test_pycst.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/document_store/test_lancedb_store.py to test_lancedb_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/document_store/test_milvus_store.py to test_milvus_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/document_store/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/document_store/test_faiss_store.py to test_faiss_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/document_store/test_document.py to test_document.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/document_store/test_chromadb_store.py to test_chromadb_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/document_store/test_qdrant_store.py to test_qdrant_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/test_environment.py to test_environment.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/test_researcher.py to test_researcher.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/test_qa_engineer.py to test_qa_engineer.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/test_ui.py to test_ui.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/test_architect.py to test_architect.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/mock.py to mock.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/test_product_manager.py to test_product_manager.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/test_tutorial_assistant.py to test_tutorial_assistant.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/test_engineer.py to test_engineer.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/ui_role.py to ui_role.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/roles/test_project_manager.py to test_project_manager.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_action_output.py to test_action_output.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_write_docstring.py to test_write_docstring.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_action.py to test_action.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_run_code.py to test_run_code.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_write_prd.py to test_write_prd.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_write_test.py to test_write_test.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_debug_error.py to test_debug_error.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_write_prd_review.py to test_write_prd_review.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_design_api.py to test_design_api.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_write_code_review.py to test_write_code_review.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_project_management.py to test_project_management.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/mock.py to mock.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_write_code.py to test_write_code.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_ui_design.py to test_ui_design.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_azure_tts.py to test_azure_tts.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_write_tutorial.py to test_write_tutorial.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/actions/test_design_api_review.py to test_design_api_review.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/test_gpt.py to test_gpt.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/test_action.py to test_action.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/provider/test_base_gpt_api.py to test_base_gpt_api.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/provider/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/memory/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/memory/test_longterm_memory.py to test_longterm_memory.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/memory/test_memory_storage.py to test_memory_storage.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/management/test_skill_manager.py to test_skill_manager.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/management/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/test_llm.py to test_llm.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/test_schema.py to test_schema.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/test_software_company.py to test_software_company.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/test_role.py to test_role.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/test_ut_generator.py to test_ut_generator.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/test_web_browser_engine.py to test_web_browser_engine.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/test_web_browser_engine_playwright.py to test_web_browser_engine_playwright.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/test_web_browser_engine_selenium.py to test_web_browser_engine_selenium.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/test_sd_tool.py to test_sd_tool.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/test_prompt_generator.py to test_prompt_generator.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/test_translate.py to test_translate.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/test_search_engine.py to test_search_engine.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/test_summarize.py to test_summarize.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/tests/metagpt/tools/test_search_engine_meilisearch.py to test_search_engine_meilisearch.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/logs.py to logs.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/token_counter.py to token_counter.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/parse_html.py to parse_html.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/mmdc_playwright.py to mmdc_playwright.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/text.py to text.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/file.py to file.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/common.py to common.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/special_tokens.py to special_tokens.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/singleton.py to singleton.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/mmdc_pyppeteer.py to mmdc_pyppeteer.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/pycst.py to pycst.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/serialize.py to serialize.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/read_document.py to read_document.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/mmdc_ink.py to mmdc_ink.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/utils/mermaid.py to mermaid.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/const.py to const.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/document_store/document.py to document.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/document_store/base_store.py to base_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/document_store/faiss_store.py to faiss_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/document_store/chromadb_store.py to chromadb_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/document_store/qdrant_store.py to qdrant_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/document_store/milvus_store.py to milvus_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/document_store/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/document_store/lancedb_store.py to lancedb_store.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/customer_service.py to customer_service.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/qa_engineer.py to qa_engineer.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/architect.py to architect.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/engineer.py to engineer.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/role.py to role.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/product_manager.py to product_manager.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/prompt.py to prompt.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/researcher.py to researcher.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/project_manager.py to project_manager.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/seacher.py to seacher.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/sales.py to sales.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/roles/tutorial_assistant.py to tutorial_assistant.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/write_prd_review.py to write_prd_review.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/write_tutorial.py to write_tutorial.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/design_filenames.py to design_filenames.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/run_code.py to run_code.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/write_test.py to write_test.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/action.py to action.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/write_prd.py to write_prd.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/azure_tts.py to azure_tts.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/project_management.py to project_management.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/add_requirement.py to add_requirement.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/write_code.py to write_code.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/action_output.py to action_output.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/write_code_review.py to write_code_review.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/debug_error.py to debug_error.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/write_docstring.py to write_docstring.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/analyze_dep_libs.py to analyze_dep_libs.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/design_api_review.py to design_api_review.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/design_api.py to design_api.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/research.py to research.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/actions/search_and_summarize.py to search_and_summarize.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/llm.py to llm.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/provider/openai_api.py to openai_api.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/provider/anthropic_api.py to anthropic_api.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/provider/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/provider/base_chatbot.py to base_chatbot.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/provider/base_gpt_api.py to base_gpt_api.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/memory/memory.py to memory.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/memory/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/memory/longterm_memory.py to longterm_memory.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/memory/memory_storage.py to memory_storage.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/config.py to config.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/management/skill_manager.py to skill_manager.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/management/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/schema.py to schema.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/software_company.py to software_company.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/inspect_module.py to inspect_module.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/environment.py to environment.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/web_browser_engine.py to web_browser_engine.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/ut_writer.py to ut_writer.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/web_browser_engine_selenium.py to web_browser_engine_selenium.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/web_browser_engine_playwright.py to web_browser_engine_playwright.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/search_engine_googleapi.py to search_engine_googleapi.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/search_engine.py to search_engine.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/sd_engine.py to sd_engine.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/prompt_writer.py to prompt_writer.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/translator.py to translator.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/search_engine_serper.py to search_engine_serper.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/search_engine_meilisearch.py to search_engine_meilisearch.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/search_engine_ddg.py to search_engine_ddg.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/tools/search_engine_serpapi.py to search_engine_serpapi.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/manager.py to manager.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/prompts/metagpt_sample.py to metagpt_sample.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/prompts/decompose.py to decompose.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/prompts/summarize.py to summarize.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/prompts/structure_action.py to structure_action.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/prompts/structure_goal.py to structure_goal.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/prompts/__init__.py to __init__.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/prompts/use_lib_sop.py to use_lib_sop.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/prompts/sales.py to sales.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/prompts/tutorial_assistant.py to tutorial_assistant.cpython-39.pyc byte-compiling build/bdist.linux-x86_64/egg/metagpt/learn/__init__.py to __init__.cpython-39.pyc creating build/bdist.linux-x86_64/egg/EGG-INFO copying metagpt.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO copying metagpt.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO copying metagpt.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO copying metagpt.egg-info/requires.txt -> build/bdist.linux-x86_64/egg/EGG-INFO copying metagpt.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO creating 'dist/metagpt-0.1-py3.9.egg' and adding 'build/bdist.linux-x86_64/egg' to it removing 'build/bdist.linux-x86_64/egg' (and everything under it) Processing metagpt-0.1-py3.9.egg removing '/data/miniconda3/envs/metagpt/lib/python3.9/site-packages/metagpt-0.1-py3.9.egg' (and everything under it) creating /data/miniconda3/envs/metagpt/lib/python3.9/site-packages/metagpt-0.1-py3.9.egg Extracting metagpt-0.1-py3.9.egg to /data/miniconda3/envs/metagpt/lib/python3.9/site-packages metagpt 0.1 is already the active version in easy-install.pth Installed /data/miniconda3/envs/metagpt/lib/python3.9/site-packages/metagpt-0.1-py3.9.egg Processing dependencies for metagpt==0.1 Searching for qdrant-client==1.4.0 Best match: qdrant-client 1.4.0 Processing qdrant_client-1.4.0-py3.9.egg qdrant-client 1.4.0 is already the active version in easy-install.pth Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages/qdrant_client-1.4.0-py3.9.egg Searching for libcst==1.0.1 Best match: libcst 1.0.1 Adding libcst 1.0.1 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for typing-extensions==4.5.0 Best match: typing-extensions 4.5.0 Adding typing-extensions 4.5.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for typing-inspect==0.8.0 Best match: typing-inspect 0.8.0 Adding typing-inspect 0.8.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for anthropic==0.3.6 Best match: anthropic 0.3.6 Adding anthropic 0.3.6 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for tqdm==4.64.0 Best match: tqdm 4.64.0 Adding tqdm 4.64.0 to easy-install.pth file Installing tqdm script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for tiktoken==0.3.3 Best match: tiktoken 0.3.3 Adding tiktoken 0.3.3 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for tenacity==8.2.2 Best match: tenacity 8.2.2 Adding tenacity 8.2.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for setuptools==65.6.3 Best match: setuptools 65.6.3 Adding setuptools 65.6.3 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for PyYAML==6.0 Best match: PyYAML 6.0 Adding PyYAML 6.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for python-docx==0.8.11 Best match: python-docx 0.8.11 Adding python-docx 0.8.11 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for pytest==7.2.2 Best match: pytest 7.2.2 Adding pytest 7.2.2 to easy-install.pth file Installing py.test script to /data/miniconda3/envs/metagpt/bin Installing pytest script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for pydantic==1.10.8 Best match: pydantic 1.10.8 Processing pydantic-1.10.8-py3.9.egg pydantic 1.10.8 is already the active version in easy-install.pth Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages/pydantic-1.10.8-py3.9.egg Searching for pandas==2.0.3 Best match: pandas 2.0.3 Adding pandas 2.0.3 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for beautifulsoup4==4.12.2 Best match: beautifulsoup4 4.12.2 Adding beautifulsoup4 4.12.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for openpyxl==3.1.2 Best match: openpyxl 3.1.2 Adding openpyxl 3.1.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for openai==0.27.8 Best match: openai 0.27.8 Adding openai 0.27.8 to easy-install.pth file Installing openai script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for numpy==1.24.3 Best match: numpy 1.24.3 Adding numpy 1.24.3 to easy-install.pth file Installing f2py script to /data/miniconda3/envs/metagpt/bin Installing f2py3 script to /data/miniconda3/envs/metagpt/bin Installing f2py3.9 script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for meilisearch==0.21.0 Best match: meilisearch 0.21.0 Adding meilisearch 0.21.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for loguru==0.6.0 Best match: loguru 0.6.0 Adding loguru 0.6.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for langchain==0.0.231 Best match: langchain 0.0.231 Adding langchain 0.0.231 to easy-install.pth file Installing langchain-server script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for lancedb==0.1.16 Best match: lancedb 0.1.16 Adding lancedb 0.1.16 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for fire==0.4.0 Best match: fire 0.4.0 Adding fire 0.4.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for faiss-cpu==1.7.4 Best match: faiss-cpu 1.7.4 Adding faiss-cpu 1.7.4 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for channels==4.0.0 Best match: channels 4.0.0 Adding channels 4.0.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for aiohttp==3.8.4 Best match: aiohttp 3.8.4 Adding aiohttp 3.8.4 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for urllib3==1.26.14 Best match: urllib3 1.26.14 Adding urllib3 1.26.14 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for portalocker==2.7.0 Best match: portalocker 2.7.0 Processing portalocker-2.7.0-py3.9.egg portalocker 2.7.0 is already the active version in easy-install.pth Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages/portalocker-2.7.0-py3.9.egg Searching for httpx==0.24.1 Best match: httpx 0.24.1 Adding httpx 0.24.1 to easy-install.pth file Installing httpx script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for grpcio-tools==1.57.0 Best match: grpcio-tools 1.57.0 Processing grpcio_tools-1.57.0-py3.9-linux-x86_64.egg grpcio-tools 1.57.0 is already the active version in easy-install.pth Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages/grpcio_tools-1.57.0-py3.9-linux-x86_64.egg Searching for grpcio==1.57.0 Best match: grpcio 1.57.0 Adding grpcio 1.57.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for mypy-extensions==1.0.0 Best match: mypy-extensions 1.0.0 Adding mypy-extensions 1.0.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for tokenizers==0.13.3 Best match: tokenizers 0.13.3 Adding tokenizers 0.13.3 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for distro==1.8.0 Best match: distro 1.8.0 Adding distro 1.8.0 to easy-install.pth file Installing distro script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for anyio==3.7.1 Best match: anyio 3.7.1 Adding anyio 3.7.1 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for requests==2.27.0 Best match: requests 2.27.0 Adding requests 2.27.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for regex==2023.6.3 Best match: regex 2023.6.3 Adding regex 2023.6.3 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for lxml==4.9.3 Best match: lxml 4.9.3 Adding lxml 4.9.3 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for tomli==2.0.1 Best match: tomli 2.0.1 Adding tomli 2.0.1 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for exceptiongroup==1.1.2 Best match: exceptiongroup 1.1.2 Adding exceptiongroup 1.1.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for pluggy==1.2.0 Best match: pluggy 1.2.0 Adding pluggy 1.2.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for packaging==23.1 Best match: packaging 23.1 Adding packaging 23.1 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for iniconfig==2.0.0 Best match: iniconfig 2.0.0 Adding iniconfig 2.0.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for attrs==23.1.0 Best match: attrs 23.1.0 Adding attrs 23.1.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for tzdata==2023.3 Best match: tzdata 2023.3 Adding tzdata 2023.3 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for pytz==2021.3 Best match: pytz 2021.3 Adding pytz 2021.3 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for python-dateutil==2.8.2 Best match: python-dateutil 2.8.2 Adding python-dateutil 2.8.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for soupsieve==2.4.1 Best match: soupsieve 2.4.1 Adding soupsieve 2.4.1 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for et-xmlfile==1.1.0 Best match: et-xmlfile 1.1.0 Adding et-xmlfile 1.1.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for camel-converter==3.0.2 Best match: camel-converter 3.0.2 Adding camel-converter 3.0.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for openapi-schema-pydantic==1.2.4 Best match: openapi-schema-pydantic 1.2.4 Adding openapi-schema-pydantic 1.2.4 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for numexpr==2.8.4 Best match: numexpr 2.8.4 Adding numexpr 2.8.4 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for langchainplus-sdk==0.0.20 Best match: langchainplus-sdk 0.0.20 Adding langchainplus-sdk 0.0.20 to easy-install.pth file Installing langchain script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for dataclasses-json==0.5.12 Best match: dataclasses-json 0.5.12 Adding dataclasses-json 0.5.12 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for async-timeout==4.0.2 Best match: async-timeout 4.0.2 Adding async-timeout 4.0.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for SQLAlchemy==2.0.19 Best match: SQLAlchemy 2.0.19 Adding SQLAlchemy 2.0.19 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for semver==3.0.1 Best match: semver 3.0.1 Adding semver 3.0.1 to easy-install.pth file Installing pysemver script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for attr==0.3.2 Best match: attr 0.3.2 Adding attr 0.3.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for retry==0.9.2 Best match: retry 0.9.2 Adding retry 0.9.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for ratelimiter==1.2.0.post0 Best match: ratelimiter 1.2.0.post0 Adding ratelimiter 1.2.0.post0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for pylance==0.5.10 Best match: pylance 0.5.10 Adding pylance 0.5.10 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for termcolor==2.3.0 Best match: termcolor 2.3.0 Adding termcolor 2.3.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for six==1.16.0 Best match: six 1.16.0 Adding six 1.16.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for asgiref==3.7.2 Best match: asgiref 3.7.2 Adding asgiref 3.7.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for Django==4.2.3 Best match: Django 4.2.3 Adding Django 4.2.3 to easy-install.pth file Installing django-admin script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for aiosignal==1.3.1 Best match: aiosignal 1.3.1 Adding aiosignal 1.3.1 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for frozenlist==1.4.0 Best match: frozenlist 1.4.0 Adding frozenlist 1.4.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for yarl==1.9.2 Best match: yarl 1.9.2 Adding yarl 1.9.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for multidict==6.0.4 Best match: multidict 6.0.4 Adding multidict 6.0.4 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for charset-normalizer==2.0.10 Best match: charset-normalizer 2.0.10 Adding charset-normalizer 2.0.10 to easy-install.pth file Installing normalizer script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for h2==3.2.0 Best match: h2 3.2.0 Adding h2 3.2.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for sniffio==1.3.0 Best match: sniffio 1.3.0 Adding sniffio 1.3.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for idna==3.3 Best match: idna 3.3 Adding idna 3.3 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for httpcore==0.17.3 Best match: httpcore 0.17.3 Adding httpcore 0.17.3 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for certifi==2021.10.8 Best match: certifi 2021.10.8 Adding certifi 2021.10.8 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for protobuf==4.23.4 Best match: protobuf 4.23.4 Adding protobuf 4.23.4 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for marshmallow==3.19.0 Best match: marshmallow 3.19.0 Adding marshmallow 3.19.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for greenlet==2.0.2 Best match: greenlet 2.0.2 Adding greenlet 2.0.2 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for py==1.11.0 Best match: py 1.11.0 Adding py 1.11.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for decorator==5.1.1 Best match: decorator 5.1.1 Adding decorator 5.1.1 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for pyarrow==13.0.0 Best match: pyarrow 13.0.0 Adding pyarrow 13.0.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for sqlparse==0.4.4 Best match: sqlparse 0.4.4 Adding sqlparse 0.4.4 to easy-install.pth file Installing sqlformat script to /data/miniconda3/envs/metagpt/bin Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for hpack==3.0.0 Best match: hpack 3.0.0 Adding hpack 3.0.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for hyperframe==5.2.0 Best match: hyperframe 5.2.0 Adding hyperframe 5.2.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Searching for h11==0.14.0 Best match: h11 0.14.0 Adding h11 0.14.0 to easy-install.pth file Using /data/miniconda3/envs/metagpt/lib/python3.9/site-packages Finished processing dependencies for metagpt==0.1 by Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple Obtaining file:///home/cheng/projects/MetaGPT Preparing metadata (setup.py): started Preparing metadata (setup.py): finished with status 'done' Requirement already satisfied: aiohttp==3.8.4 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (3.8.4) Requirement already satisfied: channels==4.0.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (4.0.0) Requirement already satisfied: faiss_cpu==1.7.4 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (1.7.4) Requirement already satisfied: fire==0.4.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (0.4.0) Requirement already satisfied: lancedb==0.1.16 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (0.1.16) Requirement already satisfied: langchain==0.0.231 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (0.0.231) Requirement already satisfied: loguru==0.6.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (0.6.0) Requirement already satisfied: meilisearch==0.21.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (0.21.0) Requirement already satisfied: numpy==1.24.3 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (1.24.3) Requirement already satisfied: openai==0.27.8 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (0.27.8) Requirement already satisfied: openpyxl in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (3.1.2) Requirement already satisfied: beautifulsoup4==4.12.2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (4.12.2) Requirement already satisfied: pandas==2.0.3 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (2.0.3) Requirement already satisfied: pydantic==1.10.8 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (1.10.8) Requirement already satisfied: pytest==7.2.2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (7.2.2) Requirement already satisfied: python_docx==0.8.11 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (0.8.11) Requirement already satisfied: PyYAML==6.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (6.0) Requirement already satisfied: setuptools==65.6.3 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (65.6.3) Requirement already satisfied: tenacity==8.2.2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (8.2.2) Requirement already satisfied: tiktoken==0.3.3 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (0.3.3) Requirement already satisfied: tqdm==4.64.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (4.64.0) Requirement already satisfied: anthropic==0.3.6 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (0.3.6) Requirement already satisfied: typing-inspect==0.8.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (0.8.0) Requirement already satisfied: typing_extensions==4.5.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (4.5.0) Requirement already satisfied: libcst==1.0.1 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from metagpt==0.1) (1.0.1) Requirement already satisfied: qdrant-client==1.4.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages/qdrant_client-1.4.0-py3.9.egg (from metagpt==0.1) (1.4.0) Requirement already satisfied: attrs>=17.3.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from aiohttp==3.8.4->metagpt==0.1) (23.1.0) Requirement already satisfied: charset-normalizer<4.0,>=2.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from aiohttp==3.8.4->metagpt==0.1) (2.0.10) Requirement already satisfied: multidict<7.0,>=4.5 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from aiohttp==3.8.4->metagpt==0.1) (6.0.4) Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from aiohttp==3.8.4->metagpt==0.1) (4.0.2) Requirement already satisfied: yarl<2.0,>=1.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from aiohttp==3.8.4->metagpt==0.1) (1.9.2) Requirement already satisfied: frozenlist>=1.1.1 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from aiohttp==3.8.4->metagpt==0.1) (1.4.0) Requirement already satisfied: aiosignal>=1.1.2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from aiohttp==3.8.4->metagpt==0.1) (1.3.1) Requirement already satisfied: anyio<4,>=3.5.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from anthropic==0.3.6->metagpt==0.1) (3.7.1) Requirement already satisfied: distro<2,>=1.7.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from anthropic==0.3.6->metagpt==0.1) (1.8.0) Requirement already satisfied: httpx<1,>=0.23.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from anthropic==0.3.6->metagpt==0.1) (0.24.1) Requirement already satisfied: tokenizers>=0.13.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from anthropic==0.3.6->metagpt==0.1) (0.13.3) Requirement already satisfied: soupsieve>1.2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from beautifulsoup4==4.12.2->metagpt==0.1) (2.4.1) Requirement already satisfied: Django>=3.2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from channels==4.0.0->metagpt==0.1) (4.2.3) Requirement already satisfied: asgiref<4,>=3.5.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from channels==4.0.0->metagpt==0.1) (3.7.2) Requirement already satisfied: six in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from fire==0.4.0->metagpt==0.1) (1.16.0) Requirement already satisfied: termcolor in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from fire==0.4.0->metagpt==0.1) (2.3.0) Requirement already satisfied: pylance==0.5.10 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from lancedb==0.1.16->metagpt==0.1) (0.5.10) Requirement already satisfied: ratelimiter in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from lancedb==0.1.16->metagpt==0.1) (1.2.0.post0) Requirement already satisfied: retry in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from lancedb==0.1.16->metagpt==0.1) (0.9.2) Requirement already satisfied: attr in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from lancedb==0.1.16->metagpt==0.1) (0.3.2) Requirement already satisfied: semver in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from lancedb==0.1.16->metagpt==0.1) (3.0.1) Requirement already satisfied: SQLAlchemy<3,>=1.4 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from langchain==0.0.231->metagpt==0.1) (2.0.19) Requirement already satisfied: dataclasses-json<0.6.0,>=0.5.7 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from langchain==0.0.231->metagpt==0.1) (0.5.12) Requirement already satisfied: langchainplus-sdk<0.0.21,>=0.0.20 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from langchain==0.0.231->metagpt==0.1) (0.0.20) Requirement already satisfied: numexpr<3.0.0,>=2.8.4 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from langchain==0.0.231->metagpt==0.1) (2.8.4) Requirement already satisfied: openapi-schema-pydantic<2.0,>=1.2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from langchain==0.0.231->metagpt==0.1) (1.2.4) Requirement already satisfied: requests<3,>=2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from langchain==0.0.231->metagpt==0.1) (2.27.0) Requirement already satisfied: camel-converter[pydantic] in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from meilisearch==0.21.0->metagpt==0.1) (3.0.2) Requirement already satisfied: python-dateutil>=2.8.2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from pandas==2.0.3->metagpt==0.1) (2.8.2) Requirement already satisfied: pytz>=2020.1 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from pandas==2.0.3->metagpt==0.1) (2021.3) Requirement already satisfied: tzdata>=2022.1 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from pandas==2.0.3->metagpt==0.1) (2023.3) Requirement already satisfied: iniconfig in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from pytest==7.2.2->metagpt==0.1) (2.0.0) Requirement already satisfied: packaging in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from pytest==7.2.2->metagpt==0.1) (23.1) Requirement already satisfied: pluggy<2.0,>=0.12 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from pytest==7.2.2->metagpt==0.1) (1.2.0) Requirement already satisfied: exceptiongroup>=1.0.0rc8 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from pytest==7.2.2->metagpt==0.1) (1.1.2) Requirement already satisfied: tomli>=1.0.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from pytest==7.2.2->metagpt==0.1) (2.0.1) Requirement already satisfied: lxml>=2.3.2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from python_docx==0.8.11->metagpt==0.1) (4.9.3) Requirement already satisfied: grpcio>=1.41.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from qdrant-client==1.4.0->metagpt==0.1) (1.57.0) Requirement already satisfied: grpcio-tools>=1.41.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages/grpcio_tools-1.57.0-py3.9-linux-x86_64.egg (from qdrant-client==1.4.0->metagpt==0.1) (1.57.0) Requirement already satisfied: portalocker<3.0.0,>=2.7.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages/portalocker-2.7.0-py3.9.egg (from qdrant-client==1.4.0->metagpt==0.1) (2.7.0) Requirement already satisfied: urllib3<2.0.0,>=1.26.14 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from qdrant-client==1.4.0->metagpt==0.1) (1.26.14) Requirement already satisfied: regex>=2022.1.18 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from tiktoken==0.3.3->metagpt==0.1) (2023.6.3) Requirement already satisfied: mypy-extensions>=0.3.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from typing-inspect==0.8.0->metagpt==0.1) (1.0.0) Requirement already satisfied: pyarrow>=10 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from pylance==0.5.10->lancedb==0.1.16->metagpt==0.1) (13.0.0) Requirement already satisfied: et-xmlfile in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from openpyxl->metagpt==0.1) (1.1.0) Requirement already satisfied: idna>=2.8 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from anyio<4,>=3.5.0->anthropic==0.3.6->metagpt==0.1) (3.3) Requirement already satisfied: sniffio>=1.1 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from anyio<4,>=3.5.0->anthropic==0.3.6->metagpt==0.1) (1.3.0) Requirement already satisfied: marshmallow<4.0.0,>=3.18.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from dataclasses-json<0.6.0,>=0.5.7->langchain==0.0.231->metagpt==0.1) (3.19.0) Requirement already satisfied: sqlparse>=0.3.1 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from Django>=3.2->channels==4.0.0->metagpt==0.1) (0.4.4) Requirement already satisfied: protobuf<5.0dev,>=4.21.6 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from grpcio-tools>=1.41.0->qdrant-client==1.4.0->metagpt==0.1) (4.23.4) Requirement already satisfied: certifi in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from httpx<1,>=0.23.0->anthropic==0.3.6->metagpt==0.1) (2021.10.8) Requirement already satisfied: httpcore<0.18.0,>=0.15.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from httpx<1,>=0.23.0->anthropic==0.3.6->metagpt==0.1) (0.17.3) Requirement already satisfied: h2<5,>=3 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from httpx<1,>=0.23.0->anthropic==0.3.6->metagpt==0.1) (3.2.0) Requirement already satisfied: greenlet!=0.4.17 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from SQLAlchemy<3,>=1.4->langchain==0.0.231->metagpt==0.1) (2.0.2) Requirement already satisfied: decorator>=3.4.2 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from retry->lancedb==0.1.16->metagpt==0.1) (5.1.1) Requirement already satisfied: py<2.0.0,>=1.4.26 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from retry->lancedb==0.1.16->metagpt==0.1) (1.11.0) Requirement already satisfied: hyperframe<6,>=5.2.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from h2<5,>=3->httpx<1,>=0.23.0->anthropic==0.3.6->metagpt==0.1) (5.2.0) Requirement already satisfied: hpack<4,>=3.0 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from h2<5,>=3->httpx<1,>=0.23.0->anthropic==0.3.6->metagpt==0.1) (3.0.0) Requirement already satisfied: h11<0.15,>=0.13 in /data/miniconda3/envs/metagpt/lib/python3.9/site-packages (from httpcore<0.18.0,>=0.15.0->httpx<1,>=0.23.0->anthropic==0.3.6->metagpt==0.1) (0.14.0) Installing collected packages: metagpt Attempting uninstall: metagpt Found existing installation: metagpt 0.1 Uninstalling metagpt-0.1: Successfully uninstalled metagpt-0.1 Running setup.py develop for metagpt Successfully installed metagpt-0.1 --- .devcontainer/postCreateCommand.sh | 2 +- Dockerfile | 2 +- README.md | 4 ++-- docs/README_CN.md | 12 ++++++------ docs/README_JA.md | 12 ++++++------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index 06d12e408..46788e306 100644 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -4,4 +4,4 @@ sudo npm install -g @mermaid-js/mermaid-cli # Step 2: Ensure that Python 3.9+ is installed on your system. You can check this by using: python --version -python setup.py install \ No newline at end of file +pip install -e. \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 537bbc72e..8ab180e28 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ COPY . /app/metagpt WORKDIR /app/metagpt RUN mkdir workspace &&\ pip install --no-cache-dir -r requirements.txt &&\ - python setup.py install + pip install -e. # Running with an infinite loop using the tail command CMD ["sh", "-c", "tail -f /dev/null"] diff --git a/README.md b/README.md index 52556b07f..91a5483e0 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ # Step 2: Ensure that Python 3.9+ is installed on your system. You can check thi # Step 3: Clone the repository to your local machine, and install it. git clone https://github.com/geekan/metagpt cd metagpt -python setup.py install +pip install -e. ``` **Note:** @@ -91,7 +91,7 @@ # Step 3: Clone the repository to your local machine, and install it. MMDC: "./node_modules/.bin/mmdc" ``` -- if `python setup.py install` fails with error `[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'`, try instead running `python setup.py install --user` +- if `pip install -e.` fails with error `[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'`, try instead running `pip install -e. --user` - To convert Mermaid charts to SVG, PNG, and PDF formats. In addition to the Node.js version of Mermaid-CLI, you now have the option to use Python version Playwright, pyppeteer or mermaid.ink for this task. diff --git a/docs/README_CN.md b/docs/README_CN.md index e96193d47..1372bf9f4 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -61,7 +61,7 @@ # 第 2 步:确保您的系统上安装了 Python 3.9+。您可以使用以下 # 第 3 步:克隆仓库到您的本地机器,并进行安装。 git clone https://github.com/geekan/metagpt cd metagpt -python setup.py install +pip install -e. ``` **注意:** @@ -81,7 +81,7 @@ # 第 3 步:克隆仓库到您的本地机器,并进行安装。 MMDC: "./node_modules/.bin/mmdc" ``` -- 如果`python setup.py install`失败并显示错误`[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'`,请尝试使用`python setup.py install --user`运行。 +- 如果`pip install -e.`失败并显示错误`[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'`,请尝试使用`pip install -e. --user`运行。 ### Docker安装 @@ -136,10 +136,10 @@ # 复制配置文件并进行必要的修改 cp config/config.yaml config/key.yaml ``` -| 变量名 | config/key.yaml | env | -|--------------------------------------------|-------------------------------------------|--------------------------------| -| OPENAI_API_KEY # 用您自己的密钥替换 | OPENAI_API_KEY: "sk-..." | export OPENAI_API_KEY="sk-..." | -| OPENAI_API_BASE # 可选 | OPENAI_API_BASE: "https:///v1" | export OPENAI_API_BASE="https:///v1" | +| 变量名 | config/key.yaml | env | +| ----------------------------------- | ----------------------------------------- | ----------------------------------------------- | +| OPENAI_API_KEY # 用您自己的密钥替换 | OPENAI_API_KEY: "sk-..." | export OPENAI_API_KEY="sk-..." | +| OPENAI_API_BASE # 可选 | OPENAI_API_BASE: "https:///v1" | export OPENAI_API_BASE="https:///v1" | ## 示例:启动一个创业公司 diff --git a/docs/README_JA.md b/docs/README_JA.md index e84d0fba1..8d6c2fe84 100644 --- a/docs/README_JA.md +++ b/docs/README_JA.md @@ -65,7 +65,7 @@ # ステップ 2: Python 3.9+ がシステムにインストールされてい # ステップ 3: リポジトリをローカルマシンにクローンし、インストールする。 git clone https://github.com/geekan/metagpt cd metagpt -python setup.py install +pip install -e. ``` **注:** @@ -86,7 +86,7 @@ # ステップ 3: リポジトリをローカルマシンにクローンし、 MMDC: "./node_modules/.bin/mmdc" ``` -- もし `python setup.py install` がエラー `[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'` で失敗したら、代わりに `python setup.py install --user` を実行してみてください +- もし `pip install -e.` がエラー `[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'` で失敗したら、代わりに `pip install -e. --user` を実行してみてください ### Docker によるインストール @@ -141,10 +141,10 @@ # 設定ファイルをコピーし、必要な修正を加える。 cp config/config.yaml config/key.yaml ``` -| 変数名 | config/key.yaml | env | -| ------------------------------------------ | ----------------------------------------- | ----------------------------------------------- | -| OPENAI_API_KEY # 自分のキーに置き換える | OPENAI_API_KEY: "sk-..." | export OPENAI_API_KEY="sk-..." | -| OPENAI_API_BASE # オプション | OPENAI_API_BASE: "https:///v1" | export OPENAI_API_BASE="https:///v1" | +| 変数名 | config/key.yaml | env | +| --------------------------------------- | ----------------------------------------- | ----------------------------------------------- | +| OPENAI_API_KEY # 自分のキーに置き換える | OPENAI_API_KEY: "sk-..." | export OPENAI_API_KEY="sk-..." | +| OPENAI_API_BASE # オプション | OPENAI_API_BASE: "https:///v1" | export OPENAI_API_BASE="https:///v1" | ## チュートリアル: スタートアップの開始 From fcc9566f793953faa7fbecc425eabab8c1a9225c Mon Sep 17 00:00:00 2001 From: shenchucheng Date: Mon, 18 Sep 2023 22:37:54 +0800 Subject: [PATCH 76/76] fix RuntimeError: Event loop is closed in windows --- metagpt/__init__.py | 4 +++- metagpt/_compat.py | 15 +++++++++++++++ startup.py | 35 +++++++++++++++++++---------------- 3 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 metagpt/_compat.py diff --git a/metagpt/__init__.py b/metagpt/__init__.py index b9c530d24..71ddd1aff 100644 --- a/metagpt/__init__.py +++ b/metagpt/__init__.py @@ -1,5 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2023/4/24 22:26 # @Author : alexanderwu # @File : __init__.py + +from metagpt import _compat as _ # noqa: F401 diff --git a/metagpt/_compat.py b/metagpt/_compat.py new file mode 100644 index 000000000..e94a4d095 --- /dev/null +++ b/metagpt/_compat.py @@ -0,0 +1,15 @@ +import platform +import sys +import warnings + +if sys.implementation.name == "cpython" and platform.system() == "Windows" and sys.version_info[:2] == (3, 9): + # https://github.com/python/cpython/pull/92842 + + from asyncio.proactor_events import _ProactorBasePipeTransport + + def pacth_del(self, _warn=warnings.warn): + if self._sock is not None: + _warn(f"unclosed transport {self!r}", ResourceWarning, source=self) + self._sock.close() + + _ProactorBasePipeTransport.__del__ = pacth_del diff --git a/startup.py b/startup.py index e6d5fc4e9..e2a903c9b 100644 --- a/startup.py +++ b/startup.py @@ -1,11 +1,16 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- import asyncio -import platform + import fire -from metagpt.roles import Architect, Engineer, ProductManager -from metagpt.roles import ProjectManager, QaEngineer +from metagpt.roles import ( + Architect, + Engineer, + ProductManager, + ProjectManager, + QaEngineer, +) from metagpt.software_company import SoftwareCompany @@ -15,15 +20,17 @@ async def startup( n_round: int = 5, code_review: bool = False, run_tests: bool = False, - implement: bool = True + implement: bool = True, ): """Run a startup. Be a boss.""" company = SoftwareCompany() - company.hire([ - ProductManager(), - Architect(), - ProjectManager(), - ]) + company.hire( + [ + ProductManager(), + Architect(), + ProjectManager(), + ] + ) # if implement or code_review if implement or code_review: @@ -46,7 +53,7 @@ def main( n_round: int = 5, code_review: bool = True, run_tests: bool = False, - implement: bool = True + implement: bool = True, ): """ We are a software startup comprised of AI. By investing in us, @@ -58,12 +65,8 @@ def main( :param code_review: Whether to use code review. :return: """ - if platform.system() == "Windows": - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - asyncio.run(startup(idea, investment, n_round, - code_review, run_tests, implement)) + asyncio.run(startup(idea, investment, n_round, code_review, run_tests, implement)) -if __name__ == '__main__': +if __name__ == "__main__": fire.Fire(main) -

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 HbNYV+YjO5V From 1e09c5384a43c714a7250d2113e0d4639376e252 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:30:03 +0800 Subject: [PATCH 44/76] Delete docs/resources/MetaGPT-WorkWeChatGroup-6.jpg rm wechat group img --- docs/resources/MetaGPT-WorkWeChatGroup-6.jpg | Bin 86287 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/resources/MetaGPT-WorkWeChatGroup-6.jpg diff --git a/docs/resources/MetaGPT-WorkWeChatGroup-6.jpg b/docs/resources/MetaGPT-WorkWeChatGroup-6.jpg deleted file mode 100644 index 77a4668f7526369879e2647b745b9a1f979cd9b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86287 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%0A-0JT*xa=xmyq;|p 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 Date: Tue, 12 Sep 2023 07:23:07 +0000 Subject: [PATCH 45/76] bug fix --- metagpt/utils/mermaid.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/metagpt/utils/mermaid.py b/metagpt/utils/mermaid.py index 713f49601..b13199a93 100644 --- a/metagpt/utils/mermaid.py +++ b/metagpt/utils/mermaid.py @@ -13,8 +13,7 @@ from metagpt.const import PROJECT_ROOT from metagpt.logs import logger from metagpt.utils.common import check_cmd_exists import os - - +import sys def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048) -> int: """suffix: png/svg/pdf @@ -28,13 +27,12 @@ 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") tmp.write_text(mermaid_code, encoding="utf-8") - - if check_cmd_exists("mmdc") != 0: - logger.warning("RUN `npm install -g @mermaid-js/mermaid-cli` to install mmdc") - return -1 engine = CONFIG.mermaid_engine.lower() - if engine == "nodejs": + if check_cmd_exists("mmdc") != 0: + logger.warning("RUN `npm install -g @mermaid-js/mermaid-cli` to install mmdc") + return -1 + 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 @@ -59,12 +57,12 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height else: subprocess.run([CONFIG.mmdc, "-i", str(tmp), "-o", output_file, "-w", str(width), "-H", str(height)]) else: - if engine not in ['playwright', 'puppeteer', 'ink']: + if engine not in ['playwright', 'pyppeteer', 'ink']: logger.warning(f"Unsupported mermaid engine: {engine}") return -1 __dirname = os.path.dirname(os.path.abspath(__file__)) module_path = os.path.join(__dirname, f'mmdc_{engine}.py') - import sys + # 构建命令行参数 command = [ sys.executable, From 95391ca32d2a5c2381b6eb20ff6bafbb33e41b58 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Tue, 12 Sep 2023 08:44:50 +0000 Subject: [PATCH 46/76] bug fix --- README.md | 16 +++++++++------- metagpt/utils/common.py | 1 + metagpt/utils/mmdc_pyppeteer.py | 13 +++++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 5faf5d9de..5e67483d6 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ # Step 3: Clone the repository to your local machine, and install it. - **Install the Required Browsers** - to support PDF conversion, had better install Chrominum. + to support PDF conversion, please install Chrominum. ```bash playwright install --with-deps chromium @@ -114,18 +114,20 @@ # Step 3: Clone the repository to your local machine, and install it. pip install pyppeteer ``` - - **Install the Required Browsers** + - **Use your own Browsers** - ```bash - pyppeteer-install - ``` - - pyppeteer alow you use already installed browsers, if you do not want to run the above command, please set the following envirment + pyppeteer alow you use installed browsers, please set the following envirment ```bash export PUPPETEER_EXECUTABLE_PATH = /path/to/your/chromium or edge or chrome ``` + please do not use this command to install browser, it is too old + + ```bash + pyppeteer-install + ``` + - **modify `config.yaml`** uncomment MERMAID_ENGINE from config.yaml and change it to `pyppeteer` diff --git a/metagpt/utils/common.py b/metagpt/utils/common.py index 99038dc64..5f94de066 100644 --- a/metagpt/utils/common.py +++ b/metagpt/utils/common.py @@ -192,6 +192,7 @@ class CodeParser: logger.error(f"{pattern} not match following text:") logger.error(text) # raise Exception + return "" return code @classmethod diff --git a/metagpt/utils/mmdc_pyppeteer.py b/metagpt/utils/mmdc_pyppeteer.py index e6986bc76..f3e00d053 100644 --- a/metagpt/utils/mmdc_pyppeteer.py +++ b/metagpt/utils/mmdc_pyppeteer.py @@ -34,10 +34,15 @@ def mermaid_to_file(mermaid_code, output_file_without_suffix, width, height): async def mermaid_to_file0(mermaid_code, output_file_without_suffix, width=2048, height=2048, suffixes=['png', 'svg', 'pdf'])-> int: __dirname = os.path.dirname(os.path.abspath(__file__)) - browser = await launch(headless=True, - executablePath=os.getenv('PUPPETEER_EXECUTABLE_PATH',"/opt/homebrew/bin/chromium"), - args=['--disable-extensions',"--no-sandbox"] - ) + executablePath = os.getenv('PUPPETEER_EXECUTABLE_PATH',"") + if executablePath: + browser = await launch(headless=True, + executablePath=executablePath, + args=['--disable-extensions',"--no-sandbox"] + ) + else: + print("Please set the environment variable:PUPPETEER_EXECUTABLE_PATH.") + return -1 page = await browser.newPage() device_scale_factor = 1.0 From a725a2398d1d77d3fa03e0a6141ca63239167ba8 Mon Sep 17 00:00:00 2001 From: stellaHSR <34952977+stellaHSR@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:07:56 +0800 Subject: [PATCH 47/76] Update README.md update info and agentstore waitlist --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2a4f18c1..b4a272ef0 100644 --- a/README.md +++ b/README.md @@ -12,16 +12,17 @@ # MetaGPT: The Multi-Agent Framework CN doc EN doc JA doc -Discord Follow -License: MIT +Discord Follow +License: MIT roadmap Twitter Follow