diff --git a/metagpt/actions/__init__.py b/metagpt/actions/__init__.py index b004bd58e..f2e939e0e 100644 --- a/metagpt/actions/__init__.py +++ b/metagpt/actions/__init__.py @@ -12,16 +12,12 @@ from metagpt.actions.action_output import ActionOutput from metagpt.actions.add_requirement import BossRequirement from metagpt.actions.debug_error import DebugError from metagpt.actions.design_api import WriteDesign -from metagpt.actions.design_api_review import DesignReview -from metagpt.actions.design_filenames import DesignFilenames from metagpt.actions.project_management import AssignTasks, WriteTasks -from metagpt.actions.research import CollectLinks, WebBrowseAndSummarize, ConductResearch from metagpt.actions.run_code import RunCode from metagpt.actions.search_and_summarize import SearchAndSummarize from metagpt.actions.write_code import WriteCode from metagpt.actions.write_code_review import WriteCodeReview from metagpt.actions.write_prd import WritePRD -from metagpt.actions.write_prd_review import WritePRDReview from metagpt.actions.write_test import WriteTest @@ -30,10 +26,7 @@ class ActionType(Enum): ADD_REQUIREMENT = BossRequirement WRITE_PRD = WritePRD - WRITE_PRD_REVIEW = WritePRDReview WRITE_DESIGN = WriteDesign - DESIGN_REVIEW = DesignReview - DESIGN_FILENAMES = DesignFilenames WRTIE_CODE = WriteCode WRITE_CODE_REVIEW = WriteCodeReview WRITE_TEST = WriteTest @@ -42,9 +35,6 @@ class ActionType(Enum): WRITE_TASKS = WriteTasks ASSIGN_TASKS = AssignTasks SEARCH_AND_SUMMARIZE = SearchAndSummarize - COLLECT_LINKS = CollectLinks - WEB_BROWSE_AND_SUMMARIZE = WebBrowseAndSummarize - CONDUCT_RESEARCH = ConductResearch __all__ = [ diff --git a/metagpt/actions/analyze_dep_libs.py b/metagpt/actions/analyze_dep_libs.py deleted file mode 100644 index 53d40200a..000000000 --- a/metagpt/actions/analyze_dep_libs.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/5/19 12:01 -@Author : alexanderwu -@File : analyze_dep_libs.py -""" - -from metagpt.actions import Action - -PROMPT = """You are an AI developer, trying to write a program that generates code for users based on their intentions. - -For the user's prompt: - ---- -The API is: {prompt} ---- - -We decide the generated files are: {filepaths_string} - -Now that we have a file list, we need to understand the shared dependencies they have. -Please list and briefly describe the shared contents between the files we are generating, including exported variables, -data patterns, id names of all DOM elements that javascript functions will use, message names and function names. -Focus only on the names of shared dependencies, do not add any other explanations. -""" - - -class AnalyzeDepLibs(Action): - def __init__(self, name, context=None, llm=None): - super().__init__(name, context, llm) - self.desc = "Analyze the runtime dependencies of the program based on the context" - - async def run(self, requirement, filepaths_string): - # prompt = f"Below is the product requirement document (PRD):\n\n{prd}\n\n{PROMPT}" - prompt = PROMPT.format(prompt=requirement, filepaths_string=filepaths_string) - design_filenames = await self._aask(prompt) - return design_filenames diff --git a/metagpt/actions/azure_tts.py b/metagpt/actions/azure_tts.py deleted file mode 100644 index c13a4750d..000000000 --- a/metagpt/actions/azure_tts.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/6/9 22:22 -@Author : Leo Xiao -@File : azure_tts.py -""" -from azure.cognitiveservices.speech import AudioConfig, SpeechConfig, SpeechSynthesizer - -from metagpt.actions.action import Action -from metagpt.config import Config - - -class AzureTTS(Action): - def __init__(self, name, context=None, llm=None): - super().__init__(name, context, llm) - self.config = Config() - - # Parameters reference: https://learn.microsoft.com/zh-cn/azure/cognitive-services/speech-service/language-support?tabs=tts#voice-styles-and-roles - def synthesize_speech(self, lang, voice, role, text, output_file): - subscription_key = self.config.get('AZURE_TTS_SUBSCRIPTION_KEY') - region = self.config.get('AZURE_TTS_REGION') - speech_config = SpeechConfig( - subscription=subscription_key, region=region) - - speech_config.speech_synthesis_voice_name = voice - audio_config = AudioConfig(filename=output_file) - synthesizer = SpeechSynthesizer( - speech_config=speech_config, - audio_config=audio_config) - - # if voice=="zh-CN-YunxiNeural": - ssml_string = f""" - - - - {text} - - - - """ - - synthesizer.speak_ssml_async(ssml_string).get() - - -if __name__ == "__main__": - azure_tts = AzureTTS("azure_tts") - azure_tts.synthesize_speech( - "zh-CN", - "zh-CN-YunxiNeural", - "Boy", - "Hello, I am Kaka", - "output.wav") diff --git a/metagpt/actions/clone_function.py b/metagpt/actions/clone_function.py deleted file mode 100644 index cf7d22f04..000000000 --- a/metagpt/actions/clone_function.py +++ /dev/null @@ -1,65 +0,0 @@ -from pathlib import Path -import traceback - -from metagpt.actions.write_code import WriteCode -from metagpt.logs import logger -from metagpt.schema import Message -from metagpt.utils.highlight import highlight - -CLONE_PROMPT = """ -*context* -Please convert the function code ```{source_code}``` into the the function format: ```{template_func}```. -*Please Write code based on the following list and context* -1. Write code start with ```, and end with ```. -2. Please implement it in one function if possible, except for import statements. for exmaple: -```python -import pandas as pd -def run(*args) -> pd.DataFrame: - ... -``` -3. Do not use public member functions that do not exist in your design. -4. The output function name, input parameters and return value must be the same as ```{template_func}```. -5. Make sure the results before and after the code conversion are required to be exactly the same. -6. Don't repeat my context in your replies. -7. Return full results, for example, if the return value has df.head(), please return df. -8. If you must use a third-party package, use the most popular ones, for example: pandas, numpy, ta, ... -""" - - -class CloneFunction(WriteCode): - def __init__(self, name="CloneFunction", context: list[Message] = None, llm=None): - super().__init__(name, context, llm) - - def _save(self, code_path, code): - if isinstance(code_path, str): - code_path = Path(code_path) - code_path.parent.mkdir(parents=True, exist_ok=True) - code_path.write_text(code) - logger.info(f"Saving Code to {code_path}") - - async def run(self, template_func: str, source_code: str) -> str: - """将source_code转换成template_func一样的入参和返回类型""" - prompt = CLONE_PROMPT.format(source_code=source_code, template_func=template_func) - logger.info(f"query for CloneFunction: \n {prompt}") - code = await self.write_code(prompt) - logger.info(f'CloneFunction code is \n {highlight(code)}') - return code - - -def run_function_code(func_code: str, func_name: str, *args, **kwargs): - """Run function code from string code.""" - try: - locals_ = {} - exec(func_code, locals_) - func = locals_[func_name] - return func(*args, **kwargs), "" - except Exception: - return "", traceback.format_exc() - - -def run_function_script(code_script_path: str, func_name: str, *args, **kwargs): - """Run function code from script.""" - if isinstance(code_script_path, str): - code_path = Path(code_script_path) - code = code_path.read_text(encoding='utf-8') - return run_function_code(code, func_name, *args, **kwargs) diff --git a/metagpt/actions/design_api_review.py b/metagpt/actions/design_api_review.py deleted file mode 100644 index 9bb822a62..000000000 --- a/metagpt/actions/design_api_review.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/5/11 19:31 -@Author : alexanderwu -@File : design_api_review.py -""" -from metagpt.actions.action import Action - - -class DesignReview(Action): - def __init__(self, name, context=None, llm=None): - super().__init__(name, context, llm) - - async def run(self, prd, api_design): - prompt = f"Here is the Product Requirement Document (PRD):\n\n{prd}\n\nHere is the list of APIs designed " \ - f"based on this PRD:\n\n{api_design}\n\nPlease review whether this API design meets the requirements" \ - f" of the PRD, and whether it complies with good design practices." - - api_review = await self._aask(prompt) - return api_review - \ No newline at end of file diff --git a/metagpt/actions/design_filenames.py b/metagpt/actions/design_filenames.py deleted file mode 100644 index 29400e950..000000000 --- a/metagpt/actions/design_filenames.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/5/19 11:50 -@Author : alexanderwu -@File : design_filenames.py -""" -from metagpt.actions import Action -from metagpt.logs import logger - -PROMPT = """You are an AI developer, trying to write a program that generates code for users based on their intentions. -When given their intentions, provide a complete and exhaustive list of file paths needed to write the program for the user. -Only list the file paths you will write and return them as a Python string list. -Do not add any other explanations, just return a Python string list.""" - - -class DesignFilenames(Action): - def __init__(self, name, context=None, llm=None): - super().__init__(name, context, llm) - self.desc = "Based on the PRD, consider system design, and carry out the basic design of the corresponding " \ - "APIs, data structures, and database tables. Please give your design, feedback clearly and in detail." - - async def run(self, prd): - prompt = f"The following is the Product Requirement Document (PRD):\n\n{prd}\n\n{PROMPT}" - design_filenames = await self._aask(prompt) - logger.debug(prompt) - logger.debug(design_filenames) - return design_filenames - \ No newline at end of file diff --git a/metagpt/actions/detail_mining.py b/metagpt/actions/detail_mining.py deleted file mode 100644 index e29d6911b..000000000 --- a/metagpt/actions/detail_mining.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/9/12 17:45 -@Author : fisherdeng -@File : detail_mining.py -""" -from metagpt.actions import Action, ActionOutput -from metagpt.logs import logger - -PROMPT_TEMPLATE = """ -##TOPIC -{topic} - -##RECORD -{record} - -##Format example -{format_example} ------ - -Task: Refer to the "##TOPIC" (discussion objectives) and "##RECORD" (discussion records) to further inquire about the details that interest you, within a word limit of 150 words. -Special Note 1: Your intention is solely to ask questions without endorsing or negating any individual's viewpoints. -Special Note 2: This output should only include the topic "##OUTPUT". Do not add, remove, or modify the topic. Begin the output with '##OUTPUT', followed by an immediate line break, and then proceed to provide the content in the specified format as outlined in the "##Format example" section. -Special Note 3: The output should be in the same language as the input. -""" -FORMAT_EXAMPLE = """ - -## - -##OUTPUT -...(Please provide the specific details you would like to inquire about here.) - -## - -## -""" -OUTPUT_MAPPING = { - "OUTPUT": (str, ...), -} - - -class DetailMining(Action): - """This class allows LLM to further mine noteworthy details based on specific "##TOPIC"(discussion topic) and "##RECORD" (discussion records), thereby deepening the discussion. - """ - def __init__(self, name="", context=None, llm=None): - super().__init__(name, context, llm) - - async def run(self, topic, record) -> ActionOutput: - prompt = PROMPT_TEMPLATE.format(topic=topic, record=record, format_example=FORMAT_EXAMPLE) - rsp = await self._aask_v1(prompt, "detail_mining", OUTPUT_MAPPING) - return rsp diff --git a/metagpt/actions/execute_task.py b/metagpt/actions/execute_task.py deleted file mode 100644 index afdeda323..000000000 --- a/metagpt/actions/execute_task.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/9/13 12:26 -@Author : femto Zheng -@File : execute_task.py -""" -from metagpt.actions import Action -from metagpt.schema import Message - - -class ExecuteTask(Action): - def __init__(self, name="ExecuteTask", context: list[Message] = None, llm=None): - super().__init__(name, context, llm) - - def run(self, *args, **kwargs): - pass diff --git a/metagpt/actions/prepare_interview.py b/metagpt/actions/prepare_interview.py deleted file mode 100644 index 5db3a9f37..000000000 --- a/metagpt/actions/prepare_interview.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/9/19 15:02 -@Author : DevXiaolan -@File : prepare_interview.py -""" -from metagpt.actions import Action - -PROMPT_TEMPLATE = """ -# Context -{context} - -## Format example ---- -Q1: question 1 here -References: - - point 1 - - point 2 - -Q2: question 2 here... ---- - ------ -Role: You are an interviewer of our company who is well-knonwn in frontend or backend develop; -Requirement: Provide a list of questions for the interviewer to ask the interviewee, by reading the resume of the interviewee in the context. -Attention: Provide as markdown block as the format above, at least 10 questions. -""" - -# prepare for a interview - - -class PrepareInterview(Action): - def __init__(self, name, context=None, llm=None): - super().__init__(name, context, llm) - - async def run(self, context): - prompt = PROMPT_TEMPLATE.format(context=context) - question_list = await self._aask_v1(prompt) - return question_list - diff --git a/metagpt/actions/research.py b/metagpt/actions/research.py deleted file mode 100644 index 49a981e86..000000000 --- a/metagpt/actions/research.py +++ /dev/null @@ -1,278 +0,0 @@ -#!/usr/bin/env python - -from __future__ import annotations - -import asyncio -import json -from typing import Callable - -from pydantic import parse_obj_as - -from metagpt.actions import Action -from metagpt.config import CONFIG -from metagpt.logs import logger -from metagpt.tools.search_engine import SearchEngine -from metagpt.tools.web_browser_engine import WebBrowserEngine, WebBrowserEngineType -from metagpt.utils.common import OutputParser -from metagpt.utils.text import generate_prompt_chunk, reduce_message_length - -LANG_PROMPT = "Please respond in {language}." - -RESEARCH_BASE_SYSTEM = """You are an AI critical thinker research assistant. Your sole purpose is to write well \ -written, critically acclaimed, objective and structured reports on the given text.""" - -RESEARCH_TOPIC_SYSTEM = "You are an AI researcher assistant, and your research topic is:\n#TOPIC#\n{topic}" - -SEARCH_TOPIC_PROMPT = """Please provide up to 2 necessary keywords related to your research topic for Google search. \ -Your response must be in JSON format, for example: ["keyword1", "keyword2"].""" - -SUMMARIZE_SEARCH_PROMPT = """### Requirements -1. The keywords related to your research topic and the search results are shown in the "Search Result Information" section. -2. Provide up to {decomposition_nums} queries related to your research topic base on the search results. -3. Please respond in the following JSON format: ["query1", "query2", "query3", ...]. - -### Search Result Information -{search_results} -""" - -COLLECT_AND_RANKURLS_PROMPT = """### Topic -{topic} -### Query -{query} - -### The online search results -{results} - -### Requirements -Please remove irrelevant search results that are not related to the query or topic. Then, sort the remaining search results \ -based on the link credibility. If two results have equal credibility, prioritize them based on the relevance. Provide the -ranked results' indices in JSON format, like [0, 1, 3, 4, ...], without including other words. -""" - -WEB_BROWSE_AND_SUMMARIZE_PROMPT = '''### Requirements -1. Utilize the text in the "Reference Information" section to respond to the question "{query}". -2. If the question cannot be directly answered using the text, but the text is related to the research topic, please provide \ -a comprehensive summary of the text. -3. If the text is entirely unrelated to the research topic, please reply with a simple text "Not relevant." -4. Include all relevant factual information, numbers, statistics, etc., if available. - -### Reference Information -{content} -''' - - -CONDUCT_RESEARCH_PROMPT = '''### Reference Information -{content} - -### Requirements -Please provide a detailed research report in response to the following topic: "{topic}", using the information provided \ -above. The report must meet the following requirements: - -- Focus on directly addressing the chosen topic. -- Ensure a well-structured and in-depth presentation, incorporating relevant facts and figures where available. -- Present data and findings in an intuitive manner, utilizing feature comparative tables, if applicable. -- The report should have a minimum word count of 2,000 and be formatted with Markdown syntax following APA style guidelines. -- Include all source URLs in APA format at the end of the report. -''' - - -class CollectLinks(Action): - """Action class to collect links from a search engine.""" - def __init__( - self, - name: str = "", - *args, - rank_func: Callable[[list[str]], None] | None = None, - **kwargs, - ): - super().__init__(name, *args, **kwargs) - self.desc = "Collect links from a search engine." - self.search_engine = SearchEngine() - self.rank_func = rank_func - - async def run( - self, - topic: str, - decomposition_nums: int = 4, - url_per_query: int = 4, - system_text: str | None = None, - ) -> dict[str, list[str]]: - """Run the action to collect links. - - Args: - topic: The research topic. - decomposition_nums: The number of search questions to generate. - url_per_query: The number of URLs to collect per search question. - system_text: The system text. - - Returns: - A dictionary containing the search questions as keys and the collected URLs as values. - """ - system_text = system_text if system_text else RESEARCH_TOPIC_SYSTEM.format(topic=topic) - keywords = await self._aask(SEARCH_TOPIC_PROMPT, [system_text]) - try: - keywords = OutputParser.extract_struct(keywords, list) - keywords = parse_obj_as(list[str], keywords) - except Exception as e: - logger.exception(f"fail to get keywords related to the research topic \"{topic}\" for {e}") - keywords = [topic] - results = await asyncio.gather(*(self.search_engine.run(i, as_string=False) for i in keywords)) - - def gen_msg(): - while True: - search_results = "\n".join(f"#### Keyword: {i}\n Search Result: {j}\n" for (i, j) in zip(keywords, results)) - prompt = SUMMARIZE_SEARCH_PROMPT.format(decomposition_nums=decomposition_nums, search_results=search_results) - yield prompt - remove = max(results, key=len) - remove.pop() - if len(remove) == 0: - break - prompt = reduce_message_length(gen_msg(), self.llm.model, system_text, CONFIG.max_tokens_rsp) - logger.debug(prompt) - queries = await self._aask(prompt, [system_text]) - try: - queries = OutputParser.extract_struct(queries, list) - queries = parse_obj_as(list[str], queries) - except Exception as e: - logger.exception(f"fail to break down the research question due to {e}") - queries = keywords - ret = {} - for query in queries: - ret[query] = await self._search_and_rank_urls(topic, query, url_per_query) - return ret - - async def _search_and_rank_urls(self, topic: str, query: str, num_results: int = 4) -> list[str]: - """Search and rank URLs based on a query. - - Args: - topic: The research topic. - query: The search query. - num_results: The number of URLs to collect. - - Returns: - A list of ranked URLs. - """ - max_results = max(num_results * 2, 6) - results = await self.search_engine.run(query, max_results=max_results, as_string=False) - _results = "\n".join(f"{i}: {j}" for i, j in zip(range(max_results), results)) - prompt = COLLECT_AND_RANKURLS_PROMPT.format(topic=topic, query=query, results=_results) - logger.debug(prompt) - indices = await self._aask(prompt) - try: - indices = OutputParser.extract_struct(indices, list) - assert all(isinstance(i, int) for i in indices) - except Exception as e: - logger.exception(f"fail to rank results for {e}") - indices = list(range(max_results)) - results = [results[i] for i in indices] - if self.rank_func: - results = self.rank_func(results) - return [i["link"] for i in results[:num_results]] - - -class WebBrowseAndSummarize(Action): - """Action class to explore the web and provide summaries of articles and webpages.""" - def __init__( - self, - *args, - browse_func: Callable[[list[str]], None] | None = None, - **kwargs, - ): - super().__init__(*args, **kwargs) - if CONFIG.model_for_researcher_summary: - self.llm.model = CONFIG.model_for_researcher_summary - self.web_browser_engine = WebBrowserEngine( - engine=WebBrowserEngineType.CUSTOM if browse_func else None, - run_func=browse_func, - ) - self.desc = "Explore the web and provide summaries of articles and webpages." - - async def run( - self, - url: str, - *urls: str, - query: str, - system_text: str = RESEARCH_BASE_SYSTEM, - ) -> dict[str, str]: - """Run the action to browse the web and provide summaries. - - Args: - url: The main URL to browse. - urls: Additional URLs to browse. - query: The research question. - system_text: The system text. - - Returns: - A dictionary containing the URLs as keys and their summaries as values. - """ - contents = await self.web_browser_engine.run(url, *urls) - if not urls: - contents = [contents] - - summaries = {} - prompt_template = WEB_BROWSE_AND_SUMMARIZE_PROMPT.format(query=query, content="{}") - for u, content in zip([url, *urls], contents): - content = content.inner_text - chunk_summaries = [] - for prompt in generate_prompt_chunk(content, prompt_template, self.llm.model, system_text, CONFIG.max_tokens_rsp): - logger.debug(prompt) - summary = await self._aask(prompt, [system_text]) - if summary == "Not relevant.": - continue - chunk_summaries.append(summary) - - if not chunk_summaries: - summaries[u] = None - continue - - if len(chunk_summaries) == 1: - summaries[u] = chunk_summaries[0] - continue - - content = "\n".join(chunk_summaries) - prompt = WEB_BROWSE_AND_SUMMARIZE_PROMPT.format(query=query, content=content) - summary = await self._aask(prompt, [system_text]) - summaries[u] = summary - return summaries - - -class ConductResearch(Action): - """Action class to conduct research and generate a research report.""" - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if CONFIG.model_for_researcher_report: - self.llm.model = CONFIG.model_for_researcher_report - - async def run( - self, - topic: str, - content: str, - system_text: str = RESEARCH_BASE_SYSTEM, - ) -> str: - """Run the action to conduct research and generate a research report. - - Args: - topic: The research topic. - content: The content for research. - system_text: The system text. - - Returns: - The generated research report. - """ - prompt = CONDUCT_RESEARCH_PROMPT.format(topic=topic, content=content) - logger.debug(prompt) - self.llm.auto_max_tokens = True - return await self._aask(prompt, [system_text]) - - -def get_research_system_text(topic: str, language: str): - """Get the system text for conducting research. - - Args: - topic: The research topic. - language: The language for the system text. - - Returns: - The system text for conducting research. - """ - return " ".join((RESEARCH_TOPIC_SYSTEM.format(topic=topic), LANG_PROMPT.format(language=language))) diff --git a/metagpt/actions/write_docstring.py b/metagpt/actions/write_docstring.py deleted file mode 100644 index 5c7815793..000000000 --- a/metagpt/actions/write_docstring.py +++ /dev/null @@ -1,214 +0,0 @@ -"""Code Docstring Generator. - -This script provides a tool to automatically generate docstrings for Python code. It uses the specified style to create -docstrings for the given code and system text. - -Usage: - python3 -m metagpt.actions.write_docstring [--overwrite] [--style=] - -Arguments: - filename The path to the Python file for which you want to generate docstrings. - -Options: - --overwrite If specified, overwrite the original file with the code containing docstrings. - --style= Specify the style of the generated docstrings. - Valid values: 'google', 'numpy', or 'sphinx'. - Default: 'google' - -Example: - python3 -m metagpt.actions.write_docstring startup.py --overwrite False --style=numpy - -This script uses the 'fire' library to create a command-line interface. It generates docstrings for the given Python code using -the specified docstring style and adds them to the code. -""" -import ast -from typing import Literal - -from metagpt.actions.action import Action -from metagpt.utils.common import OutputParser -from metagpt.utils.pycst import merge_docstring - -PYTHON_DOCSTRING_SYSTEM = '''### Requirements -1. Add docstrings to the given code following the {style} style. -2. Replace the function body with an Ellipsis object(...) to reduce output. -3. If the types are already annotated, there is no need to include them in the docstring. -4. Extract only class, function or the docstrings for the module parts from the given Python code, avoiding any other text. - -### Input Example -```python -def function_with_pep484_type_annotations(param1: int) -> bool: - return isinstance(param1, int) - -class ExampleError(Exception): - def __init__(self, msg: str): - self.msg = msg -``` - -### Output Example -```python -{example} -``` -''' - -# https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html - -PYTHON_DOCSTRING_EXAMPLE_GOOGLE = ''' -def function_with_pep484_type_annotations(param1: int) -> bool: - """Example function with PEP 484 type annotations. - - Extended description of function. - - Args: - param1: The first parameter. - - Returns: - The return value. True for success, False otherwise. - """ - ... - -class ExampleError(Exception): - """Exceptions are documented in the same way as classes. - - The __init__ method was documented in the class level docstring. - - Args: - msg: Human readable string describing the exception. - - Attributes: - msg: Human readable string describing the exception. - """ - ... -''' - -PYTHON_DOCSTRING_EXAMPLE_NUMPY = ''' -def function_with_pep484_type_annotations(param1: int) -> bool: - """ - Example function with PEP 484 type annotations. - - Extended description of function. - - Parameters - ---------- - param1 - The first parameter. - - Returns - ------- - bool - The return value. True for success, False otherwise. - """ - ... - -class ExampleError(Exception): - """ - Exceptions are documented in the same way as classes. - - The __init__ method was documented in the class level docstring. - - Parameters - ---------- - msg - Human readable string describing the exception. - - Attributes - ---------- - msg - Human readable string describing the exception. - """ - ... -''' - -PYTHON_DOCSTRING_EXAMPLE_SPHINX = ''' -def function_with_pep484_type_annotations(param1: int) -> bool: - """Example function with PEP 484 type annotations. - - Extended description of function. - - :param param1: The first parameter. - :type param1: int - - :return: The return value. True for success, False otherwise. - :rtype: bool - """ - ... - -class ExampleError(Exception): - """Exceptions are documented in the same way as classes. - - The __init__ method was documented in the class level docstring. - - :param msg: Human-readable string describing the exception. - :type msg: str - """ - ... -''' - -_python_docstring_style = { - "google": PYTHON_DOCSTRING_EXAMPLE_GOOGLE.strip(), - "numpy": PYTHON_DOCSTRING_EXAMPLE_NUMPY.strip(), - "sphinx": PYTHON_DOCSTRING_EXAMPLE_SPHINX.strip(), -} - - -class WriteDocstring(Action): - """This class is used to write docstrings for code. - - Attributes: - desc: A string describing the action. - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.desc = "Write docstring for code." - - async def run( - self, code: str, - system_text: str = PYTHON_DOCSTRING_SYSTEM, - style: Literal["google", "numpy", "sphinx"] = "google", - ) -> str: - """Writes docstrings for the given code and system text in the specified style. - - Args: - code: A string of Python code. - system_text: A string of system text. - style: A string specifying the style of the docstring. Can be 'google', 'numpy', or 'sphinx'. - - Returns: - The Python code with docstrings added. - """ - system_text = system_text.format(style=style, example=_python_docstring_style[style]) - simplified_code = _simplify_python_code(code) - documented_code = await self._aask(f"```python\n{simplified_code}\n```", [system_text]) - documented_code = OutputParser.parse_python_code(documented_code) - return merge_docstring(code, documented_code) - - -def _simplify_python_code(code: str) -> None: - """Simplifies the given Python code by removing expressions and the last if statement. - - Args: - code: A string of Python code. - - Returns: - The simplified Python code. - """ - code_tree = ast.parse(code) - code_tree.body = [i for i in code_tree.body if not isinstance(i, ast.Expr)] - if isinstance(code_tree.body[-1], ast.If): - code_tree.body.pop() - return ast.unparse(code_tree) - - -if __name__ == "__main__": - import fire - - async def run(filename: str, overwrite: bool = False, style: Literal["google", "numpy", "sphinx"] = "google"): - with open(filename) as f: - code = f.read() - code = await WriteDocstring().run(code, style=style) - if overwrite: - with open(filename, "w") as f: - f.write(code) - return code - - fire.Fire(run) diff --git a/metagpt/actions/write_prd_review.py b/metagpt/actions/write_prd_review.py deleted file mode 100644 index 5c922d3bc..000000000 --- a/metagpt/actions/write_prd_review.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/5/11 17:45 -@Author : alexanderwu -@File : write_prd_review.py -""" -from metagpt.actions.action import Action - - -class WritePRDReview(Action): - def __init__(self, name, context=None, llm=None): - super().__init__(name, context, llm) - self.prd = None - self.desc = "Based on the PRD, conduct a PRD Review, providing clear and detailed feedback" - self.prd_review_prompt_template = """ - Given the following Product Requirement Document (PRD): - {prd} - - As a project manager, please review it and provide your feedback and suggestions. - """ - - async def run(self, prd): - self.prd = prd - prompt = self.prd_review_prompt_template.format(prd=self.prd) - review = await self._aask(prompt) - return review - \ No newline at end of file diff --git a/metagpt/actions/write_tutorial.py b/metagpt/actions/write_tutorial.py deleted file mode 100644 index 23e3560e8..000000000 --- a/metagpt/actions/write_tutorial.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/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. -""" - -from typing import Dict - -from metagpt.actions import Action -from metagpt.prompts.tutorial_assistant import DIRECTORY_PROMPT, CONTENT_PROMPT -from metagpt.utils.common import OutputParser - - -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, including {"title": "xxx", "directory": [{"dir 1": ["sub dir 1", "sub dir 2"]}]}. - """ - prompt = DIRECTORY_PROMPT.format(topic=topic, language=self.language) - resp = await self._aask(prompt=prompt) - return OutputParser.extract_struct(resp, dict) - - -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) -