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)
-