Merge branch 'main' into feature/json_write_prd

This commit is contained in:
femto 2023-09-21 12:00:10 +08:00 committed by GitHub
commit 7b34c433cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
69 changed files with 1911 additions and 56 deletions

View file

@ -0,0 +1,65 @@
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)

View file

@ -0,0 +1,17 @@
#!/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

View file

@ -0,0 +1,41 @@
#!/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

View file

@ -13,6 +13,7 @@ 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}."
@ -110,7 +111,7 @@ class CollectLinks(Action):
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 = json.loads(keywords)
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}")
@ -130,7 +131,7 @@ class CollectLinks(Action):
logger.debug(prompt)
queries = await self._aask(prompt, [system_text])
try:
queries = json.loads(queries)
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}")
@ -158,7 +159,7 @@ class CollectLinks(Action):
logger.debug(prompt)
indices = await self._aask(prompt)
try:
indices = json.loads(indices)
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}")

View file

@ -6,12 +6,12 @@
@File : tutorial_assistant.py
@Describe : Actions of the tutorial assistant, including writing directories and document content.
"""
import json
from typing import Dict
from metagpt.actions import Action
from metagpt.logs import logger
from metagpt.prompts.tutorial_assistant import DIRECTORY_PROMPT, CONTENT_PROMPT
from metagpt.utils.common import OutputParser
class WriteDirectory(Action):
@ -26,33 +26,6 @@ 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.
@ -64,7 +37,7 @@ class WriteDirectory(Action):
"""
prompt = DIRECTORY_PROMPT.format(topic=topic, language=self.language)
resp = await self._aask(prompt=prompt)
return await self._handle_resp(resp)
return OutputParser.extract_struct(resp, dict)
class WriteContent(Action):